input: add scripts for new script enforced lease commitment type

The new commitment type consists of adding an additional CLTV
requirement to guarantee a leased channel's expiration on any commitment
and HTLC outputs that pay directly to the channel initiator.
This commit is contained in:
Wilmer Paulino
2021-07-13 16:20:24 -07:00
committed by Olaoluwa Osuntokun
parent 564ec0fd9b
commit b84307e62e
2 changed files with 755 additions and 0 deletions

View File

@@ -775,6 +775,78 @@ func SecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey,
return builder.Script() 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:
// * <revoke sig> 1
//
// * To claim an HTLC output, either with a pre-image or due to a timeout:
// * <delay sig> 0
//
// OP_IF
// <revoke key>
// OP_ELSE
// <lease maturity in blocks>
// OP_CHECKLOCKTIMEVERIFY
// OP_DROP
// <delay in blocks>
// OP_CHECKSEQUENCEVERIFY
// OP_DROP
// <delay key>
// 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 // 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 // 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. // 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() 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: <sig> 1
// SENDRSWEEP: <sig> <emptyvector>
//
// Output Script:
// OP_IF
// <revokeKey>
// OP_ELSE
// <absoluteLeaseExpiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
// <numRelativeBlocks> OP_CHECKSEQUENCEVERIFY OP_DROP
// <timeKey>
// 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 // CommitSpendTimeout constructs a valid witness allowing the owner of a
// particular commitment transaction to spend the output returning settled // particular commitment transaction to spend the output returning settled
// funds back to themselves after a relative block timeout. In order to // 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() 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: <sig>
//
// Output Script:
// <key> OP_CHECKSIGVERIFY
// <lease maturity in blocks> 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 // CommitSpendToRemoteConfirmed constructs a valid witness allowing a node to
// spend their settled output on the counterparty's commitment transaction when // spend their settled output on the counterparty's commitment transaction when
// it has one confirmetion. This is used for the anchor channel type. The // it has one confirmetion. This is used for the anchor channel type. The

View File

@@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/require"
) )
// assertEngineExecution executes the VM returned by the newEngine closure, // 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 // TestCommitSpendToRemoteConfirmed checks that the delayed version of the
// to_remote version can only be spent by the owner, and after one // to_remote version can only be spent by the owner, and after one
// confirmation. // 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 // TestSpendAnchor checks that we can spend the anchors using the various spend
// paths. // paths.
func TestSpendAnchor(t *testing.T) { func TestSpendAnchor(t *testing.T) {