input/script_utils: add anchor scripts + tests
This commit is contained in:
parent
6deb913a8d
commit
50199aeaf3
@ -1024,6 +1024,74 @@ func CommitSpendToRemoteConfirmed(signer Signer, signDesc *SignDescriptor,
|
||||
return witnessStack, nil
|
||||
}
|
||||
|
||||
// CommitScriptAnchor constructs the script for the anchor output spendable by
|
||||
// the given key immediately, or by anyone after 16 confirmations.
|
||||
//
|
||||
// Possible Input Scripts:
|
||||
// By owner: <sig>
|
||||
// By anyone (after 16 conf): <emptyvector>
|
||||
//
|
||||
// Output Script:
|
||||
// <funding_pubkey> OP_CHECKSIG OP_IFDUP
|
||||
// OP_NOTIF
|
||||
// OP_16 OP_CSV
|
||||
// OP_ENDIF
|
||||
func CommitScriptAnchor(key *btcec.PublicKey) ([]byte, error) {
|
||||
builder := txscript.NewScriptBuilder()
|
||||
|
||||
// Spend immediately with key.
|
||||
builder.AddData(key.SerializeCompressed())
|
||||
builder.AddOp(txscript.OP_CHECKSIG)
|
||||
|
||||
// Duplicate the value if true, since it will be consumed by the NOTIF.
|
||||
builder.AddOp(txscript.OP_IFDUP)
|
||||
|
||||
// Otherwise spendable by anyone after 16 confirmations.
|
||||
builder.AddOp(txscript.OP_NOTIF)
|
||||
builder.AddOp(txscript.OP_16)
|
||||
builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY)
|
||||
builder.AddOp(txscript.OP_ENDIF)
|
||||
|
||||
return builder.Script()
|
||||
}
|
||||
|
||||
// CommitSpendAnchor constructs a valid witness allowing a node to spend their
|
||||
// anchor output on the commitment transaction using their funding key. This is
|
||||
// used for the anchor channel type.
|
||||
func CommitSpendAnchor(signer Signer, signDesc *SignDescriptor,
|
||||
sweepTx *wire.MsgTx) (wire.TxWitness, error) {
|
||||
|
||||
if signDesc.KeyDesc.PubKey == nil {
|
||||
return nil, fmt.Errorf("cannot generate witness with nil " +
|
||||
"KeyDesc pubkey")
|
||||
}
|
||||
|
||||
// Create a signature.
|
||||
sweepSig, err := signer.SignOutputRaw(sweepTx, signDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The witness here is just a signature and the redeem script.
|
||||
witnessStack := make([][]byte, 2)
|
||||
witnessStack[0] = append(sweepSig, byte(signDesc.HashType))
|
||||
witnessStack[1] = signDesc.WitnessScript
|
||||
|
||||
return witnessStack, nil
|
||||
}
|
||||
|
||||
// CommitSpendAnchorAnyone constructs a witness allowing anyone to spend the
|
||||
// anchor output after it has gotten 16 confirmations. Since no signing is
|
||||
// required, only knowledge of the redeem script is necessary to spend it.
|
||||
func CommitSpendAnchorAnyone(script []byte) (wire.TxWitness, error) {
|
||||
// The witness here is just the redeem script.
|
||||
witnessStack := make([][]byte, 2)
|
||||
witnessStack[0] = nil
|
||||
witnessStack[1] = script
|
||||
|
||||
return witnessStack, nil
|
||||
}
|
||||
|
||||
// SingleTweakBytes computes set of bytes we call the single tweak. The purpose
|
||||
// of the single tweak is to randomize all regular delay and payment base
|
||||
// points. To do this, we generate a hash that binds the commitment point to
|
||||
|
@ -1251,6 +1251,113 @@ func TestCommitSpendToRemoteConfirmed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSpendAnchor checks that we can spend the anchors using the various spend
|
||||
// paths.
|
||||
func TestSpendAnchor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const anchorSize = 294
|
||||
|
||||
// First we'll set up some initial key state for Alice.
|
||||
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||
testWalletPrivKey)
|
||||
|
||||
// Create a fake anchor outpoint that we'll use to generate the
|
||||
// sweeping transaction.
|
||||
txid, err := chainhash.NewHash(testHdSeed.CloneBytes())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create txid: %v", err)
|
||||
}
|
||||
anchorOutPoint := &wire.OutPoint{
|
||||
Hash: *txid,
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
sweepTx := wire.NewMsgTx(2)
|
||||
sweepTx.AddTxIn(wire.NewTxIn(anchorOutPoint, nil, nil))
|
||||
sweepTx.AddTxOut(
|
||||
&wire.TxOut{
|
||||
PkScript: []byte("doesn't matter"),
|
||||
Value: 1 * 10e8,
|
||||
},
|
||||
)
|
||||
|
||||
// Generate the anchor script that can be spent by Alice immediately,
|
||||
// or by anyone after 16 blocks.
|
||||
anchorScript, err := CommitScriptAnchor(aliceKeyPub)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create htlc script: %v", err)
|
||||
}
|
||||
anchorPkScript, err := WitnessScriptHash(anchorScript)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create htlc output: %v", err)
|
||||
}
|
||||
|
||||
anchorOutput := &wire.TxOut{
|
||||
PkScript: anchorPkScript,
|
||||
Value: int64(anchorSize),
|
||||
}
|
||||
|
||||
// Create mock signer for Alice.
|
||||
aliceSigner := &MockSigner{Privkeys: []*btcec.PrivateKey{aliceKeyPriv}}
|
||||
|
||||
testCases := []struct {
|
||||
witness func() wire.TxWitness
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
// Alice can spend immediately.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
sweepTx.TxIn[0].Sequence = wire.MaxTxInSequenceNum
|
||||
sweepTxSigHashes := txscript.NewTxSigHashes(sweepTx)
|
||||
|
||||
signDesc := &SignDescriptor{
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
WitnessScript: anchorScript,
|
||||
Output: anchorOutput,
|
||||
HashType: txscript.SigHashAll,
|
||||
SigHashes: sweepTxSigHashes,
|
||||
InputIndex: 0,
|
||||
}
|
||||
|
||||
return CommitSpendAnchor(aliceSigner, signDesc,
|
||||
sweepTx)
|
||||
}),
|
||||
true,
|
||||
},
|
||||
{
|
||||
// Anyone can spend after 16 blocks.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
sweepTx.TxIn[0].Sequence = LockTimeToSequence(false, 16)
|
||||
return CommitSpendAnchorAnyone(anchorScript)
|
||||
}),
|
||||
true,
|
||||
},
|
||||
{
|
||||
// Anyone cannot spend before 16 blocks.
|
||||
makeWitnessTestCase(t, func() (wire.TxWitness, error) {
|
||||
sweepTx.TxIn[0].Sequence = LockTimeToSequence(false, 15)
|
||||
return CommitSpendAnchorAnyone(anchorScript)
|
||||
}),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
sweepTx.TxIn[0].Witness = testCase.witness()
|
||||
|
||||
newEngine := func() (*txscript.Engine, error) {
|
||||
return txscript.NewEngine(anchorPkScript,
|
||||
sweepTx, 0, txscript.StandardVerifyFlags, nil,
|
||||
nil, int64(anchorSize))
|
||||
}
|
||||
|
||||
assertEngineExecution(t, i, testCase.valid, newEngine)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSpecificationKeyDerivation implements the test vectors provided in
|
||||
// BOLT-03, Appendix E.
|
||||
func TestSpecificationKeyDerivation(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user