mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
itest: add new test for SignOutputRaw RPC
This commit is contained in:
parent
1670000fd8
commit
e86a0ba197
@ -1,12 +1,18 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"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"
|
||||
)
|
||||
@ -190,6 +196,201 @@ func runDeriveSharedKey(t *harnessTest, alice *lntest.HarnessNode) {
|
||||
assertErrorMatch("use either raw_key_bytes or key_index", req)
|
||||
}
|
||||
|
||||
// testSignOutputRaw makes sure that the SignOutputRaw RPC can be used with all
|
||||
// custom ways of specifying the signing key in the key descriptor/locator.
|
||||
func testSignOutputRaw(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runSignOutputRaw(t, net, net.Alice)
|
||||
}
|
||||
|
||||
// runSignOutputRaw makes sure that the SignOutputRaw RPC can be used with all
|
||||
// custom ways of specifying the signing key in the key descriptor/locator.
|
||||
func runSignOutputRaw(t *harnessTest, net *lntest.NetworkHarness,
|
||||
alice *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
// For the next step, we need a public key. Let's use a special family
|
||||
// for this. We want this to be an index of zero.
|
||||
const testCustomKeyFamily = 44
|
||||
keyDesc, err := alice.WalletKitClient.DeriveNextKey(
|
||||
ctxt, &walletrpc.KeyReq{
|
||||
KeyFamily: testCustomKeyFamily,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
require.Equal(t.t, int32(0), keyDesc.KeyLoc.KeyIndex)
|
||||
|
||||
targetPubKey, err := btcec.ParsePubKey(keyDesc.RawKeyBytes)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// First, try with a key descriptor that only sets the public key.
|
||||
assertSignOutputRaw(
|
||||
t, net, alice, targetPubKey, &signrpc.KeyDescriptor{
|
||||
RawKeyBytes: keyDesc.RawKeyBytes,
|
||||
},
|
||||
)
|
||||
|
||||
// Now try again, this time only with the (0 index!) key locator.
|
||||
assertSignOutputRaw(
|
||||
t, net, alice, targetPubKey, &signrpc.KeyDescriptor{
|
||||
KeyLoc: &signrpc.KeyLocator{
|
||||
KeyFamily: keyDesc.KeyLoc.KeyFamily,
|
||||
KeyIndex: keyDesc.KeyLoc.KeyIndex,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// And now test everything again with a new key where we know the index
|
||||
// is not 0.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
keyDesc, err = alice.WalletKitClient.DeriveNextKey(
|
||||
ctxt, &walletrpc.KeyReq{
|
||||
KeyFamily: testCustomKeyFamily,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
require.Equal(t.t, int32(1), keyDesc.KeyLoc.KeyIndex)
|
||||
|
||||
targetPubKey, err = btcec.ParsePubKey(keyDesc.RawKeyBytes)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// First, try with a key descriptor that only sets the public key.
|
||||
assertSignOutputRaw(
|
||||
t, net, alice, targetPubKey, &signrpc.KeyDescriptor{
|
||||
RawKeyBytes: keyDesc.RawKeyBytes,
|
||||
},
|
||||
)
|
||||
|
||||
// Now try again, this time only with the key locator.
|
||||
assertSignOutputRaw(
|
||||
t, net, alice, targetPubKey, &signrpc.KeyDescriptor{
|
||||
KeyLoc: &signrpc.KeyLocator{
|
||||
KeyFamily: keyDesc.KeyLoc.KeyFamily,
|
||||
KeyIndex: keyDesc.KeyLoc.KeyIndex,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// assertSignOutputRaw sends coins to a p2wkh address derived from the given
|
||||
// target public key and then tries to spend that output again by invoking the
|
||||
// SignOutputRaw RPC with the key descriptor provided.
|
||||
func assertSignOutputRaw(t *harnessTest, net *lntest.NetworkHarness,
|
||||
alice *lntest.HarnessNode, targetPubKey *btcec.PublicKey,
|
||||
keyDesc *signrpc.KeyDescriptor) {
|
||||
|
||||
ctxb := context.Background()
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
pubKeyHash := btcutil.Hash160(targetPubKey.SerializeCompressed())
|
||||
targetAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||
pubKeyHash, harnessNetParams,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
targetScript, err := txscript.PayToAddrScript(targetAddr)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Send some coins to the generated p2wpkh address.
|
||||
_, err = alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{
|
||||
Addr: targetAddr.String(),
|
||||
Amount: 800_000,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Wait until the TX is found in the mempool.
|
||||
txid, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
targetOutputIndex := getOutputIndex(
|
||||
t, net.Miner, txid, targetAddr.String(),
|
||||
)
|
||||
|
||||
// Clear the mempool.
|
||||
mineBlocks(t, net, 1, 1)
|
||||
|
||||
// Try to spend the output now to a new p2wkh address.
|
||||
p2wkhResp, err := alice.NewAddress(ctxt, &lnrpc.NewAddressRequest{
|
||||
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
|
||||
p2wkhAdrr, err := btcutil.DecodeAddress(
|
||||
p2wkhResp.Address, harnessNetParams,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
p2wkhPkScript, err := txscript.PayToAddrScript(p2wkhAdrr)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
tx := wire.NewMsgTx(2)
|
||||
tx.TxIn = []*wire.TxIn{{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: *txid,
|
||||
Index: uint32(targetOutputIndex),
|
||||
},
|
||||
}}
|
||||
value := int64(800_000 - 200)
|
||||
tx.TxOut = []*wire.TxOut{{
|
||||
PkScript: p2wkhPkScript,
|
||||
Value: value,
|
||||
}}
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t.t, tx.Serialize(&buf))
|
||||
|
||||
signResp, err := alice.SignerClient.SignOutputRaw(
|
||||
ctxt, &signrpc.SignReq{
|
||||
RawTxBytes: buf.Bytes(),
|
||||
SignDescs: []*signrpc.SignDescriptor{{
|
||||
Output: &signrpc.TxOut{
|
||||
PkScript: targetScript,
|
||||
Value: 800_000,
|
||||
},
|
||||
InputIndex: 0,
|
||||
KeyDesc: keyDesc,
|
||||
Sighash: uint32(txscript.SigHashAll),
|
||||
WitnessScript: targetScript,
|
||||
}},
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
tx.TxIn[0].Witness = wire.TxWitness{
|
||||
append(signResp.RawSigs[0], byte(txscript.SigHashAll)),
|
||||
targetPubKey.SerializeCompressed(),
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
require.NoError(t.t, tx.Serialize(&buf))
|
||||
|
||||
_, err = alice.WalletKitClient.PublishTransaction(
|
||||
ctxt, &walletrpc.Transaction{
|
||||
TxHex: buf.Bytes(),
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Wait until the spending tx is found.
|
||||
txid, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
||||
require.NoError(t.t, err)
|
||||
p2wkhOutputIndex := getOutputIndex(
|
||||
t, net.Miner, txid, p2wkhAdrr.String(),
|
||||
)
|
||||
op := &lnrpc.OutPoint{
|
||||
TxidBytes: txid[:],
|
||||
OutputIndex: uint32(p2wkhOutputIndex),
|
||||
}
|
||||
assertWalletUnspent(t, alice, op)
|
||||
|
||||
// Mine another block to clean up the mempool and to make sure the spend
|
||||
// tx is actually included in a block.
|
||||
mineBlocks(t, net, 1, 1)
|
||||
}
|
||||
|
||||
// deriveCustomizedKey uses the family and index to derive a public key from
|
||||
// the node's walletkit client.
|
||||
func deriveCustomizedKey(ctx context.Context, node *lntest.HarnessNode,
|
||||
|
@ -157,6 +157,10 @@ var allTestCases = []*testCase{
|
||||
name: "derive shared key",
|
||||
test: testDeriveSharedKey,
|
||||
},
|
||||
{
|
||||
name: "sign output raw",
|
||||
test: testSignOutputRaw,
|
||||
},
|
||||
{
|
||||
name: "async payments benchmark",
|
||||
test: testAsyncPayments,
|
||||
|
@ -8,7 +8,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
@ -463,3 +465,31 @@ func findTxAtHeight(t *harnessTest, height int32,
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOutputIndex returns the output index of the given address in the given
|
||||
// transaction.
|
||||
func getOutputIndex(t *harnessTest, miner *lntest.HarnessMiner,
|
||||
txid *chainhash.Hash, addr string) int {
|
||||
|
||||
t.t.Helper()
|
||||
|
||||
// We'll then extract the raw transaction from the mempool in order to
|
||||
// determine the index of the p2tr output.
|
||||
tx, err := miner.Client.GetRawTransaction(txid)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
p2trOutputIndex := -1
|
||||
for i, txOut := range tx.MsgTx().TxOut {
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
||||
txOut.PkScript, miner.ActiveNet,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
if addrs[0].String() == addr {
|
||||
p2trOutputIndex = i
|
||||
}
|
||||
}
|
||||
require.Greater(t.t, p2trOutputIndex, -1)
|
||||
|
||||
return p2trOutputIndex
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user