Skip to content
This repository has been archived by the owner on Mar 2, 2023. It is now read-only.

Commit

Permalink
Merge pull request #22 from square/alok/subcommands
Browse files Browse the repository at this point in the history
sub-commands
  • Loading branch information
mbyczkowski committed Oct 17, 2018
2 parents 2b1e6a1 + 2a36176 commit 9292e37
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 233 deletions.
203 changes: 87 additions & 116 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,133 +4,104 @@ Beancounter
[![license](http://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/square/beancounter/master/LICENSE) [![travis](https://img.shields.io/travis/com/square/beancounter.svg?maxAge=3600&logo=travis&label=travis)](https://travis-ci.com/square/beancounter)
[![coverage](https://coveralls.io/repos/github/square/beancounter/badge.svg?branch=master)](https://coveralls.io/r/square/beancounter) [![report](https://goreportcard.com/badge/github.com/square/beancounter)](https://goreportcard.com/report/github.com/square/beancounter)

Beancounter is a command line utility to audit the balance of Hierarchical Deterministic (HD) wallets. The tool is
designed to scale and work for wallets with a large number of addresses or a large number of transactions.
The tool supports various types of wallets, including multisig + segwit.
Beancounter is a command line utility to audit the balance of Hierarchical Deterministic (HD) wallets at a given point in time (or block height). The tool is designed to scale and work for wallets with a large number of addresses or a large number of transactions.

We picked Go as the programming language, because Go makes building the tool for different platforms trivial. We also
appreciate the ability to distribute static binaries which don't have external dependencies.
The tool supports various types of wallets ranging from simple watch wallets to more complicated multisig + segwit.

We built Beancounter because we wanted a tool which can compute the balance of a wallet at any
given block height.
Beancounter currently supports two types of backends to query the blockchain:
1. Electrum public servers. When using these servers, Beancounter behaves in a similar fashion to an Electrum client wallet. The servers are queried for transaction history for specific addresses. Using Electrum servers is easiest but requires trusting public servers to return accurate information. There is also potential privacy exposure.

The process to determine a deterministic wallet's balance involves the following:
1. Use the blockchain to build an address index. Bitcoin Core doesn't offer this, but Btcd,
Electrum, and a few other tools do.
2. Derive external (receive) and internal (change) addresses.
3. Fetch the transaction history for a single address. Prune the history at the desired block height.
4. Compute the credit and debit for each transaction. Repeat until we find a large number of
unused addresses.

This tool currently supports two backends: Electrum and Btcd. Using Electrum is easier (connects to
public nodes) but has some disadvantages (need to trust a third party, potential privacy leak, etc.).
Using Btcd requires running a private node and the initial sync can take a long time.
2. Private Btcd node. Btcd is a Bitcoin full node which implements transaction indexes. Setting up a Btcd node can take some time (the initial sync takes ~7 days) and requires maintaining the node up-to-date. The benefit is however a higher level of guarantee that the transaction history is accurate.

![logo](https://raw.githubusercontent.com/square/beancounter/master/coffee.jpg)

Getting Started (using Electrum)
================================
Getting Started
===============

```
brew install dep
Installing
----------
If missing, install [https://github.com/golang/dep](dep)

dep ensure
go run main.go -m 2 -n 4 --account 1234
```
$ git clone https://github.com/square/beancounter/
$ cd beancounter
$ dep ensure
$ go build
```

Deriving the child pubkey
-------------------------
Let's imagine we want to track the balance of `tpubD8L6UhrL8ML9...` and the derivation being used is `m/1'/1234/change/index`.

Sample Output
=============
We need to manually perform the `1234` derivation:

```
$ go run main.go -m 1 -n 1 --account 1234 --network testnet --lookahead 10
Enter tpub #1 out of #1:
$ ./beancounter keytree 1234
Enter pubkey #1 out of #1:
tpubD8L6UhrL8ML9Ao47k4pmdvUoiA6QUJVzrJ9BXLgU9idRKnvdRFGgjcxmVxojWGvCcjMi6QWCp8uMpCwWdSFRDNJ7utizxLy27sVWXQT4Jz7
Checking balance for m/1'/1234/0/0 mzoeuyGqMudyvKbkNx5dtNBNN59oKEAsPn ... 100000000 100000000
Checking balance for m/1'/1234/0/1 mz37noAanMGMW1BMGvyhNnY1fqfTg726Ka ... 15674439 115674439
Checking balance for m/1'/1234/0/2 mzPCbXLqiHLNhVj6reah8VWXVT8X69Ssuf ... 30000000 145674439
Checking balance for m/1'/1234/0/3 my1FMCXyo84tC1LkFXX8LctbtpycUmUnPx ... 0 145674439
Checking balance for m/1'/1234/0/4 n1GohMiYdx8Q8PSBynH34vdgZXH1tid7cW ... 0 145674439
Checking balance for m/1'/1234/0/5 mzMF12VsXCL23ov7RwSbZdVnLzJ1MnxEzT ... ∅
Checking balance for m/1'/1234/0/6 n1EstV7h4Jyx1XKLY7fdx6ufFRMxBVdieN ... 100000000 245674439
Checking balance for m/1'/1234/0/7 mi2udMvJHeeJJNp5wWKToa86L2cJUKzrby ... 3000000 248674439
Checking balance for m/1'/1234/0/8 mwiGFquFDNhCE7z66xU1GPZxr3Vr838ayJ ... ∅
Checking balance for m/1'/1234/0/9 mzQinQemMnnfKtJmqvnwC4PcvmJrKxnSMi ... ∅
Checking balance for m/1'/1234/0/10 mofjpvyi5Hn5veLnU6xvWiFvuuN9ABMiez ... ∅
Checking balance for m/1'/1234/0/11 mosHkEQ17EzDbBotFeZdUdw9JymCB6ygdD ... ∅
Checking balance for m/1'/1234/0/12 n2fo4VZADUoboCbGhyw3Rrp5s9HtdeiS1L ... ∅
Checking balance for m/1'/1234/0/13 mybf8GEdfxqBRhzwPMfxu1LNnCWoR2oSro ... ∅
Checking balance for m/1'/1234/0/14 mmfFY4UJHJBSjz3ve7tvaSrxNwCReVtifC ... 0 248674439
Checking balance for m/1'/1234/0/15 myEyz9WPaZmXqnhtN86xM8S66wVysBRNtN ... 0 248674439
Checking balance for m/1'/1234/0/16 mp1eTBsFMfE1akuhEad4z2GaYLXRxnQ5wR ... ∅
Checking balance for m/1'/1234/0/17 mqyEEeArE2VdWcvkRCE14kKPxngJn5vrw3 ... ∅
Checking balance for m/1'/1234/0/18 mqRHRhMwGv1eARSicG4Jr2PGjnNomTJvU1 ... ∅
Checking balance for m/1'/1234/0/19 mskQduVL7QBZCEWJUit8u9N8Cj4CFouPWX ... 1000000 249674439
Checking balance for m/1'/1234/0/20 mpMrwsAcYpNV5DsDq3HBv4F45TzZwvFCNo ... ∅
Checking balance for m/1'/1234/0/21 n3dXhVju9R2kJPjhnZME2E1yiQm1Scq6yn ... ∅
Checking balance for m/1'/1234/0/22 ms2f9UsMBE1n71kDZrH2yiUTSCoaS3C1sX ... ∅
Checking balance for m/1'/1234/0/23 moo5ASJxjmYomHXFRsqV9DFdbd9j5Knk2w ... ∅
Checking balance for m/1'/1234/0/24 n2AYb9qVFmBtyBQSUonFMcaqo1pXDwhHKC ... ∅
Checking balance for m/1'/1234/0/25 n4FWvGyuimxGjQJd7ZWht56vF3nudxYVeo ... ∅
Checking balance for m/1'/1234/0/26 mmNXWy7aXQoiKfqyYpX2yfDvqUKjMEp9SL ... ∅
Checking balance for m/1'/1234/0/27 n4rT8fSspf7WRSE46drbdEPjyYvncbrdgz ... ∅
Checking balance for m/1'/1234/0/28 mh972ndpUc46sa81vqDNz31U9QvdRpYgUB ... ∅
Checking balance for m/1'/1234/1/0 moHN13u4RoMxujdaPxvuaTaawgWZ3LaGyo ... 246600 249921039
Checking balance for m/1'/1234/1/1 n2aNi43rgX8YD5NMJK55dgHp7n7rdGzbYj ... 6504400 256425439
Checking balance for m/1'/1234/1/2 mv5dfNyTKMwED5g26LMZJYwN5QckEXrqe2 ... 300000 256725439
Checking balance for m/1'/1234/1/3 mth6eDXa7Yx6Bccci6j2PfgWgGVPZYw8qo ... ∅
Checking balance for m/1'/1234/1/4 mhx1M6yV58z9LAyEKemU6dcn5vNSdHU6rN ... ∅
Checking balance for m/1'/1234/1/5 mp9MMATLEzuJgmCVodLeN385zcB7J4crRR ... ∅
Checking balance for m/1'/1234/1/6 n2eB9gA1ywoUh1tszRSwBHTW4kunQqCPJm ... ∅
Checking balance for m/1'/1234/1/7 n4Yt3Wng8waeykRC8Tgr1JQ6Gz5SQFZaYD ... ∅
Checking balance for m/1'/1234/1/8 mnJBZg297ETSQZrJKGHDLLfuQwTMmK5U9h ... ∅
Checking balance for m/1'/1234/1/9 mvaqsto6jVBUTX7Fxgzx6GW3un94xuGW3D ... ∅
Checking balance for m/1'/1234/1/10 mrqAS4K81RftZ1n6TJPDMMJUzThNZh1SVV ... ∅
Checking balance for m/1'/1234/1/11 muxFaecKfqKt98G4VNUyqEVwVvqoPAtspj ... ∅
+----------------+------------------------------------+------------------------------------------------------------------+
| PATH | ADDRESS | TRANSACTION HASH |
+----------------+------------------------------------+------------------------------------------------------------------+
| m/1'/1234/0/0 | mzoeuyGqMudyvKbkNx5dtNBNN59oKEAsPn | ac3b83a9f90f73c7cac1e07b017d5c78ce6c79e74a0d72a6c80e84fb0adeb6ba |
| m/1'/1234/0/1 | mz37noAanMGMW1BMGvyhNnY1fqfTg726Ka | 820198df9af70251fdfcc3cae3f2995f09093a1677e6ac7800f6417d6679e929 |
| m/1'/1234/0/2 | mzPCbXLqiHLNhVj6reah8VWXVT8X69Ssuf | 60014a64f5c808ce7af726ae28e60ad986dff519a5d8014528df39976b10d705 |
| m/1'/1234/0/3 | my1FMCXyo84tC1LkFXX8LctbtpycUmUnPx | 7d9abe7323077358acfb80ef2ec0374a37c07bc097609c48da486d6a9266581e |
| m/1'/1234/0/3 | my1FMCXyo84tC1LkFXX8LctbtpycUmUnPx | f2e6bc46d8bf08d19854bc8dfde1a3d4968fa8d09f2185ea6e2711ec0ce449ec |
| m/1'/1234/0/4 | n1GohMiYdx8Q8PSBynH34vdgZXH1tid7cW | 6b344d0823c3f3ff1e084232674320e1409a12d2af50a6320063cd8cf5797e9a |
| m/1'/1234/0/4 | n1GohMiYdx8Q8PSBynH34vdgZXH1tid7cW | 75b465e8785b87fe11b625f5fd030e4f314c028c25b3ea84ae96bb1a3d369796 |
| m/1'/1234/0/6 | n1EstV7h4Jyx1XKLY7fdx6ufFRMxBVdieN | f49001a572942a506bd414c51f5a0e9cd349899d05c868b1de2fa6742a66d5aa |
| m/1'/1234/0/7 | mi2udMvJHeeJJNp5wWKToa86L2cJUKzrby | 5554c15d13002786a70a7151aad4eddce76633c60bc7f90e3dc70eb4f9c4b2b0 |
| m/1'/1234/0/7 | mi2udMvJHeeJJNp5wWKToa86L2cJUKzrby | bd09a74381ffad78c162976ec27fc9c1dceda3c2bfe367541a7140b8dd6e1f4c |
| m/1'/1234/0/14 | mmfFY4UJHJBSjz3ve7tvaSrxNwCReVtifC | c499c223f401175b14c8cb28893219dcd8b49563e59b9b4865dd4407c2de27c8 |
| m/1'/1234/0/14 | mmfFY4UJHJBSjz3ve7tvaSrxNwCReVtifC | df47e4bf599ecf27d8f3b34e85fcd7384d753823208d06171ff16899bc1709ce |
| m/1'/1234/0/15 | myEyz9WPaZmXqnhtN86xM8S66wVysBRNtN | acffdca982d1ce6fcd1f51dbb9dda686441058f93686008a2c446c42a94a66a7 |
| m/1'/1234/0/15 | myEyz9WPaZmXqnhtN86xM8S66wVysBRNtN | df47e4bf599ecf27d8f3b34e85fcd7384d753823208d06171ff16899bc1709ce |
| m/1'/1234/0/19 | mskQduVL7QBZCEWJUit8u9N8Cj4CFouPWX | 77bddf5ea1feef8eaf52375fdda8e08a5df6a581d0f34f5b3a849448f54c8d83 |
| m/1'/1234/1/0 | moHN13u4RoMxujdaPxvuaTaawgWZ3LaGyo | f2e6bc46d8bf08d19854bc8dfde1a3d4968fa8d09f2185ea6e2711ec0ce449ec |
| m/1'/1234/1/1 | n2aNi43rgX8YD5NMJK55dgHp7n7rdGzbYj | bd09a74381ffad78c162976ec27fc9c1dceda3c2bfe367541a7140b8dd6e1f4c |
| m/1'/1234/1/2 | mv5dfNyTKMwED5g26LMZJYwN5QckEXrqe2 | df47e4bf599ecf27d8f3b34e85fcd7384d753823208d06171ff16899bc1709ce |
+----------------+------------------------------------+------------------------------------------------------------------+
+----------------+------------------------------------+-----------+
| PATH | ADDRESS | BALANCE |
+----------------+------------------------------------+-----------+
| m/1'/1234/0/0 | mzoeuyGqMudyvKbkNx5dtNBNN59oKEAsPn | 100000000 |
| m/1'/1234/0/1 | mz37noAanMGMW1BMGvyhNnY1fqfTg726Ka | 15674439 |
| m/1'/1234/0/2 | mzPCbXLqiHLNhVj6reah8VWXVT8X69Ssuf | 30000000 |
| m/1'/1234/0/3 | my1FMCXyo84tC1LkFXX8LctbtpycUmUnPx | 0 |
| m/1'/1234/0/4 | n1GohMiYdx8Q8PSBynH34vdgZXH1tid7cW | 0 |
| m/1'/1234/0/6 | n1EstV7h4Jyx1XKLY7fdx6ufFRMxBVdieN | 100000000 |
| m/1'/1234/0/7 | mi2udMvJHeeJJNp5wWKToa86L2cJUKzrby | 3000000 |
| m/1'/1234/0/14 | mmfFY4UJHJBSjz3ve7tvaSrxNwCReVtifC | 0 |
| m/1'/1234/0/15 | myEyz9WPaZmXqnhtN86xM8S66wVysBRNtN | 0 |
| m/1'/1234/0/19 | mskQduVL7QBZCEWJUit8u9N8Cj4CFouPWX | 1000000 |
| m/1'/1234/1/0 | moHN13u4RoMxujdaPxvuaTaawgWZ3LaGyo | 246600 |
| m/1'/1234/1/1 | n2aNi43rgX8YD5NMJK55dgHp7n7rdGzbYj | 6504400 |
| m/1'/1234/1/2 | mv5dfNyTKMwED5g26LMZJYwN5QckEXrqe2 | 300000 |
+----------------+------------------------------------+-----------+
+---------------+--------------------+-------------------+---------------------+
| TOTAL BALANCE | LAST RECEIVE INDEX | LAST CHANGE INDEX | REPORT TIME |
+---------------+--------------------+-------------------+---------------------+
| 256725439 | 28 | 11 | 26 Sep 18 13:38 PDT |
+---------------+--------------------+-------------------+---------------------+
Child pubkey #1: tpubDBrCAXucLxvjC9n9nZGGcYS8pk4X1N97YJmUgdDSwG2p36gbSqeRuytHYCHe2dHxLsV2EchX9ePaFdRwp7cNLrSpnr3PsoPLUQqbvLBDWvh
```

We can then use `tpubDBrCAXucLxvj...` to compute the balance.

Compute balance (using Electrum)
--------------------------------
```
$ ./beancounter compute-balance --type multisig --block-height 1438791
Enter pubkey #1 out of #1:
tpubDBrCAXucLxvjC9n9nZGGcYS8pk4X1N97YJmUgdDSwG2p36gbSqeRuytHYCHe2dHxLsV2EchX9ePaFdRwp7cNLrSpnr3PsoPLUQqbvLBDWvh
...
Balance: 267893477
```

Compute balance (using Btcd)
--------------------------------

[https://github.com/btcsuite/btcd](Btcd) contains information on how to setup a node.

Beancounter requires `addrindex=1`, `txindex=1`, and `notls=1`. If your node is on a remote server,
we recommend tunneling the RPC traffic over ssh or some other secure tunnel.

```
rpcuser=mia
rpcpass=ilovebrownies
rpclisten=127.0.0.1:8334
notls=1
blocksonly=1
addrindex=1
txindex=1
```

Once the Btcd is up and running, you can do:
```
$ ./beancounter compute-balance --type multisig --block-height 1438791 --backend btcd --addr localhost:8334 --rpcuser mia --rpcpass ilovebrownies
Enter pubkey #1 out of #1:
tpubDBrCAXucLxvjC9n9nZGGcYS8pk4X1N97YJmUgdDSwG2p36gbSqeRuytHYCHe2dHxLsV2EchX9ePaFdRwp7cNLrSpnr3PsoPLUQqbvLBDWvh
...
Balance: 267893477
```

Details
#######
Beancounter is implemented in Go. We picked Go because we wanted an easy build and distribution process. We appreciate the ability to cross-compile and distribute static binaries which don't have external dependencies.

We use the following process to determine a deterministic wallet's balance at a given block height:

1. Derive external (receive) and internal (change) addresses.
2. For each address, query the backend for a list of transactions. We keep deriving additional addresses until we find a large number of unused addresses.
3. Prune the transaction list to remove transactions newer than the block height.
4. For each transaction, query the backend for the raw transaction.
5. For each transaction, track whether the output belongs to the wallet and whether
it got spent.
6. Iterate over the transactions and compute the final balance.

Contributing
############

We appreciate any pull request which fixes bugs or adds features!

If you need ideas on how to contribute, we would enjoy a 3rd backend (Bitcoin-core based, processing
each block by streaming the entire blockchain) as well as additional wallet types (e.g. multisig non-segwit).

Additional unittests and improvements to comments/docs are also always welcome.
4 changes: 2 additions & 2 deletions accounter/accounter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ func TestProcessTransactions(t *testing.T) {
}

func TestComputeBalanceTestnet(t *testing.T) {
pubs := []string{"tpubD8L6UhrL8ML9Ao47k4pmdvUoiA6QUJVzrJ9BXLgU9idRKnvdRFGgjcxmVxojWGvCcjMi6QWCp8uMpCwWdSFRDNJ7utizxLy27sVWXQT4Jz7"}
deriver := deriver.NewAddressDeriver(utils.Testnet, pubs, 1, 1234, "")
pubs := []string{"tpubDBrCAXucLxvjC9n9nZGGcYS8pk4X1N97YJmUgdDSwG2p36gbSqeRuytHYCHe2dHxLsV2EchX9ePaFdRwp7cNLrSpnr3PsoPLUQqbvLBDWvh"}
deriver := deriver.NewAddressDeriver(utils.Testnet, pubs, 1, "")
b, err := backend.NewFixtureBackend("testdata/tpub_data.json")
assert.NoError(t, err)
a := New(b, deriver, 100, 1435169)
Expand Down
26 changes: 2 additions & 24 deletions deriver/address_deriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type AddressDeriver struct {
network Network
xpubs []string
m int
account uint32
singleAddress string
}

Expand Down Expand Up @@ -88,12 +87,11 @@ func (a *Address) Script() string {
}

// NewAddressDeriver returns a new instance of AddressDeriver
func NewAddressDeriver(network Network, xpubs []string, m int, account uint32, singleAddress string) *AddressDeriver {
func NewAddressDeriver(network Network, xpubs []string, m int, singleAddress string) *AddressDeriver {
return &AddressDeriver{
network: network,
xpubs: xpubs,
m: m,
account: account,
singleAddress: singleAddress,
}
}
Expand All @@ -111,7 +109,7 @@ func (d *AddressDeriver) Derive(change uint32, addressIndex uint32) *Address {
}
}

path := fmt.Sprintf("m/%s/%d/%d/%d", coinType(d.network), d.account, change, addressIndex)
path := fmt.Sprintf("m/.../%d/%d", change, addressIndex)
addr := &Address{path: path, net: d.network, change: change, addrIndex: addressIndex}
if len(d.xpubs) == 1 {
addr.addr = d.singleDerive(change, addressIndex)
Expand All @@ -126,11 +124,6 @@ func (d *AddressDeriver) singleDerive(change uint32, addressIndex uint32) string
key, err := hdkeychain.NewKeyFromString(d.xpubs[0])
PanicOnError(err)

if d.account != 4294967295 {
key, err = key.Child(d.account)
PanicOnError(err)
}

key, err = key.Child(change)
PanicOnError(err)

Expand All @@ -152,9 +145,6 @@ func (d *AddressDeriver) multiSigSegwitDerive(change uint32, addressIndex uint32
key, err := hdkeychain.NewKeyFromString(xpub)
PanicOnError(err)

key, err = key.Child(d.account)
PanicOnError(err)

key, err = key.Child(change)
PanicOnError(err)

Expand Down Expand Up @@ -226,15 +216,3 @@ func sortByteArrays(src [][]byte) [][]byte {
sort.Sort(sorted)
return sorted
}

// as per SLIP-0044 https://github.com/satoshilabs/slips/blob/master/slip-0044.md
func coinType(n Network) string {
switch n {
case Mainnet:
return "0'"
case Testnet:
return "1'"
default:
panic("unreachable")
}
}
14 changes: 7 additions & 7 deletions deriver/address_deriver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import (

func TestDeriveMultiSigSegwit(t *testing.T) {
xpubs := []string{
"tpubD9jBarsLCKot45kvTTu7yWxmNnkBPbpn2CgS1F3yuxZGgohTkamYwJJKenZHrsYwPRJY66dk3ZUt3aZwZqFf7QGsgUUUcNSvvb9NXHFt5Vb",
"tpubD8u5eHk8B6q63G4ekfBB2eVCeBYTrW2uCvi7Z3BF6bY2z33jW14Uzna8f5cFQWS3HFwdAzUWxqESq7j5x2CUQ7uBPgpXnpf8X9FPnh63XYd",
"tpubD9dkteiWZqa3jL17meqVvy1RSUQsmJvU3hBfAosMzrSa69DacRku8yHy3E2fma1Q4Den25ukcsBL3bYTFyKjKbF8CEWHu86Xg9YXiY6CkeC",
"tpubD8GrNWdYHDjdJryH5tng8LUzHSrEo4gToaguKnzthEqTxyfF13jTsp4sMtmso4n1VC58R5Wvt4Ua4npZTecR1xaGGYJgLLQj5sQGdD2xh2N",
"tpubDAiPiLZeUdwo9oJiE9GZnteXj2E2MEMUb4knc4yCD87bL9siDgYcvrZSHZQZcYTyraL3fxVBRCcMiyfr3oQfH1wNo8J5i8aRAN56dDXaZxC",
"tpubDBYBpkSfvt9iVSfdX2ArZq1Q8bVSro3sotbJhdZCG9rgfjdr4aZp7g7AF1P9w95X5fzuJzdZAqYWWU7nb37c594wR22hPY5VpYziXUN2yez",
"tpubDAaTEMnf9SPKJweLaptFdy3Vmyhim5DKQxXRbsCxmAaUp8F84YD5GhdfmABwLddjHTftSVvUPuSru6vJ3b5N2hBveiGmZNE5N5yvB6WZ96c",
"tpubDAXKYCetkje8HRRhAvUbAyuC5iF3SgfFWCVXfmrGCw3H9ExCYZVTEoeg7TjtDhgkS7TNHDRZUQNzGACWVzZCAYXy79vqku5z1geYmnsNLaa",
}
deriver := NewAddressDeriver(Testnet, xpubs, 2, 0, "")
deriver := NewAddressDeriver(Testnet, xpubs, 2, "")
assert.Equal(t, "2N4TmnHspa8wqFEUfxfjzHoSUAgwoUwNWhr", deriver.Derive(0, 0).String())
}

func TestDeriveGateway(t *testing.T) {
xpubs := []string{
"tpubD8L6UhrL8ML9Ao47k4pmdvUoiA6QUJVzrJ9BXLgU9idRKnvdRFGgjcxmVxojWGvCcjMi6QWCp8uMpCwWdSFRDNJ7utizxLy27sVWXQT4Jz7",
"tpubDBrCAXucLxvjC9n9nZGGcYS8pk4X1N97YJmUgdDSwG2p36gbSqeRuytHYCHe2dHxLsV2EchX9ePaFdRwp7cNLrSpnr3PsoPLUQqbvLBDWvh",
}
deriver := NewAddressDeriver(Testnet, xpubs, 1, 1234, "")
deriver := NewAddressDeriver(Testnet, xpubs, 1, "")
assert.Equal(t, "mzoeuyGqMudyvKbkNx5dtNBNN59oKEAsPn", deriver.Derive(0, 0).String())
assert.Equal(t, "moHN13u4RoMxujdaPxvuaTaawgWZ3LaGyo", deriver.Derive(1, 0).String())
}

0 comments on commit 9292e37

Please sign in to comment.