Merge pull request #6006 from guggero/remote-signer-no-chain
Allow remote signer to run without chain backend
This commit is contained in:
40 files changed
+2960
-934
No files matched your search
+195
-38
@@ -6,12 +6,15 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcutil/psbt"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -40,6 +43,12 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
dave *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
|
||||
// Everything we do here should be done within a second or two, so we
|
||||
// can just keep a single timeout context around for all calls.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
const chanSize = funding.MaxBtcFundingAmount
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave)
|
||||
|
||||
@@ -63,8 +72,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
// Now that we have the pending channel ID, Carol will open the channel
|
||||
// by specifying a PSBT shim. We use the NoPublish flag here to avoid
|
||||
// publishing the whole batch TX too early.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
chanUpdates, tempPsbt, err := openChannelPsbt(
|
||||
ctxt, carol, dave, lntest.OpenChannelParams{
|
||||
Amt: chanSize,
|
||||
@@ -83,8 +90,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
// Let's add a second channel to the batch. This time between Carol and
|
||||
// Alice. We will publish the batch TX once this channel funding is
|
||||
// complete.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
chanUpdates2, psbtBytes2, err := openChannelPsbt(
|
||||
ctxt, carol, net.Alice, lntest.OpenChannelParams{
|
||||
Amt: chanSize,
|
||||
@@ -104,8 +109,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
// We'll now ask Dave's wallet to fund the PSBT for us. This will return
|
||||
// a packet with inputs and outputs set but without any witness data.
|
||||
// This is exactly what we need for the next step.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
fundReq := &walletrpc.FundPsbtRequest{
|
||||
Template: &walletrpc.FundPsbtRequest_Psbt{
|
||||
Psbt: psbtBytes2,
|
||||
@@ -140,8 +143,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
|
||||
// Now we'll ask Dave's wallet to sign the PSBT so we can finish the
|
||||
// funding flow.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
finalizeReq := &walletrpc.FinalizePsbtRequest{
|
||||
FundedPsbt: fundResp.FundedPsbt,
|
||||
}
|
||||
@@ -161,8 +162,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
|
||||
// Consume the "channel pending" update. This waits until the funding
|
||||
// transaction was fully compiled.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
updateResp, err := receiveChanUpdate(ctxt, chanUpdates)
|
||||
require.NoError(t.t, err)
|
||||
upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||
@@ -195,8 +194,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
// Consume the "channel pending" update for the second channel. This
|
||||
// waits until the funding transaction was fully compiled and in this
|
||||
// case published.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
updateResp2, err := receiveChanUpdate(ctxt, chanUpdates2)
|
||||
require.NoError(t.t, err)
|
||||
upd2, ok := updateResp2.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||
@@ -225,8 +222,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
// With the channel open, ensure that it is counted towards Carol's
|
||||
// total channel balance.
|
||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
balRes, err := carol.ChannelBalance(ctxt, balReq)
|
||||
require.NoError(t.t, err)
|
||||
require.NotEqual(t.t, int64(0), balRes.LocalBalance.Sat)
|
||||
@@ -238,7 +233,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
Memo: "new chans",
|
||||
Value: int64(payAmt),
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := dave.AddInvoice(ctxt, invoice)
|
||||
require.NoError(t.t, err)
|
||||
err = completePaymentRequests(
|
||||
@@ -261,6 +255,11 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
const chanSize = funding.MaxBtcFundingAmount
|
||||
|
||||
// Everything we do here should be done within a second or two, so we
|
||||
// can just keep a single timeout context around for all calls.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
// First, we'll create two new nodes that we'll use to open channels
|
||||
// between for this test. Both these nodes have an empty wallet as Alice
|
||||
// will be funding the channel.
|
||||
@@ -290,8 +289,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// Now that we have the pending channel ID, Carol will open the channel
|
||||
// by specifying a PSBT shim. We use the NoPublish flag here to avoid
|
||||
// publishing the whole batch TX too early.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
chanUpdates, tempPsbt, err := openChannelPsbt(
|
||||
ctxt, carol, dave, lntest.OpenChannelParams{
|
||||
Amt: chanSize,
|
||||
@@ -310,8 +307,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// Let's add a second channel to the batch. This time between Carol and
|
||||
// Alice. We will publish the batch TX once this channel funding is
|
||||
// complete.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
chanUpdates2, psbtBytes2, err := openChannelPsbt(
|
||||
ctxt, carol, net.Alice, lntest.OpenChannelParams{
|
||||
Amt: chanSize,
|
||||
@@ -331,8 +326,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// We'll now ask Alice's wallet to fund the PSBT for us. This will
|
||||
// return a packet with inputs and outputs set but without any witness
|
||||
// data. This is exactly what we need for the next step.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
fundReq := &walletrpc.FundPsbtRequest{
|
||||
Template: &walletrpc.FundPsbtRequest_Psbt{
|
||||
Psbt: psbtBytes2,
|
||||
@@ -375,8 +368,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Consume the "channel pending" update. This waits until the funding
|
||||
// transaction was fully compiled for both channels.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
updateResp, err := receiveChanUpdate(ctxt, chanUpdates)
|
||||
require.NoError(t.t, err)
|
||||
upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||
@@ -403,8 +394,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Now we'll ask Alice's wallet to sign the PSBT so we can finish the
|
||||
// funding flow.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
finalizeReq := &walletrpc.FinalizePsbtRequest{
|
||||
FundedPsbt: fundResp.FundedPsbt,
|
||||
}
|
||||
@@ -439,8 +428,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// With the channel open, ensure that it is counted towards Carol's
|
||||
// total channel balance.
|
||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
balRes, err := carol.ChannelBalance(ctxt, balReq)
|
||||
require.NoError(t.t, err)
|
||||
require.NotEqual(t.t, int64(0), balRes.LocalBalance.Sat)
|
||||
@@ -452,7 +439,6 @@ func testPsbtChanFundingExternal(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
Memo: "new chans",
|
||||
Value: int64(payAmt),
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := dave.AddInvoice(ctxt, invoice)
|
||||
require.NoError(t.t, err)
|
||||
err = completePaymentRequests(
|
||||
@@ -476,6 +462,11 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
const chanSize = funding.MaxBtcFundingAmount
|
||||
|
||||
// Everything we do here should be done within a second or two, so we
|
||||
// can just keep a single timeout context around for all calls.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS)
|
||||
|
||||
// First, we'll create two new nodes that we'll use to open channels
|
||||
@@ -513,8 +504,6 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Now that we have the pending channel ID, Carol will open the channel
|
||||
// by specifying a PSBT shim.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
chanUpdates, tempPsbt, err := openChannelPsbt(
|
||||
ctxt, carol, dave, lntest.OpenChannelParams{
|
||||
Amt: chanSize,
|
||||
@@ -547,8 +536,6 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
err = decodedPsbt.Serialize(&psbtBytes)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
fundReq := &walletrpc.FundPsbtRequest{
|
||||
Template: &walletrpc.FundPsbtRequest_Psbt{
|
||||
Psbt: psbtBytes.Bytes(),
|
||||
@@ -583,8 +570,6 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Now we'll ask Alice's wallet to sign the PSBT so we can finish the
|
||||
// funding flow.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
finalizeReq := &walletrpc.FinalizePsbtRequest{
|
||||
FundedPsbt: fundResp.FundedPsbt,
|
||||
}
|
||||
@@ -604,8 +589,6 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Consume the "channel pending" update. This waits until the funding
|
||||
// transaction was fully compiled.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
updateResp, err := receiveChanUpdate(ctxt, chanUpdates)
|
||||
require.NoError(t.t, err)
|
||||
upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||
@@ -634,8 +617,6 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
Memo: "new chans",
|
||||
Value: int64(payAmt),
|
||||
}
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
resp, err := dave.AddInvoice(ctxt, invoice)
|
||||
require.NoError(t.t, err)
|
||||
err = completePaymentRequests(
|
||||
@@ -651,6 +632,182 @@ func testPsbtChanFundingSingleStep(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
closeChannelAndAssert(t, net, carol, chanPoint, false)
|
||||
}
|
||||
|
||||
// runSignPsbt tests that the SignPsbt RPC works correctly.
|
||||
func testSignPsbt(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runSignPsbt(t, net, net.Alice)
|
||||
}
|
||||
|
||||
// runSignPsbt tests that the SignPsbt RPC works correctly.
|
||||
func runSignPsbt(t *harnessTest, net *lntest.NetworkHarness,
|
||||
alice *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
|
||||
// Everything we do here should be done within a second or two, so we
|
||||
// can just keep a single timeout context around for all calls.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
// We test that we can sign a PSBT that spends funds from an input that
|
||||
// the wallet doesn't know about. To set up that test case, we first
|
||||
// derive an address manually that the wallet won't be watching on
|
||||
// chain. We can do that by exporting the account xpub of lnd's main
|
||||
// account.
|
||||
accounts, err := alice.WalletKitClient.ListAccounts(
|
||||
ctxt, &walletrpc.ListAccountsRequest{},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
require.NotEmpty(t.t, accounts.Accounts)
|
||||
|
||||
// We also need to parse the accounts, so we have easy access to the
|
||||
// parsed derivation paths.
|
||||
parsedAccounts, err := walletrpc.AccountsToWatchOnly(accounts.Accounts)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
account := parsedAccounts[0]
|
||||
xpub, err := hdkeychain.NewKeyFromString(account.Xpub)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
const (
|
||||
changeIndex = 1
|
||||
addrIndex = 1337
|
||||
)
|
||||
fullDerivationPath := []uint32{
|
||||
hdkeychain.HardenedKeyStart + account.Purpose,
|
||||
hdkeychain.HardenedKeyStart + account.CoinType,
|
||||
hdkeychain.HardenedKeyStart + account.Account,
|
||||
changeIndex,
|
||||
addrIndex,
|
||||
}
|
||||
|
||||
// Let's simulate a change address.
|
||||
change, err := xpub.DeriveNonStandard(changeIndex) // nolint:staticcheck
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// At an index that we are certainly not watching in the wallet.
|
||||
addrKey, err := change.DeriveNonStandard(addrIndex) // nolint:staticcheck
|
||||
require.NoError(t.t, err)
|
||||
|
||||
addrPubKey, err := addrKey.ECPubKey()
|
||||
require.NoError(t.t, err)
|
||||
pubKeyHash := btcutil.Hash160(addrPubKey.SerializeCompressed())
|
||||
witnessAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||
pubKeyHash, harnessNetParams,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
pkScript, err := txscript.PayToAddrScript(witnessAddr)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Let's send some coins to that address now.
|
||||
utxo := &wire.TxOut{
|
||||
Value: 600_000,
|
||||
PkScript: pkScript,
|
||||
}
|
||||
resp, err := alice.WalletKitClient.SendOutputs(
|
||||
ctxt, &walletrpc.SendOutputsRequest{
|
||||
Outputs: []*signrpc.TxOut{{
|
||||
Value: utxo.Value,
|
||||
PkScript: utxo.PkScript,
|
||||
}},
|
||||
MinConfs: 0,
|
||||
SpendUnconfirmed: true,
|
||||
SatPerKw: 2500,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
prevTx := wire.NewMsgTx(2)
|
||||
err = prevTx.Deserialize(bytes.NewReader(resp.RawTx))
|
||||
require.NoError(t.t, err)
|
||||
|
||||
prevOut := -1
|
||||
for idx, txOut := range prevTx.TxOut {
|
||||
if bytes.Equal(txOut.PkScript, pkScript) {
|
||||
prevOut = idx
|
||||
}
|
||||
}
|
||||
require.Greater(t.t, prevOut, -1)
|
||||
|
||||
// Okay, we have everything we need to create a PSBT now.
|
||||
pendingTx := &wire.MsgTx{
|
||||
Version: 2,
|
||||
TxIn: []*wire.TxIn{{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: prevTx.TxHash(),
|
||||
Index: uint32(prevOut),
|
||||
},
|
||||
}},
|
||||
// We send to the same address again, but deduct some fees.
|
||||
TxOut: []*wire.TxOut{{
|
||||
Value: utxo.Value - 600,
|
||||
PkScript: utxo.PkScript,
|
||||
}},
|
||||
}
|
||||
packet, err := psbt.NewFromUnsignedTx(pendingTx)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Now let's add the meta information that we need for signing.
|
||||
packet.Inputs[0].Bip32Derivation = []*psbt.Bip32Derivation{{
|
||||
PubKey: addrPubKey.SerializeCompressed(),
|
||||
Bip32Path: fullDerivationPath,
|
||||
}}
|
||||
packet.Inputs[0].WitnessUtxo = utxo
|
||||
packet.Inputs[0].NonWitnessUtxo = prevTx
|
||||
packet.Inputs[0].SighashType = txscript.SigHashAll
|
||||
|
||||
// That's it, we should be able to sign the PSBT now.
|
||||
var buf bytes.Buffer
|
||||
err = packet.Serialize(&buf)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
signResp, err := alice.WalletKitClient.SignPsbt(
|
||||
ctxt, &walletrpc.SignPsbtRequest{
|
||||
FundedPsbt: buf.Bytes(),
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Let's make sure we have a partial signature.
|
||||
signedPacket, err := psbt.NewFromRawBytes(
|
||||
bytes.NewReader(signResp.SignedPsbt), false,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
require.Len(t.t, signedPacket.Inputs, 1)
|
||||
require.Len(t.t, signedPacket.Inputs[0].PartialSigs, 1)
|
||||
|
||||
partialSig := signedPacket.Inputs[0].PartialSigs[0]
|
||||
require.Equal(t.t, partialSig.PubKey, addrPubKey.SerializeCompressed())
|
||||
require.Greater(t.t, len(partialSig.Signature), btcec.MinSigLen)
|
||||
|
||||
// We should be able to finalize the PSBT and extract the final TX now.
|
||||
err = psbt.MaybeFinalizeAll(signedPacket)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
finalTx, err := psbt.Extract(signedPacket)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
buf.Reset()
|
||||
err = finalTx.Serialize(&buf)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Publish the second transaction and then mine both of them.
|
||||
_, err = alice.WalletKitClient.PublishTransaction(
|
||||
ctxt, &walletrpc.Transaction{
|
||||
TxHex: buf.Bytes(),
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Mine one block which should contain two transactions.
|
||||
block := mineBlocks(t, net, 1, 2)[0]
|
||||
firstTxHash := prevTx.TxHash()
|
||||
secondTxHash := finalTx.TxHash()
|
||||
assertTxInBlock(t, block, &firstTxHash)
|
||||
assertTxInBlock(t, block, &secondTxHash)
|
||||
}
|
||||
|
||||
// openChannelPsbt attempts to open a channel between srcNode and destNode with
|
||||
// the passed channel funding parameters. If the passed context has a timeout,
|
||||
// then if the timeout is reached before the channel pending notification is
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
@@ -39,76 +40,6 @@ var (
|
||||
Xpub: "tpubDDWAWrSLRSFrG1KdqXMQQyTKYGSKLKaY7gxpvK7RdV3e3Dkhvu" +
|
||||
"W2GgsFvsPN4RGmuoYtUgZ1LHZE8oftz7T4mzc1BxGt5rt8zJcVQi" +
|
||||
"KTPPV",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyMultiSig),
|
||||
Xpub: "tpubDDXFHr67Ro2tHKVWG2gNjjijKUH1Lyv5NKFYdJnuaLGVNBVwyV" +
|
||||
"5AbykhR43iy8wYozEMbw2QfmAqZhb8gnuL5mm9sZh8YsR6FjGAbe" +
|
||||
"w1xoT",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyRevocationBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tKkccDqNfDqZpd5wCs2n6XRV2Uh185DzCTbkDaE" +
|
||||
"d9v7P837zZTYBNVfaRriuxgGVgxbGjDui4CKxyzBzwz4aAZxjn2P" +
|
||||
"hNcQy",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyHtlcBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tNH4KH41i4oTsWfRjFigoH1Ee7urvHow51opH9x" +
|
||||
"J7mu1qSPMPVtkVqQZ5tE4NTuFJPrbDqno7TQietyUDmPTwyVviJb" +
|
||||
"GCwXk",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyPaymentBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tQj5Zvav2ALhkU6dRQAhEtNPnYJVBC8hs2U1A9e" +
|
||||
"cqxRY3XTiJKBDD7e8tudhmTRs8aGWJAiAXJN5kXy3Hi6cmiwGWjX" +
|
||||
"K5Cv5",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyDelayBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tSSR2LLBJtotxx2U45cuESLWKA72YT9td3SzVKH" +
|
||||
"AptzDEx5chsUNZ4WRMY5h6HJxRSebjRatxQKX1uUsux1LvKS1wsf" +
|
||||
"NJ2PH",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyRevocationRoot),
|
||||
Xpub: "tpubDDXFHr67Ro2tTwzfWvNoMoPpZbxdMEfe1WhbXJxvXikGixPa4g" +
|
||||
"gSRZeGx6T5yxVHTVT3rjVh35Veqsowj7emX8SZfXKDKDKcLduXCe" +
|
||||
"WPUU3",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyNodeKey),
|
||||
Xpub: "tpubDDXFHr67Ro2tYEDS2EByRedfsUoEwBtrzVbS1qdPrX6sAkUYGL" +
|
||||
"rZWvMmQv8KZDZ4zd9r8WzM9bJ2nGp7XuNVC4w2EBtWg7i76gbrmu" +
|
||||
"EWjQh",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyStaticBackup),
|
||||
Xpub: "tpubDDXFHr67Ro2tYpwnFJEQaM8eAPM2UV5uY6gFgXeSzS5aC5T9Tf" +
|
||||
"zXuawYKBbQMZJn8qHXLafY4tAutoda1aKP5h6Nbgy3swPbnhWbFj" +
|
||||
"S5wnX",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyTowerSession),
|
||||
Xpub: "tpubDDXFHr67Ro2tddKpAjUegXqt7EGxRXnHkeLbUkfuFMGbLJYgRp" +
|
||||
"G4ew5pMmGg2nmcGmHFQ29w3juNhd8N5ZZ8HwJdymC4f5ukQLJ4yg" +
|
||||
"9rEr3",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyTowerID),
|
||||
Xpub: "tpubDDXFHr67Ro2tgE89V8ZdgMytC2Jq1iT9ttGhdzR1X7haQJNBmX" +
|
||||
"t8kau6taC6DGASYzbrjmo9z9w6JQFcaLNqbhS2h2PVSzKf79j265" +
|
||||
"Zi8hF",
|
||||
}}
|
||||
)
|
||||
|
||||
@@ -166,6 +97,7 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
name: "psbt",
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runPsbtChanFunding(net, tt, carol, wo)
|
||||
runSignPsbt(tt, net, wo)
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -180,7 +112,7 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
password := []byte("itestpassword")
|
||||
var (
|
||||
signerNodePubKey = nodePubKey
|
||||
watchOnlyAccounts = accounts
|
||||
watchOnlyAccounts = deriveCustomScopeAccounts(t.t)
|
||||
signer *lntest.HarnessNode
|
||||
err error
|
||||
)
|
||||
@@ -237,10 +169,6 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
if subTest.sendCoins {
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, watchOnly)
|
||||
assertAccountBalance(
|
||||
t.t, signer, "default",
|
||||
btcutil.SatoshiPerBitcoin, 0,
|
||||
)
|
||||
assertAccountBalance(
|
||||
t.t, watchOnly, "default",
|
||||
btcutil.SatoshiPerBitcoin, 0,
|
||||
@@ -264,3 +192,55 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deriveCustomScopeAccounts derives the first 255 default accounts of the custom lnd
|
||||
// internal key scope.
|
||||
func deriveCustomScopeAccounts(t *testing.T) []*lnrpc.WatchOnlyAccount {
|
||||
allAccounts := make([]*lnrpc.WatchOnlyAccount, 0, 255+len(accounts))
|
||||
allAccounts = append(allAccounts, accounts...)
|
||||
|
||||
extendedRootKey, err := hdkeychain.NewKeyFromString(rootKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
path := []uint32{
|
||||
keychain.BIP0043Purpose + hdkeychain.HardenedKeyStart,
|
||||
harnessNetParams.HDCoinType + hdkeychain.HardenedKeyStart,
|
||||
}
|
||||
coinTypeKey, err := derivePath(extendedRootKey, path)
|
||||
require.NoError(t, err)
|
||||
for idx := uint32(0); idx <= 255; idx++ {
|
||||
accountPath := []uint32{idx + hdkeychain.HardenedKeyStart}
|
||||
accountKey, err := derivePath(coinTypeKey, accountPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountXPub, err := accountKey.Neuter()
|
||||
require.NoError(t, err)
|
||||
|
||||
allAccounts = append(allAccounts, &lnrpc.WatchOnlyAccount{
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: idx,
|
||||
Xpub: accountXPub.String(),
|
||||
})
|
||||
}
|
||||
|
||||
return allAccounts
|
||||
}
|
||||
|
||||
// derivePath derives the given path from an extended key.
|
||||
func derivePath(key *hdkeychain.ExtendedKey, path []uint32) (
|
||||
*hdkeychain.ExtendedKey, error) {
|
||||
|
||||
var (
|
||||
currentKey = key
|
||||
err error
|
||||
)
|
||||
for _, pathPart := range path {
|
||||
currentKey, err = currentKey.Derive(pathPart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return currentKey, nil
|
||||
}
|
||||
@@ -283,6 +283,10 @@ var allTestCases = []*testCase{
|
||||
name: "psbt channel funding external",
|
||||
test: testPsbtChanFundingExternal,
|
||||
},
|
||||
{
|
||||
name: "sign psbt",
|
||||
test: testSignPsbt,
|
||||
},
|
||||
{
|
||||
name: "batch channel funding",
|
||||
test: testBatchChanFunding,
|
||||
|
||||
@@ -53,15 +53,25 @@ func (w *WalletController) FetchInputInfo(
|
||||
return utxo, nil
|
||||
}
|
||||
|
||||
// ScriptForOutput returns the address, witness program and redeem script for a
|
||||
// given UTXO. An error is returned if the UTXO does not belong to our wallet or
|
||||
// it is not a managed pubKey address.
|
||||
func (w *WalletController) ScriptForOutput(*wire.TxOut) (
|
||||
waddrmgr.ManagedPubKeyAddress, []byte, []byte, error) {
|
||||
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
// ConfirmedBalance currently returns dummy values.
|
||||
func (w *WalletController) ConfirmedBalance(confs int32,
|
||||
_ string) (btcutil.Amount, error) {
|
||||
func (w *WalletController) ConfirmedBalance(int32, string) (btcutil.Amount,
|
||||
error) {
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// NewAddress is called to get new addresses for delivery, change etc.
|
||||
func (w *WalletController) NewAddress(addrType lnwallet.AddressType,
|
||||
change bool, _ string) (btcutil.Address, error) {
|
||||
func (w *WalletController) NewAddress(lnwallet.AddressType, bool,
|
||||
string) (btcutil.Address, error) {
|
||||
|
||||
addr, _ := btcutil.NewAddressPubKey(
|
||||
w.RootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams,
|
||||
@@ -70,19 +80,21 @@ func (w *WalletController) NewAddress(addrType lnwallet.AddressType,
|
||||
}
|
||||
|
||||
// LastUnusedAddress currently returns dummy values.
|
||||
func (w *WalletController) LastUnusedAddress(addrType lnwallet.AddressType,
|
||||
_ string) (btcutil.Address, error) {
|
||||
func (w *WalletController) LastUnusedAddress(lnwallet.AddressType,
|
||||
string) (btcutil.Address, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// IsOurAddress currently returns a dummy value.
|
||||
func (w *WalletController) IsOurAddress(a btcutil.Address) bool {
|
||||
func (w *WalletController) IsOurAddress(btcutil.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ListAccounts currently returns a dummy value.
|
||||
func (w *WalletController) ListAccounts(_ string,
|
||||
_ *waddrmgr.KeyScope) ([]*waddrmgr.AccountProperties, error) {
|
||||
func (w *WalletController) ListAccounts(string,
|
||||
*waddrmgr.KeyScope) ([]*waddrmgr.AccountProperties, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -90,33 +102,35 @@ func (w *WalletController) ListAccounts(_ string,
|
||||
func (w *WalletController) ImportAccount(string, *hdkeychain.ExtendedKey,
|
||||
uint32, *waddrmgr.AddressType, bool) (*waddrmgr.AccountProperties,
|
||||
[]btcutil.Address, []btcutil.Address, error) {
|
||||
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
// ImportPublicKey currently returns a dummy value.
|
||||
func (w *WalletController) ImportPublicKey(*btcec.PublicKey,
|
||||
waddrmgr.AddressType) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendOutputs currently returns dummy values.
|
||||
func (w *WalletController) SendOutputs(outputs []*wire.TxOut,
|
||||
_ chainfee.SatPerKWeight, _ int32, _ string) (*wire.MsgTx, error) {
|
||||
func (w *WalletController) SendOutputs([]*wire.TxOut,
|
||||
chainfee.SatPerKWeight, int32, string) (*wire.MsgTx, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CreateSimpleTx currently returns dummy values.
|
||||
func (w *WalletController) CreateSimpleTx(outputs []*wire.TxOut,
|
||||
_ chainfee.SatPerKWeight, _ int32, _ bool) (*txauthor.AuthoredTx, error) {
|
||||
func (w *WalletController) CreateSimpleTx([]*wire.TxOut,
|
||||
chainfee.SatPerKWeight, int32, bool) (*txauthor.AuthoredTx, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ListUnspentWitness is called by the wallet when doing coin selection. We just
|
||||
// need one unspent for the funding transaction.
|
||||
func (w *WalletController) ListUnspentWitness(minConfs,
|
||||
maxConfs int32, _ string) ([]*lnwallet.Utxo, error) {
|
||||
func (w *WalletController) ListUnspentWitness(int32, int32,
|
||||
string) ([]*lnwallet.Utxo, error) {
|
||||
|
||||
// If the mock already has a list of utxos, return it.
|
||||
if w.Utxos != nil {
|
||||
@@ -140,8 +154,8 @@ func (w *WalletController) ListUnspentWitness(minConfs,
|
||||
}
|
||||
|
||||
// ListTransactionDetails currently returns dummy values.
|
||||
func (w *WalletController) ListTransactionDetails(_,
|
||||
_ int32, _ string) ([]*lnwallet.TransactionDetail, error) {
|
||||
func (w *WalletController) ListTransactionDetails(int32, int32,
|
||||
string) ([]*lnwallet.TransactionDetail, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -169,12 +183,17 @@ func (w *WalletController) ListLeasedOutputs() ([]*wtxmgr.LockedOutput, error) {
|
||||
}
|
||||
|
||||
// FundPsbt currently does nothing.
|
||||
func (w *WalletController) FundPsbt(_ *psbt.Packet, _ int32,
|
||||
_ chainfee.SatPerKWeight, _ string) (int32, error) {
|
||||
func (w *WalletController) FundPsbt(*psbt.Packet, int32, chainfee.SatPerKWeight,
|
||||
string) (int32, error) {
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// SignPsbt currently does nothing.
|
||||
func (w *WalletController) SignPsbt(*psbt.Packet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FinalizePsbt currently does nothing.
|
||||
func (w *WalletController) FinalizePsbt(_ *psbt.Packet, _ string) error {
|
||||
return nil
|
||||
@@ -187,8 +206,8 @@ func (w *WalletController) PublishTransaction(tx *wire.MsgTx, _ string) error {
|
||||
}
|
||||
|
||||
// LabelTransaction currently does nothing.
|
||||
func (w *WalletController) LabelTransaction(_ chainhash.Hash, _ string,
|
||||
_ bool) error {
|
||||
func (w *WalletController) LabelTransaction(chainhash.Hash, string,
|
||||
bool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user