Skip to content

Commit e0dba5b

Browse files
committed
itest: add harness for invoking chantools binary
Add a test harness to execute the chantools binary via the command line and parse its output.
1 parent 09e24f7 commit e0dba5b

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

itest/chantools_harness.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package itest
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"regexp"
9+
"strings"
10+
"testing"
11+
12+
"github.com/btcsuite/btcd/btcutil/psbt"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
// chantoolsCmdBuilder is a helper function that creates a new exec.Cmd instance
17+
// for the chantools binary.
18+
func chantoolsCmdBuilder(binPath string, workingDir string,
19+
args ...string) exec.Cmd {
20+
21+
cmd := exec.Command(binPath, args...)
22+
23+
// Set the working directory so that any log files are handled
24+
// gracefully.
25+
cmd.Dir = workingDir
26+
27+
// Set the wallet password.
28+
cmd.Env = append(os.Environ(), "WALLET_PASSWORD=-")
29+
30+
// Set the seed generation passphrase.
31+
cmd.Env = append(cmd.Env, "AEZEED_PASSPHRASE=-")
32+
33+
return *cmd
34+
}
35+
36+
// ChantoolsHarness is a helper struct that provides a way to interact with
37+
// the chantools binary.
38+
type ChantoolsHarness struct {
39+
// path is the path to the chantools binary.
40+
path string
41+
42+
// workingDir is the chantools harness working directory.
43+
workingDir string
44+
45+
// walletDbPath is the path to the wallet.db file created by chantools.
46+
walletDbPath string
47+
}
48+
49+
// NewChantoolsHarness creates a new instance of the ChantoolsHarness struct.
50+
func NewChantoolsHarness(t *testing.T) ChantoolsHarness {
51+
path, err := exec.LookPath("chantools")
52+
require.NoError(t, err, "chantools is not installed or not in PATH")
53+
54+
t.Logf("Using chantools binary at: %v", path)
55+
56+
// Create a temporary directory to store the log file and wallet.db
57+
// file.
58+
workingDir, err := os.MkdirTemp("", "itest-tapd-chantools")
59+
require.NoError(t, err, "failed to create chantools working directory")
60+
61+
// Assert that the version of chantools is as expected.
62+
cmd := chantoolsCmdBuilder(path, workingDir, "--version")
63+
versionOut, err := cmd.CombinedOutput()
64+
require.NoError(t, err, "failed to get chantools version")
65+
66+
versionOutStr := string(versionOut)
67+
if !strings.Contains(versionOutStr, "chantools version v0.13.5") {
68+
t.Fatalf("unexpected chantools version: %v", versionOutStr)
69+
}
70+
71+
return ChantoolsHarness{
72+
path: path,
73+
workingDir: workingDir,
74+
walletDbPath: filepath.Join(workingDir, "wallet.db"),
75+
}
76+
}
77+
78+
// buildCmd is a helper method that creates a new exec.Cmd instance for the
79+
// chantools binary.
80+
func (c *ChantoolsHarness) buildCmd(args ...string) exec.Cmd {
81+
return chantoolsCmdBuilder(c.path, c.workingDir, args...)
82+
}
83+
84+
// CreateWallet creates a new wallet using the chantools binary.
85+
func (c *ChantoolsHarness) CreateWallet(t *testing.T) {
86+
cmd := c.buildCmd(
87+
"--regtest", "createwallet", "--bip39",
88+
"--generateseed", "--walletdbdir", c.workingDir,
89+
)
90+
91+
cmdOut, err := cmd.CombinedOutput()
92+
require.NoError(t, err)
93+
94+
t.Logf("Chantools createwallet output: %v", string(cmdOut))
95+
}
96+
97+
// DeriveKey derives a new key using the chantools binary.
98+
func (c *ChantoolsHarness) DeriveKey(t *testing.T) (string, string) {
99+
cmd := c.buildCmd(
100+
"--regtest", "derivekey", "--walletdb", c.walletDbPath,
101+
"--path", "m/86'/1'/0'", "--neuter",
102+
)
103+
104+
cmdOut, err := cmd.CombinedOutput()
105+
require.NoError(t, err)
106+
107+
cmdOutStr := string(cmdOut)
108+
t.Logf("Chantools derivekey output: %v", cmdOutStr)
109+
110+
// Parsing for xpub.
111+
xpubPattern := `Extended public key \(xpub\):\s+([a-zA-Z0-9]+)`
112+
xpubRegex := regexp.MustCompile(xpubPattern)
113+
matches := xpubRegex.FindStringSubmatch(cmdOutStr)
114+
require.Len(t, matches, 2)
115+
116+
xpub := matches[1]
117+
require.NotEmpty(t, xpub)
118+
119+
// Parsing for master fingerprint.
120+
fingerprintPattern := `Master Fingerprint:\s+([a-f0-9]+)`
121+
fingerprintRegex := regexp.MustCompile(fingerprintPattern)
122+
matches = fingerprintRegex.FindStringSubmatch(cmdOutStr)
123+
require.Len(t, matches, 2)
124+
125+
masterFingerprint := matches[1]
126+
require.Len(t, masterFingerprint, 8)
127+
128+
return xpub, masterFingerprint
129+
}
130+
131+
// SignPsbt signs a PSBT using the chantools binary.
132+
func (c *ChantoolsHarness) SignPsbt(t *testing.T, psbtStr string) psbt.Packet {
133+
cmd := c.buildCmd(
134+
"--regtest", "signpsbt", "--walletdb", c.walletDbPath,
135+
"--psbt", psbtStr,
136+
)
137+
138+
cmdOut, err := cmd.CombinedOutput()
139+
require.NoError(t, err)
140+
141+
cmdOutStr := string(cmdOut)
142+
t.Logf("Chantools signpsbt output: %v", cmdOutStr)
143+
144+
// Extract the signed PSBT.
145+
psbtPattern := `Successfully signed PSBT:\n\n([a-zA-Z0-9+/=]+)`
146+
psbtRegex := regexp.MustCompile(psbtPattern)
147+
matches := psbtRegex.FindStringSubmatch(cmdOutStr)
148+
require.Len(t, matches, 2)
149+
150+
signedPsbtStr := matches[1]
151+
require.NotEmpty(t, signedPsbtStr)
152+
153+
// Parse the signed PSBT.
154+
signedPsbt, err := psbt.NewFromRawBytes(bytes.NewReader(
155+
[]byte(signedPsbtStr)), true,
156+
)
157+
require.NoError(t, err)
158+
159+
return *signedPsbt
160+
}

0 commit comments

Comments
 (0)