diff --git a/input/script_utils.go b/input/script_utils.go index 055fb7af2..6a672a704 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -775,6 +775,78 @@ func SecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey, return builder.Script() } +// LeaseSecondLevelHtlcScript is the uniform script that's used as the output for +// the second-level HTLC transactions. The second level transaction acts as a +// sort of covenant, ensuring that a 2-of-2 multi-sig output can only be +// spent in a particular way, and to a particular output. +// +// Possible Input Scripts: +// * To revoke an HTLC output that has been transitioned to the claim+delay +// state: +// * 1 +// +// * To claim an HTLC output, either with a pre-image or due to a timeout: +// * 0 +// +// OP_IF +// +// OP_ELSE +// +// OP_CHECKLOCKTIMEVERIFY +// OP_DROP +// +// OP_CHECKSEQUENCEVERIFY +// OP_DROP +// +// OP_ENDIF +// OP_CHECKSIG +func LeaseSecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey, + csvDelay, cltvExpiry uint32) ([]byte, error) { + + builder := txscript.NewScriptBuilder() + + // If this is the revocation clause for this script is to be executed, + // the spender will push a 1, forcing us to hit the true clause of this + // if statement. + builder.AddOp(txscript.OP_IF) + + // If this this is the revocation case, then we'll push the revocation + // public key on the stack. + builder.AddData(revocationKey.SerializeCompressed()) + + // Otherwise, this is either the sender or receiver of the HTLC + // attempting to claim the HTLC output. + builder.AddOp(txscript.OP_ELSE) + + // The channel initiator always has the additional channel lease + // expiration constraint for outputs that pay to them which must be + // satisfied. + builder.AddInt64(int64(cltvExpiry)) + builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) + builder.AddOp(txscript.OP_DROP) + + // In order to give the other party time to execute the revocation + // clause above, we require a relative timeout to pass before the + // output can be spent. + builder.AddInt64(int64(csvDelay)) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + builder.AddOp(txscript.OP_DROP) + + // If the relative timelock passes, then we'll add the delay key to the + // stack to ensure that we properly authenticate the spending party. + builder.AddData(delayKey.SerializeCompressed()) + + // Close out the if statement. + builder.AddOp(txscript.OP_ENDIF) + + // In either case, we'll ensure that only either the party possessing + // the revocation private key, or the delay private key is able to + // spend this output. + builder.AddOp(txscript.OP_CHECKSIG) + + return builder.Script() +} + // HtlcSpendSuccess spends a second-level HTLC output. This function is to be // used by the sender of an HTLC to claim the output after a relative timeout // or the receiver of the HTLC to claim on-chain with the pre-image. @@ -938,6 +1010,66 @@ func CommitScriptToSelf(csvTimeout uint32, selfKey, revokeKey *btcec.PublicKey) return builder.Script() } +// LeaseCommitScriptToSelf constructs the public key script for the output on the +// commitment transaction paying to the "owner" of said commitment transaction. +// If the other party learns of the preimage to the revocation hash, then they +// can claim all the settled funds in the channel, plus the unsettled funds. +// +// Possible Input Scripts: +// REVOKE: 1 +// SENDRSWEEP: +// +// Output Script: +// OP_IF +// +// OP_ELSE +// OP_CHECKLOCKTIMEVERIFY OP_DROP +// OP_CHECKSEQUENCEVERIFY OP_DROP +// +// OP_ENDIF +// OP_CHECKSIG +func LeaseCommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, + csvTimeout, leaseExpiry uint32) ([]byte, error) { + + // This script is spendable under two conditions: either the + // 'csvTimeout' has passed and we can redeem our funds, or they can + // produce a valid signature with the revocation public key. The + // revocation public key will *only* be known to the other party if we + // have divulged the revocation hash, allowing them to homomorphically + // derive the proper private key which corresponds to the revoke public + // key. + builder := txscript.NewScriptBuilder() + + builder.AddOp(txscript.OP_IF) + + // If a valid signature using the revocation key is presented, then + // allow an immediate spend provided the proper signature. + builder.AddData(revokeKey.SerializeCompressed()) + + builder.AddOp(txscript.OP_ELSE) + + // Otherwise, we can re-claim our funds after once the CLTV lease + // maturity has been met, along with the CSV delay of 'csvTimeout' + // timeout blocks, and a valid signature. + builder.AddInt64(int64(leaseExpiry)) + builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) + builder.AddOp(txscript.OP_DROP) + + builder.AddInt64(int64(csvTimeout)) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + builder.AddOp(txscript.OP_DROP) + + builder.AddData(selfKey.SerializeCompressed()) + + builder.AddOp(txscript.OP_ENDIF) + + // Finally, we'll validate the signature against the public key that's + // left on the top of the stack. + builder.AddOp(txscript.OP_CHECKSIG) + + return builder.Script() +} + // CommitSpendTimeout constructs a valid witness allowing the owner of a // particular commitment transaction to spend the output returning settled // funds back to themselves after a relative block timeout. In order to @@ -1082,6 +1214,40 @@ func CommitScriptToRemoteConfirmed(key *btcec.PublicKey) ([]byte, error) { return builder.Script() } +// LeaseCommitScriptToRemoteConfirmed constructs the script for the output on +// the commitment transaction paying to the remote party of said commitment +// transaction. The money can only be spend after one confirmation. +// +// Possible Input Scripts: +// SWEEP: +// +// Output Script: +// OP_CHECKSIGVERIFY +// OP_CHECKLOCKTIMEVERIFY OP_DROP +// 1 OP_CHECKSEQUENCEVERIFY +func LeaseCommitScriptToRemoteConfirmed(key *btcec.PublicKey, + leaseExpiry uint32) ([]byte, error) { + + builder := txscript.NewScriptBuilder() + + // Only the given key can spend the output. + builder.AddData(key.SerializeCompressed()) + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + + // The channel initiator always has the additional channel lease + // expiration constraint for outputs that pay to them which must be + // satisfied. + builder.AddInt64(int64(leaseExpiry)) + builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) + builder.AddOp(txscript.OP_DROP) + + // Check that it has one confirmation. + builder.AddOp(txscript.OP_1) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + + return builder.Script() +} + // CommitSpendToRemoteConfirmed constructs a valid witness allowing a node to // spend their settled output on the counterparty's commitment transaction when // it has one confirmetion. This is used for the anchor channel type. The diff --git a/input/script_utils_test.go b/input/script_utils_test.go index 5978d3eec..e72c8c68c 100644 --- a/input/script_utils_test.go +++ b/input/script_utils_test.go @@ -13,6 +13,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/keychain" + "github.com/stretchr/testify/require" ) // assertEngineExecution executes the VM returned by the newEngine closure, @@ -1184,6 +1185,452 @@ func TestSecondLevelHtlcSpends(t *testing.T) { } } +// TestLeaseSecondLevelHtlcSpends tests all the possible redemption clauses from +// the HTLC success and timeout covenant transactions in script enforced lease +// commitments. +func TestLeaseSecondLevelHtlcSpends(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 + + // In all of our scenarios, the CLTV timelock will expire at height + // 1337. + const leaseExpiry = 1337 + + // 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()) + require.NoError(t, 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 := LeaseSecondLevelHtlcScript( + revocationKey, delayKey, claimDelay, leaseExpiry, + ) + require.NoError(t, err) + htlcPkScript, err := WitnessScriptHash(htlcWitnessScript) + require.NoError(t, err) + + htlcOutput := &wire.TxOut{ + PkScript: htlcPkScript, + Value: int64(htlcAmt), + } + + // 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), even after the CLTV + // timelock expires. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + 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 + // and after the CLTV timelock expires, but uses the + // wrong key (leaves off the single tweak). + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + 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, but before the CTLV + // timelock expires. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = 0 + 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, + ) + }), + false, + }, + { + // Receiver of the HTLC sweeps with the proper CSV + // delay, and the correct key after the CTLV timelock + // expires. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + 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() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine(htlcPkScript, + sweepTx, 0, txscript.StandardVerifyFlags, nil, + nil, int64(htlcAmt)) + } + + assertEngineExecution(t, i, testCase.valid, newEngine) + } +} + +// TestLeaseCommmitSpendToSelf tests all the possible redemption clauses from +// the to_self output in a script enforced lease commitment transaction. +func TestLeaseCommmitSpendToSelf(t *testing.T) { + t.Parallel() + + const ( + outputVal = btcutil.Amount(2 * 10e8) + csvDelay = 5 + leaseExpiry = 1337 + ) + + // 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, + ) + + // We'll have Bob take the revocation path in some cases. + revokePreimage := testHdSeed.CloneBytes() + commitSecret, commitPoint := btcec.PrivKeyFromBytes( + btcec.S256(), revokePreimage, + ) + revocationKey := DeriveRevocationPubkey(bobKeyPub, commitPoint) + + // Construct the script enforced lease to_self commitment transaction + // output. + txid, err := chainhash.NewHash(testHdSeed.CloneBytes()) + require.NoError(t, err) + commitOut := &wire.OutPoint{ + Hash: *txid, + Index: 0, + } + commitScript, err := LeaseCommitScriptToSelf( + aliceKeyPub, revocationKey, csvDelay, leaseExpiry, + ) + require.NoError(t, err) + commitPkScript, err := WitnessScriptHash(commitScript) + require.NoError(t, err) + + commitOutput := &wire.TxOut{ + PkScript: commitPkScript, + Value: int64(outputVal), + } + + sweepTx := wire.NewMsgTx(2) + sweepTx.AddTxIn(wire.NewTxIn(commitOut, nil, nil)) + sweepTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + Value: 1 * 10e8, + }, + ) + + // Create mock signers for both parties to ensure signatures are + // produced and verified correctly. + aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}} + bobSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{bobKeyPriv}} + + testCases := []struct { + witness func() wire.TxWitness + valid bool + }{ + { + // Bob can spend with his revocation key, but not + // without the proper tweak. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendRevoke( + bobSigner, signDesc, sweepTx, + ) + }), + false, + }, + { + // Bob can spend with his revocation key with the proper + // tweak. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: bobKeyPub, + }, + DoubleTweak: commitSecret, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendRevoke( + bobSigner, signDesc, sweepTx, + ) + }), + true, + }, + { + // Alice cannot spend with the proper key before the CSV + // delay and after the CLTV timelock has expired. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + sweepTx.TxIn[0].Sequence = LockTimeToSequence( + false, csvDelay/2, + ) + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DoubleTweak: commitSecret, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendTimeout( + aliceSigner, signDesc, sweepTx, + ) + }), + false, + }, + { + // Alice cannot spend with the proper key after the CSV + // delay and before the CLTV timelock has expired. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = 0 + sweepTx.TxIn[0].Sequence = LockTimeToSequence( + false, csvDelay, + ) + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + DoubleTweak: commitSecret, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendTimeout( + aliceSigner, signDesc, sweepTx, + ) + }), + false, + }, + { + // Alice can spend with the proper key after the CSV + // delay and CLTV timelock have expired. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + sweepTx.TxIn[0].Sequence = LockTimeToSequence( + false, csvDelay, + ) + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendTimeout( + aliceSigner, signDesc, sweepTx, + ) + }), + true, + }, + } + + for i, testCase := range testCases { + sweepTx.TxIn[0].Witness = testCase.witness() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine(commitPkScript, + sweepTx, 0, txscript.StandardVerifyFlags, nil, + nil, int64(outputVal)) + } + + assertEngineExecution(t, i, testCase.valid, newEngine) + } +} + // TestCommitSpendToRemoteConfirmed checks that the delayed version of the // to_remote version can only be spent by the owner, and after one // confirmation. @@ -1291,6 +1738,148 @@ func TestCommitSpendToRemoteConfirmed(t *testing.T) { } } +// TestLeaseCommitSpendToRemoteConfirmed checks that the delayed version of the +// to_remote version can only be spent by the owner, after one confirmation, and +// after the lease expiration has been met. +func TestLeaseCommitSpendToRemoteConfirmed(t *testing.T) { + t.Parallel() + + const ( + outputVal = btcutil.Amount(2 * 10e8) + leaseExpiry = 1337 + ) + + aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), testWalletPrivKey, + ) + + txid, err := chainhash.NewHash(testHdSeed.CloneBytes()) + require.NoError(t, err) + commitOut := &wire.OutPoint{ + Hash: *txid, + Index: 0, + } + commitScript, err := LeaseCommitScriptToRemoteConfirmed( + aliceKeyPub, leaseExpiry, + ) + require.NoError(t, err) + commitPkScript, err := WitnessScriptHash(commitScript) + require.NoError(t, err) + + commitOutput := &wire.TxOut{ + PkScript: commitPkScript, + Value: int64(outputVal), + } + + sweepTx := wire.NewMsgTx(2) + sweepTx.AddTxIn(wire.NewTxIn(commitOut, nil, nil)) + sweepTx.AddTxOut( + &wire.TxOut{ + PkScript: []byte("doesn't matter"), + Value: 1 * 10e8, + }, + ) + + aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}} + + testCases := []struct { + witness func() wire.TxWitness + valid bool + }{ + { + // Alice can spend after the CSV delay and CLTV timelock + // has passed. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + sweepTx.TxIn[0].Sequence = LockTimeToSequence( + false, 1, + ) + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendToRemoteConfirmed( + aliceSigner, signDesc, sweepTx, + ) + }), + true, + }, + { + // Alice cannot spend output without sequence set, even + // once the CLTV timelock has expired. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = leaseExpiry + sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendToRemoteConfirmed( + aliceSigner, signDesc, sweepTx, + ) + }), + false, + }, + { + // Alice cannot spend output without sequence or + // locktime set. + makeWitnessTestCase(t, func() (wire.TxWitness, error) { + sweepTx.LockTime = 0 + sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum + sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx) + + signDesc := &SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + PubKey: aliceKeyPub, + }, + WitnessScript: commitScript, + Output: commitOutput, + HashType: txscript.SigHashAll, + SigHashes: sweepTxSigHashes, + InputIndex: 0, + } + + return CommitSpendToRemoteConfirmed( + aliceSigner, signDesc, sweepTx, + ) + }), + false, + }, + } + + for i, testCase := range testCases { + sweepTx.TxIn[0].Witness = testCase.witness() + + newEngine := func() (*txscript.Engine, error) { + return txscript.NewEngine( + commitPkScript, sweepTx, 0, + txscript.StandardVerifyFlags, nil, nil, + int64(outputVal), + ) + } + + assertEngineExecution(t, i, testCase.valid, newEngine) + } +} + // TestSpendAnchor checks that we can spend the anchors using the various spend // paths. func TestSpendAnchor(t *testing.T) {