Skip to content

Commit

Permalink
server: Add Address method.
Browse files Browse the repository at this point in the history
Add logic for Address grpc request.
  • Loading branch information
JoeGruffins committed Dec 12, 2022
1 parent a333c1f commit 80a0e71
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 0 deletions.
32 changes: 32 additions & 0 deletions internal/rpc/rpcserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,38 @@ func (s *walletServer) NextAddress(ctx context.Context, req *pb.NextAddressReque
}, nil
}

func (s *walletServer) Address(ctx context.Context, req *pb.AddressRequest) (
*pb.AddressResponse, error) {
var branch uint32
switch req.Kind {
case pb.AddressRequest_BIP0044_EXTERNAL:
case pb.AddressRequest_BIP0044_INTERNAL:
branch = 1
default:
return nil, status.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
}
addr, err := s.wallet.AddressAtIdx(ctx, req.Account, branch, req.Index)
if err != nil {
return nil, translateError(err)
}
var pubKeyAddrString string
switch addr := addr.(type) {
case wallet.PubKeyHashAddress:
pubKey := addr.PubKey()
pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
pubKey, s.wallet.ChainParams())
if err != nil {
return nil, translateError(err)
}
pubKeyAddrString = pubKeyAddr.String()
}

return &pb.AddressResponse{
Address: addr.String(),
PublicKey: pubKeyAddrString,
}, nil
}

func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPrivateKeyRequest) (
*pb.ImportPrivateKeyResponse, error) {

Expand Down
38 changes: 38 additions & 0 deletions rpc/documentation/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ The service provides the following methods:
- [`UnlockAccount`](#UnlockAccount)
- [`LockAccount`](#LockAccount)
- [`GetTrackedVSPTickets`](#GetTrackedVSPTickets)
- [`Address`](#Address)

#### `Ping`

Expand Down Expand Up @@ -2612,6 +2613,43 @@ following the new vspd mode.

- `bytes fee_hash`: The hash of the fee transaction that pays to the VSP.

___

#### `Address`

The `Address` method queries the wallet for an address and public key from a
certain account, branch, and index.

**Request:** `AddressRequest`

- `uint32 account`: The account number.

- `Kind kind`: The branch.

**Nested enum:** `Kind`

- `BIP0044_EXTERNAL`: The request specifies to use the account's BIP0044
external key chain.

- `BIP0044_INTERNAL`: The request specifies to use the account's BIP0044
internal key chain.

- `uint32 index`: The address index.

**Response:** `AddressResponse`

- `string address`: The address's p2pkh.

- `string pulic_key`: The address's pkh if available.

**Expected errors:**

- `InvalidArgument`: Kind is not 0 or 1.

- `NotFound`: Unknown account.

- `Unknown`: Index is out of range.

## `SeedService`

The `SeedService` service provides RPC clients with the ability to generate
Expand Down
39 changes: 39 additions & 0 deletions rpc/grpc_example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,30 @@ func run() error {
fmt.Println(prototext.MarshalOptions{Multiline: true}.Format(validateAddrResp))
}

fmt.Println("Address...")
fmt.Println()

for _, path := range addrPaths {
addrRequest := &pb.AddressRequest{
Account: path.acct,
Kind: pb.AddressRequest_Kind(path.branch),
Index: path.idx,
}
fmt.Printf("Name: %s\n", path.name)
addrResp, err := wsClient.Address(context.Background(), addrRequest)
if path.wantErr {
if err != nil {
fmt.Println(err)
continue
}
return fmt.Errorf("Address: expected error for %v", path.name)
}
if err != nil {
return err
}
fmt.Println(prototext.MarshalOptions{Multiline: true}.Format(addrResp))
}

return nil
}

Expand Down Expand Up @@ -253,3 +277,18 @@ type nextAddr struct {
var nextAddrs = []nextAddr{{
name: "default external",
}}

var addrPaths = []struct {
name string
acct, branch, idx uint32
wantErr bool
}{{
name: "all zeros",
}, {
name: "internal branch",
branch: 1,
}, {
name: "bad branch",
branch: 2,
wantErr: true,
}}
113 changes: 113 additions & 0 deletions wallet/addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,119 @@ func (w *Wallet) nextAddress(ctx context.Context, op errors.Op, persist persistR
}
}

// AddressAtIndex returns the address at branch and childIdx. It does not persist
// the returned address in the database.
func (w *Wallet) AddressAtIdx(ctx context.Context, account, branch,
childIdx uint32) (stdaddr.Address, error) {
const op errors.Op = "wallet.AddressAtIdx"
defer w.addressBuffersMu.Unlock()
w.addressBuffersMu.Lock()
ad, ok := w.addressBuffers[account]
if !ok {
return nil, errors.E(op, errors.NotExist, errors.Errorf("account %d", account))
}

var alb *addressBuffer
switch branch {
case 0:
alb = &ad.albExternal
case 1:
alb = &ad.albInternal
default:
return nil, errors.E(op, errors.Invalid, "branch must be external (0) or internal (1)")
}

if childIdx >= hdkeychain.HardenedKeyStart {
return nil, errors.E(op, errors.Errorf("child out of range"))
}
child, err := alb.branchXpub.Child(childIdx)
if err != nil {
return nil, errors.E(op, err)
}
apkh, err := compat.HD2Address(child, w.chainParams)
if err != nil {
return nil, errors.E(op, err)
}
addr := &xpubAddress{
AddressPubKeyHashEcdsaSecp256k1V0: apkh,
xpub: ad.xpub,
account: account,
branch: branch,
child: childIdx,
}
log.Infof("Returning address (account=%v branch=%v child=%v)", account, branch, childIdx)
return addr, nil
}

func (w *Wallet) nextImportedXpubAddress(ctx context.Context, op errors.Op,
maybeDBTX walletdb.ReadWriteTx, accountName string, account uint32, branch uint32,
callOpts ...NextAddressCallOption) (addr stdaddr.Address, err error) {

dbtx := maybeDBTX
if dbtx == nil {
dbtx, err = w.db.BeginReadWriteTx()
if err != nil {
return nil, err
}
defer func() {
if err == nil {
err = dbtx.Commit()
} else {
dbtx.Rollback()
}
}()
}

ns := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
xpub, err := w.manager.AccountExtendedPubKey(dbtx, account)
if err != nil {
return nil, errors.E(op, err)
}
props, err := w.manager.AccountProperties(ns, account)
branchKey, err := xpub.Child(branch)
if err != nil {
return nil, errors.E(op, err)
}
var childKey *hdkeychain.ExtendedKey
var child uint32
switch branch {
case 0:
child = props.LastReturnedExternalIndex + 1
case 1:
child = props.LastReturnedInternalIndex + 1
default:
return nil, errors.E(op, "branch is required to be 0 or 1")
}
for {
childKey, err = branchKey.Child(child)
if err == hdkeychain.ErrInvalidChild {
child++
continue
}
if err != nil {
return nil, errors.E(op, err)
}
break
}
pkh := dcrutil.Hash160(childKey.SerializedPubKey())
apkh, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkh, w.chainParams)
if err != nil {
return nil, errors.E(op, err)
}
addr = &xpubAddress{
AddressPubKeyHashEcdsaSecp256k1V0: apkh,
xpub: xpub,
account: account,
branch: branch,
child: child,
}
err = w.manager.MarkReturnedChildIndex(dbtx, account, branch, child)
if err != nil {
return nil, errors.E(op, err)
}
return addr, nil
}

func minUint32(a, b uint32) uint32 {
if a < b {
return a
Expand Down

0 comments on commit 80a0e71

Please sign in to comment.