From 58686d4582c22a6659d8fb5d1018267962754c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Thu, 16 Dec 2021 11:15:03 +0000 Subject: [PATCH 01/10] renamed unlock/unlockall to signoff/signoffall --- examples/create_tx/create_tx.go | 2 +- .../create_tx_with_opreturn.go | 2 +- localunlocker_test.go | 12 +++++----- tx_test.go | 22 +++++++++---------- txchange_test.go | 14 ++++++------ txjson_node_test.go | 8 +++---- txjson_test.go | 6 ++--- txunlock.go | 10 ++++----- txunlock_test.go | 6 ++--- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/examples/create_tx/create_tx.go b/examples/create_tx/create_tx.go index df105540..d6911cf6 100644 --- a/examples/create_tx/create_tx.go +++ b/examples/create_tx/create_tx.go @@ -22,7 +22,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") - if err := tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}); err != nil { + if err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}); err != nil { log.Fatal(err.Error()) } log.Printf("tx: %s\n", tx) diff --git a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go index b023eaa3..cb5c0e4f 100644 --- a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go +++ b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go @@ -24,7 +24,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("L3VJH2hcRGYYG6YrbWGmsxQC1zyYixA82YjgEyrEUWDs4ALgk8Vu") - err := tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}) + err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}) if err != nil { log.Fatal(err.Error()) } diff --git a/localunlocker_test.go b/localunlocker_test.go index c13674a3..cd1095dc 100644 --- a/localunlocker_test.go +++ b/localunlocker_test.go @@ -12,11 +12,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestLocalUnlocker_UnlockAll(t *testing.T) { +func TestLocalUnlocker_SignOffAll(t *testing.T) { t.Parallel() - incompleteTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d25072326510000000000ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" - tx, err := bt.NewTxFromString(incompleteTx) + unsignedOffTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d25072326510000000000ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" + tx, err := bt.NewTxFromString(unsignedOffTx) assert.NoError(t, err) assert.NotNil(t, tx) @@ -31,12 +31,12 @@ func TestLocalUnlocker_UnlockAll(t *testing.T) { assert.NoError(t, err) unlocker := bt.LocalUnlockerGetter{PrivateKey: w.PrivKey} - err = tx.UnlockAll(context.Background(), &unlocker) + err = tx.SignOffAll(context.Background(), &unlocker) assert.NoError(t, err) expectedSignedTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d2507232651000000006b483045022100c1d77036dc6cd1f3fa1214b0688391ab7f7a16cd31ea4e5a1f7a415ef167df820220751aced6d24649fa235132f1e6969e163b9400f80043a72879237dab4a1190ad412103b8b40a84123121d260f5c109bc5a46ec819c2e4002e5ba08638783bfb4e01435ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" assert.Equal(t, expectedSignedTx, tx.String()) - assert.NotEqual(t, incompleteTx, tx.String()) + assert.NotEqual(t, unsignedOffTx, tx.String()) } func TestLocalUnlocker_ValidSignature(t *testing.T) { @@ -202,7 +202,7 @@ func TestLocalUnlocker_NonSignature(t *testing.T) { t: t, unlockerFunc: test.unlockerFunc, } - assert.NoError(t, tx.UnlockAll(context.Background(), ug)) + assert.NoError(t, tx.SignOffAll(context.Background(), ug)) for i, script := range test.expUnlockingScripts { asm, err := tx.Inputs[i].UnlockingScript.ToASM() assert.NoError(t, err) diff --git a/tx_test.go b/tx_test.go index cc9dd813..f50932e7 100644 --- a/tx_test.go +++ b/tx_test.go @@ -215,7 +215,7 @@ func TestTx_CreateTx(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) } @@ -249,7 +249,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, true, tx.HasDataOutputs()) @@ -275,7 +275,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, false, tx.HasDataOutputs()) @@ -646,7 +646,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -667,7 +667,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -688,7 +688,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -711,7 +711,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -831,7 +831,7 @@ func Test_IsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -850,7 +850,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -869,7 +869,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -890,7 +890,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), diff --git a/txchange_test.go b/txchange_test.go index 93cae564..c630026c 100644 --- a/txchange_test.go +++ b/txchange_test.go @@ -82,7 +82,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, expectedTx.String(), tx.String()) @@ -108,7 +108,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) // Correct fee for the tx @@ -150,7 +150,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, @@ -190,7 +190,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", tx.String()) @@ -223,7 +223,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a47304402206bbb4b23349bdf86e6fbc9067226e9a7b15c977fa530999b39cd0a6d9c83360d02202dd8ffdc610e58b3fc92b44400d99e38c78866765f31acb40d98007a52e7a826412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff0240420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -257,7 +257,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006b483045022100fd07316603e9abf393e695192e8ce1e7f808d2735cc57039109a2210ad32d9a7022000e301e2a988b23ab3872b041df8b6eb0315238e0918944cbaf8b6abdde75cac412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff023b420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -294,7 +294,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000028ee20a442cdbcc9f9f927d9c2c9370e611675ebc24c064e8e94508ec8eca889e000000006b483045022100f88298f5a380244dd5b91f70be99394f8e562d2a61976ca8cf2aaeb381ee6e6a0220069243fc951061b624cf96124263b857a65a53400846080b543e4a8c16e097ce4121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff42eaf7bdddc797a0beb97717ff8846f03c963fb5fe15a2b555b9cbd477b0254e000000006b483045022100afa7a986e6e0faf725a9779fe8e61fd19b5973544dc7707fd758cdd45912332a0220760fe07fc8610d867be5281f29778e3cd1a18a6eef74470d0f1a4ede95c848924121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff01c82b0000000000001976a9147824dec00be2c45dad83c9b5e9f5d7ef05ba3cf988ac00000000", tx.String()) diff --git a/txjson_node_test.go b/txjson_node_test.go index c6d6863a..7ddf4c6a 100644 --- a/txjson_node_test.go +++ b/txjson_node_test.go @@ -31,7 +31,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +54,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -151,7 +151,7 @@ func TestTxJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -439,7 +439,7 @@ func TestTxsJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) tx2, err := bt.NewTxFromString("020000000117d2011c2a3b8a309d481930bae86e88017b0f55845ada17f96c464684b3af520000000048473044022014a60c3e84cf0160cb7e4ee7d87a3b78c5efb6dd3b66c76970b680affdb95e8f02207f6d9e3268a934e5e278ae513a3bc6dee3bec7bae37204574480305bfb5dea0e41feffffff0240101024010000001976a9149933e4bad50e7dd4b48c1f0be98436ca7d4392a288ac00e1f505000000001976a914abbe187ad301e4326e59587e43d602edd318364e88ac77000000") diff --git a/txjson_test.go b/txjson_test.go index a9d6a401..3f1476e2 100644 --- a/txjson_test.go +++ b/txjson_test.go @@ -31,7 +31,7 @@ func TestTx_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +54,7 @@ func TestTx_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -135,7 +135,7 @@ func TestTx_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), diff --git a/txunlock.go b/txunlock.go index 24390d61..8489ec9a 100644 --- a/txunlock.go +++ b/txunlock.go @@ -8,12 +8,12 @@ import ( "github.com/libsv/go-bt/v2/sighash" ) -// Unlock is used to unlock the transaction at a specific input index. +// SignOff is used to unlock the transaction at a specific input index. // It takes an Unlocker interface as a parameter so that different // unlocking implementations can be used to unlock the transaction - // for example local or external unlocking (hardware wallet), or // signature/nonsignature based. -func (tx *Tx) Unlock(ctx context.Context, u Unlocker, idx uint32, shf sighash.Flag) error { +func (tx *Tx) SignOff(ctx context.Context, u Unlocker, idx uint32, shf sighash.Flag) error { if shf == 0 { shf = sighash.AllForkID } @@ -21,11 +21,11 @@ func (tx *Tx) Unlock(ctx context.Context, u Unlocker, idx uint32, shf sighash.Fl return u.Unlock(ctx, tx, idx, shf) } -// UnlockAll is used to sign all inputs. It takes an UnlockerGetter interface +// SignOffAll is used to sign all inputs. It takes an UnlockerGetter interface // as a parameter so that different unlocking implementations can // be used to sign the transaction - for example local/external // signing, or P2PKH/contract signing. -func (tx *Tx) UnlockAll(ctx context.Context, ug UnlockerGetter) error { +func (tx *Tx) SignOffAll(ctx context.Context, ug UnlockerGetter) error { shf := sighash.AllForkID // use SIGHASHALLFORFORKID to sign automatically for i, in := range tx.Inputs { @@ -34,7 +34,7 @@ func (tx *Tx) UnlockAll(ctx context.Context, ug UnlockerGetter) error { return err } - if err = tx.Unlock(ctx, u, uint32(i), shf); err != nil { + if err = tx.SignOff(ctx, u, uint32(i), shf); err != nil { return err } } diff --git a/txunlock_test.go b/txunlock_test.go index d037e0a9..4a9b603f 100644 --- a/txunlock_test.go +++ b/txunlock_test.go @@ -13,7 +13,7 @@ func TestTx_Sign(t *testing.T) { // todo: add tests } -func TestTx_UnlockAll(t *testing.T) { +func TestTx_SignOffAll(t *testing.T) { t.Parallel() t.Run("valid tx (basic)", func(t *testing.T) { @@ -37,7 +37,7 @@ func TestTx_UnlockAll(t *testing.T) { rawTxBefore := tx.String() - err = tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.NotEqual(t, rawTxBefore, tx.String()) @@ -49,7 +49,7 @@ func TestTx_UnlockAll(t *testing.T) { rawTxBefore := tx.String() - err := tx.UnlockAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: nil}) + err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: nil}) assert.NoError(t, err) assert.Equal(t, rawTxBefore, tx.String()) From 41cf38822c94bb6878f7b415b9478ab99c8d603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 11:43:22 +0000 Subject: [PATCH 02/10] added unlockinputparams, renamed unlock funcs to be input level, updated docs, added test --- errors.go | 1 + examples/create_tx/create_tx.go | 2 +- .../create_tx_with_opreturn.go | 2 +- localunlocker_test.go | 6 +- tx_test.go | 22 +++--- txchange_test.go | 14 ++-- txjson_node_test.go | 8 +- txjson_test.go | 6 +- txunlock.go | 38 +++++++--- txunlock_test.go | 74 +++++++++++++++++-- 10 files changed, 128 insertions(+), 45 deletions(-) diff --git a/errors.go b/errors.go index 5319572d..86f51005 100644 --- a/errors.go +++ b/errors.go @@ -11,6 +11,7 @@ var ( ErrEmptyValues = errors.New("empty value or values passed, all arguments are required and cannot be empty") ErrUnsupportedScript = errors.New("non-P2PKH input used in the tx - unsupported") ErrInvalidScriptType = errors.New("invalid script type") + ErrNoUnlocker = errors.New("unlocker not supplied") ) // Sentinal errors reported by inputs. diff --git a/examples/create_tx/create_tx.go b/examples/create_tx/create_tx.go index d6911cf6..be6404e7 100644 --- a/examples/create_tx/create_tx.go +++ b/examples/create_tx/create_tx.go @@ -22,7 +22,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") - if err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}); err != nil { + if err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}); err != nil { log.Fatal(err.Error()) } log.Printf("tx: %s\n", tx) diff --git a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go index cb5c0e4f..7b4d50d3 100644 --- a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go +++ b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go @@ -24,7 +24,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("L3VJH2hcRGYYG6YrbWGmsxQC1zyYixA82YjgEyrEUWDs4ALgk8Vu") - err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}) + err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}) if err != nil { log.Fatal(err.Error()) } diff --git a/localunlocker_test.go b/localunlocker_test.go index cd1095dc..6fc4f14a 100644 --- a/localunlocker_test.go +++ b/localunlocker_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestLocalUnlocker_SignOffAll(t *testing.T) { +func TestLocalUnlocker_UnlockAllInputs(t *testing.T) { t.Parallel() unsignedOffTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d25072326510000000000ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" @@ -31,7 +31,7 @@ func TestLocalUnlocker_SignOffAll(t *testing.T) { assert.NoError(t, err) unlocker := bt.LocalUnlockerGetter{PrivateKey: w.PrivKey} - err = tx.SignOffAll(context.Background(), &unlocker) + err = tx.UnlockAllInputs(context.Background(), &unlocker) assert.NoError(t, err) expectedSignedTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d2507232651000000006b483045022100c1d77036dc6cd1f3fa1214b0688391ab7f7a16cd31ea4e5a1f7a415ef167df820220751aced6d24649fa235132f1e6969e163b9400f80043a72879237dab4a1190ad412103b8b40a84123121d260f5c109bc5a46ec819c2e4002e5ba08638783bfb4e01435ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" @@ -202,7 +202,7 @@ func TestLocalUnlocker_NonSignature(t *testing.T) { t: t, unlockerFunc: test.unlockerFunc, } - assert.NoError(t, tx.SignOffAll(context.Background(), ug)) + assert.NoError(t, tx.UnlockAllInputs(context.Background(), ug)) for i, script := range test.expUnlockingScripts { asm, err := tx.Inputs[i].UnlockingScript.ToASM() assert.NoError(t, err) diff --git a/tx_test.go b/tx_test.go index f50932e7..3ad026e5 100644 --- a/tx_test.go +++ b/tx_test.go @@ -215,7 +215,7 @@ func TestTx_CreateTx(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) } @@ -249,7 +249,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, true, tx.HasDataOutputs()) @@ -275,7 +275,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, false, tx.HasDataOutputs()) @@ -646,7 +646,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -667,7 +667,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -688,7 +688,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -711,7 +711,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -831,7 +831,7 @@ func Test_IsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -850,7 +850,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -869,7 +869,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -890,7 +890,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), diff --git a/txchange_test.go b/txchange_test.go index c630026c..16daebe7 100644 --- a/txchange_test.go +++ b/txchange_test.go @@ -82,7 +82,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, expectedTx.String(), tx.String()) @@ -108,7 +108,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) // Correct fee for the tx @@ -150,7 +150,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, @@ -190,7 +190,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", tx.String()) @@ -223,7 +223,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a47304402206bbb4b23349bdf86e6fbc9067226e9a7b15c977fa530999b39cd0a6d9c83360d02202dd8ffdc610e58b3fc92b44400d99e38c78866765f31acb40d98007a52e7a826412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff0240420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -257,7 +257,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006b483045022100fd07316603e9abf393e695192e8ce1e7f808d2735cc57039109a2210ad32d9a7022000e301e2a988b23ab3872b041df8b6eb0315238e0918944cbaf8b6abdde75cac412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff023b420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -294,7 +294,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000028ee20a442cdbcc9f9f927d9c2c9370e611675ebc24c064e8e94508ec8eca889e000000006b483045022100f88298f5a380244dd5b91f70be99394f8e562d2a61976ca8cf2aaeb381ee6e6a0220069243fc951061b624cf96124263b857a65a53400846080b543e4a8c16e097ce4121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff42eaf7bdddc797a0beb97717ff8846f03c963fb5fe15a2b555b9cbd477b0254e000000006b483045022100afa7a986e6e0faf725a9779fe8e61fd19b5973544dc7707fd758cdd45912332a0220760fe07fc8610d867be5281f29778e3cd1a18a6eef74470d0f1a4ede95c848924121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff01c82b0000000000001976a9147824dec00be2c45dad83c9b5e9f5d7ef05ba3cf988ac00000000", tx.String()) diff --git a/txjson_node_test.go b/txjson_node_test.go index 7ddf4c6a..3a69ad71 100644 --- a/txjson_node_test.go +++ b/txjson_node_test.go @@ -31,7 +31,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +54,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -151,7 +151,7 @@ func TestTxJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -439,7 +439,7 @@ func TestTxsJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) tx2, err := bt.NewTxFromString("020000000117d2011c2a3b8a309d481930bae86e88017b0f55845ada17f96c464684b3af520000000048473044022014a60c3e84cf0160cb7e4ee7d87a3b78c5efb6dd3b66c76970b680affdb95e8f02207f6d9e3268a934e5e278ae513a3bc6dee3bec7bae37204574480305bfb5dea0e41feffffff0240101024010000001976a9149933e4bad50e7dd4b48c1f0be98436ca7d4392a288ac00e1f505000000001976a914abbe187ad301e4326e59587e43d602edd318364e88ac77000000") diff --git a/txjson_test.go b/txjson_test.go index 3f1476e2..fa719370 100644 --- a/txjson_test.go +++ b/txjson_test.go @@ -31,7 +31,7 @@ func TestTx_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +54,7 @@ func TestTx_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -135,7 +135,7 @@ func TestTx_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), diff --git a/txunlock.go b/txunlock.go index 8489ec9a..70dd37df 100644 --- a/txunlock.go +++ b/txunlock.go @@ -8,33 +8,51 @@ import ( "github.com/libsv/go-bt/v2/sighash" ) -// SignOff is used to unlock the transaction at a specific input index. +// UnlockInputParams params used for unlocking an input with a `bt.Unlocker`. +type UnlockInputParams struct { + // Unlocker to be used. [REQUIRED] + Unlocker Unlocker + // InputIdx the input to be unlocked. [DEFAULT 0] + InputIdx uint32 + // SigHashFlags the be applied [DEFAULT ALL|FORKID] + SigHashFlags sighash.Flag +} + +// UnlockInput is used to unlock the transaction at a specific input index. // It takes an Unlocker interface as a parameter so that different // unlocking implementations can be used to unlock the transaction - // for example local or external unlocking (hardware wallet), or // signature/nonsignature based. -func (tx *Tx) SignOff(ctx context.Context, u Unlocker, idx uint32, shf sighash.Flag) error { - if shf == 0 { - shf = sighash.AllForkID +func (tx *Tx) UnlockInput(ctx context.Context, params UnlockInputParams) error { + if params.Unlocker == nil { + return ErrNoUnlocker } - return u.Unlock(ctx, tx, idx, shf) + if params.SigHashFlags == 0 { + params.SigHashFlags = sighash.AllForkID + } + + return params.Unlocker.Unlock(ctx, tx, params.InputIdx, params.SigHashFlags) } -// SignOffAll is used to sign all inputs. It takes an UnlockerGetter interface +// UnlockAllInputs is used to sign all inputs. It takes an UnlockerGetter interface // as a parameter so that different unlocking implementations can // be used to sign the transaction - for example local/external // signing, or P2PKH/contract signing. -func (tx *Tx) SignOffAll(ctx context.Context, ug UnlockerGetter) error { - shf := sighash.AllForkID // use SIGHASHALLFORFORKID to sign automatically - +// +// Given this signs inputs and outputs, sighash `ALL|FORKID` is used. +func (tx *Tx) UnlockAllInputs(ctx context.Context, ug UnlockerGetter) error { for i, in := range tx.Inputs { u, err := ug.Unlocker(ctx, in.PreviousTxScript) if err != nil { return err } - if err = tx.SignOff(ctx, u, uint32(i), shf); err != nil { + if err = tx.UnlockInput(ctx, UnlockInputParams{ + Unlocker: u, + InputIdx: uint32(i), + SigHashFlags: sighash.AllForkID, // use SIGHASHALLFORFORKID to sign automatically + }); err != nil { return err } } diff --git a/txunlock_test.go b/txunlock_test.go index 4a9b603f..b3a51b06 100644 --- a/txunlock_test.go +++ b/txunlock_test.go @@ -6,14 +6,78 @@ import ( . "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/sighash" "github.com/stretchr/testify/assert" ) -func TestTx_Sign(t *testing.T) { - // todo: add tests +func TestTx_UnlockInput(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + inputIdx uint32 + shf sighash.Flag + unlocker bt.Unlocker + expHex string + expErr error + }{ + "standard unlock": { + inputIdx: 0, + shf: sighash.AllForkID, + unlocker: func() bt.Unlocker { + var wif *WIF + wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") + assert.NoError(t, err) + + return &bt.LocalUnlocker{PrivateKey: wif.PrivKey} + }(), + expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", + }, + "sighash all is used as default": { + inputIdx: 0, + unlocker: func() bt.Unlocker { + var wif *WIF + wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") + assert.NoError(t, err) + + return &bt.LocalUnlocker{PrivateKey: wif.PrivKey} + }(), + expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", + }, + "no unlocker errors": { + inputIdx: 0, + shf: sighash.AllForkID, + expErr: bt.ErrNoUnlocker, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + tx := bt.NewTx() + assert.NoError(t, tx.From( + "07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b", + 0, + "76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac", + 4000000, + )) + assert.NoError(t, tx.ChangeToAddress("mwV3YgnowbJJB3LcyCuqiKpdivvNNFiK7M", bt.NewFeeQuote())) + + err := tx.UnlockInput(context.Background(), bt.UnlockInputParams{ + InputIdx: test.inputIdx, + Unlocker: test.unlocker, + SigHashFlags: test.shf, + }) + if test.expErr != nil { + assert.Error(t, err) + assert.EqualError(t, err, test.expErr.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, test.expHex, tx.String()) + } + }) + } } -func TestTx_SignOffAll(t *testing.T) { +func TestTx_UnlockAllInputs(t *testing.T) { t.Parallel() t.Run("valid tx (basic)", func(t *testing.T) { @@ -37,7 +101,7 @@ func TestTx_SignOffAll(t *testing.T) { rawTxBefore := tx.String() - err = tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.NotEqual(t, rawTxBefore, tx.String()) @@ -49,7 +113,7 @@ func TestTx_SignOffAll(t *testing.T) { rawTxBefore := tx.String() - err := tx.SignOffAll(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: nil}) + err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: nil}) assert.NoError(t, err) assert.Equal(t, rawTxBefore, tx.String()) From eafb89ac22ce84e43a8469897664501e63bd87c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 12:17:50 +0000 Subject: [PATCH 03/10] renamed test var --- localunlocker_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/localunlocker_test.go b/localunlocker_test.go index 6fc4f14a..dece7a78 100644 --- a/localunlocker_test.go +++ b/localunlocker_test.go @@ -15,8 +15,8 @@ import ( func TestLocalUnlocker_UnlockAllInputs(t *testing.T) { t.Parallel() - unsignedOffTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d25072326510000000000ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" - tx, err := bt.NewTxFromString(unsignedOffTx) + incompleteTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d25072326510000000000ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" + tx, err := bt.NewTxFromString(incompleteTx) assert.NoError(t, err) assert.NotNil(t, tx) @@ -36,7 +36,7 @@ func TestLocalUnlocker_UnlockAllInputs(t *testing.T) { expectedSignedTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d2507232651000000006b483045022100c1d77036dc6cd1f3fa1214b0688391ab7f7a16cd31ea4e5a1f7a415ef167df820220751aced6d24649fa235132f1e6969e163b9400f80043a72879237dab4a1190ad412103b8b40a84123121d260f5c109bc5a46ec819c2e4002e5ba08638783bfb4e01435ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" assert.Equal(t, expectedSignedTx, tx.String()) - assert.NotEqual(t, unsignedOffTx, tx.String()) + assert.NotEqual(t, incompleteTx, tx.String()) } func TestLocalUnlocker_ValidSignature(t *testing.T) { From 787dfa743d14ce5020378a492f439fb2ba091401 Mon Sep 17 00:00:00 2001 From: Jad Wahab Date: Fri, 17 Dec 2021 13:59:08 +0000 Subject: [PATCH 04/10] refactor!: remove useless funcs fix localUnlocker check at the beginning ApplyUnlockingScript -> InsertInputUnlockingScript --- localunlocker.go | 31 ++++++++++++++++++------------- localunlocker_test.go | 2 +- txinput.go | 11 +++++++++++ txunlock.go | 24 ------------------------ 4 files changed, 30 insertions(+), 38 deletions(-) diff --git a/localunlocker.go b/localunlocker.go index b10b90fb..15a3fe7d 100644 --- a/localunlocker.go +++ b/localunlocker.go @@ -38,22 +38,27 @@ func (lu *LocalUnlocker) Unlock(ctx context.Context, tx *Tx, idx uint32, shf sig shf = sighash.AllForkID } - sh, err := tx.CalcInputSignatureHash(idx, shf) - if err != nil { - return err - } + switch tx.Inputs[idx].PreviousTxScript.ScriptType() { + case bscript.ScriptTypePubKeyHash: + sh, err := tx.CalcInputSignatureHash(idx, shf) + if err != nil { + return err + } - sig, err := lu.PrivateKey.Sign(sh) - if err != nil { - return err - } + sig, err := lu.PrivateKey.Sign(sh) + if err != nil { + return err + } - pubKey := lu.PrivateKey.PubKey().SerialiseCompressed() - signature := sig.Serialise() + pubKey := lu.PrivateKey.PubKey().SerialiseCompressed() + signature := sig.Serialise() - switch tx.Inputs[idx].PreviousTxScript.ScriptType() { - case bscript.ScriptTypePubKeyHash: - return tx.ApplyP2PKHUnlockingScript(idx, pubKey, signature, shf) + uls, err := bscript.NewP2PKHUnlockingScript(pubKey, signature, shf) + if err != nil { + return err + } + + return tx.InsertInputUnlockingScript(idx, uls) } return errors.New("currently only p2pkh supported") diff --git a/localunlocker_test.go b/localunlocker_test.go index dece7a78..2d22b8f0 100644 --- a/localunlocker_test.go +++ b/localunlocker_test.go @@ -135,7 +135,7 @@ func (m *mockUnlocker) Unlock(ctx context.Context, tx *bt.Tx, idx uint32, shf si script, err := bscript.NewFromASM(m.script) assert.NoError(m.t, err) - return tx.ApplyUnlockingScript(idx, script) + return tx.InsertInputUnlockingScript(idx, script) } func TestLocalUnlocker_NonSignature(t *testing.T) { diff --git a/txinput.go b/txinput.go index 937898d7..a8e372f7 100644 --- a/txinput.go +++ b/txinput.go @@ -217,3 +217,14 @@ func (tx *Tx) SequenceHash() []byte { return crypto.Sha256d(buf) } + +// InsertInputUnlockingScript applies a script to the transaction at a specific index in +// unlocking script field. +func (tx *Tx) InsertInputUnlockingScript(index uint32, s *bscript.Script) error { + if tx.Inputs[index] != nil { + tx.Inputs[index].UnlockingScript = s + return nil + } + + return fmt.Errorf("no input at index %d", index) +} diff --git a/txunlock.go b/txunlock.go index 70dd37df..2c19ad7e 100644 --- a/txunlock.go +++ b/txunlock.go @@ -2,9 +2,7 @@ package bt import ( "context" - "fmt" - "github.com/libsv/go-bt/v2/bscript" "github.com/libsv/go-bt/v2/sighash" ) @@ -59,25 +57,3 @@ func (tx *Tx) UnlockAllInputs(ctx context.Context, ug UnlockerGetter) error { return nil } - -// ApplyP2PKHUnlockingScript applies a script to the transaction at a specific index in -// unlocking script field. -func (tx *Tx) ApplyP2PKHUnlockingScript(index uint32, pubKey []byte, sig []byte, shf sighash.Flag) error { - uls, err := bscript.NewP2PKHUnlockingScript(pubKey, sig, shf) - if err != nil { - return err - } - - return tx.ApplyUnlockingScript(index, uls) -} - -// ApplyUnlockingScript applies a script to the transaction at a specific index in -// unlocking script field. -func (tx *Tx) ApplyUnlockingScript(index uint32, s *bscript.Script) error { - if tx.Inputs[index] != nil { - tx.Inputs[index].UnlockingScript = s - return nil - } - - return fmt.Errorf("no input at index %d", index) -} From 62ae674bd4d457efd212af3e8646c5ea8fa8a224 Mon Sep 17 00:00:00 2001 From: Jad Wahab Date: Fri, 17 Dec 2021 14:32:39 +0000 Subject: [PATCH 05/10] WIP: refactor --- localunlocker.go | 25 +++++++++++++------------ localunlocker_test.go | 11 +++++++---- txunlock.go | 24 +++++++++--------------- unlocker.go | 10 +++++++++- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/localunlocker.go b/localunlocker.go index 15a3fe7d..3d3e7531 100644 --- a/localunlocker.go +++ b/localunlocker.go @@ -18,7 +18,8 @@ type LocalUnlockerGetter struct { // Unlocker builds a new *bt.LocalUnlocker with the same private key // as the calling *bt.LocalUnlockerGetter. func (lg *LocalUnlockerGetter) Unlocker(ctx context.Context, lockingScript *bscript.Script) (Unlocker, error) { - return &LocalUnlocker{PrivateKey: lg.PrivateKey}, nil + // return &LocalUnlocker{PrivateKey: lg.PrivateKey}, nil + return nil, nil } // LocalUnlocker implements the unlocker interface. It is used to unlock a tx locally using a @@ -33,33 +34,33 @@ type LocalUnlocker struct { // as well as the public key corresponding to the private key used. The produced // signature is deterministic (same message and same key yield the same signature) and // canonical in accordance with RFC6979 and BIP0062. -func (lu *LocalUnlocker) Unlock(ctx context.Context, tx *Tx, idx uint32, shf sighash.Flag) error { - if shf == 0 { - shf = sighash.AllForkID +func (lu *LocalUnlocker) Unlock(ctx context.Context, tx *Tx, params UnlockerParams) (*bscript.Script, error) { + if params.SigHashFlags == 0 { + params.SigHashFlags = sighash.AllForkID } - switch tx.Inputs[idx].PreviousTxScript.ScriptType() { + switch tx.Inputs[params.InputIdx].PreviousTxScript.ScriptType() { case bscript.ScriptTypePubKeyHash: - sh, err := tx.CalcInputSignatureHash(idx, shf) + sh, err := tx.CalcInputSignatureHash(params.InputIdx, params.SigHashFlags) if err != nil { - return err + return nil, err } sig, err := lu.PrivateKey.Sign(sh) if err != nil { - return err + return nil, err } pubKey := lu.PrivateKey.PubKey().SerialiseCompressed() signature := sig.Serialise() - uls, err := bscript.NewP2PKHUnlockingScript(pubKey, signature, shf) + uscript, err := bscript.NewP2PKHUnlockingScript(pubKey, signature, params.SigHashFlags) if err != nil { - return err + return nil, err } - return tx.InsertInputUnlockingScript(idx, uls) + return uscript, nil } - return errors.New("currently only p2pkh supported") + return nil, errors.New("currently only p2pkh supported") } diff --git a/localunlocker_test.go b/localunlocker_test.go index 2d22b8f0..7604b1ed 100644 --- a/localunlocker_test.go +++ b/localunlocker_test.go @@ -94,7 +94,10 @@ func TestLocalUnlocker_ValidSignature(t *testing.T) { assert.NoError(t, err) unlocker := &bt.LocalUnlocker{PrivateKey: w.PrivKey} - assert.NoError(t, unlocker.Unlock(context.Background(), tx, 0, sighash.AllForkID)) + uscript, err := unlocker.Unlock(context.Background(), tx, bt.UnlockerParams{}) + assert.NoError(t, err) + + assert.NoError(t, tx.InsertInputUnlockingScript(0, uscript)) parts, err := bscript.DecodeParts(*tx.Inputs[0].UnlockingScript) assert.NoError(t, err) @@ -131,11 +134,11 @@ type mockUnlocker struct { script string } -func (m *mockUnlocker) Unlock(ctx context.Context, tx *bt.Tx, idx uint32, shf sighash.Flag) error { - script, err := bscript.NewFromASM(m.script) +func (m *mockUnlocker) UnlockingScript(ctx context.Context, tx *bt.Tx, params bt.UnlockerParams) (*bscript.Script, error) { + uscript, err := bscript.NewFromASM(m.script) assert.NoError(m.t, err) - return tx.InsertInputUnlockingScript(idx, script) + return uscript, nil } func TestLocalUnlocker_NonSignature(t *testing.T) { diff --git a/txunlock.go b/txunlock.go index 2c19ad7e..c4b30849 100644 --- a/txunlock.go +++ b/txunlock.go @@ -6,23 +6,13 @@ import ( "github.com/libsv/go-bt/v2/sighash" ) -// UnlockInputParams params used for unlocking an input with a `bt.Unlocker`. -type UnlockInputParams struct { - // Unlocker to be used. [REQUIRED] - Unlocker Unlocker - // InputIdx the input to be unlocked. [DEFAULT 0] - InputIdx uint32 - // SigHashFlags the be applied [DEFAULT ALL|FORKID] - SigHashFlags sighash.Flag -} - // UnlockInput is used to unlock the transaction at a specific input index. // It takes an Unlocker interface as a parameter so that different // unlocking implementations can be used to unlock the transaction - // for example local or external unlocking (hardware wallet), or // signature/nonsignature based. -func (tx *Tx) UnlockInput(ctx context.Context, params UnlockInputParams) error { - if params.Unlocker == nil { +func (tx *Tx) UnlockInput(ctx context.Context, unlocker Unlocker, params UnlockerParams) error { + if unlocker == nil { return ErrNoUnlocker } @@ -30,7 +20,12 @@ func (tx *Tx) UnlockInput(ctx context.Context, params UnlockInputParams) error { params.SigHashFlags = sighash.AllForkID } - return params.Unlocker.Unlock(ctx, tx, params.InputIdx, params.SigHashFlags) + uscript, err := unlocker.UnlockingScript(ctx, tx, params) + if err != nil { + return err + } + + return tx.InsertInputUnlockingScript(params.InputIdx, uscript) } // UnlockAllInputs is used to sign all inputs. It takes an UnlockerGetter interface @@ -46,8 +41,7 @@ func (tx *Tx) UnlockAllInputs(ctx context.Context, ug UnlockerGetter) error { return err } - if err = tx.UnlockInput(ctx, UnlockInputParams{ - Unlocker: u, + if err = tx.UnlockInput(ctx, u, UnlockerParams{ InputIdx: uint32(i), SigHashFlags: sighash.AllForkID, // use SIGHASHALLFORFORKID to sign automatically }); err != nil { diff --git a/unlocker.go b/unlocker.go index 86622f84..8db59848 100644 --- a/unlocker.go +++ b/unlocker.go @@ -7,10 +7,18 @@ import ( "github.com/libsv/go-bt/v2/sighash" ) +// UnlockerParams params used for unlocking an input with a `bt.Unlocker`. +type UnlockerParams struct { + // InputIdx the input to be unlocked. [DEFAULT 0] + InputIdx uint32 + // SigHashFlags the be applied [DEFAULT ALL|FORKID] + SigHashFlags sighash.Flag +} + // Unlocker interface to allow custom implementations of different unlocking mechanisms. // Implement the Unlocker function as shown in LocalUnlocker, for example. type Unlocker interface { - Unlock(ctx context.Context, tx *Tx, idx uint32, shf sighash.Flag) error + UnlockingScript(ctx context.Context, tx *Tx, up UnlockerParams) (uscript *bscript.Script, err error) } // UnlockerGetter interfaces getting an unlocker for a given output/locking script. From c9869538d03549aedb6830a9ebf3602de0c9835c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 15:21:10 +0000 Subject: [PATCH 06/10] big change --- bscript/interpreter/debug/debugger_test.go | 84 ++++++------ bscript/interpreter/engine_test.go | 88 ++++++------- bscript/interpreter/thread.go | 20 +-- examples/create_tx/create_tx.go | 3 +- .../create_tx_with_opreturn.go | 3 +- tx_test.go | 33 +++-- txchange_test.go | 15 ++- txinput.go | 47 +++++++ txinput_test.go | 110 ++++++++++++++++ txjson_node_test.go | 9 +- txjson_test.go | 7 +- txunlock.go | 53 -------- txunlock_test.go | 121 ------------------ localunlocker.go => unlocker/local.go | 33 ++--- .../local_test.go | 13 +- utxojson.go | 8 +- 16 files changed, 319 insertions(+), 328 deletions(-) delete mode 100644 txunlock.go delete mode 100644 txunlock_test.go rename localunlocker.go => unlocker/local.go (51%) rename localunlocker_test.go => unlocker/local_test.go (97%) diff --git a/bscript/interpreter/debug/debugger_test.go b/bscript/interpreter/debug/debugger_test.go index a44bf3e5..0781b77c 100644 --- a/bscript/interpreter/debug/debugger_test.go +++ b/bscript/interpreter/debug/debugger_test.go @@ -41,10 +41,10 @@ func TestDebugger_BeforeExecute(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) var timesCalled int @@ -60,7 +60,7 @@ func TestDebugger_BeforeExecute(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -137,10 +137,10 @@ func TestDebugger_BeforeStep(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -160,7 +160,7 @@ func TestDebugger_BeforeStep(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -237,10 +237,10 @@ func TestDebugger_AfterStep(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -260,7 +260,7 @@ func TestDebugger_AfterStep(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -338,10 +338,10 @@ func TestDebugger_BeforeExecuteOpcode(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -361,7 +361,7 @@ func TestDebugger_BeforeExecuteOpcode(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -438,10 +438,10 @@ func TestDebugger_AfterExecuteOpcode(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -461,7 +461,7 @@ func TestDebugger_AfterExecuteOpcode(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -521,10 +521,10 @@ func TestDebugger_BeforeScriptChange(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -547,7 +547,7 @@ func TestDebugger_BeforeScriptChange(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -608,10 +608,10 @@ func TestDebugger_AfterScriptChange(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -634,7 +634,7 @@ func TestDebugger_AfterScriptChange(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -677,10 +677,10 @@ func TestDebugger_AfterExecution(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) stack := make([]string, 0) @@ -695,7 +695,7 @@ func TestDebugger_AfterExecution(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -735,10 +735,10 @@ func TestDebugger_AfterError(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) stack := make([]string, 0) @@ -755,7 +755,7 @@ func TestDebugger_AfterError(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -801,10 +801,10 @@ func TestDebugger_AfterSuccess(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) stack := make([]string, 0) @@ -821,7 +821,7 @@ func TestDebugger_AfterSuccess(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -907,10 +907,10 @@ func TestDebugger_BeforeStackPush(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -932,7 +932,7 @@ func TestDebugger_BeforeStackPush(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -1016,10 +1016,10 @@ func TestDebugger_AfterStackPush(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -1041,7 +1041,7 @@ func TestDebugger_AfterStackPush(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -1117,10 +1117,10 @@ func TestDebugger_BeforeStackPop(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -1140,7 +1140,7 @@ func TestDebugger_BeforeStackPop(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) @@ -1220,10 +1220,10 @@ func TestDebugger_AfterStackPop(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.lockingScriptHex) + lscript, err := bscript.NewFromHexString(test.lockingScriptHex) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.unlockingScriptHex) + uscript, err := bscript.NewFromHexString(test.unlockingScriptHex) assert.NoError(t, err) history := &stateHistory{ @@ -1245,7 +1245,7 @@ func TestDebugger_AfterStackPop(t *testing.T) { }) interpreter.NewEngine().Execute( - interpreter.WithScripts(ls, uls), + interpreter.WithScripts(lscript, uscript), interpreter.WithAfterGenesis(), interpreter.WithDebugger(debugger), ) diff --git a/bscript/interpreter/engine_test.go b/bscript/interpreter/engine_test.go index 40fb1944..e0bd43fa 100644 --- a/bscript/interpreter/engine_test.go +++ b/bscript/interpreter/engine_test.go @@ -28,7 +28,7 @@ func TestBadPC(t *testing.T) { {script: 0, off: 2}, } - uls, err := bscript.NewFromASM("OP_NOP") + uscript, err := bscript.NewFromASM("OP_NOP") if err != nil { t.Errorf("failed to create unlocking script %e", err) } @@ -37,7 +37,7 @@ func TestBadPC(t *testing.T) { Version: 1, Inputs: []*bt.Input{{ PreviousTxOutIndex: 0, - UnlockingScript: uls, + UnlockingScript: uscript, SequenceNumber: 4294967295, }}, Outputs: []*bt.Output{{ @@ -46,12 +46,12 @@ func TestBadPC(t *testing.T) { LockTime: 0, } - ls, err := bscript.NewFromASM("OP_NOP") + lscript, err := bscript.NewFromASM("OP_NOP") if err != nil { t.Errorf("failed to created locking script %e", err) } txOut := &bt.Output{ - LockingScript: ls, + LockingScript: lscript, } for _, test := range tests { @@ -103,12 +103,12 @@ func TestCheckErrorCondition(t *testing.T) { LockTime: 0, } - ls, err := bscript.NewFromASM("OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_TRUE") + lscript, err := bscript.NewFromASM("OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_NOP OP_TRUE") if err != nil { t.Errorf("failed to created locking script %e", err) } txOut := &bt.Output{ - LockingScript: ls, + LockingScript: lscript, } vm := &thread{ @@ -126,12 +126,12 @@ func TestCheckErrorCondition(t *testing.T) { } var done bool - for i := 0; i < len(*ls); i++ { + for i := 0; i < len(*lscript); i++ { done, err = vm.Step() if err != nil { t.Fatalf("failed to step %dth time: %v", i, err) } - if done && i != len(*ls)-1 { + if done && i != len(*lscript)-1 { t.Fatalf("finshed early on %dth time", i) } } @@ -153,10 +153,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -221,10 +221,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -248,10 +248,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -306,10 +306,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -339,10 +339,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -362,10 +362,10 @@ func TestValidateParams(t *testing.T) { err := tx.From("ae81577c1a2434929a1224cf19aa63e167d88029965e2ca6de24defff014d031", 0, "76a91454807ccc44c0eec0b0e187b3ce0e137e9c6cd65d88ac", 0) assert.NoError(t, err) - uls, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") + uscript, err := bscript.NewFromHexString("483045022100a4d9da733aeb29f9ba94dcaa578e71662cf29dd9742ce4b022c098211f4fdb06022041d24db4eda239fa15a12cf91229f6c352adab3c1c10091fc2aa517fe0f487c5412102454c535854802e5eaeaf5cbecd20e0aa508486063b71194dfde34744f19f1a5d") assert.NoError(t, err) - tx.Inputs[0].UnlockingScript = uls + tx.Inputs[0].UnlockingScript = uscript return tx }(), @@ -404,7 +404,7 @@ func TestInvalidFlagCombinations(t *testing.T) { scriptflag.VerifyCleanStack, } - uls, err := bscript.NewFromASM("OP_NOP") + uscript, err := bscript.NewFromASM("OP_NOP") if err != nil { t.Errorf("failed to create unlocking script %e", err) } @@ -413,7 +413,7 @@ func TestInvalidFlagCombinations(t *testing.T) { Version: 1, Inputs: []*bt.Input{{ PreviousTxOutIndex: 0, - UnlockingScript: uls, + UnlockingScript: uscript, SequenceNumber: 4294967295, }}, Outputs: []*bt.Output{{ @@ -422,12 +422,12 @@ func TestInvalidFlagCombinations(t *testing.T) { LockTime: 0, } - ls, err := bscript.NewFromASM("OP_NOP") + lscript, err := bscript.NewFromASM("OP_NOP") if err != nil { t.Errorf("failed to created locking script %e", err) } txOut := &bt.Output{ - LockingScript: ls, + LockingScript: lscript, } for i, test := range tests { @@ -828,13 +828,13 @@ func TestCheckHashTypeEncoding(t *testing.T) { func TestEngine_WithState(t *testing.T) { tests := map[string]struct { - ls string - uls string - state *State + lscript string + uscript string + state *State }{ "start midway": { - ls: "5253958852529387", - uls: "5456", + lscript: "5253958852529387", + uscript: "5456", state: &State{ ScriptIdx: 1, OpcodeIdx: 1, @@ -849,19 +849,19 @@ func TestEngine_WithState(t *testing.T) { NumOps: 3, SavedFirstStack: [][]byte{}, Scripts: func() []ParsedScript { - ls, err := bscript.NewFromHexString("5253958852529387") + lscript, err := bscript.NewFromHexString("5253958852529387") assert.NoError(t, err) - uls, err := bscript.NewFromHexString("5456") + uscript, err := bscript.NewFromHexString("5456") assert.NoError(t, err) var parser DefaultOpcodeParser - pls, err := parser.Parse(ls) + parsedLScript, err := parser.Parse(lscript) assert.NoError(t, err) - puls, err := parser.Parse(uls) + parsedUScript, err := parser.Parse(uscript) assert.NoError(t, err) - return []ParsedScript{puls, pls} + return []ParsedScript{parsedUScript, parsedLScript} }(), Genesis: struct { AfterGenesis bool @@ -872,8 +872,8 @@ func TestEngine_WithState(t *testing.T) { }, }, "start at operation": { - ls: "5253958852529387", - uls: "5456", + lscript: "5253958852529387", + uscript: "5456", state: &State{ ScriptIdx: 1, OpcodeIdx: 6, @@ -888,19 +888,19 @@ func TestEngine_WithState(t *testing.T) { NumOps: 8, SavedFirstStack: [][]byte{}, Scripts: func() []ParsedScript { - ls, err := bscript.NewFromHexString("5253958852529387") + lscript, err := bscript.NewFromHexString("5253958852529387") assert.NoError(t, err) - uls, err := bscript.NewFromHexString("5456") + uscript, err := bscript.NewFromHexString("5456") assert.NoError(t, err) var parser DefaultOpcodeParser - pls, err := parser.Parse(ls) + parsedLScript, err := parser.Parse(lscript) assert.NoError(t, err) - puls, err := parser.Parse(uls) + parsedUScript, err := parser.Parse(uscript) assert.NoError(t, err) - return []ParsedScript{puls, pls} + return []ParsedScript{parsedUScript, parsedLScript} }(), Genesis: struct { AfterGenesis bool @@ -914,13 +914,13 @@ func TestEngine_WithState(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - ls, err := bscript.NewFromHexString(test.ls) + lscript, err := bscript.NewFromHexString(test.lscript) assert.NoError(t, err) - uls, err := bscript.NewFromHexString(test.uls) + uscript, err := bscript.NewFromHexString(test.uscript) assert.NoError(t, err) assert.NoError(t, NewEngine().Execute( - WithScripts(ls, uls), + WithScripts(lscript, uscript), WithForkID(), WithAfterGenesis(), WithState(test.state), diff --git a/bscript/interpreter/thread.go b/bscript/interpreter/thread.go index b7c0ef64..6a6fffdb 100644 --- a/bscript/interpreter/thread.go +++ b/bscript/interpreter/thread.go @@ -281,14 +281,14 @@ func (t *thread) apply(opts *execOpts) error { t.cfg = &afterGenesisConfig{} } - uls := opts.unlockingScript - ls := opts.lockingScript + uscript := opts.unlockingScript + lscript := opts.lockingScript // When both the signature script and public key script are empty the // result is necessarily an error since the stack would end up being // empty which is equivalent to a false top element. Thus, just return // the relevant error now as an optimization. - if (uls == nil || len(*uls) == 0) && (ls == nil || len(*ls) == 0) { + if (uscript == nil || len(*uscript) == 0) && (lscript == nil || len(*lscript) == 0) { return errs.NewError(errs.ErrEvalFalse, "false stack entry at end of script execution") } @@ -296,19 +296,19 @@ func (t *thread) apply(opts *execOpts) error { return errs.NewError(errs.ErrInvalidFlags, "invalid scriptflag combination") } - if len(*uls) > t.cfg.MaxScriptSize() { + if len(*uscript) > t.cfg.MaxScriptSize() { return errs.NewError( errs.ErrScriptTooBig, "unlocking script size %d is larger than the max allowed size %d", - len(*uls), + len(*uscript), t.cfg.MaxScriptSize(), ) } - if len(*ls) > t.cfg.MaxScriptSize() { + if len(*lscript) > t.cfg.MaxScriptSize() { return errs.NewError( errs.ErrScriptTooBig, "locking script size %d is larger than the max allowed size %d", - len(*uls), + len(*uscript), t.cfg.MaxScriptSize(), ) } @@ -318,7 +318,7 @@ func (t *thread) apply(opts *execOpts) error { // with a pay-to-script-hash transaction, there will be ultimately be // a third script to execute. t.scripts = make([]ParsedScript, 2) - for i, script := range []*bscript.Script{uls, ls} { + for i, script := range []*bscript.Script{uscript, lscript} { pscript, err := t.scriptParser.Parse(script) if err != nil { return err @@ -336,11 +336,11 @@ func (t *thread) apply(opts *execOpts) error { // Advance the program counter to the public key script if the signature // script is empty since there is nothing to execute for it in that // case. - if len(*uls) == 0 { + if len(*uscript) == 0 { t.scriptIdx++ } - if t.hasFlag(scriptflag.Bip16) && ls.IsP2SH() { + if t.hasFlag(scriptflag.Bip16) && lscript.IsP2SH() { // Only accept input scripts that push data for P2SH. if !t.scripts[0].IsPushOnly() { return errs.NewError(errs.ErrNotPushOnly, "pay to script hash is not push only") diff --git a/examples/create_tx/create_tx.go b/examples/create_tx/create_tx.go index be6404e7..ed64504b 100644 --- a/examples/create_tx/create_tx.go +++ b/examples/create_tx/create_tx.go @@ -6,6 +6,7 @@ import ( "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/unlocker" ) func main() { @@ -22,7 +23,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") - if err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}); err != nil { + if err := tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: decodedWif.PrivKey}); err != nil { log.Fatal(err.Error()) } log.Printf("tx: %s\n", tx) diff --git a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go index 7b4d50d3..2a3bdb46 100644 --- a/examples/create_tx_with_opreturn/create_tx_with_opreturn.go +++ b/examples/create_tx_with_opreturn/create_tx_with_opreturn.go @@ -6,6 +6,7 @@ import ( "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/unlocker" ) func main() { @@ -24,7 +25,7 @@ func main() { decodedWif, _ := wif.DecodeWIF("L3VJH2hcRGYYG6YrbWGmsxQC1zyYixA82YjgEyrEUWDs4ALgk8Vu") - err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: decodedWif.PrivKey}) + err := tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: decodedWif.PrivKey}) if err != nil { log.Fatal(err.Error()) } diff --git a/tx_test.go b/tx_test.go index 3ad026e5..1e8c3449 100644 --- a/tx_test.go +++ b/tx_test.go @@ -16,6 +16,7 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" "github.com/libsv/go-bt/v2/testing/data" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -85,13 +86,13 @@ func TestNewTxFromString(t *testing.T) { assert.Equal(t, 1, len(tx.Outputs)) // New output - var ls *bscript.Script - ls, err = bscript.NewFromHexString("76a91418392a59fc1f76ad6a3c7ffcea20cfcb17bda9eb88ac") + var lscript *bscript.Script + lscript, err = bscript.NewFromHexString("76a91418392a59fc1f76ad6a3c7ffcea20cfcb17bda9eb88ac") assert.NoError(t, err) - assert.NotNil(t, ls) + assert.NotNil(t, lscript) // Check the type - o := bt.Output{Satoshis: 4999000000, LockingScript: ls} + o := bt.Output{Satoshis: 4999000000, LockingScript: lscript} assert.Equal(t, true, reflect.DeepEqual(*tx.Outputs[0], o)) }) } @@ -215,7 +216,7 @@ func TestTx_CreateTx(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) } @@ -249,7 +250,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, true, tx.HasDataOutputs()) @@ -275,7 +276,7 @@ func TestTx_HasDataOutputs(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, false, tx.HasDataOutputs()) @@ -646,7 +647,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -667,7 +668,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -688,8 +689,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) - assert.Nil(t, err) + assert.NoError(t, tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey})) return tx }(), expSize: &bt.TxSize{ @@ -711,8 +711,7 @@ func Test_EstimateIsFeePaidEnough(t *testing.T) { )) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) - assert.Nil(t, err) + assert.Nil(t, tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey})) return tx }(), expSize: &bt.TxSize{ @@ -831,7 +830,7 @@ func Test_IsFeePaidEnough(t *testing.T) { assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 256559)) assert.NoError(t, tx.ChangeToAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", bt.NewFeeQuote())) - tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -850,7 +849,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 904)) - tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) return tx }(), expSize: &bt.TxSize{ @@ -869,7 +868,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 894)) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), @@ -890,7 +889,7 @@ func Test_IsFeePaidEnough(t *testing.T) { 0, "76a914ff8c9344d4e76c0580420142f697e5fc2ce5c98e88ac", 1000)) assert.NoError(t, tx.AddOpReturnOutput([]byte("hellohello"))) assert.NoError(t, tx.AddP2PKHOutputFromAddress("mtestD3vRB7AoYWK2n6kLdZmAMLbLhDsLr", 895)) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.Nil(t, err) return tx }(), diff --git a/txchange_test.go b/txchange_test.go index 16daebe7..55895bd5 100644 --- a/txchange_test.go +++ b/txchange_test.go @@ -6,6 +6,7 @@ import ( . "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -82,7 +83,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, expectedTx.String(), tx.String()) @@ -108,7 +109,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) // Correct fee for the tx @@ -150,7 +151,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, @@ -190,7 +191,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", tx.String()) @@ -223,7 +224,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a47304402206bbb4b23349bdf86e6fbc9067226e9a7b15c977fa530999b39cd0a6d9c83360d02202dd8ffdc610e58b3fc92b44400d99e38c78866765f31acb40d98007a52e7a826412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff0240420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -257,7 +258,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006b483045022100fd07316603e9abf393e695192e8ce1e7f808d2735cc57039109a2210ad32d9a7022000e301e2a988b23ab3872b041df8b6eb0315238e0918944cbaf8b6abdde75cac412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff023b420f00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88acc0c62d00000000001976a914b6aa34534d2b11e66b438c7525f819aee01e397c88ac00000000", tx.String()) @@ -294,7 +295,7 @@ func TestTx_Change(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, wif) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey}) assert.NoError(t, err) assert.Equal(t, "01000000028ee20a442cdbcc9f9f927d9c2c9370e611675ebc24c064e8e94508ec8eca889e000000006b483045022100f88298f5a380244dd5b91f70be99394f8e562d2a61976ca8cf2aaeb381ee6e6a0220069243fc951061b624cf96124263b857a65a53400846080b543e4a8c16e097ce4121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff42eaf7bdddc797a0beb97717ff8846f03c963fb5fe15a2b555b9cbd477b0254e000000006b483045022100afa7a986e6e0faf725a9779fe8e61fd19b5973544dc7707fd758cdd45912332a0220760fe07fc8610d867be5281f29778e3cd1a18a6eef74470d0f1a4ede95c848924121034aaeabc056f33fd960d1e43fc8a0672723af02f275e54c31381af66a334634caffffffff01c82b0000000000001976a9147824dec00be2c45dad83c9b5e9f5d7ef05ba3cf988ac00000000", tx.String()) diff --git a/txinput.go b/txinput.go index a8e372f7..b12caf39 100644 --- a/txinput.go +++ b/txinput.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/sighash" ) // UTXOGetterFunc is used for tx.Fund(...). It provides the amount of satoshis required @@ -228,3 +229,49 @@ func (tx *Tx) InsertInputUnlockingScript(index uint32, s *bscript.Script) error return fmt.Errorf("no input at index %d", index) } + +// FillInput is used to unlock the transaction at a specific input index. +// It takes an Unlocker interface as a parameter so that different +// unlocking implementations can be used to unlock the transaction - +// for example local or external unlocking (hardware wallet), or +// signature/nonsignature based. +func (tx *Tx) FillInput(ctx context.Context, unlocker Unlocker, params UnlockerParams) error { + if unlocker == nil { + return ErrNoUnlocker + } + + if params.SigHashFlags == 0 { + params.SigHashFlags = sighash.AllForkID + } + + uscript, err := unlocker.UnlockingScript(ctx, tx, params) + if err != nil { + return err + } + + return tx.InsertInputUnlockingScript(params.InputIdx, uscript) +} + +// FillAllInputs is used to sign all inputs. It takes an UnlockerGetter interface +// as a parameter so that different unlocking implementations can +// be used to sign the transaction - for example local/external +// signing, or P2PKH/contract signing. +// +// Given this signs inputs and outputs, sighash `ALL|FORKID` is used. +func (tx *Tx) FillAllInputs(ctx context.Context, ug UnlockerGetter) error { + for i, in := range tx.Inputs { + u, err := ug.Unlocker(ctx, in.PreviousTxScript) + if err != nil { + return err + } + + if err = tx.FillInput(ctx, u, UnlockerParams{ + InputIdx: uint32(i), + SigHashFlags: sighash.AllForkID, // use SIGHASHALLFORFORKID to sign automatically + }); err != nil { + return err + } + } + + return nil +} diff --git a/txinput_test.go b/txinput_test.go index ba37117e..6c68d2b5 100644 --- a/txinput_test.go +++ b/txinput_test.go @@ -7,8 +7,11 @@ import ( "math" "testing" + . "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/sighash" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -602,3 +605,110 @@ func TestTx_Fund_Deficit(t *testing.T) { }) } } + +func TestTx_FillInput(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + inputIdx uint32 + shf sighash.Flag + unlocker bt.Unlocker + expHex string + expErr error + }{ + "standard unlock": { + inputIdx: 0, + shf: sighash.AllForkID, + unlocker: func() bt.Unlocker { + var wif *WIF + wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") + assert.NoError(t, err) + + return &unlocker.Local{PrivateKey: wif.PrivKey} + }(), + expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", + }, + "sighash all is used as default": { + inputIdx: 0, + unlocker: func() bt.Unlocker { + var wif *WIF + wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") + assert.NoError(t, err) + + return &unlocker.Local{PrivateKey: wif.PrivKey} + }(), + expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", + }, + "no unlocker errors": { + inputIdx: 0, + shf: sighash.AllForkID, + expErr: bt.ErrNoUnlocker, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + tx := bt.NewTx() + assert.NoError(t, tx.From( + "07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b", + 0, + "76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac", + 4000000, + )) + assert.NoError(t, tx.ChangeToAddress("mwV3YgnowbJJB3LcyCuqiKpdivvNNFiK7M", bt.NewFeeQuote())) + + err := tx.FillInput(context.Background(), test.unlocker, bt.UnlockerParams{ + InputIdx: test.inputIdx, + SigHashFlags: test.shf, + }) + if test.expErr != nil { + assert.Error(t, err) + assert.EqualError(t, err, test.expErr.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, test.expHex, tx.String()) + } + }) + } +} + +func TestTx_FillAllInputs(t *testing.T) { + t.Parallel() + + t.Run("valid tx (basic)", func(t *testing.T) { + tx := bt.NewTx() + assert.NotNil(t, tx) + + err := tx.From( + "07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b", + 0, + "76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac", + 4000000) + assert.NoError(t, err) + + err = tx.ChangeToAddress("mwV3YgnowbJJB3LcyCuqiKpdivvNNFiK7M", bt.NewFeeQuote()) + assert.NoError(t, err) + + var wif *WIF + wif, err = DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") + assert.NoError(t, err) + assert.NotNil(t, wif) + + rawTxBefore := tx.String() + + assert.NoError(t, tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: wif.PrivKey})) + + assert.NotEqual(t, rawTxBefore, tx.String()) + }) + + t.Run("no input or output", func(t *testing.T) { + tx := bt.NewTx() + assert.NotNil(t, tx) + + rawTxBefore := tx.String() + + assert.NoError(t, tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: nil})) + + assert.Equal(t, rawTxBefore, tx.String()) + }) +} diff --git a/txjson_node_test.go b/txjson_node_test.go index 3a69ad71..80be8daf 100644 --- a/txjson_node_test.go +++ b/txjson_node_test.go @@ -8,6 +8,7 @@ import ( "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -31,7 +32,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +55,7 @@ func TestTxJSON_Node_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -151,7 +152,7 @@ func TestTxJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -439,7 +440,7 @@ func TestTxsJSON_Node_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) tx2, err := bt.NewTxFromString("020000000117d2011c2a3b8a309d481930bae86e88017b0f55845ada17f96c464684b3af520000000048473044022014a60c3e84cf0160cb7e4ee7d87a3b78c5efb6dd3b66c76970b680affdb95e8f02207f6d9e3268a934e5e278ae513a3bc6dee3bec7bae37204574480305bfb5dea0e41feffffff0240101024010000001976a9149933e4bad50e7dd4b48c1f0be98436ca7d4392a288ac00e1f505000000001976a914abbe187ad301e4326e59587e43d602edd318364e88ac77000000") diff --git a/txjson_test.go b/txjson_test.go index fa719370..1227175b 100644 --- a/txjson_test.go +++ b/txjson_test.go @@ -8,6 +8,7 @@ import ( "github.com/libsv/go-bk/wif" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -31,7 +32,7 @@ func TestTx_JSON(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -54,7 +55,7 @@ func TestTx_JSON(t *testing.T) { tx.AddOutput(&bt.Output{ LockingScript: s, }) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), @@ -135,7 +136,7 @@ func TestTx_MarshallJSON(t *testing.T) { w, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") assert.NoError(t, err) assert.NotNil(t, w) - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: w.PrivKey}) + err = tx.FillAllInputs(context.Background(), &unlocker.Getter{PrivateKey: w.PrivKey}) assert.NoError(t, err) return tx }(), diff --git a/txunlock.go b/txunlock.go deleted file mode 100644 index c4b30849..00000000 --- a/txunlock.go +++ /dev/null @@ -1,53 +0,0 @@ -package bt - -import ( - "context" - - "github.com/libsv/go-bt/v2/sighash" -) - -// UnlockInput is used to unlock the transaction at a specific input index. -// It takes an Unlocker interface as a parameter so that different -// unlocking implementations can be used to unlock the transaction - -// for example local or external unlocking (hardware wallet), or -// signature/nonsignature based. -func (tx *Tx) UnlockInput(ctx context.Context, unlocker Unlocker, params UnlockerParams) error { - if unlocker == nil { - return ErrNoUnlocker - } - - if params.SigHashFlags == 0 { - params.SigHashFlags = sighash.AllForkID - } - - uscript, err := unlocker.UnlockingScript(ctx, tx, params) - if err != nil { - return err - } - - return tx.InsertInputUnlockingScript(params.InputIdx, uscript) -} - -// UnlockAllInputs is used to sign all inputs. It takes an UnlockerGetter interface -// as a parameter so that different unlocking implementations can -// be used to sign the transaction - for example local/external -// signing, or P2PKH/contract signing. -// -// Given this signs inputs and outputs, sighash `ALL|FORKID` is used. -func (tx *Tx) UnlockAllInputs(ctx context.Context, ug UnlockerGetter) error { - for i, in := range tx.Inputs { - u, err := ug.Unlocker(ctx, in.PreviousTxScript) - if err != nil { - return err - } - - if err = tx.UnlockInput(ctx, u, UnlockerParams{ - InputIdx: uint32(i), - SigHashFlags: sighash.AllForkID, // use SIGHASHALLFORFORKID to sign automatically - }); err != nil { - return err - } - } - - return nil -} diff --git a/txunlock_test.go b/txunlock_test.go deleted file mode 100644 index b3a51b06..00000000 --- a/txunlock_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package bt_test - -import ( - "context" - "testing" - - . "github.com/libsv/go-bk/wif" - "github.com/libsv/go-bt/v2" - "github.com/libsv/go-bt/v2/sighash" - "github.com/stretchr/testify/assert" -) - -func TestTx_UnlockInput(t *testing.T) { - t.Parallel() - - tests := map[string]struct { - inputIdx uint32 - shf sighash.Flag - unlocker bt.Unlocker - expHex string - expErr error - }{ - "standard unlock": { - inputIdx: 0, - shf: sighash.AllForkID, - unlocker: func() bt.Unlocker { - var wif *WIF - wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") - assert.NoError(t, err) - - return &bt.LocalUnlocker{PrivateKey: wif.PrivKey} - }(), - expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", - }, - "sighash all is used as default": { - inputIdx: 0, - unlocker: func() bt.Unlocker { - var wif *WIF - wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") - assert.NoError(t, err) - - return &bt.LocalUnlocker{PrivateKey: wif.PrivKey} - }(), - expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", - }, - "no unlocker errors": { - inputIdx: 0, - shf: sighash.AllForkID, - expErr: bt.ErrNoUnlocker, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - tx := bt.NewTx() - assert.NoError(t, tx.From( - "07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b", - 0, - "76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac", - 4000000, - )) - assert.NoError(t, tx.ChangeToAddress("mwV3YgnowbJJB3LcyCuqiKpdivvNNFiK7M", bt.NewFeeQuote())) - - err := tx.UnlockInput(context.Background(), bt.UnlockInputParams{ - InputIdx: test.inputIdx, - Unlocker: test.unlocker, - SigHashFlags: test.shf, - }) - if test.expErr != nil { - assert.Error(t, err) - assert.EqualError(t, err, test.expErr.Error()) - } else { - assert.NoError(t, err) - assert.Equal(t, test.expHex, tx.String()) - } - }) - } -} - -func TestTx_UnlockAllInputs(t *testing.T) { - t.Parallel() - - t.Run("valid tx (basic)", func(t *testing.T) { - tx := bt.NewTx() - assert.NotNil(t, tx) - - err := tx.From( - "07912972e42095fe58daaf09161c5a5da57be47c2054dc2aaa52b30fefa1940b", - 0, - "76a914af2590a45ae401651fdbdf59a76ad43d1862534088ac", - 4000000) - assert.NoError(t, err) - - err = tx.ChangeToAddress("mwV3YgnowbJJB3LcyCuqiKpdivvNNFiK7M", bt.NewFeeQuote()) - assert.NoError(t, err) - - var wif *WIF - wif, err = DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") - assert.NoError(t, err) - assert.NotNil(t, wif) - - rawTxBefore := tx.String() - - err = tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: wif.PrivKey}) - assert.NoError(t, err) - - assert.NotEqual(t, rawTxBefore, tx.String()) - }) - - t.Run("no input or output", func(t *testing.T) { - tx := bt.NewTx() - assert.NotNil(t, tx) - - rawTxBefore := tx.String() - - err := tx.UnlockAllInputs(context.Background(), &bt.LocalUnlockerGetter{PrivateKey: nil}) - assert.NoError(t, err) - - assert.Equal(t, rawTxBefore, tx.String()) - }) -} diff --git a/localunlocker.go b/unlocker/local.go similarity index 51% rename from localunlocker.go rename to unlocker/local.go index 3d3e7531..d1d67bbb 100644 --- a/localunlocker.go +++ b/unlocker/local.go @@ -1,40 +1,43 @@ -package bt +package unlocker import ( "context" "errors" "github.com/libsv/go-bk/bec" + "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" "github.com/libsv/go-bt/v2/sighash" ) -// LocalUnlockerGetter implements the UnlockerGetter interface. It unlocks a Tx locally, +// Getter implements the `bt.UnlockerGetter` interface. It unlocks a Tx locally, // using a bec PrivateKey. -type LocalUnlockerGetter struct { +type Getter struct { PrivateKey *bec.PrivateKey } -// Unlocker builds a new *bt.LocalUnlocker with the same private key -// as the calling *bt.LocalUnlockerGetter. -func (lg *LocalUnlockerGetter) Unlocker(ctx context.Context, lockingScript *bscript.Script) (Unlocker, error) { - // return &LocalUnlocker{PrivateKey: lg.PrivateKey}, nil - return nil, nil +// Unlocker builds a new `*unlocker.Local` with the same private key +// as the calling `*local.Getter`. +func (g *Getter) Unlocker(ctx context.Context, lockingScript *bscript.Script) (bt.Unlocker, error) { + return &Local{PrivateKey: g.PrivateKey}, nil } -// LocalUnlocker implements the unlocker interface. It is used to unlock a tx locally using a +// Local implements the `bt.Unlocker` interface. It is used to unlock a tx locally using a // bec Private Key. -type LocalUnlocker struct { +type Local struct { PrivateKey *bec.PrivateKey } -// Unlock a transaction at a given input using the PrivateKey passed in through the LocalUnlocker +// UnlockingScript create the unlocking script for a given input using the PrivateKey passed in through the `unlocker.Local` // struct. -// Unlock generates, applies, and returns an ECDSA signature for the provided hash digest using the private key +// +// UnlockingScript generates and uses an ECDSA signature for the provided hash digest using the private key // as well as the public key corresponding to the private key used. The produced // signature is deterministic (same message and same key yield the same signature) and // canonical in accordance with RFC6979 and BIP0062. -func (lu *LocalUnlocker) Unlock(ctx context.Context, tx *Tx, params UnlockerParams) (*bscript.Script, error) { +// +// For example usage, see `examples/create_tx/create_tx.go` +func (l *Local) UnlockingScript(ctx context.Context, tx *bt.Tx, params bt.UnlockerParams) (*bscript.Script, error) { if params.SigHashFlags == 0 { params.SigHashFlags = sighash.AllForkID } @@ -46,12 +49,12 @@ func (lu *LocalUnlocker) Unlock(ctx context.Context, tx *Tx, params UnlockerPara return nil, err } - sig, err := lu.PrivateKey.Sign(sh) + sig, err := l.PrivateKey.Sign(sh) if err != nil { return nil, err } - pubKey := lu.PrivateKey.PubKey().SerialiseCompressed() + pubKey := l.PrivateKey.PubKey().SerialiseCompressed() signature := sig.Serialise() uscript, err := bscript.NewP2PKHUnlockingScript(pubKey, signature, params.SigHashFlags) diff --git a/localunlocker_test.go b/unlocker/local_test.go similarity index 97% rename from localunlocker_test.go rename to unlocker/local_test.go index 7604b1ed..776835e1 100644 --- a/localunlocker_test.go +++ b/unlocker/local_test.go @@ -1,4 +1,4 @@ -package bt_test +package unlocker_test import ( "context" @@ -9,6 +9,7 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" "github.com/libsv/go-bt/v2/sighash" + "github.com/libsv/go-bt/v2/unlocker" "github.com/stretchr/testify/assert" ) @@ -30,8 +31,8 @@ func TestLocalUnlocker_UnlockAllInputs(t *testing.T) { w, err = wif.DecodeWIF("cNGwGSc7KRrTmdLUZ54fiSXWbhLNDc2Eg5zNucgQxyQCzuQ5YRDq") assert.NoError(t, err) - unlocker := bt.LocalUnlockerGetter{PrivateKey: w.PrivKey} - err = tx.UnlockAllInputs(context.Background(), &unlocker) + unlocker := unlocker.Getter{PrivateKey: w.PrivKey} + err = tx.FillAllInputs(context.Background(), &unlocker) assert.NoError(t, err) expectedSignedTx := "010000000193a35408b6068499e0d5abd799d3e827d9bfe70c9b75ebe209c91d2507232651000000006b483045022100c1d77036dc6cd1f3fa1214b0688391ab7f7a16cd31ea4e5a1f7a415ef167df820220751aced6d24649fa235132f1e6969e163b9400f80043a72879237dab4a1190ad412103b8b40a84123121d260f5c109bc5a46ec819c2e4002e5ba08638783bfb4e01435ffffffff02404b4c00000000001976a91404ff367be719efa79d76e4416ffb072cd53b208888acde94a905000000001976a91404d03f746652cfcb6cb55119ab473a045137d26588ac00000000" @@ -93,8 +94,8 @@ func TestLocalUnlocker_ValidSignature(t *testing.T) { w, err := wif.DecodeWIF("cNGwGSc7KRrTmdLUZ54fiSXWbhLNDc2Eg5zNucgQxyQCzuQ5YRDq") assert.NoError(t, err) - unlocker := &bt.LocalUnlocker{PrivateKey: w.PrivKey} - uscript, err := unlocker.Unlock(context.Background(), tx, bt.UnlockerParams{}) + unlocker := &unlocker.Local{PrivateKey: w.PrivKey} + uscript, err := unlocker.UnlockingScript(context.Background(), tx, bt.UnlockerParams{}) assert.NoError(t, err) assert.NoError(t, tx.InsertInputUnlockingScript(0, uscript)) @@ -205,7 +206,7 @@ func TestLocalUnlocker_NonSignature(t *testing.T) { t: t, unlockerFunc: test.unlockerFunc, } - assert.NoError(t, tx.UnlockAllInputs(context.Background(), ug)) + assert.NoError(t, tx.FillAllInputs(context.Background(), ug)) for i, script := range test.expUnlockingScripts { asm, err := tx.Inputs[i].UnlockingScript.ToASM() assert.NoError(t, err) diff --git a/utxojson.go b/utxojson.go index 3161bbdf..59062551 100644 --- a/utxojson.go +++ b/utxojson.go @@ -39,13 +39,13 @@ func (u *UTXO) UnmarshalJSON(body []byte) error { return err } - ls, err := bscript.NewFromHexString(j.LockingScript) + lscript, err := bscript.NewFromHexString(j.LockingScript) if err != nil { return err } u.TxID = txID - u.LockingScript = ls + u.LockingScript = lscript u.Vout = j.Vout u.Satoshis = j.Satoshis @@ -83,14 +83,14 @@ func (n *nodeUTXOWrapper) UnmarshalJSON(b []byte) error { return err } - ls, err := bscript.NewFromHexString(uj.ScriptPubKey) + lscript, err := bscript.NewFromHexString(uj.ScriptPubKey) if err != nil { return err } n.UTXO.Satoshis = uint64(uj.Amount * 100000000) n.UTXO.Vout = uj.Vout - n.UTXO.LockingScript = ls + n.UTXO.LockingScript = lscript n.UTXO.TxID = txID return nil From dd03fc53d73d1f0313f413d78d7c759a47a5da38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 15:22:16 +0000 Subject: [PATCH 07/10] fixed linter --- unlocker/local.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unlocker/local.go b/unlocker/local.go index d1d67bbb..0067dde0 100644 --- a/unlocker/local.go +++ b/unlocker/local.go @@ -28,8 +28,8 @@ type Local struct { PrivateKey *bec.PrivateKey } -// UnlockingScript create the unlocking script for a given input using the PrivateKey passed in through the `unlocker.Local` -// struct. +// UnlockingScript create the unlocking script for a given input using the PrivateKey passed in through the +// the `unlock.Local` struct. // // UnlockingScript generates and uses an ECDSA signature for the provided hash digest using the private key // as well as the public key corresponding to the private key used. The produced From f43104149cb74d9215d0685b850e410799872c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 22:51:52 +0000 Subject: [PATCH 08/10] renamed unlocker.Local to unlocker.Simple. Created unlocker getter example --- examples/unlocker_getter/account.go | 103 ++++++++++++++++++++++++++ examples/unlocker_getter/main.go | 107 ++++++++++++++++++++++++++++ txinput_test.go | 4 +- unlocker/local.go | 10 +-- unlocker/local_test.go | 2 +- 5 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 examples/unlocker_getter/account.go create mode 100644 examples/unlocker_getter/main.go diff --git a/examples/unlocker_getter/account.go b/examples/unlocker_getter/account.go new file mode 100644 index 00000000..35c2320e --- /dev/null +++ b/examples/unlocker_getter/account.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "crypto/rand" + "encoding/binary" + "errors" + + "github.com/libsv/go-bk/bip32" + "github.com/libsv/go-bk/chaincfg" + "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/unlocker" +) + +// account for creating destination scripts and stores these scripts with their derivations. +type account struct { + // masterPrivKey of which all locking scripts and private keys are generated from. + masterPrivKey *bip32.ExtendedKey + // scriptToPathMap for mapping a locking script hex to its derivation path. + scriptToPathMap map[string]string + // pathToScriptMap for mapping a derivation path to its locking script. + pathToScriptMap map[string]*bscript.Script +} + +func newAccount() *account { + // Generate the master private key. + seed, err := bip32.GenerateSeed(bip32.RecommendedSeedLen) + if err != nil { + panic(err) + } + privKey, err := bip32.NewMaster(seed, &chaincfg.MainNet) + if err != nil { + panic(err) + } + + return &account{ + masterPrivKey: privKey, + scriptToPathMap: make(map[string]string, 0), + pathToScriptMap: make(map[string]*bscript.Script, 0), + } +} + +func (a *account) createDestination() *bscript.Script { + var path string + // key generating a new path until unique one is found + for ok := true; ok; _, ok = a.pathToScriptMap[path] { + path = bip32.DerivePath(randUint64()) + } + + // Derive a public key from the new derivation path. + pubKey, err := a.masterPrivKey.DerivePublicKeyFromPath(path) + if err != nil { + panic(err) + } + + // Create a locking script from this public key. + s, err := bscript.NewP2PKHFromPubKeyBytes(pubKey) + if err != nil { + panic(err) + } + + // Store the locking script and its path for later use. + a.pathToScriptMap[path] = s + a.scriptToPathMap[s.String()] = path + + return s +} + +// Unlocker get the correct unlocker for a given locking script. +func (a *account) Unlocker(ctx context.Context, lockingScript *bscript.Script) (bt.Unlocker, error) { + // Retrieve the path for the given locking script. + path, ok := a.scriptToPathMap[lockingScript.String()] + if !ok { + panic(errors.New("oh no")) + } + + // Derive a private key from the stored derivation path. This private key will be pair to + // the public key we derived when this locking script was created. + extPrivKey, err := a.masterPrivKey.DeriveChildFromPath(path) + if err != nil { + panic(err) + } + + // Convert the extended key into an eliptic curve private key. + privKey, err := extPrivKey.ECPrivKey() + if err != nil { + panic(err) + } + + return &unlocker.Simple{ + PrivateKey: privKey, + }, nil +} + +func randUint64() uint64 { + var bb [8]byte + if _, err := rand.Read(bb[:]); err != nil { + panic(err) + } + + return binary.LittleEndian.Uint64(bb[:]) +} diff --git a/examples/unlocker_getter/main.go b/examples/unlocker_getter/main.go new file mode 100644 index 00000000..3cbf5dde --- /dev/null +++ b/examples/unlocker_getter/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + + "github.com/libsv/go-bk/wif" + "github.com/libsv/go-bt/v2" + "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/unlocker" +) + +// This example gives a simple in-memory based example of how to implement and use a `bt.UnlockerGetter` +// using derivated public/private keys. +// +// The basic idea is, we have accounts, each with a master private key. If someone would like to send money +// to an account, they request "destinations" from the account. These destinations are added to the +// tx, which is then ultimately broadcast. +// +// A destination in this example is simply a P2PKH locking script, however, the PK Hash will be unique +// on each call as under the hood, the account is deriving a new private/public key pair from its master +// key, creating a P2PKH script from that this pair, and storing the value used to derive this private/public +// key pair against the P2PKH script that it produced. +// +// When an account wishes to spend a fund it received in this manner, after adding the funds to the tx it is +// currently building, it calls `tx.FillAllInputs`. This function iterates all inputs on the tx, passing +// their `PreviousTxScript` to the `bt.UnlockerGetter` provided to the `bt.FillAllInputs` call. +// This allows an account when receiving a locking script to refer to its own script=>derivation mapping, +// and ultimately derive the private key used to create the public key that used to create the locking script. +// Finally allowing for an `unlocker.Simple` to be returned, with this derived private key. +func main() { + // Create two accounts. The first is our account, which we will pretend to fund to begin with. + // The second is the merchant, which we will pretend to send money to. + myAccount := newAccount() + merchantAccount := newAccount() + + // Base Tx to "fund" our account. + baseTx := bt.NewTx() + if err := baseTx.From( + "11b476ad8e0a48fcd40807a111a050af51114877e09283bfa7f3505081a1819d", + 0, + "76a914b48b288c48e6cd7246876e19f848a60f46ab4a6188ac", + 1500, + ); err != nil { + panic(err) + } + + // decocdedWif just for signing the base tx. It isn't relevant to myAccount or merchantAccount, + // and can be ignored. + decodedWif, err := wif.DecodeWIF("KznvCNc6Yf4iztSThoMH6oHWzH9EgjfodKxmeuUGPq5DEX5maspS") + if err != nil { + panic(err) + } + + // Get three destinations (p2pkh scripts) from our account and add them to the baseTx. + // We must get these from the account we're sending to, to allow the account to build its + // own internal mapping for later spending. + for i := 0; i < 3; i++ { + destination := myAccount.createDestination() + if err := baseTx.AddP2PKHOutputFromScript(destination, 400); err != nil { + panic(err) + } + } + + changeScript, err := bscript.NewP2PKHFromPubKeyEC(decodedWif.PrivKey.PubKey()) + if err != nil { + panic(err) + } + + if err = baseTx.Change(changeScript, bt.NewFeeQuote()); err != nil { + panic(err) + } + + if err = baseTx.FillInput( + context.Background(), + &unlocker.Simple{PrivateKey: decodedWif.PrivKey}, + bt.UnlockerParams{}, + ); err != nil { + panic(err) + } + + // Here we would broadcast the transaction, to the account. Assume this has been done + // and that we are starting anew, only this time it's the account who is building and + // broadcasting a transaction to elsewhere. + + // Create the tx which we are going to send to the merchant. + tx := bt.NewTx() + // Add the three UTXOs from the baseTx for funding. + for i := 0; i < 3; i++ { + if err = tx.From(baseTx.TxID(), uint32(i), baseTx.Outputs[i].LockingScript.String(), 400); err != nil { + panic(err) + } + } + + if err := tx.AddP2PKHOutputFromScript(merchantAccount.createDestination(), 1000); err != nil { + panic(err) + } + + if err := tx.Change(myAccount.createDestination(), bt.NewFeeQuote()); err != nil { + panic(err) + } + + // Call fill all inputs and pass in the signing account as the UnlockerGetter. The account + // struct implements `bt.UnlockerGetter`. + if err := tx.FillAllInputs(context.Background(), myAccount); err != nil { + panic(err) + } +} diff --git a/txinput_test.go b/txinput_test.go index 6c68d2b5..faa93bc3 100644 --- a/txinput_test.go +++ b/txinput_test.go @@ -624,7 +624,7 @@ func TestTx_FillInput(t *testing.T) { wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") assert.NoError(t, err) - return &unlocker.Local{PrivateKey: wif.PrivKey} + return &unlocker.Simple{PrivateKey: wif.PrivKey} }(), expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", }, @@ -635,7 +635,7 @@ func TestTx_FillInput(t *testing.T) { wif, err := DecodeWIF("L3MhnEn1pLWcggeYLk9jdkvA2wUK1iWwwrGkBbgQRqv6HPCdRxuw") assert.NoError(t, err) - return &unlocker.Local{PrivateKey: wif.PrivKey} + return &unlocker.Simple{PrivateKey: wif.PrivKey} }(), expHex: "01000000010b94a1ef0fb352aa2adc54207ce47ba55d5a1c1609afda58fe9520e472299107000000006a473044022049ee0c0f26c00e6a6b3af5990fc8296c66eab3e3e42ab075069b89b1be6fefec02206079e49dd8c9e1117ef06fbe99714d822620b1f0f5d19f32a1128f5d29b7c3c4412102c8803fdd437d902f08e3c2344cb33065c99d7c99982018ff9f7219c3dd352ff0ffffffff01a0083d00000000001976a914af2590a45ae401651fdbdf59a76ad43d1862534088ac00000000", }, diff --git a/unlocker/local.go b/unlocker/local.go index 0067dde0..77a08590 100644 --- a/unlocker/local.go +++ b/unlocker/local.go @@ -19,12 +19,12 @@ type Getter struct { // Unlocker builds a new `*unlocker.Local` with the same private key // as the calling `*local.Getter`. func (g *Getter) Unlocker(ctx context.Context, lockingScript *bscript.Script) (bt.Unlocker, error) { - return &Local{PrivateKey: g.PrivateKey}, nil + return &Simple{PrivateKey: g.PrivateKey}, nil } -// Local implements the `bt.Unlocker` interface. It is used to unlock a tx locally using a -// bec Private Key. -type Local struct { +// Simple implements the a simple `bt.Unlocker` interface. It is used to build an unlocking script +// using a bec Private Key. +type Simple struct { PrivateKey *bec.PrivateKey } @@ -37,7 +37,7 @@ type Local struct { // canonical in accordance with RFC6979 and BIP0062. // // For example usage, see `examples/create_tx/create_tx.go` -func (l *Local) UnlockingScript(ctx context.Context, tx *bt.Tx, params bt.UnlockerParams) (*bscript.Script, error) { +func (l *Simple) UnlockingScript(ctx context.Context, tx *bt.Tx, params bt.UnlockerParams) (*bscript.Script, error) { if params.SigHashFlags == 0 { params.SigHashFlags = sighash.AllForkID } diff --git a/unlocker/local_test.go b/unlocker/local_test.go index 776835e1..397e416a 100644 --- a/unlocker/local_test.go +++ b/unlocker/local_test.go @@ -94,7 +94,7 @@ func TestLocalUnlocker_ValidSignature(t *testing.T) { w, err := wif.DecodeWIF("cNGwGSc7KRrTmdLUZ54fiSXWbhLNDc2Eg5zNucgQxyQCzuQ5YRDq") assert.NoError(t, err) - unlocker := &unlocker.Local{PrivateKey: w.PrivKey} + unlocker := &unlocker.Simple{PrivateKey: w.PrivKey} uscript, err := unlocker.UnlockingScript(context.Background(), tx, bt.UnlockerParams{}) assert.NoError(t, err) From b1cad8689621438a07e90a34e0d1aef20e51a787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Fri, 17 Dec 2021 22:57:20 +0000 Subject: [PATCH 09/10] added ref to example in docstring --- unlocker/local.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unlocker/local.go b/unlocker/local.go index 77a08590..9bcc1661 100644 --- a/unlocker/local.go +++ b/unlocker/local.go @@ -18,6 +18,8 @@ type Getter struct { // Unlocker builds a new `*unlocker.Local` with the same private key // as the calling `*local.Getter`. +// +// For an example implementation, see `examples/unlocker_getter/`. func (g *Getter) Unlocker(ctx context.Context, lockingScript *bscript.Script) (bt.Unlocker, error) { return &Simple{PrivateKey: g.PrivateKey}, nil } From 2b29fcb99ad61d04efad2257c9f37b58c9d63ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Sun, 19 Dec 2021 18:21:00 +0000 Subject: [PATCH 10/10] rename local.go to simple.go --- unlocker/{local.go => simple.go} | 0 unlocker/{local_test.go => simple_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename unlocker/{local.go => simple.go} (100%) rename unlocker/{local_test.go => simple_test.go} (100%) diff --git a/unlocker/local.go b/unlocker/simple.go similarity index 100% rename from unlocker/local.go rename to unlocker/simple.go diff --git a/unlocker/local_test.go b/unlocker/simple_test.go similarity index 100% rename from unlocker/local_test.go rename to unlocker/simple_test.go