multi: move Input interface and related code

This commit is a step to split the lnwallet package. It puts the Input
interface and implementations in a separate package along with all their
dependencies from lnwallet.
This commit is contained in:
Joost Jager
2019-01-16 15:47:43 +01:00
parent 667474db75
commit 9e012ecc93
66 changed files with 811 additions and 689 deletions

178
input/input.go Normal file
View File

@@ -0,0 +1,178 @@
package input
import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// Input represents an abstract UTXO which is to be spent using a sweeping
// transaction. The method provided give the caller all information needed to
// construct a valid input within a sweeping transaction to sweep this
// lingering UTXO.
type Input interface {
// Outpoint returns the reference to the output being spent, used to
// construct the corresponding transaction input.
OutPoint() *wire.OutPoint
// WitnessType returns an enum specifying the type of witness that must
// be generated in order to spend this output.
WitnessType() WitnessType
// SignDesc returns a reference to a spendable output's sign
// descriptor, which is used during signing to compute a valid witness
// that spends this output.
SignDesc() *SignDescriptor
// CraftInputScript returns a valid set of input scripts allowing this
// output to be spent. The returns input scripts should target the
// input at location txIndex within the passed transaction. The input
// scripts generated by this method support spending p2wkh, p2wsh, and
// also nested p2sh outputs.
CraftInputScript(signer Signer, txn *wire.MsgTx,
hashCache *txscript.TxSigHashes,
txinIdx int) (*Script, error)
// BlocksToMaturity returns the relative timelock, as a number of
// blocks, that must be built on top of the confirmation height before
// the output can be spent. For non-CSV locked inputs this is always
// zero.
BlocksToMaturity() uint32
// HeightHint returns the minimum height at which a confirmed spending
// tx can occur.
HeightHint() uint32
}
type inputKit struct {
outpoint wire.OutPoint
witnessType WitnessType
signDesc SignDescriptor
heightHint uint32
}
// OutPoint returns the breached output's identifier that is to be included as
// a transaction input.
func (i *inputKit) OutPoint() *wire.OutPoint {
return &i.outpoint
}
// WitnessType returns the type of witness that must be generated to spend the
// breached output.
func (i *inputKit) WitnessType() WitnessType {
return i.witnessType
}
// SignDesc returns the breached output's SignDescriptor, which is used during
// signing to compute the witness.
func (i *inputKit) SignDesc() *SignDescriptor {
return &i.signDesc
}
// HeightHint returns the minimum height at which a confirmed spending
// tx can occur.
func (i *inputKit) HeightHint() uint32 {
return i.heightHint
}
// BaseInput contains all the information needed to sweep a basic output
// (CSV/CLTV/no time lock)
type BaseInput struct {
inputKit
}
// MakeBaseInput assembles a new BaseInput that can be used to construct a
// sweep transaction.
func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
signDescriptor *SignDescriptor, heightHint uint32) BaseInput {
return BaseInput{
inputKit{
outpoint: *outpoint,
witnessType: witnessType,
signDesc: *signDescriptor,
heightHint: heightHint,
},
}
}
// CraftInputScript returns a valid set of input scripts allowing this output
// to be spent. The returns input scripts should target the input at location
// txIndex within the passed transaction. The input scripts generated by this
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
witnessFunc := bi.witnessType.GenWitnessFunc(
signer, bi.SignDesc(),
)
return witnessFunc(txn, hashCache, txinIdx)
}
// BlocksToMaturity returns the relative timelock, as a number of blocks, that
// must be built on top of the confirmation height before the output can be
// spent. For non-CSV locked inputs this is always zero.
func (bi *BaseInput) BlocksToMaturity() uint32 {
return 0
}
// HtlcSucceedInput constitutes a sweep input that needs a pre-image. The input
// is expected to reside on the commitment tx of the remote party and should
// not be a second level tx output.
type HtlcSucceedInput struct {
inputKit
preimage []byte
}
// MakeHtlcSucceedInput assembles a new redeem input that can be used to
// construct a sweep transaction.
func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
signDescriptor *SignDescriptor,
preimage []byte, heightHint uint32) HtlcSucceedInput {
return HtlcSucceedInput{
inputKit: inputKit{
outpoint: *outpoint,
witnessType: HtlcAcceptedRemoteSuccess,
signDesc: *signDescriptor,
heightHint: heightHint,
},
preimage: preimage,
}
}
// CraftInputScript returns a valid set of input scripts allowing this output
// to be spent. The returns input scripts should target the input at location
// txIndex within the passed transaction. The input scripts generated by this
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
desc := h.signDesc
desc.SigHashes = hashCache
desc.InputIndex = txinIdx
witness, err := SenderHtlcSpendRedeem(
signer, &desc, txn, h.preimage,
)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
}
// BlocksToMaturity returns the relative timelock, as a number of blocks, that
// must be built on top of the confirmation height before the output can be
// spent.
func (h *HtlcSucceedInput) BlocksToMaturity() uint32 {
return 0
}
// Compile-time constraints to ensure each input struct implement the Input
// interface.
var _ Input = (*BaseInput)(nil)
var _ Input = (*HtlcSucceedInput)(nil)

1125
input/script_utils.go Normal file

File diff suppressed because it is too large Load Diff

917
input/script_utils_test.go Normal file
View File

@@ -0,0 +1,917 @@
package input
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"testing"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/keychain"
)
// TestRevocationKeyDerivation tests that given a public key, and a revocation
// hash, the homomorphic revocation public and private key derivation work
// properly.
func TestRevocationKeyDerivation(t *testing.T) {
t.Parallel()
// First, we'll generate a commitment point, and a commitment secret.
// These will be used to derive the ultimate revocation keys.
revocationPreimage := testHdSeed.CloneBytes()
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
revocationPreimage)
// With the commitment secrets generated, we'll now create the base
// keys we'll use to derive the revocation key from.
basePriv, basePub := btcec.PrivKeyFromBytes(btcec.S256(),
testWalletPrivKey)
// With the point and key obtained, we can now derive the revocation
// key itself.
revocationPub := DeriveRevocationPubkey(basePub, commitPoint)
// The revocation public key derived from the original public key, and
// the one derived from the private key should be identical.
revocationPriv := DeriveRevocationPrivKey(basePriv, commitSecret)
if !revocationPub.IsEqual(revocationPriv.PubKey()) {
t.Fatalf("derived public keys don't match!")
}
}
// TestTweakKeyDerivation tests that given a public key, and commitment tweak,
// then we're able to properly derive a tweaked private key that corresponds to
// the computed tweak public key. This scenario ensure that our key derivation
// for any of the non revocation keys on the commitment transaction is correct.
func TestTweakKeyDerivation(t *testing.T) {
t.Parallel()
// First, we'll generate a base public key that we'll be "tweaking".
baseSecret := testHdSeed.CloneBytes()
basePriv, basePub := btcec.PrivKeyFromBytes(btcec.S256(), baseSecret)
// With the base key create, we'll now create a commitment point, and
// from that derive the bytes we'll used to tweak the base public key.
commitPoint := ComputeCommitmentPoint(bobsPrivKey)
commitTweak := SingleTweakBytes(commitPoint, basePub)
// Next, we'll modify the public key. When we apply the same operation
// to the private key we should get a key that matches.
tweakedPub := TweakPubKey(basePub, commitPoint)
// Finally, attempt to re-generate the private key that matches the
// tweaked public key. The derived key should match exactly.
derivedPriv := TweakPrivKey(basePriv, commitTweak)
if !derivedPriv.PubKey().IsEqual(tweakedPub) {
t.Fatalf("pub keys don't match")
}
}
// makeWitnessTestCase is a helper function used within test cases involving
// the validity of a crafted witness. This function is a wrapper function which
// allows constructing table-driven tests. In the case of an error while
// constructing the witness, the test fails fatally.
func makeWitnessTestCase(t *testing.T,
f func() (wire.TxWitness, error)) func() wire.TxWitness {
return func() wire.TxWitness {
witness, err := f()
if err != nil {
t.Fatalf("unable to create witness test case: %v", err)
}
return witness
}
}
// TestHTLCSenderSpendValidation tests all possible valid+invalid redemption
// paths in the script used within the sender's commitment transaction for an
// outgoing HTLC.
//
// The following cases are exercised by this test:
// sender script:
// * receiver spends
// * revoke w/ sig
// * HTLC with invalid preimage size
// * HTLC with valid preimage size + sig
// * sender spends
// * invalid lock-time for CLTV
// * invalid sequence for CSV
// * valid lock-time+sequence, valid sig
func TestHTLCSenderSpendValidation(t *testing.T) {
t.Parallel()
// We generate a fake output, and the corresponding txin. This output
// doesn't need to exist, as we'll only be validating spending from the
// transaction that references this.
txid, err := chainhash.NewHash(testHdSeed.CloneBytes())
if err != nil {
t.Fatalf("unable to create txid: %v", err)
}
fundingOut := &wire.OutPoint{
Hash: *txid,
Index: 50,
}
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
// Next we'll the commitment secret for our commitment tx and also the
// revocation key that we'll use as well.
revokePreimage := testHdSeed.CloneBytes()
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
revokePreimage)
// Generate a payment preimage to be used below.
paymentPreimage := revokePreimage
paymentPreimage[0] ^= 1
paymentHash := sha256.Sum256(paymentPreimage[:])
// We'll also need some tests keys for alice and bob, and metadata of
// the HTLC output.
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
testWalletPrivKey)
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
bobsPrivKey)
paymentAmt := btcutil.Amount(1 * 10e8)
aliceLocalKey := TweakPubKey(aliceKeyPub, commitPoint)
bobLocalKey := TweakPubKey(bobKeyPub, commitPoint)
// As we'll be modeling spends from Alice's commitment transaction,
// we'll be using Bob's base point for the revocation key.
revocationKey := DeriveRevocationPubkey(bobKeyPub, commitPoint)
// Generate the raw HTLC redemption scripts, and its p2wsh counterpart.
htlcWitnessScript, err := SenderHTLCScript(aliceLocalKey, bobLocalKey,
revocationKey, paymentHash[:])
if err != nil {
t.Fatalf("unable to create htlc sender script: %v", err)
}
htlcPkScript, err := WitnessScriptHash(htlcWitnessScript)
if err != nil {
t.Fatalf("unable to create p2wsh htlc script: %v", err)
}
// This will be Alice's commitment transaction. In this scenario Alice
// is sending an HTLC to a node she has a path to (could be Bob, could
// be multiple hops down, it doesn't really matter).
htlcOutput := &wire.TxOut{
Value: int64(paymentAmt),
PkScript: htlcPkScript,
}
senderCommitTx := wire.NewMsgTx(2)
senderCommitTx.AddTxIn(fakeFundingTxIn)
senderCommitTx.AddTxOut(htlcOutput)
prevOut := &wire.OutPoint{
Hash: senderCommitTx.TxHash(),
Index: 0,
}
sweepTx := wire.NewMsgTx(2)
sweepTx.AddTxIn(wire.NewTxIn(prevOut, nil, nil))
sweepTx.AddTxOut(
&wire.TxOut{
PkScript: []byte("doesn't matter"),
Value: 1 * 10e8,
},
)
sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx)
bobCommitTweak := SingleTweakBytes(commitPoint, bobKeyPub)
aliceCommitTweak := SingleTweakBytes(commitPoint, aliceKeyPub)
// Finally, we'll create mock signers for both of them based on their
// private keys. This test simplifies a bit and uses the same key as
// the base point for all scripts and derivations.
bobSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{bobKeyPriv}}
aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}}
// We'll also generate a signature on the sweep transaction above
// that will act as Bob's signature to Alice for the second level HTLC
// transaction.
bobSignDesc := SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
bobRecvrSig, err := bobSigner.SignOutputRaw(sweepTx, &bobSignDesc)
if err != nil {
t.Fatalf("unable to generate alice signature: %v", err)
}
testCases := []struct {
witness func() wire.TxWitness
valid bool
}{
{
// revoke w/ sig
// TODO(roasbeef): test invalid revoke
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return SenderHtlcSpendRevokeWithKey(bobSigner, signDesc,
revocationKey, sweepTx)
}),
true,
},
{
// HTLC with invalid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return SenderHtlcSpendRedeem(bobSigner, signDesc,
sweepTx,
// Invalid preimage length
bytes.Repeat([]byte{1}, 45))
}),
false,
},
{
// HTLC with valid preimage size + sig
// TODO(roasbeef): invalid preimage
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return SenderHtlcSpendRedeem(bobSigner, signDesc,
sweepTx, paymentPreimage)
}),
true,
},
{
// valid spend to the transition the state of the HTLC
// output with the second level HTLC timeout
// transaction.
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return SenderHtlcSpendTimeout(bobRecvrSig, aliceSigner,
signDesc, sweepTx)
}),
true,
},
}
// TODO(roasbeef): set of cases to ensure able to sign w/ keypath and
// not
for i, testCase := range testCases {
sweepTx.TxIn[0].Witness = testCase.witness()
vm, err := txscript.NewEngine(htlcPkScript,
sweepTx, 0, txscript.StandardVerifyFlags, nil,
nil, int64(paymentAmt))
if err != nil {
t.Fatalf("unable to create engine: %v", err)
}
// This buffer will trace execution of the Script, only dumping
// out to stdout in the case that a test fails.
var debugBuf bytes.Buffer
done := false
for !done {
dis, err := vm.DisasmPC()
if err != nil {
t.Fatalf("stepping (%v)\n", err)
}
debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis))
done, err = vm.Step()
if err != nil && testCase.valid {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v failed, spend "+
"should be valid: %v", i, err)
} else if err == nil && !testCase.valid && done {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v succeed, spend "+
"should be invalid: %v", i, err)
}
debugBuf.WriteString(fmt.Sprintf("Stack: %v", vm.GetStack()))
debugBuf.WriteString(fmt.Sprintf("AltStack: %v", vm.GetAltStack()))
}
}
}
// TestHTLCReceiverSpendValidation tests all possible valid+invalid redemption
// paths in the script used within the receiver's commitment transaction for an
// incoming HTLC.
//
// The following cases are exercised by this test:
// * receiver spends
// * HTLC redemption w/ invalid preimage size
// * HTLC redemption w/ invalid sequence
// * HTLC redemption w/ valid preimage size
// * sender spends
// * revoke w/ sig
// * refund w/ invalid lock time
// * refund w/ valid lock time
func TestHTLCReceiverSpendValidation(t *testing.T) {
t.Parallel()
// We generate a fake output, and the corresponding txin. This output
// doesn't need to exist, as we'll only be validating spending from the
// transaction that references this.
txid, err := chainhash.NewHash(testHdSeed.CloneBytes())
if err != nil {
t.Fatalf("unable to create txid: %v", err)
}
fundingOut := &wire.OutPoint{
Hash: *txid,
Index: 50,
}
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
// Next we'll the commitment secret for our commitment tx and also the
// revocation key that we'll use as well.
revokePreimage := testHdSeed.CloneBytes()
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
revokePreimage)
// Generate a payment preimage to be used below.
paymentPreimage := revokePreimage
paymentPreimage[0] ^= 1
paymentHash := sha256.Sum256(paymentPreimage[:])
// We'll also need some tests keys for alice and bob, and metadata of
// the HTLC output.
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
testWalletPrivKey)
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
bobsPrivKey)
paymentAmt := btcutil.Amount(1 * 10e8)
cltvTimeout := uint32(8)
aliceLocalKey := TweakPubKey(aliceKeyPub, commitPoint)
bobLocalKey := TweakPubKey(bobKeyPub, commitPoint)
// As we'll be modeling spends from Bob's commitment transaction, we'll
// be using Alice's base point for the revocation key.
revocationKey := DeriveRevocationPubkey(aliceKeyPub, commitPoint)
// Generate the raw HTLC redemption scripts, and its p2wsh counterpart.
htlcWitnessScript, err := ReceiverHTLCScript(cltvTimeout, aliceLocalKey,
bobLocalKey, revocationKey, paymentHash[:])
if err != nil {
t.Fatalf("unable to create htlc sender script: %v", err)
}
htlcPkScript, err := WitnessScriptHash(htlcWitnessScript)
if err != nil {
t.Fatalf("unable to create p2wsh htlc script: %v", err)
}
// This will be Bob's commitment transaction. In this scenario Alice is
// sending an HTLC to a node she has a path to (could be Bob, could be
// multiple hops down, it doesn't really matter).
htlcOutput := &wire.TxOut{
Value: int64(paymentAmt),
PkScript: htlcWitnessScript,
}
receiverCommitTx := wire.NewMsgTx(2)
receiverCommitTx.AddTxIn(fakeFundingTxIn)
receiverCommitTx.AddTxOut(htlcOutput)
prevOut := &wire.OutPoint{
Hash: receiverCommitTx.TxHash(),
Index: 0,
}
sweepTx := wire.NewMsgTx(2)
sweepTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *prevOut,
})
sweepTx.AddTxOut(
&wire.TxOut{
PkScript: []byte("doesn't matter"),
Value: 1 * 10e8,
},
)
sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx)
bobCommitTweak := SingleTweakBytes(commitPoint, bobKeyPub)
aliceCommitTweak := SingleTweakBytes(commitPoint, aliceKeyPub)
// Finally, we'll create mock signers for both of them based on their
// private keys. This test simplifies a bit and uses the same key as
// the base point for all scripts and derivations.
bobSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{bobKeyPriv}}
aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}}
// We'll also generate a signature on the sweep transaction above
// that will act as Alice's signature to Bob for the second level HTLC
// transaction.
aliceSignDesc := SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
aliceSenderSig, err := aliceSigner.SignOutputRaw(sweepTx, &aliceSignDesc)
if err != nil {
t.Fatalf("unable to generate alice signature: %v", err)
}
// TODO(roasbeef): modify valid to check precise script errors?
testCases := []struct {
witness func() wire.TxWitness
valid bool
}{
{
// HTLC redemption w/ invalid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return ReceiverHtlcSpendRedeem(aliceSenderSig,
bytes.Repeat([]byte{1}, 45), bobSigner,
signDesc, sweepTx)
}),
false,
},
{
// HTLC redemption w/ valid preimage size
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: bobCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return ReceiverHtlcSpendRedeem(aliceSenderSig,
paymentPreimage[:], bobSigner,
signDesc, sweepTx)
}),
true,
},
{
// revoke w/ sig
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return ReceiverHtlcSpendRevokeWithKey(aliceSigner,
signDesc, revocationKey, sweepTx)
}),
true,
},
{
// refund w/ invalid lock time
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return ReceiverHtlcSpendTimeout(aliceSigner, signDesc,
sweepTx, int32(cltvTimeout-2))
}),
false,
},
{
// refund w/ valid lock time
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
SingleTweak: aliceCommitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return ReceiverHtlcSpendTimeout(aliceSigner, signDesc,
sweepTx, int32(cltvTimeout))
}),
true,
},
}
for i, testCase := range testCases {
sweepTx.TxIn[0].Witness = testCase.witness()
vm, err := txscript.NewEngine(htlcPkScript,
sweepTx, 0, txscript.StandardVerifyFlags, nil,
nil, int64(paymentAmt))
if err != nil {
t.Fatalf("unable to create engine: %v", err)
}
// This buffer will trace execution of the Script, only dumping
// out to stdout in the case that a test fails.
var debugBuf bytes.Buffer
done := false
for !done {
dis, err := vm.DisasmPC()
if err != nil {
t.Fatalf("stepping (%v)\n", err)
}
debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis))
done, err = vm.Step()
if err != nil && testCase.valid {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v failed, spend should be valid: %v", i, err)
} else if err == nil && !testCase.valid && done {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v succeed, spend should be invalid: %v", i, err)
}
debugBuf.WriteString(fmt.Sprintf("Stack: %v", vm.GetStack()))
debugBuf.WriteString(fmt.Sprintf("AltStack: %v", vm.GetAltStack()))
}
}
}
// TestSecondLevelHtlcSpends tests all the possible redemption clauses from the
// HTLC success and timeout covenant transactions.
func TestSecondLevelHtlcSpends(t *testing.T) {
t.Parallel()
// We'll start be creating a creating a 2BTC HTLC.
const htlcAmt = btcutil.Amount(2 * 10e8)
// In all of our scenarios, the CSV timeout to claim a self output will
// be 5 blocks.
const claimDelay = 5
// First we'll set up some initial key state for Alice and Bob that
// will be used in the scripts we created below.
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
testWalletPrivKey)
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
bobsPrivKey)
revokePreimage := testHdSeed.CloneBytes()
commitSecret, commitPoint := btcec.PrivKeyFromBytes(
btcec.S256(), revokePreimage)
// As we're modeling this as Bob sweeping the HTLC on-chain from his
// commitment transaction after a period of time, we'll be using a
// revocation key derived from Alice's base point and his secret.
revocationKey := DeriveRevocationPubkey(aliceKeyPub, commitPoint)
// Next, craft a fake HTLC outpoint that we'll use to generate the
// sweeping transaction using.
txid, err := chainhash.NewHash(testHdSeed.CloneBytes())
if err != nil {
t.Fatalf("unable to create txid: %v", err)
}
htlcOutPoint := &wire.OutPoint{
Hash: *txid,
Index: 0,
}
sweepTx := wire.NewMsgTx(2)
sweepTx.AddTxIn(wire.NewTxIn(htlcOutPoint, nil, nil))
sweepTx.AddTxOut(
&wire.TxOut{
PkScript: []byte("doesn't matter"),
Value: 1 * 10e8,
},
)
sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx)
// The delay key will be crafted using Bob's public key as the output
// we created will be spending from Alice's commitment transaction.
delayKey := TweakPubKey(bobKeyPub, commitPoint)
// The commit tweak will be required in order for Bob to derive the
// proper key need to spend the output.
commitTweak := SingleTweakBytes(commitPoint, bobKeyPub)
// Finally we'll generate the HTLC script itself that we'll be spending
// from. The revocation clause can be claimed by Alice, while Bob can
// sweep the output after a particular delay.
htlcWitnessScript, err := SecondLevelHtlcScript(revocationKey,
delayKey, claimDelay)
if err != nil {
t.Fatalf("unable to create htlc script: %v", err)
}
htlcPkScript, err := WitnessScriptHash(htlcWitnessScript)
if err != nil {
t.Fatalf("unable to create htlc output: %v", err)
}
htlcOutput := &wire.TxOut{
PkScript: htlcPkScript,
Value: int64(htlcAmt),
}
// TODO(roasbeef): make actually use timeout/success txns?
// Finally, we'll create mock signers for both of them based on their
// private keys. This test simplifies a bit and uses the same key as
// the base point for all scripts and derivations.
bobSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{bobKeyPriv}}
aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}}
testCases := []struct {
witness func() wire.TxWitness
valid bool
}{
{
// Sender of the HTLC attempts to activate the
// revocation clause, but uses the wrong key (fails to
// use the double tweak in this case).
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return HtlcSpendRevoke(aliceSigner, signDesc,
sweepTx)
}),
false,
},
{
// Sender of HTLC activates the revocation clause.
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: aliceKeyPub,
},
DoubleTweak: commitSecret,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return HtlcSpendRevoke(aliceSigner, signDesc,
sweepTx)
}),
true,
},
{
// Receiver of the HTLC attempts to sweep, but tries to
// do so pre-maturely with a smaller CSV delay (2
// blocks instead of 5 blocks).
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: commitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return HtlcSpendSuccess(bobSigner, signDesc,
sweepTx, claimDelay-3)
}),
false,
},
{
// Receiver of the HTLC sweeps with the proper CSV
// delay, but uses the wrong key (leaves off the single
// tweak).
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return HtlcSpendSuccess(bobSigner, signDesc,
sweepTx, claimDelay)
}),
false,
},
{
// Receiver of the HTLC sweeps with the proper CSV
// delay, and the correct key.
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
signDesc := &SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
PubKey: bobKeyPub,
},
SingleTweak: commitTweak,
WitnessScript: htlcWitnessScript,
Output: htlcOutput,
HashType: txscript.SigHashAll,
SigHashes: sweepTxSigHashes,
InputIndex: 0,
}
return HtlcSpendSuccess(bobSigner, signDesc,
sweepTx, claimDelay)
}),
true,
},
}
for i, testCase := range testCases {
sweepTx.TxIn[0].Witness = testCase.witness()
vm, err := txscript.NewEngine(htlcPkScript,
sweepTx, 0, txscript.StandardVerifyFlags, nil,
nil, int64(htlcAmt))
if err != nil {
t.Fatalf("unable to create engine: %v", err)
}
// This buffer will trace execution of the Script, only dumping
// out to stdout in the case that a test fails.
var debugBuf bytes.Buffer
done := false
for !done {
dis, err := vm.DisasmPC()
if err != nil {
t.Fatalf("stepping (%v)\n", err)
}
debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis))
done, err = vm.Step()
if err != nil && testCase.valid {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v failed, spend "+
"should be valid: %v", i, err)
} else if err == nil && !testCase.valid && done {
fmt.Println(debugBuf.String())
t.Fatalf("spend test case #%v succeed, spend "+
"should be invalid: %v", i, err)
}
debugBuf.WriteString(fmt.Sprintf("Stack: %v", vm.GetStack()))
debugBuf.WriteString(fmt.Sprintf("AltStack: %v", vm.GetAltStack()))
}
}
}
// TestSpecificationKeyDerivation implements the test vectors provided in
// BOLT-03, Appendix E.
func TestSpecificationKeyDerivation(t *testing.T) {
const (
baseSecretHex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
perCommitmentSecretHex = "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"
basePointHex = "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"
perCommitmentPointHex = "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"
)
baseSecret, err := privkeyFromHex(baseSecretHex)
if err != nil {
t.Fatalf("Failed to parse serialized privkey: %v", err)
}
perCommitmentSecret, err := privkeyFromHex(perCommitmentSecretHex)
if err != nil {
t.Fatalf("Failed to parse serialized privkey: %v", err)
}
basePoint, err := pubkeyFromHex(basePointHex)
if err != nil {
t.Fatalf("Failed to parse serialized pubkey: %v", err)
}
perCommitmentPoint, err := pubkeyFromHex(perCommitmentPointHex)
if err != nil {
t.Fatalf("Failed to parse serialized pubkey: %v", err)
}
// name: derivation of key from basepoint and per_commitment_point
const expectedLocalKeyHex = "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"
actualLocalKey := TweakPubKey(basePoint, perCommitmentPoint)
actualLocalKeyHex := pubkeyToHex(actualLocalKey)
if actualLocalKeyHex != expectedLocalKeyHex {
t.Errorf("Incorrect derivation of local public key: "+
"expected %v, got %v", expectedLocalKeyHex, actualLocalKeyHex)
}
// name: derivation of secret key from basepoint secret and per_commitment_secret
const expectedLocalPrivKeyHex = "cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f"
tweak := SingleTweakBytes(perCommitmentPoint, basePoint)
actualLocalPrivKey := TweakPrivKey(baseSecret, tweak)
actualLocalPrivKeyHex := privkeyToHex(actualLocalPrivKey)
if actualLocalPrivKeyHex != expectedLocalPrivKeyHex {
t.Errorf("Incorrect derivation of local private key: "+
"expected %v, got %v, %v", expectedLocalPrivKeyHex,
actualLocalPrivKeyHex, hex.EncodeToString(tweak))
}
// name: derivation of revocation key from basepoint and per_commitment_point
const expectedRevocationKeyHex = "02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0"
actualRevocationKey := DeriveRevocationPubkey(basePoint, perCommitmentPoint)
actualRevocationKeyHex := pubkeyToHex(actualRevocationKey)
if actualRevocationKeyHex != expectedRevocationKeyHex {
t.Errorf("Incorrect derivation of revocation public key: "+
"expected %v, got %v", expectedRevocationKeyHex,
actualRevocationKeyHex)
}
// name: derivation of revocation secret from basepoint_secret and per_commitment_secret
const expectedRevocationPrivKeyHex = "d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110"
actualRevocationPrivKey := DeriveRevocationPrivKey(baseSecret,
perCommitmentSecret)
actualRevocationPrivKeyHex := privkeyToHex(actualRevocationPrivKey)
if actualRevocationPrivKeyHex != expectedRevocationPrivKeyHex {
t.Errorf("Incorrect derivation of revocation private key: "+
"expected %v, got %v", expectedRevocationPrivKeyHex,
actualRevocationPrivKeyHex)
}
}

225
input/signdescriptor.go Normal file
View File

@@ -0,0 +1,225 @@
package input
import (
"encoding/binary"
"errors"
"io"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/keychain"
)
var (
// ErrTweakOverdose signals a SignDescriptor is invalid because both of its
// SingleTweak and DoubleTweak are non-nil.
ErrTweakOverdose = errors.New("sign descriptor should only have one tweak")
)
// SignDescriptor houses the necessary information required to successfully sign
// a given output. This struct is used by the Signer interface in order to gain
// access to critical data needed to generate a valid signature.
type SignDescriptor struct {
// KeyDesc is a descriptor that precisely describes *which* key to use
// for signing. This may provide the raw public key directly, or
// require the Signer to re-derive the key according to the populated
// derivation path.
KeyDesc keychain.KeyDescriptor
// SingleTweak is a scalar value that will be added to the private key
// corresponding to the above public key to obtain the private key to
// be used to sign this input. This value is typically derived via the
// following computation:
//
// * derivedKey = privkey + sha256(perCommitmentPoint || pubKey) mod N
//
// NOTE: If this value is nil, then the input can be signed using only
// the above public key. Either a SingleTweak should be set or a
// DoubleTweak, not both.
SingleTweak []byte
// DoubleTweak is a private key that will be used in combination with
// its corresponding private key to derive the private key that is to
// be used to sign the target input. Within the Lightning protocol,
// this value is typically the commitment secret from a previously
// revoked commitment transaction. This value is in combination with
// two hash values, and the original private key to derive the private
// key to be used when signing.
//
// * k = (privKey*sha256(pubKey || tweakPub) +
// tweakPriv*sha256(tweakPub || pubKey)) mod N
//
// NOTE: If this value is nil, then the input can be signed using only
// the above public key. Either a SingleTweak should be set or a
// DoubleTweak, not both.
DoubleTweak *btcec.PrivateKey
// WitnessScript is the full script required to properly redeem the
// output. This field will only be populated if a p2wsh or a p2sh
// output is being signed.
WitnessScript []byte
// Output is the target output which should be signed. The PkScript and
// Value fields within the output should be properly populated,
// otherwise an invalid signature may be generated.
Output *wire.TxOut
// HashType is the target sighash type that should be used when
// generating the final sighash, and signature.
HashType txscript.SigHashType
// SigHashes is the pre-computed sighash midstate to be used when
// generating the final sighash for signing.
SigHashes *txscript.TxSigHashes
// InputIndex is the target input within the transaction that should be
// signed.
InputIndex int
}
// WriteSignDescriptor serializes a SignDescriptor struct into the passed
// io.Writer stream.
//
// NOTE: We assume the SigHashes and InputIndex fields haven't been assigned
// yet, since that is usually done just before broadcast by the witness
// generator.
func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
err := binary.Write(w, binary.BigEndian, sd.KeyDesc.Family)
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, sd.KeyDesc.Index)
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, sd.KeyDesc.PubKey != nil)
if err != nil {
return err
}
if sd.KeyDesc.PubKey != nil {
serializedPubKey := sd.KeyDesc.PubKey.SerializeCompressed()
if err := wire.WriteVarBytes(w, 0, serializedPubKey); err != nil {
return err
}
}
if err := wire.WriteVarBytes(w, 0, sd.SingleTweak); err != nil {
return err
}
var doubleTweakBytes []byte
if sd.DoubleTweak != nil {
doubleTweakBytes = sd.DoubleTweak.Serialize()
}
if err := wire.WriteVarBytes(w, 0, doubleTweakBytes); err != nil {
return err
}
if err := wire.WriteVarBytes(w, 0, sd.WitnessScript); err != nil {
return err
}
if err := writeTxOut(w, sd.Output); err != nil {
return err
}
var scratch [4]byte
binary.BigEndian.PutUint32(scratch[:], uint32(sd.HashType))
if _, err := w.Write(scratch[:]); err != nil {
return err
}
return nil
}
// ReadSignDescriptor deserializes a SignDescriptor struct from the passed
// io.Reader stream.
func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error {
err := binary.Read(r, binary.BigEndian, &sd.KeyDesc.Family)
if err != nil {
return err
}
err = binary.Read(r, binary.BigEndian, &sd.KeyDesc.Index)
if err != nil {
return err
}
var hasKey bool
err = binary.Read(r, binary.BigEndian, &hasKey)
if err != nil {
return err
}
if hasKey {
pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey")
if err != nil {
return err
}
sd.KeyDesc.PubKey, err = btcec.ParsePubKey(
pubKeyBytes, btcec.S256(),
)
if err != nil {
return err
}
}
singleTweak, err := wire.ReadVarBytes(r, 0, 32, "singleTweak")
if err != nil {
return err
}
// Serializing a SignDescriptor with a nil-valued SingleTweak results
// in deserializing a zero-length slice. Since a nil-valued SingleTweak
// has special meaning and a zero-length slice for a SingleTweak is
// invalid, we can use the zero-length slice as the flag for a
// nil-valued SingleTweak.
if len(singleTweak) == 0 {
sd.SingleTweak = nil
} else {
sd.SingleTweak = singleTweak
}
doubleTweakBytes, err := wire.ReadVarBytes(r, 0, 32, "doubleTweak")
if err != nil {
return err
}
// Serializing a SignDescriptor with a nil-valued DoubleTweak results
// in deserializing a zero-length slice. Since a nil-valued DoubleTweak
// has special meaning and a zero-length slice for a DoubleTweak is
// invalid, we can use the zero-length slice as the flag for a
// nil-valued DoubleTweak.
if len(doubleTweakBytes) == 0 {
sd.DoubleTweak = nil
} else {
sd.DoubleTweak, _ = btcec.PrivKeyFromBytes(btcec.S256(), doubleTweakBytes)
}
// Only one tweak should ever be set, fail if both are present.
if sd.SingleTweak != nil && sd.DoubleTweak != nil {
return ErrTweakOverdose
}
witnessScript, err := wire.ReadVarBytes(r, 0, 500, "witnessScript")
if err != nil {
return err
}
sd.WitnessScript = witnessScript
txOut := &wire.TxOut{}
if err := readTxOut(r, txOut); err != nil {
return err
}
sd.Output = txOut
var hashType [4]byte
if _, err := io.ReadFull(r, hashType[:]); err != nil {
return err
}
sd.HashType = txscript.SigHashType(binary.BigEndian.Uint32(hashType[:]))
return nil
}

View File

@@ -0,0 +1,129 @@
package input
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/keychain"
)
func TestSignDescriptorSerialization(t *testing.T) {
keys := [][]byte{
{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
0xb4, 0x12, 0xa3,
},
{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e,
0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca,
0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0,
0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64,
0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9,
0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56,
0xb4, 0x12, 0xa3,
},
}
signDescriptors := []SignDescriptor{
{
SingleTweak: []byte{
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02,
},
WitnessScript: []byte{
0x00, 0x14, 0xee, 0x91, 0x41, 0x7e, 0x85, 0x6c, 0xde,
0x10, 0xa2, 0x91, 0x1e, 0xdc, 0xbd, 0xbd, 0x69, 0xe2,
0xef, 0xb5, 0x71, 0x48,
},
Output: &wire.TxOut{
Value: 5000000000,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
HashType: txscript.SigHashAll,
},
// Test serializing a SignDescriptor with a nil-valued PrivateTweak
{
SingleTweak: nil,
WitnessScript: []byte{
0x00, 0x14, 0xee, 0x91, 0x41, 0x7e, 0x85, 0x6c, 0xde,
0x10, 0xa2, 0x91, 0x1e, 0xdc, 0xbd, 0xbd, 0x69, 0xe2,
0xef, 0xb5, 0x71, 0x48,
},
Output: &wire.TxOut{
Value: 5000000000,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
HashType: txscript.SigHashAll,
},
}
for i := 0; i < len(signDescriptors); i++ {
// Parse pubkeys for each sign descriptor.
sd := &signDescriptors[i]
pubkey, err := btcec.ParsePubKey(keys[i], btcec.S256())
if err != nil {
t.Fatalf("unable to parse pubkey: %v", err)
}
sd.KeyDesc = keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: 50,
Index: 99,
},
PubKey: pubkey,
}
// Test that serialize -> deserialize yields same result as original.
var buf bytes.Buffer
if err := WriteSignDescriptor(&buf, sd); err != nil {
t.Fatalf("unable to serialize sign descriptor[%v]: %v", i, sd)
}
desSd := &SignDescriptor{}
if err := ReadSignDescriptor(&buf, desSd); err != nil {
t.Fatalf("unable to deserialize sign descriptor[%v]: %v", i, sd)
}
if !reflect.DeepEqual(sd, desSd) {
t.Fatalf("original and deserialized sign descriptors not equal:\n"+
"original : %+v\n"+
"deserialized : %+v\n",
sd, desSd)
}
}
}

41
input/signer.go Normal file
View File

@@ -0,0 +1,41 @@
package input
import (
"github.com/btcsuite/btcd/wire"
)
// Signer represents an abstract object capable of generating raw signatures as
// well as full complete input scripts given a valid SignDescriptor and
// transaction. This interface fully abstracts away signing paving the way for
// Signer implementations such as hardware wallets, hardware tokens, HSM's, or
// simply a regular wallet.
type Signer interface {
// SignOutputRaw generates a signature for the passed transaction
// according to the data within the passed SignDescriptor.
//
// NOTE: The resulting signature should be void of a sighash byte.
SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error)
// ComputeInputScript generates a complete InputIndex for the passed
// transaction with the signature as defined within the passed
// SignDescriptor. This method should be capable of generating the
// proper input script for both regular p2wkh output and p2wkh outputs
// nested within a regular p2sh output.
//
// NOTE: This method will ignore any tweak parameters set within the
// passed SignDescriptor as it assumes a set of typical script
// templates (p2wkh, np2wkh, etc).
ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*Script, error)
}
// Script represents any script inputs required to redeem a previous
// output. This struct is used rather than just a witness, or scripSig in order
// to accommodate nested p2sh which utilizes both types of input scripts.
type Script struct {
// Witness is the full witness stack required to unlock this output.
Witness wire.TxWitness
// SigScript will only be populated if this is an input script sweeping
// a nested p2sh output.
SigScript []byte
}

497
input/size.go Normal file
View File

@@ -0,0 +1,497 @@
package input
import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/wire"
)
const (
// CommitWeight is the weight of the base commitment transaction which
// includes: one p2wsh input, out p2wkh output, and one p2wsh output.
CommitWeight int64 = 724
// HtlcWeight is the weight of an HTLC output.
HtlcWeight int64 = 172
)
const (
// witnessScaleFactor determines the level of "discount" witness data
// receives compared to "base" data. A scale factor of 4, denotes that
// witness data is 1/4 as cheap as regular non-witness data. Value copied
// here for convenience.
witnessScaleFactor = blockchain.WitnessScaleFactor
// The weight(weight), which is different from the !size! (see BIP-141),
// is calculated as:
// Weight = 4 * BaseSize + WitnessSize (weight).
// BaseSize - size of the transaction without witness data (bytes).
// WitnessSize - witness size (bytes).
// Weight - the metric for determining the weight of the transaction.
// P2WPKHSize 22 bytes
// - OP_0: 1 byte
// - OP_DATA: 1 byte (PublicKeyHASH160 length)
// - PublicKeyHASH160: 20 bytes
P2WPKHSize = 1 + 1 + 20
// P2WSHSize 34 bytes
// - OP_0: 1 byte
// - OP_DATA: 1 byte (WitnessScriptSHA256 length)
// - WitnessScriptSHA256: 32 bytes
P2WSHSize = 1 + 1 + 32
// P2PKHOutputSize 34 bytes
// - value: 8 bytes
// - var_int: 1 byte (pkscript_length)
// - pkscript (p2pkh): 25 bytes
P2PKHOutputSize = 8 + 1 + 25
// P2WKHOutputSize 31 bytes
// - value: 8 bytes
// - var_int: 1 byte (pkscript_length)
// - pkscript (p2wpkh): 22 bytes
P2WKHOutputSize = 8 + 1 + P2WPKHSize
// P2WSHOutputSize 43 bytes
// - value: 8 bytes
// - var_int: 1 byte (pkscript_length)
// - pkscript (p2wsh): 34 bytes
P2WSHOutputSize = 8 + 1 + P2WSHSize
// P2SHOutputSize 32 bytes
// - value: 8 bytes
// - var_int: 1 byte (pkscript_length)
// - pkscript (p2sh): 23 bytes
P2SHOutputSize = 8 + 1 + 23
// P2PKHScriptSigSize 108 bytes
// - OP_DATA: 1 byte (signature length)
// - signature
// - OP_DATA: 1 byte (pubkey length)
// - pubkey
P2PKHScriptSigSize = 1 + 73 + 1 + 33
// P2WKHWitnessSize 109 bytes
// - number_of_witness_elements: 1 byte
// - signature_length: 1 byte
// - signature
// - pubkey_length: 1 byte
// - pubkey
P2WKHWitnessSize = 1 + 1 + 73 + 1 + 33
// MultiSigSize 71 bytes
// - OP_2: 1 byte
// - OP_DATA: 1 byte (pubKeyAlice length)
// - pubKeyAlice: 33 bytes
// - OP_DATA: 1 byte (pubKeyBob length)
// - pubKeyBob: 33 bytes
// - OP_2: 1 byte
// - OP_CHECKMULTISIG: 1 byte
MultiSigSize = 1 + 1 + 33 + 1 + 33 + 1 + 1
// WitnessSize 222 bytes
// - NumberOfWitnessElements: 1 byte
// - NilLength: 1 byte
// - sigAliceLength: 1 byte
// - sigAlice: 73 bytes
// - sigBobLength: 1 byte
// - sigBob: 73 bytes
// - WitnessScriptLength: 1 byte
// - WitnessScript (MultiSig)
WitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + MultiSigSize
// InputSize 41 bytes
// - PreviousOutPoint:
// - Hash: 32 bytes
// - Index: 4 bytes
// - OP_DATA: 1 byte (ScriptSigLength)
// - ScriptSig: 0 bytes
// - Witness <---- we use "Witness" instead of "ScriptSig" for
// transaction validation, but "Witness" is stored
// separately and weight for it size is smaller. So
// we separate the calculation of ordinary data
// from witness data.
// - Sequence: 4 bytes
InputSize = 32 + 4 + 1 + 4
// FundingInputSize represents the size of an input to a funding
// transaction, and is equivalent to the size of a standard segwit input
// as calculated above.
FundingInputSize = InputSize
// CommitmentDelayOutput 43 bytes
// - Value: 8 bytes
// - VarInt: 1 byte (PkScript length)
// - PkScript (P2WSH)
CommitmentDelayOutput = 8 + 1 + P2WSHSize
// CommitmentKeyHashOutput 31 bytes
// - Value: 8 bytes
// - VarInt: 1 byte (PkScript length)
// - PkScript (P2WPKH)
CommitmentKeyHashOutput = 8 + 1 + P2WPKHSize
// HTLCSize 43 bytes
// - Value: 8 bytes
// - VarInt: 1 byte (PkScript length)
// - PkScript (PW2SH)
HTLCSize = 8 + 1 + P2WSHSize
// WitnessHeaderSize 2 bytes
// - Flag: 1 byte
// - Marker: 1 byte
WitnessHeaderSize = 1 + 1
// BaseTxSize 8 bytes
// - Version: 4 bytes
// - LockTime: 4 bytes
BaseTxSize = 4 + 4
// BaseCommitmentTxSize 125 + 43 * num-htlc-outputs bytes
// - Version: 4 bytes
// - WitnessHeader <---- part of the witness data
// - CountTxIn: 1 byte
// - TxIn: 41 bytes
// FundingInput
// - CountTxOut: 1 byte
// - TxOut: 74 + 43 * num-htlc-outputs bytes
// OutputPayingToThem,
// OutputPayingToUs,
// ....HTLCOutputs...
// - LockTime: 4 bytes
BaseCommitmentTxSize = 4 + 1 + FundingInputSize + 1 +
CommitmentDelayOutput + CommitmentKeyHashOutput + 4
// BaseCommitmentTxWeight 500 weight
BaseCommitmentTxWeight = witnessScaleFactor * BaseCommitmentTxSize
// WitnessCommitmentTxWeight 224 weight
WitnessCommitmentTxWeight = WitnessHeaderSize + WitnessSize
// HTLCWeight 172 weight
HTLCWeight = witnessScaleFactor * HTLCSize
// HtlcTimeoutWeight is the weight of the HTLC timeout transaction
// which will transition an outgoing HTLC to the delay-and-claim state.
HtlcTimeoutWeight = 663
// HtlcSuccessWeight is the weight of the HTLC success transaction
// which will transition an incoming HTLC to the delay-and-claim state.
HtlcSuccessWeight = 703
// MaxHTLCNumber is the maximum number HTLCs which can be included in a
// commitment transaction. This limit was chosen such that, in the case
// of a contract breach, the punishment transaction is able to sweep
// all the HTLC's yet still remain below the widely used standard
// weight limits.
MaxHTLCNumber = 966
// ToLocalScriptSize 79 bytes
// - OP_IF: 1 byte
// - OP_DATA: 1 byte
// - revoke_key: 33 bytes
// - OP_ELSE: 1 byte
// - OP_DATA: 1 byte
// - csv_delay: 4 bytes
// - OP_CHECKSEQUENCEVERIFY: 1 byte
// - OP_DROP: 1 byte
// - OP_DATA: 1 byte
// - delay_key: 33 bytes
// - OP_ENDIF: 1 byte
// - OP_CHECKSIG: 1 byte
ToLocalScriptSize = 1 + 1 + 33 + 1 + 1 + 4 + 1 + 1 + 1 + 33 + 1 + 1
// ToLocalTimeoutWitnessSize 156 bytes
// - number_of_witness_elements: 1 byte
// - local_delay_sig_length: 1 byte
// - local_delay_sig: 73 bytes
// - zero_length: 1 byte
// - witness_script_length: 1 byte
// - witness_script (to_local_script)
ToLocalTimeoutWitnessSize = 1 + 1 + 73 + 1 + 1 + ToLocalScriptSize
// ToLocalPenaltyWitnessSize 156 bytes
// - number_of_witness_elements: 1 byte
// - revocation_sig_length: 1 byte
// - revocation_sig: 73 bytes
// - OP_TRUE: 1 byte
// - witness_script_length: 1 byte
// - witness_script (to_local_script)
ToLocalPenaltyWitnessSize = 1 + 1 + 73 + 1 + 1 + ToLocalScriptSize
// AcceptedHtlcScriptSize 139 bytes
// - OP_DUP: 1 byte
// - OP_HASH160: 1 byte
// - OP_DATA: 1 byte (RIPEMD160(SHA256(revocationkey)) length)
// - RIPEMD160(SHA256(revocationkey)): 20 bytes
// - OP_EQUAL: 1 byte
// - OP_IF: 1 byte
// - OP_CHECKSIG: 1 byte
// - OP_ELSE: 1 byte
// - OP_DATA: 1 byte (remotekey length)
// - remotekey: 33 bytes
// - OP_SWAP: 1 byte
// - OP_SIZE: 1 byte
// - 32: 1 byte
// - OP_EQUAL: 1 byte
// - OP_IF: 1 byte
// - OP_HASH160: 1 byte
// - OP_DATA: 1 byte (RIPEMD160(payment_hash) length)
// - RIPEMD160(payment_hash): 20 bytes
// - OP_EQUALVERIFY: 1 byte
// - 2: 1 byte
// - OP_SWAP: 1 byte
// - OP_DATA: 1 byte (localkey length)
// - localkey: 33 bytes
// - 2: 1 byte
// - OP_CHECKMULTISIG: 1 byte
// - OP_ELSE: 1 byte
// - OP_DROP: 1 byte
// - OP_DATA: 1 byte (cltv_expiry length)
// - cltv_expiry: 4 bytes
// - OP_CHECKLOCKTIMEVERIFY: 1 byte
// - OP_DROP: 1 byte
// - OP_CHECKSIG: 1 byte
// - OP_ENDIF: 1 byte
// - OP_ENDIF: 1 byte
AcceptedHtlcScriptSize = 3*1 + 20 + 5*1 + 33 + 7*1 + 20 + 4*1 +
33 + 5*1 + 4 + 5*1
// AcceptedHtlcTimeoutWitnessSize 216
// - number_of_witness_elements: 1 byte
// - sender_sig_length: 1 byte
// - sender_sig: 73 bytes
// - nil_length: 1 byte
// - witness_script_length: 1 byte
// - witness_script: (accepted_htlc_script)
AcceptedHtlcTimeoutWitnessSize = 1 + 1 + 73 + 1 + 1 + AcceptedHtlcScriptSize
// AcceptedHtlcSuccessWitnessSize 322 bytes
// - number_of_witness_elements: 1 byte
// - nil_length: 1 byte
// - sig_alice_length: 1 byte
// - sig_alice: 73 bytes
// - sig_bob_length: 1 byte
// - sig_bob: 73 bytes
// - preimage_length: 1 byte
// - preimage: 32 bytes
// - witness_script_length: 1 byte
// - witness_script (accepted_htlc_script)
AcceptedHtlcSuccessWitnessSize = 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + AcceptedHtlcScriptSize
// AcceptedHtlcPenaltyWitnessSize 249 bytes
// - number_of_witness_elements: 1 byte
// - revocation_sig_length: 1 byte
// - revocation_sig: 73 bytes
// - revocation_key_length: 1 byte
// - revocation_key: 33 bytes
// - witness_script_length: 1 byte
// - witness_script (accepted_htlc_script)
AcceptedHtlcPenaltyWitnessSize = 1 + 1 + 73 + 1 + 33 + 1 + AcceptedHtlcScriptSize
// OfferedHtlcScriptSize 133 bytes
// - OP_DUP: 1 byte
// - OP_HASH160: 1 byte
// - OP_DATA: 1 byte (RIPEMD160(SHA256(revocationkey)) length)
// - RIPEMD160(SHA256(revocationkey)): 20 bytes
// - OP_EQUAL: 1 byte
// - OP_IF: 1 byte
// - OP_CHECKSIG: 1 byte
// - OP_ELSE: 1 byte
// - OP_DATA: 1 byte (remotekey length)
// - remotekey: 33 bytes
// - OP_SWAP: 1 byte
// - OP_SIZE: 1 byte
// - OP_DATA: 1 byte (32 length)
// - 32: 1 byte
// - OP_EQUAL: 1 byte
// - OP_NOTIF: 1 byte
// - OP_DROP: 1 byte
// - 2: 1 byte
// - OP_SWAP: 1 byte
// - OP_DATA: 1 byte (localkey length)
// - localkey: 33 bytes
// - 2: 1 byte
// - OP_CHECKMULTISIG: 1 byte
// - OP_ELSE: 1 byte
// - OP_HASH160: 1 byte
// - OP_DATA: 1 byte (RIPEMD160(payment_hash) length)
// - RIPEMD160(payment_hash): 20 bytes
// - OP_EQUALVERIFY: 1 byte
// - OP_CHECKSIG: 1 byte
// - OP_ENDIF: 1 byte
// - OP_ENDIF: 1 byte
OfferedHtlcScriptSize = 3*1 + 20 + 5*1 + 33 + 10*1 + 33 + 5*1 + 20 + 4*1
// OfferedHtlcTimeoutWitnessSize 285 bytes
// - number_of_witness_elements: 1 byte
// - nil_length: 1 byte
// - sig_alice_length: 1 byte
// - sig_alice: 73 bytes
// - sig_bob_length: 1 byte
// - sig_bob: 73 bytes
// - nil_length: 1 byte
// - witness_script_length: 1 byte
// - witness_script (offered_htlc_script)
OfferedHtlcTimeoutWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + OfferedHtlcScriptSize
// OfferedHtlcSuccessWitnessSize 317 bytes
// - number_of_witness_elements: 1 byte
// - nil_length: 1 byte
// - receiver_sig_length: 1 byte
// - receiver_sig: 73 bytes
// - sender_sig_length: 1 byte
// - sender_sig: 73 bytes
// - payment_preimage_length: 1 byte
// - payment_preimage: 32 bytes
// - witness_script_length: 1 byte
// - witness_script (offered_htlc_script)
OfferedHtlcSuccessWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + OfferedHtlcScriptSize
// OfferedHtlcPenaltyWitnessSize 243 bytes
// - number_of_witness_elements: 1 byte
// - revocation_sig_length: 1 byte
// - revocation_sig: 73 bytes
// - revocation_key_length: 1 byte
// - revocation_key: 33 bytes
// - witness_script_length: 1 byte
// - witness_script (offered_htlc_script)
OfferedHtlcPenaltyWitnessSize = 1 + 1 + 73 + 1 + 33 + 1 + OfferedHtlcScriptSize
)
// EstimateCommitTxWeight estimate commitment transaction weight depending on
// the precalculated weight of base transaction, witness data, which is needed
// for paying for funding tx, and htlc weight multiplied by their count.
func EstimateCommitTxWeight(count int, prediction bool) int64 {
// Make prediction about the size of commitment transaction with
// additional HTLC.
if prediction {
count++
}
htlcWeight := int64(count * HTLCWeight)
baseWeight := int64(BaseCommitmentTxWeight)
witnessWeight := int64(WitnessCommitmentTxWeight)
return htlcWeight + baseWeight + witnessWeight
}
// TxWeightEstimator is able to calculate weight estimates for transactions
// based on the input and output types. For purposes of estimation, all
// signatures are assumed to be of the maximum possible size, 73 bytes. Each
// method of the estimator returns an instance with the estimate applied. This
// allows callers to chain each of the methods
type TxWeightEstimator struct {
hasWitness bool
inputCount uint32
outputCount uint32
inputSize int
inputWitnessSize int
outputSize int
}
// AddP2PKHInput updates the weight estimate to account for an additional input
// spending a P2PKH output.
func (twe *TxWeightEstimator) AddP2PKHInput() *TxWeightEstimator {
twe.inputSize += InputSize + P2PKHScriptSigSize
twe.inputWitnessSize++
twe.inputCount++
return twe
}
// AddP2WKHInput updates the weight estimate to account for an additional input
// spending a native P2PWKH output.
func (twe *TxWeightEstimator) AddP2WKHInput() *TxWeightEstimator {
twe.AddWitnessInput(P2WKHWitnessSize)
return twe
}
// AddWitnessInput updates the weight estimate to account for an additional
// input spending a native pay-to-witness output. This accepts the total size
// of the witness as a parameter.
func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) *TxWeightEstimator {
twe.inputSize += InputSize
twe.inputWitnessSize += witnessSize
twe.inputCount++
twe.hasWitness = true
return twe
}
// AddNestedP2WKHInput updates the weight estimate to account for an additional
// input spending a P2SH output with a nested P2WKH redeem script.
func (twe *TxWeightEstimator) AddNestedP2WKHInput() *TxWeightEstimator {
twe.inputSize += InputSize + P2WPKHSize
twe.inputWitnessSize += P2WKHWitnessSize
twe.inputSize++
twe.hasWitness = true
return twe
}
// AddNestedP2WSHInput updates the weight estimate to account for an additional
// input spending a P2SH output with a nested P2WSH redeem script.
func (twe *TxWeightEstimator) AddNestedP2WSHInput(witnessSize int) *TxWeightEstimator {
twe.inputSize += InputSize + P2WSHSize
twe.inputWitnessSize += witnessSize
twe.inputSize++
twe.hasWitness = true
return twe
}
// AddP2PKHOutput updates the weight estimate to account for an additional P2PKH
// output.
func (twe *TxWeightEstimator) AddP2PKHOutput() *TxWeightEstimator {
twe.outputSize += P2PKHOutputSize
twe.outputCount++
return twe
}
// AddP2WKHOutput updates the weight estimate to account for an additional
// native P2WKH output.
func (twe *TxWeightEstimator) AddP2WKHOutput() *TxWeightEstimator {
twe.outputSize += P2WKHOutputSize
twe.outputCount++
return twe
}
// AddP2WSHOutput updates the weight estimate to account for an additional
// native P2WSH output.
func (twe *TxWeightEstimator) AddP2WSHOutput() *TxWeightEstimator {
twe.outputSize += P2WSHOutputSize
twe.outputCount++
return twe
}
// AddP2SHOutput updates the weight estimate to account for an additional P2SH
// output.
func (twe *TxWeightEstimator) AddP2SHOutput() *TxWeightEstimator {
twe.outputSize += P2SHOutputSize
twe.outputCount++
return twe
}
// Weight gets the estimated weight of the transaction.
func (twe *TxWeightEstimator) Weight() int {
txSizeStripped := BaseTxSize +
wire.VarIntSerializeSize(uint64(twe.inputCount)) + twe.inputSize +
wire.VarIntSerializeSize(uint64(twe.outputCount)) + twe.outputSize
weight := txSizeStripped * witnessScaleFactor
if twe.hasWitness {
weight += WitnessHeaderSize + twe.inputWitnessSize
}
return weight
}
// VSize gets the estimated virtual size of the transactions, in vbytes.
func (twe *TxWeightEstimator) VSize() int {
// A tx's vsize is 1/4 of the weight, rounded up.
return (twe.Weight() + witnessScaleFactor - 1) / witnessScaleFactor
}

189
input/test_utils.go Normal file
View File

@@ -0,0 +1,189 @@
package input
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
var (
// For simplicity a single priv key controls all of our test outputs.
testWalletPrivKey = []byte{
0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf,
0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9,
0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f,
0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90,
}
// We're alice :)
bobsPrivKey = []byte{
0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
}
// Use a hard-coded HD seed.
testHdSeed = chainhash.Hash{
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
}
)
// MockSigner is a simple implementation of the Signer interface. Each one has
// a set of private keys in a slice and can sign messages using the appropriate
// one.
type MockSigner struct {
Privkeys []*btcec.PrivateKey
NetParams *chaincfg.Params
}
// SignOutputRaw generates a signature for the passed transaction according to
// the data within the passed SignDescriptor.
func (m *MockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) {
pubkey := signDesc.KeyDesc.PubKey
switch {
case signDesc.SingleTweak != nil:
pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak)
case signDesc.DoubleTweak != nil:
pubkey = DeriveRevocationPubkey(pubkey, signDesc.DoubleTweak.PubKey())
}
hash160 := btcutil.Hash160(pubkey.SerializeCompressed())
privKey := m.findKey(hash160, signDesc.SingleTweak, signDesc.DoubleTweak)
if privKey == nil {
return nil, fmt.Errorf("Mock signer does not have key")
}
sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes,
signDesc.InputIndex, signDesc.Output.Value, signDesc.WitnessScript,
txscript.SigHashAll, privKey)
if err != nil {
return nil, err
}
return sig[:len(sig)-1], nil
}
// ComputeInputScript generates a complete InputIndex for the passed transaction
// with the signature as defined within the passed SignDescriptor. This method
// should be capable of generating the proper input script for both regular
// p2wkh output and p2wkh outputs nested within a regular p2sh output.
func (m *MockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*Script, error) {
scriptType, addresses, _, err := txscript.ExtractPkScriptAddrs(
signDesc.Output.PkScript, m.NetParams)
if err != nil {
return nil, err
}
switch scriptType {
case txscript.PubKeyHashTy:
privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak,
signDesc.DoubleTweak)
if privKey == nil {
return nil, fmt.Errorf("Mock signer does not have key for "+
"address %v", addresses[0])
}
sigScript, err := txscript.SignatureScript(
tx, signDesc.InputIndex, signDesc.Output.PkScript,
txscript.SigHashAll, privKey, true,
)
if err != nil {
return nil, err
}
return &Script{SigScript: sigScript}, nil
case txscript.WitnessV0PubKeyHashTy:
privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak,
signDesc.DoubleTweak)
if privKey == nil {
return nil, fmt.Errorf("Mock signer does not have key for "+
"address %v", addresses[0])
}
witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes,
signDesc.InputIndex, signDesc.Output.Value,
signDesc.Output.PkScript, txscript.SigHashAll, privKey, true)
if err != nil {
return nil, err
}
return &Script{Witness: witnessScript}, nil
default:
return nil, fmt.Errorf("Unexpected script type: %v", scriptType)
}
}
// findKey searches through all stored private keys and returns one
// corresponding to the hashed pubkey if it can be found. The public key may
// either correspond directly to the private key or to the private key with a
// tweak applied.
func (m *MockSigner) findKey(needleHash160 []byte, singleTweak []byte,
doubleTweak *btcec.PrivateKey) *btcec.PrivateKey {
for _, privkey := range m.Privkeys {
// First check whether public key is directly derived from private key.
hash160 := btcutil.Hash160(privkey.PubKey().SerializeCompressed())
if bytes.Equal(hash160, needleHash160) {
return privkey
}
// Otherwise check if public key is derived from tweaked private key.
switch {
case singleTweak != nil:
privkey = TweakPrivKey(privkey, singleTweak)
case doubleTweak != nil:
privkey = DeriveRevocationPrivKey(privkey, doubleTweak)
default:
continue
}
hash160 = btcutil.Hash160(privkey.PubKey().SerializeCompressed())
if bytes.Equal(hash160, needleHash160) {
return privkey
}
}
return nil
}
// pubkeyFromHex parses a Bitcoin public key from a hex encoded string.
func pubkeyFromHex(keyHex string) (*btcec.PublicKey, error) {
bytes, err := hex.DecodeString(keyHex)
if err != nil {
return nil, err
}
return btcec.ParsePubKey(bytes, btcec.S256())
}
// privkeyFromHex parses a Bitcoin private key from a hex encoded string.
func privkeyFromHex(keyHex string) (*btcec.PrivateKey, error) {
bytes, err := hex.DecodeString(keyHex)
if err != nil {
return nil, err
}
key, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes)
return key, nil
}
// pubkeyToHex serializes a Bitcoin public key to a hex encoded string.
func pubkeyToHex(key *btcec.PublicKey) string {
return hex.EncodeToString(key.SerializeCompressed())
}
// privkeyFromHex serializes a Bitcoin private key to a hex encoded string.
func privkeyToHex(key *btcec.PrivateKey) string {
return hex.EncodeToString(key.Serialize())
}

46
input/txout.go Normal file
View File

@@ -0,0 +1,46 @@
package input
import (
"encoding/binary"
"io"
"github.com/btcsuite/btcd/wire"
)
// writeTxOut serializes a wire.TxOut struct into the passed io.Writer stream.
func writeTxOut(w io.Writer, txo *wire.TxOut) error {
var scratch [8]byte
binary.BigEndian.PutUint64(scratch[:], uint64(txo.Value))
if _, err := w.Write(scratch[:]); err != nil {
return err
}
if err := wire.WriteVarBytes(w, 0, txo.PkScript); err != nil {
return err
}
return nil
}
// readTxOut deserializes a wire.TxOut struct from the passed io.Reader stream.
func readTxOut(r io.Reader, txo *wire.TxOut) error {
var scratch [8]byte
if _, err := io.ReadFull(r, scratch[:]); err != nil {
return err
}
value := int64(binary.BigEndian.Uint64(scratch[:]))
pkScript, err := wire.ReadVarBytes(r, 0, 80, "pkScript")
if err != nil {
return err
}
*txo = wire.TxOut{
Value: value,
PkScript: pkScript,
}
return nil
}

46
input/txout_test.go Normal file
View File

@@ -0,0 +1,46 @@
package input
import (
"bytes"
"reflect"
"testing"
"github.com/btcsuite/btcd/wire"
)
func TestTxOutSerialization(t *testing.T) {
txo := wire.TxOut{
Value: 1e7,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
}
var buf bytes.Buffer
if err := writeTxOut(&buf, &txo); err != nil {
t.Fatalf("unable to serialize txout: %v", err)
}
var deserializedTxo wire.TxOut
if err := readTxOut(&buf, &deserializedTxo); err != nil {
t.Fatalf("unable to deserialize txout: %v", err)
}
if !reflect.DeepEqual(txo, deserializedTxo) {
t.Fatalf("original and deserialized txouts are different:\n"+
"original : %+v\n"+
"deserialized : %+v\n",
txo, deserializedTxo)
}
}

248
input/witnessgen.go Normal file
View File

@@ -0,0 +1,248 @@
package input
import (
"fmt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// WitnessType determines how an output's witness will be generated. The
// default commitmentTimeLock type will generate a witness that will allow
// spending of a time-locked transaction enforced by CheckSequenceVerify.
type WitnessType uint16
const (
// CommitmentTimeLock is a witness that allows us to spend the output of
// a commitment transaction after a relative lock-time lockout.
CommitmentTimeLock WitnessType = 0
// CommitmentNoDelay is a witness that allows us to spend a settled
// no-delay output immediately on a counterparty's commitment
// transaction.
CommitmentNoDelay WitnessType = 1
// CommitmentRevoke is a witness that allows us to sweep the settled
// output of a malicious counterparty's who broadcasts a revoked
// commitment transaction.
CommitmentRevoke WitnessType = 2
// HtlcOfferedRevoke is a witness that allows us to sweep an HTLC which
// we offered to the remote party in the case that they broadcast a
// revoked commitment state.
HtlcOfferedRevoke WitnessType = 3
// HtlcAcceptedRevoke is a witness that allows us to sweep an HTLC
// output sent to us in the case that the remote party broadcasts a
// revoked commitment state.
HtlcAcceptedRevoke WitnessType = 4
// HtlcOfferedTimeoutSecondLevel is a witness that allows us to sweep
// an HTLC output that we extended to a party, but was never fulfilled.
// This HTLC output isn't directly on the commitment transaction, but
// is the result of a confirmed second-level HTLC transaction. As a
// result, we can only spend this after a CSV delay.
HtlcOfferedTimeoutSecondLevel WitnessType = 5
// HtlcAcceptedSuccessSecondLevel is a witness that allows us to sweep
// an HTLC output that was offered to us, and for which we have a
// payment preimage. This HTLC output isn't directly on our commitment
// transaction, but is the result of confirmed second-level HTLC
// transaction. As a result, we can only spend this after a CSV delay.
HtlcAcceptedSuccessSecondLevel WitnessType = 6
// HtlcOfferedRemoteTimeout is a witness that allows us to sweep an
// HTLC that we offered to the remote party which lies in the
// commitment transaction of the remote party. We can spend this output
// after the absolute CLTV timeout of the HTLC as passed.
HtlcOfferedRemoteTimeout WitnessType = 7
// HtlcAcceptedRemoteSuccess is a witness that allows us to sweep an
// HTLC that was offered to us by the remote party. We use this witness
// in the case that the remote party goes to chain, and we know the
// pre-image to the HTLC. We can sweep this without any additional
// timeout.
HtlcAcceptedRemoteSuccess WitnessType = 8
// HtlcSecondLevelRevoke is a witness that allows us to sweep an HTLC
// from the remote party's commitment transaction in the case that the
// broadcast a revoked commitment, but then also immediately attempt to
// go to the second level to claim the HTLC.
HtlcSecondLevelRevoke WitnessType = 9
// WitnessKeyHash is a witness type that allows us to spend a regular
// p2wkh output that's sent to an output which is under complete
// control of the backing wallet.
WitnessKeyHash WitnessType = 10
// NestedWitnessKeyHash is a witness type that allows us to sweep an
// output that sends to a nested P2SH script that pays to a key solely
// under our control. The witness generated needs to include the
NestedWitnessKeyHash WitnessType = 11
)
// Stirng returns a human readable version of the target WitnessType.
func (wt WitnessType) String() string {
switch wt {
case CommitmentTimeLock:
return "CommitmentTimeLock"
case CommitmentNoDelay:
return "CommitmentNoDelay"
case CommitmentRevoke:
return "CommitmentRevoke"
case HtlcOfferedRevoke:
return "HtlcOfferedRevoke"
case HtlcAcceptedRevoke:
return "HtlcAcceptedRevoke"
case HtlcOfferedTimeoutSecondLevel:
return "HtlcOfferedTimeoutSecondLevel"
case HtlcAcceptedSuccessSecondLevel:
return "HtlcAcceptedSuccessSecondLevel"
case HtlcOfferedRemoteTimeout:
return "HtlcOfferedRemoteTimeout"
case HtlcAcceptedRemoteSuccess:
return "HtlcAcceptedRemoteSuccess"
case HtlcSecondLevelRevoke:
return "HtlcSecondLevelRevoke"
default:
return fmt.Sprintf("Unknown WitnessType: %v", uint32(wt))
}
}
// WitnessGenerator represents a function which is able to generate the final
// witness for a particular public key script. Additionally, if required, this
// function will also return the sigScript for spending nested P2SH witness
// outputs. This function acts as an abstraction layer, hiding the details of
// the underlying script.
type WitnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
inputIndex int) (*Script, error)
// GenWitnessFunc will return a WitnessGenerator function that an output uses
// to generate the witness and optionally the sigScript for a sweep
// transaction. The sigScript will be generated if the witness type warrants
// one for spending, such as the NestedWitnessKeyHash witness type.
func (wt WitnessType) GenWitnessFunc(signer Signer,
descriptor *SignDescriptor) WitnessGenerator {
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
inputIndex int) (*Script, error) {
desc := descriptor
desc.SigHashes = hc
desc.InputIndex = inputIndex
switch wt {
case CommitmentTimeLock:
witness, err := CommitSpendTimeout(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case CommitmentNoDelay:
witness, err := CommitSpendNoDelay(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case CommitmentRevoke:
witness, err := CommitSpendRevoke(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcOfferedRevoke:
witness, err := ReceiverHtlcSpendRevoke(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcAcceptedRevoke:
witness, err := SenderHtlcSpendRevoke(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcOfferedTimeoutSecondLevel:
witness, err := HtlcSecondLevelSpend(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcAcceptedSuccessSecondLevel:
witness, err := HtlcSecondLevelSpend(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcOfferedRemoteTimeout:
// We pass in a value of -1 for the timeout, as we
// expect the caller to have already set the lock time
// value.
witness, err := ReceiverHtlcSpendTimeout(signer, desc, tx, -1)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case HtlcSecondLevelRevoke:
witness, err := HtlcSpendRevoke(signer, desc, tx)
if err != nil {
return nil, err
}
return &Script{
Witness: witness,
}, nil
case WitnessKeyHash:
fallthrough
case NestedWitnessKeyHash:
return signer.ComputeInputScript(tx, desc)
default:
return nil, fmt.Errorf("unknown witness type: %v", wt)
}
}
}