lnwallet: move code to transactions files
This commit is contained in:
4 files changed
+499
-487
No files matched your search
@@ -5,10 +5,12 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
@@ -910,3 +912,299 @@ func htlcViewFromHTLCs(htlcs []channeldb.HTLC) *htlcView {
|
||||
}
|
||||
return &theHTLCView
|
||||
}
|
||||
|
||||
func TestCommitTxStateHint(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stateHintTests := []struct {
|
||||
name string
|
||||
from uint64
|
||||
to uint64
|
||||
inputs int
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
name: "states 0 to 1000",
|
||||
from: 0,
|
||||
to: 1000,
|
||||
inputs: 1,
|
||||
shouldFail: false,
|
||||
},
|
||||
{
|
||||
name: "states 'maxStateHint-1000' to 'maxStateHint'",
|
||||
from: maxStateHint - 1000,
|
||||
to: maxStateHint,
|
||||
inputs: 1,
|
||||
shouldFail: false,
|
||||
},
|
||||
{
|
||||
name: "state 'maxStateHint+1'",
|
||||
from: maxStateHint + 1,
|
||||
to: maxStateHint + 10,
|
||||
inputs: 1,
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "commit transaction with two inputs",
|
||||
inputs: 2,
|
||||
shouldFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
var obfuscator [StateHintSize]byte
|
||||
copy(obfuscator[:], testHdSeed[:StateHintSize])
|
||||
timeYesterday := uint32(time.Now().Unix() - 24*60*60)
|
||||
|
||||
for _, test := range stateHintTests {
|
||||
commitTx := wire.NewMsgTx(2)
|
||||
|
||||
// Add supplied number of inputs to the commitment transaction.
|
||||
for i := 0; i < test.inputs; i++ {
|
||||
commitTx.AddTxIn(&wire.TxIn{})
|
||||
}
|
||||
|
||||
for i := test.from; i <= test.to; i++ {
|
||||
stateNum := uint64(i)
|
||||
|
||||
err := SetStateNumHint(commitTx, stateNum, obfuscator)
|
||||
if err != nil && !test.shouldFail {
|
||||
t.Fatalf("unable to set state num %v: %v", i, err)
|
||||
} else if err == nil && test.shouldFail {
|
||||
t.Fatalf("Failed(%v): test should fail but did not", test.name)
|
||||
}
|
||||
|
||||
locktime := commitTx.LockTime
|
||||
sequence := commitTx.TxIn[0].Sequence
|
||||
|
||||
// Locktime should not be less than 500,000,000 and not larger
|
||||
// than the time 24 hours ago. One day should provide a good
|
||||
// enough buffer for the tests.
|
||||
if locktime < 5e8 || locktime > timeYesterday {
|
||||
if !test.shouldFail {
|
||||
t.Fatalf("The value of locktime (%v) may cause the commitment "+
|
||||
"transaction to be unspendable", locktime)
|
||||
}
|
||||
}
|
||||
|
||||
if sequence&wire.SequenceLockTimeDisabled == 0 {
|
||||
if !test.shouldFail {
|
||||
t.Fatalf("Sequence locktime is NOT disabled when it should be")
|
||||
}
|
||||
}
|
||||
|
||||
extractedStateNum := GetStateNumHint(commitTx, obfuscator)
|
||||
if extractedStateNum != stateNum && !test.shouldFail {
|
||||
t.Fatalf("state number mismatched, expected %v, got %v",
|
||||
stateNum, extractedStateNum)
|
||||
} else if extractedStateNum == stateNum && test.shouldFail {
|
||||
t.Fatalf("Failed(%v): test should fail but did not", test.name)
|
||||
}
|
||||
}
|
||||
t.Logf("Passed: %v", test.name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCommitmentSpendValidation test the spendability of both outputs within
|
||||
// the commitment transaction.
|
||||
//
|
||||
// The following spending cases are covered by this test:
|
||||
// * Alice's spend from the delayed output on her commitment transaction.
|
||||
// * Bob's spend from Alice's delayed output when she broadcasts a revoked
|
||||
// commitment transaction.
|
||||
// * Bob's spend from his unencumbered output within Alice's commitment
|
||||
// transaction.
|
||||
func TestCommitmentSpendValidation(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)
|
||||
|
||||
const channelBalance = btcutil.Amount(1 * 10e8)
|
||||
const csvTimeout = uint32(5)
|
||||
|
||||
// We also set up set some resources for the commitment transaction.
|
||||
// Each side currently has 1 BTC within the channel, with a total
|
||||
// channel capacity of 2BTC.
|
||||
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||
testWalletPrivKey)
|
||||
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||
bobsPrivKey)
|
||||
|
||||
revocationPreimage := testHdSeed.CloneBytes()
|
||||
commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(),
|
||||
revocationPreimage)
|
||||
revokePubKey := DeriveRevocationPubkey(bobKeyPub, commitPoint)
|
||||
|
||||
aliceDelayKey := TweakPubKey(aliceKeyPub, commitPoint)
|
||||
bobPayKey := TweakPubKey(bobKeyPub, commitPoint)
|
||||
|
||||
aliceCommitTweak := SingleTweakBytes(commitPoint, aliceKeyPub)
|
||||
bobCommitTweak := SingleTweakBytes(commitPoint, bobKeyPub)
|
||||
|
||||
aliceSelfOutputSigner := &mockSigner{
|
||||
privkeys: []*btcec.PrivateKey{aliceKeyPriv},
|
||||
}
|
||||
|
||||
// With all the test data set up, we create the commitment transaction.
|
||||
// We only focus on a single party's transactions, as the scripts are
|
||||
// identical with the roles reversed.
|
||||
//
|
||||
// This is Alice's commitment transaction, so she must wait a CSV delay
|
||||
// of 5 blocks before sweeping the output, while bob can spend
|
||||
// immediately with either the revocation key, or his regular key.
|
||||
keyRing := &CommitmentKeyRing{
|
||||
DelayKey: aliceDelayKey,
|
||||
RevocationKey: revokePubKey,
|
||||
NoDelayKey: bobPayKey,
|
||||
}
|
||||
commitmentTx, err := CreateCommitTx(*fakeFundingTxIn, keyRing, csvTimeout,
|
||||
channelBalance, channelBalance, DefaultDustLimit())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create commitment transaction: %v", nil)
|
||||
}
|
||||
|
||||
delayOutput := commitmentTx.TxOut[0]
|
||||
regularOutput := commitmentTx.TxOut[1]
|
||||
|
||||
// We're testing an uncooperative close, output sweep, so construct a
|
||||
// transaction which sweeps the funds to a random address.
|
||||
targetOutput, err := CommitScriptUnencumbered(aliceKeyPub)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create target output: %v", err)
|
||||
}
|
||||
sweepTx := wire.NewMsgTx(2)
|
||||
sweepTx.AddTxIn(wire.NewTxIn(&wire.OutPoint{
|
||||
Hash: commitmentTx.TxHash(),
|
||||
Index: 0,
|
||||
}, nil, nil))
|
||||
sweepTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: targetOutput,
|
||||
Value: 0.5 * 10e8,
|
||||
})
|
||||
|
||||
// First, we'll test spending with Alice's key after the timeout.
|
||||
delayScript, err := CommitScriptToSelf(csvTimeout, aliceDelayKey,
|
||||
revokePubKey)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate alice delay script: %v", err)
|
||||
}
|
||||
sweepTx.TxIn[0].Sequence = lockTimeToSequence(false, csvTimeout)
|
||||
signDesc := &SignDescriptor{
|
||||
WitnessScript: delayScript,
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: aliceKeyPub,
|
||||
},
|
||||
SingleTweak: aliceCommitTweak,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
Output: &wire.TxOut{
|
||||
Value: int64(channelBalance),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
InputIndex: 0,
|
||||
}
|
||||
aliceWitnessSpend, err := CommitSpendTimeout(aliceSelfOutputSigner,
|
||||
signDesc, sweepTx)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate delay commit spend witness: %v", err)
|
||||
}
|
||||
sweepTx.TxIn[0].Witness = aliceWitnessSpend
|
||||
vm, err := txscript.NewEngine(delayOutput.PkScript,
|
||||
sweepTx, 0, txscript.StandardVerifyFlags, nil,
|
||||
nil, int64(channelBalance))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create engine: %v", err)
|
||||
}
|
||||
if err := vm.Execute(); err != nil {
|
||||
t.Fatalf("spend from delay output is invalid: %v", err)
|
||||
}
|
||||
|
||||
bobSigner := &mockSigner{privkeys: []*btcec.PrivateKey{bobKeyPriv}}
|
||||
|
||||
// Next, we'll test bob spending with the derived revocation key to
|
||||
// simulate the scenario when Alice broadcasts this commitment
|
||||
// transaction after it's been revoked.
|
||||
signDesc = &SignDescriptor{
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
DoubleTweak: commitSecret,
|
||||
WitnessScript: delayScript,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
Output: &wire.TxOut{
|
||||
Value: int64(channelBalance),
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
InputIndex: 0,
|
||||
}
|
||||
bobWitnessSpend, err := CommitSpendRevoke(bobSigner, signDesc,
|
||||
sweepTx)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate revocation witness: %v", err)
|
||||
}
|
||||
sweepTx.TxIn[0].Witness = bobWitnessSpend
|
||||
vm, err = txscript.NewEngine(delayOutput.PkScript,
|
||||
sweepTx, 0, txscript.StandardVerifyFlags, nil,
|
||||
nil, int64(channelBalance))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create engine: %v", err)
|
||||
}
|
||||
if err := vm.Execute(); err != nil {
|
||||
t.Fatalf("revocation spend is invalid: %v", err)
|
||||
}
|
||||
|
||||
// In order to test the final scenario, we modify the TxIn of the sweep
|
||||
// transaction to instead point to the regular output (non delay)
|
||||
// within the commitment transaction.
|
||||
sweepTx.TxIn[0] = &wire.TxIn{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: commitmentTx.TxHash(),
|
||||
Index: 1,
|
||||
},
|
||||
}
|
||||
|
||||
// Finally, we test bob sweeping his output as normal in the case that
|
||||
// Alice broadcasts this commitment transaction.
|
||||
bobScriptP2WKH, err := CommitScriptUnencumbered(bobPayKey)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create bob p2wkh script: %v", err)
|
||||
}
|
||||
signDesc = &SignDescriptor{
|
||||
KeyDesc: keychain.KeyDescriptor{
|
||||
PubKey: bobKeyPub,
|
||||
},
|
||||
SingleTweak: bobCommitTweak,
|
||||
WitnessScript: bobScriptP2WKH,
|
||||
SigHashes: txscript.NewTxSigHashes(sweepTx),
|
||||
Output: &wire.TxOut{
|
||||
Value: int64(channelBalance),
|
||||
PkScript: bobScriptP2WKH,
|
||||
},
|
||||
HashType: txscript.SigHashAll,
|
||||
InputIndex: 0,
|
||||
}
|
||||
bobRegularSpend, err := CommitSpendNoDelay(bobSigner, signDesc,
|
||||
sweepTx)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create bob regular spend: %v", err)
|
||||
}
|
||||
sweepTx.TxIn[0].Witness = bobRegularSpend
|
||||
vm, err = txscript.NewEngine(regularOutput.PkScript,
|
||||
sweepTx, 0, txscript.StandardVerifyFlags, nil,
|
||||
nil, int64(channelBalance))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create engine: %v", err)
|
||||
}
|
||||
if err := vm.Execute(); err != nil {
|
||||
t.Fatalf("bob p2wkh spend is invalid: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user