lntest+rpcwallet: support remote p2tr script spend signing

This commit is contained in:
Oliver Gugger 2022-04-27 21:05:50 +02:00
parent 7f4e977073
commit d1a151010a
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
2 changed files with 61 additions and 3 deletions

View File

@ -123,12 +123,10 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
)
defer cancel()
// TODO(guggero): Fix remote taproot signing by adding
// the required fields to PSBT.
testTaprootComputeInputScriptKeySpendBip86(
ctxt, tt, wo, net,
)
// testTaprootSignOutputRawScriptSpend(ctxt, tt, wo, net)
testTaprootSignOutputRawScriptSpend(ctxt, tt, wo, net)
testTaprootSignOutputRawKeySpendBip86(ctxt, tt, wo, net)
testTaprootSignOutputRawKeySpendRootHash(
ctxt, tt, wo, net,

View File

@ -22,6 +22,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
basewallet "github.com/btcsuite/btcwallet/wallet"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg"
@ -1029,6 +1030,50 @@ func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
// If this is a BIP0086 key spend then the tap tweak is empty,
// otherwise it's set to the Taproot root hash.
in.TaprootMerkleRoot = signDesc.TapTweak
case input.TaprootScriptSpendSignMethod:
// The script spend path is a bit more involved when doing it
// through the PSBT method. We need to specify the leaf hash
// that the signer should sign for.
leaf := txscript.TapLeaf{
LeafVersion: txscript.BaseLeafVersion,
Script: signDesc.WitnessScript,
}
leafHash := leaf.TapHash()
d := in.Bip32Derivation[0]
in.TaprootBip32Derivation = []*psbt.TaprootBip32Derivation{{
XOnlyPubKey: d.PubKey[1:],
LeafHashes: [][]byte{leafHash[:]},
MasterKeyFingerprint: d.MasterKeyFingerprint,
Bip32Path: d.Bip32Path,
}}
// We also need to supply a control block. But because we don't
// know the internal key nor the merkle proofs (both is not
// supplied through the SignOutputRaw RPC) and is technically
// not really needed by the signer (since we only want a
// signature, the full witness stack is assembled by the caller
// of this RPC), we can get by with faking certain information
// that we don't have.
fakeInternalKey, _ := btcec.ParsePubKey(d.PubKey)
fakeKeyIsOdd := d.PubKey[0] == secp.PubKeyFormatCompressedOdd
controlBlock := txscript.ControlBlock{
InternalKey: fakeInternalKey,
OutputKeyYIsOdd: fakeKeyIsOdd,
LeafVersion: leaf.LeafVersion,
}
blockBytes, err := controlBlock.ToBytes()
if err != nil {
return nil, fmt.Errorf("error serializing control "+
"block: %v", err)
}
in.TaprootLeafScript = []*psbt.TaprootTapLeafScript{{
ControlBlock: blockBytes,
Script: leaf.Script,
LeafVersion: leaf.LeafVersion,
}}
}
// Okay, let's sign the input by the remote signer now.
@ -1113,6 +1158,21 @@ func extractSignature(in *psbt.PInput,
in.TaprootKeySpendSig[:schnorr.SignatureSize],
)
case input.TaprootScriptSpendSignMethod:
if len(in.TaprootScriptSpendSig) != 1 {
return nil, fmt.Errorf("remote signer returned "+
"invalid taproot script spend signature, "+
"wanted 1, got %d",
len(in.TaprootScriptSpendSig))
}
scriptSpendSig := in.TaprootScriptSpendSig[0]
if scriptSpendSig == nil {
return nil, fmt.Errorf("remote signer returned nil " +
"taproot script spend signature")
}
return schnorr.ParseSignature(scriptSpendSig.Signature)
default:
return nil, fmt.Errorf("can't extract signature, unsupported "+
"signing method: %v", signMethod)