From e8397e383de07a1d8b214d9692e8e84c7e1aa50b Mon Sep 17 00:00:00 2001 From: Augustyn Chmiel Date: Mon, 27 May 2024 06:02:33 +0200 Subject: [PATCH] refactoring(spv-790) addressing leatest review changes --- engine/examples/client/pike/main.go | 13 ++- engine/pike/example_test.go | 52 ++++++++++ engine/pike/pike.go | 19 +++- engine/pike/pike_test.go | 10 +- engine/script/template/evaluate.go | 77 +++++++++++++++ engine/script/template/p2pkh.go | 87 ++-------------- engine/script/template/p2pkh_test.go | 126 ++++++++++++++++-------- engine/types/type42/linking_key.go | 24 +++-- engine/types/type42/linking_key_test.go | 97 ++++-------------- 9 files changed, 287 insertions(+), 218 deletions(-) create mode 100644 engine/pike/example_test.go create mode 100644 engine/script/template/evaluate.go diff --git a/engine/examples/client/pike/main.go b/engine/examples/client/pike/main.go index be5f8d0c..e37ad65c 100644 --- a/engine/examples/client/pike/main.go +++ b/engine/examples/client/pike/main.go @@ -34,8 +34,8 @@ func main() { } // example of usage pike2 - outputsTemplate, _ := template.P2PKH(10000) - fmt.Println(outputsTemplate) + outputsTemplate, _ := pike.GenerateOutputsTemplate(10000) + fmt.Println(formatOutputs(outputsTemplate)) lockingScripts, err := pike.GenerateLockingScriptsFromTemplates(outputsTemplate, senderPubKey, receiverPubKey, "reference") if err != nil { @@ -48,3 +48,12 @@ func main() { } } + +// Helper function to format the outputs into a string +func formatOutputs(outputs []*template.OutputTemplate) string { + var result string + for i, output := range outputs { + result += fmt.Sprintf("Output %d: %v\n", i+1, output) + } + return result +} diff --git a/engine/pike/example_test.go b/engine/pike/example_test.go new file mode 100644 index 00000000..90461c1d --- /dev/null +++ b/engine/pike/example_test.go @@ -0,0 +1,52 @@ +package pike_test + +import ( + "encoding/hex" + "fmt" + + "github.com/libsv/go-bk/bec" + + "github.com/bitcoin-sv/spv-wallet/engine/pike" +) + +func Example_generateLockingScripts() { + // Example sender's public key (replace with actual sender's public key) + senderPublicKeyHex := "034252e5359a1de3b8ec08e6c29b80594e88fb47e6ae9ce65ee5a94f0d371d2cde" + senderPublicKeyBytes, err := hex.DecodeString(senderPublicKeyHex) + if err != nil { + panic(err) + } + senderPubKey, err := bec.ParsePubKey(senderPublicKeyBytes, bec.S256()) + if err != nil { + panic(err) + } + + receiverPublicKeyHex := "027c1404c3ecb034053e6dd90bc68f7933284559c7d0763367584195a8796d9b0e" + receiverPublicKeyBytes, err := hex.DecodeString(receiverPublicKeyHex) + if err != nil { + panic(err) + } + receiverPubKey, err := bec.ParsePubKey(receiverPublicKeyBytes, bec.S256()) + if err != nil { + panic(err) + } + + // Example usage of GenerateOutputsTemplate + outputsTemplate, err := pike.GenerateOutputsTemplate(10000) + if err != nil { + panic(fmt.Errorf("Error generating outputs template - %w", err)) + } + + // Example usage of GenerateLockingScriptsFromTemplates + lockingScripts, err := pike.GenerateLockingScriptsFromTemplates(outputsTemplate, senderPubKey, receiverPubKey, "reference") + if err != nil { + panic(fmt.Errorf("Error generating locking scripts - %w", err)) + } + + for _, script := range lockingScripts { + fmt.Println("Locking Script:", script) + } + + // Output: + // Locking Script: 76a9147327490be831259f38b0f9ab019413e51d1b40c688ac +} diff --git a/engine/pike/pike.go b/engine/pike/pike.go index 5002ef68..d540ccdc 100644 --- a/engine/pike/pike.go +++ b/engine/pike/pike.go @@ -24,8 +24,17 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/types/type42" ) +// GenerateOutputsTemplate creates a Pike output template +func GenerateOutputsTemplate(satoshis uint64) ([]*template.OutputTemplate, error) { + p2pkhTemplate, err := template.P2PKH(satoshis) + if err != nil { + return nil, err + } + return []*template.OutputTemplate{p2pkhTemplate}, nil +} + // GenerateLockingScriptsFromTemplates converts Pike outputs templates to scripts -func GenerateLockingScriptsFromTemplates(outputsTemplate []template.P2PKHTemplate, senderPubKey, receiverPubKey *bec.PublicKey, reference string) ([]string, error) { +func GenerateLockingScriptsFromTemplates(outputsTemplate []*template.OutputTemplate, senderPubKey, receiverPubKey *bec.PublicKey, reference string) ([]string, error) { lockingScripts := make([]string, len(outputsTemplate)) for idx, output := range outputsTemplate { @@ -34,12 +43,16 @@ func GenerateLockingScriptsFromTemplates(outputsTemplate []template.P2PKHTemplat return nil, fmt.Errorf("error creating script from hex string - %w", err) } - dPK, err := type42.DeriveLinkedKey(*senderPubKey, *receiverPubKey, fmt.Sprintf("%s-%d", reference, idx)) + dPK, err := type42.DeriveLinkedKey(senderPubKey, receiverPubKey, fmt.Sprintf("%s-%d", reference, idx)) if err != nil { return nil, err } - scriptBytes := template.Evaluate(*templateScript, dPK) + scriptBytes, err := template.Evaluate(*templateScript, dPK) + if err != nil { + return nil, fmt.Errorf("error evaluating template script - %w", err) + } + finalScript := bscript.Script(scriptBytes) lockingScripts[idx] = finalScript.String() diff --git a/engine/pike/pike_test.go b/engine/pike/pike_test.go index 66c6fe8f..ec678aa4 100644 --- a/engine/pike/pike_test.go +++ b/engine/pike/pike_test.go @@ -24,19 +24,21 @@ func TestGenerateLockingScriptsFromTemplates(t *testing.T) { receiverPubKey, err := bec.ParsePubKey(receiverPubKeyBytes, bec.S256()) assert.NoError(t, err) - outputsTemplate := []template.P2PKHTemplate{ - {Script: "76a914fd88ac"}, // Example P2PKH script in hex - {Script: "76a914fe88ad"}, // Another example P2PKH script in hex + outputsTemplate := []*template.OutputTemplate{ + {Script: "76a914000000000000000000000000000000000000000088ac"}, + {Script: "76a914111111111111111111111111111111111111111188ac"}, } t.Run("Valid Case", func(t *testing.T) { lockingScripts, err := GenerateLockingScriptsFromTemplates(outputsTemplate, senderPubKey, receiverPubKey, "test-reference") assert.NoError(t, err) assert.Len(t, lockingScripts, len(outputsTemplate)) + assert.Equal(t, outputsTemplate[0].Script, lockingScripts[0]) + assert.Equal(t, outputsTemplate[1].Script, lockingScripts[1]) }) t.Run("Invalid Template Script", func(t *testing.T) { - invalidTemplate := []template.P2PKHTemplate{ + invalidTemplate := []*template.OutputTemplate{ {Script: "invalid-hex-string"}, // Invalid hex string } lockingScripts, err := GenerateLockingScriptsFromTemplates(invalidTemplate, senderPubKey, receiverPubKey, "test-reference") diff --git a/engine/script/template/evaluate.go b/engine/script/template/evaluate.go new file mode 100644 index 00000000..5df9eafe --- /dev/null +++ b/engine/script/template/evaluate.go @@ -0,0 +1,77 @@ +package template + +import ( + "errors" + + "github.com/libsv/go-bk/bec" + "github.com/libsv/go-bk/crypto" + "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter" +) + +// Evaluate processes a given Bitcoin script by parsing it, replacing certain opcodes +// with the public key hash, and returning the resulting script as a byte array. +// Will replace any OP_PUBKEYHASH or OP_PUBKEY +// +// Parameters: +// - script: A byte array representing the input script. +// - pubKey: A pointer to a bec.PublicKey which provides the dedicated public key to be used in the evaluation. +// +// Returns: +// - A byte array representing the evaluated script, or nil if an error occurs. +func Evaluate(script []byte, pubKey *bec.PublicKey) ([]byte, error) { + s := bscript.Script(script) + + parser := interpreter.DefaultOpcodeParser{} + parsedScript, err := parser.Parse(&s) + if err != nil { + return nil, err + } + + // Validate parsed opcodes + for _, op := range parsedScript { + if op.Value() == 0xFF { + return nil, errors.New("invalid opcode") + } + } + + // Serialize the public key to compressed format + dPKBytes := pubKey.SerialiseCompressed() + + // Apply Hash160 (SHA-256 followed by RIPEMD-160) to the compressed public key + dPKHash := crypto.Hash160(dPKBytes) + + // Create a new script with the public key hash + newScript := new(bscript.Script) + if err := newScript.AppendPushData(dPKHash); err != nil { + return nil, err + } + + // Parse the public key hash script + pkhParsed, err := parser.Parse(newScript) + if err != nil { + return nil, err + } + + // Replace OP_PUBKEYHASH with the actual public key hash + evaluated := make([]interpreter.ParsedOpcode, 0, len(parsedScript)) + for _, op := range parsedScript { + switch op.Value() { + case bscript.OpPUBKEYHASH: + evaluated = append(evaluated, pkhParsed...) + case bscript.OpPUBKEY: + return nil, errors.New("OP_PUBKEY not supported yet") + default: + evaluated = append(evaluated, op) + } + } + + // Unparse the evaluated opcodes back into a script + finalScript, err := parser.Unparse(evaluated) + if err != nil { + return nil, err + } + + // Cast *bscript.Script back to []byte + return []byte(*finalScript), nil +} diff --git a/engine/script/template/p2pkh.go b/engine/script/template/p2pkh.go index 60c299b4..6d07c163 100644 --- a/engine/script/template/p2pkh.go +++ b/engine/script/template/p2pkh.go @@ -5,21 +5,21 @@ import ( "encoding/hex" "errors" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/crypto" "github.com/libsv/go-bt/v2/bscript" - "github.com/libsv/go-bt/v2/bscript/interpreter" ) -// P2PKHTemplate represents the script and satoshis for a Pike output -type P2PKHTemplate struct { +// OutputTemplate represents the script and satoshis for a Pike output +type OutputTemplate struct { Script string `json:"script"` Satoshis uint64 `json:"satoshis"` } // P2PKH creates a single output with the PIKE template -func P2PKH(satoshis uint64) ([]P2PKHTemplate, error) { +func P2PKH(satoshis uint64) (*OutputTemplate, error) { + if satoshis == 0 { + return nil, errors.New("satoshis cannot be zero") + } if satoshis == ^uint64(0) { return nil, errors.New("invalid satoshis") } @@ -41,77 +41,8 @@ func P2PKH(satoshis uint64) ([]P2PKHTemplate, error) { } // Create and return the PikeOutputsTemplate - return []P2PKHTemplate{ - { - Script: scriptHex, - Satoshis: satoshis, - }, + return &OutputTemplate{ + Script: scriptHex, + Satoshis: satoshis, }, nil } - -// Evaluate processes a given Bitcoin script by parsing it, replacing certain opcodes -// with the public key hash, and returning the resulting script as a byte array. -// Will replace any OP_PUBKEYHASH or OP_PUBKEY -// -// Parameters: -// - script: A byte array representing the input script. -// - dPK: A pointer to a bec.PublicKey which provides the dedicated public key to be used in the evaluation. -// -// Returns: -// - A byte array representing the evaluated script, or nil if an error occurs. -func Evaluate(script []byte, dPK *bec.PublicKey) []byte { - s := bscript.Script(script) - - parser := interpreter.DefaultOpcodeParser{} - parsedScript, err := parser.Parse(&s) - if err != nil { - return nil - } - - // Validate parsed opcodes - for _, op := range parsedScript { - if op.Value() == 0xFF { - return nil - } - } - - // Serialize the public key to compressed format - dPKBytes := dPK.SerialiseCompressed() - - // Apply Hash160 (SHA-256 followed by RIPEMD-160) to the compressed public key - dPKBytesCompressed := crypto.Hash160(dPKBytes) - - // Create a new script with the public key hash - newScript := new(bscript.Script) - if err := newScript.AppendPushData(dPKBytesCompressed); err != nil { - return nil - } - - // Parse the public key hash script - pkhParsed, err := parser.Parse(newScript) - if err != nil { - return nil - } - - // Replace OP_PUBKEYHASH with the actual public key hash - evaluated := make([]interpreter.ParsedOpcode, 0, len(parsedScript)) - for _, op := range parsedScript { - switch op.Value() { - case bscript.OpPUBKEYHASH: - evaluated = append(evaluated, pkhParsed...) - case bscript.OpPUBKEY: - evaluated = append(evaluated, pkhParsed...) // Currently just appending the same key - default: - evaluated = append(evaluated, op) - } - } - - // Unparse the evaluated opcodes back into a script - finalScript, err := parser.Unparse(evaluated) - if err != nil { - return nil - } - - // Cast *bscript.Script back to []byte - return []byte(*finalScript) -} diff --git a/engine/script/template/p2pkh_test.go b/engine/script/template/p2pkh_test.go index e4966f85..294c6dad 100644 --- a/engine/script/template/p2pkh_test.go +++ b/engine/script/template/p2pkh_test.go @@ -14,12 +14,12 @@ func TestP2PKH(t *testing.T) { validTests := []struct { name string satoshis uint64 - expected []P2PKHTemplate + expected []OutputTemplate }{ { name: "valid input", satoshis: 1000, - expected: []P2PKHTemplate{ + expected: []OutputTemplate{ { Script: "76a9fd88ac", Satoshis: 1000, @@ -28,8 +28,8 @@ func TestP2PKH(t *testing.T) { }, { name: "zero satoshis", - satoshis: 0, - expected: []P2PKHTemplate{ + satoshis: 1, + expected: []OutputTemplate{ { Script: "76a9fd88ac", Satoshis: 0, @@ -38,16 +38,6 @@ func TestP2PKH(t *testing.T) { }, } - errorTests := []struct { - name string - satoshis uint64 - }{ - { - name: "negative satoshis", - satoshis: ^uint64(0), // Simulating a case that would cause an error, maximum uint64 value, bitwise NOT of 0 is -1 - }, - } - t.Run("Valid Cases", func(t *testing.T) { for _, tt := range validTests { tt := tt // capture range variable @@ -59,6 +49,20 @@ func TestP2PKH(t *testing.T) { } }) + errorTests := []struct { + name string + satoshis uint64 + }{ + { + name: "negative satoshis", + satoshis: ^uint64(0), // Simulating a case that would cause an error, maximum uint64 value, bitwise NOT of 0 is -1 + }, + { + name: "zero satoshis", + satoshis: 0, + }, + } + t.Run("Error Cases", func(t *testing.T) { for _, tt := range errorTests { tt := tt // capture range variable @@ -78,40 +82,80 @@ func TestEvaluate(t *testing.T) { assert.NoError(t, err) mockPubKeyHash := crypto.Hash160(mockPublicKey.SerialiseCompressed()) - validTests := []struct { - name string - script []byte - publicKey *bec.PublicKey - expected []byte - }{ - { - name: "valid script with OP_PUBKEYHASH", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEYHASH, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, - publicKey: mockPublicKey, - expected: append([]byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpDATA20}, append(mockPubKeyHash, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG)...), - }, - { - name: "valid script with OP_PUBKEY", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEY, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, - publicKey: mockPublicKey, - expected: append([]byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpDATA20}, append(mockPubKeyHash, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG)...), - }, - { - name: "valid script without OP_PUBKEYHASH or OP_PUBKEY", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, - publicKey: mockPublicKey, - expected: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, - }, - } - t.Run("Valid Cases", func(t *testing.T) { + validTests := []struct { + name string + script []byte + publicKey *bec.PublicKey + expected []byte + }{ + { + name: "valid script with OP_PUBKEYHASH", + script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEYHASH, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + publicKey: mockPublicKey, + expected: append([]byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpDATA20}, append(mockPubKeyHash, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG)...), + }, + { + name: "valid script without OP_PUBKEYHASH or OP_PUBKEY", + script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + publicKey: mockPublicKey, + expected: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + }, + { + name: "script with OP_PUSHDATA1 and hex data matching PUBKEY and PUBKEYHASH opcodes", + script: []byte{bscript.OpPUSHDATA1, 1, bscript.OpPUBKEYHASH, bscript.OpADD, bscript.OpPUSHDATA1, 1, bscript.OpPUBKEY, bscript.OpEQUALVERIFY}, + publicKey: mockPublicKey, + expected: []byte{bscript.OpPUSHDATA1, 1, bscript.OpPUBKEYHASH, bscript.OpADD, bscript.OpPUSHDATA1, 1, bscript.OpPUBKEY, bscript.OpEQUALVERIFY}, + }, + { + name: "empty script", + script: []byte{}, + publicKey: mockPublicKey, + expected: []byte{}, + }, + { + name: "script with only valid push data", + script: []byte{bscript.OpPUSHDATA1, 2, 0xaa, 0xbb}, + publicKey: mockPublicKey, + expected: []byte{bscript.OpPUSHDATA1, 2, 0xaa, 0xbb}, + }, + } + for _, tt := range validTests { tt := tt t.Run(tt.name, func(t *testing.T) { - result := Evaluate(tt.script, tt.publicKey) + result, err := Evaluate(tt.script, tt.publicKey) + assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, tt.expected, result) }) } }) + + t.Run("Invalid Cases", func(t *testing.T) { + invalidTests := []struct { + name string + script []byte + publicKey *bec.PublicKey + }{ + { + name: "invalid script", + script: []byte{0xFF}, // Invalid opcode + publicKey: mockPublicKey, + }, + { + name: "valid script with OP_PUBKEY", + script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEY, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + publicKey: mockPublicKey, + }, + } + + for _, tt := range invalidTests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + _, err := Evaluate(tt.script, tt.publicKey) + assert.Error(t, err) + }) + } + }) } diff --git a/engine/types/type42/linking_key.go b/engine/types/type42/linking_key.go index f7dfcc16..6992c1d4 100644 --- a/engine/types/type42/linking_key.go +++ b/engine/types/type42/linking_key.go @@ -20,21 +20,21 @@ import ( // // Returns: // - A byte slice containing the HMAC result as a byte slice or an error if the HMAC calculation fails. -func calculateHMAC(pubSharedSecret []byte, reference string) ([]byte, error) { - if reference == "" { +func calculateHMAC(pubSharedSecret []byte, message string) ([]byte, error) { + if message == "" { return nil, errors.New("invalid invoice number") } h := hmac.New(sha256.New, pubSharedSecret) - if _, err := h.Write([]byte(reference)); err != nil { + if _, err := h.Write([]byte(message)); err != nil { return nil, fmt.Errorf("error writing HMAC message - %w", err) } return h.Sum(nil), nil } -// calculateDedicatedPublicKey calculates the dedicated public key (dPK) using the HMAC result and the receiver's public key. +// calculateLinkedPublicKey calculates the dedicated public key (dPK) using the HMAC result and the receiver's public key. // The HMAC result is used as a scalar to perform elliptic curve scalar multiplication and point addition. // Returns the resulting public key or an error if the calculation fails. -func calculateDedicatedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) (*bec.PublicKey, error) { +func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) (*bec.PublicKey, error) { if len(hmacResult) == 0 { return nil, fmt.Errorf("HMAC result is empty") } @@ -69,7 +69,11 @@ func calculateDedicatedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKe // DeriveLinkedKey derives a child public key from the source public key and link it with public key // with use of invoiceNumber as reference of this derivation. -func DeriveLinkedKey(source bec.PublicKey, linkPubKey bec.PublicKey, invoiceNumber string) (*bec.PublicKey, error) { +func DeriveLinkedKey(source *bec.PublicKey, linkPubKey *bec.PublicKey, invoiceNumber string) (*bec.PublicKey, error) { + if source == nil || linkPubKey == nil { + return nil, errors.New("source or receiver public key is nil") + } + // Check for nil receiver public key if source.X == nil || source.Y == nil { return nil, errors.New("source public key is nil") @@ -79,19 +83,19 @@ func DeriveLinkedKey(source bec.PublicKey, linkPubKey bec.PublicKey, invoiceNumb } // Compute the shared secret - sharedSecret := source.SerialiseCompressed() + publicKeyBytes := source.SerialiseCompressed() // Compute the HMAC result - hmacResult, err := calculateHMAC(sharedSecret, invoiceNumber) + hmacResult, err := calculateHMAC(publicKeyBytes, invoiceNumber) if err != nil { return nil, err } // Calculate the dedicated public key - dPK, err := calculateDedicatedPublicKey(hmacResult, &linkPubKey) + linkedPK, err := calculateLinkedPublicKey(hmacResult, linkPubKey) if err != nil { return nil, err } - return dPK, nil + return linkedPK, nil } diff --git a/engine/types/type42/linking_key_test.go b/engine/types/type42/linking_key_test.go index 99b54180..1f3170b2 100644 --- a/engine/types/type42/linking_key_test.go +++ b/engine/types/type42/linking_key_test.go @@ -2,77 +2,12 @@ package type42 import ( "encoding/hex" - "fmt" "testing" "github.com/libsv/go-bk/bec" - "github.com/stretchr/testify/assert" + assert "github.com/stretchr/testify/require" ) -func TestCalculateHMAC(t *testing.T) { - tests := []struct { - name string - pubSharedSecret []byte - reference string - idx int - expected []byte - wantErr bool - }{ - { - name: "valid input", - pubSharedSecret: []byte("shared_secret"), - reference: "reference", - idx: 1, - expected: computeExpectedHMAC([]byte("shared_secret"), "reference", 1), - wantErr: false, - }, - { - name: "different index", - pubSharedSecret: []byte("shared_secret"), - reference: "reference", - idx: 2, - expected: computeExpectedHMAC([]byte("shared_secret"), "reference", 2), - wantErr: false, - }, - { - name: "empty shared secret", - pubSharedSecret: []byte(""), - reference: "reference", - idx: 1, - expected: computeExpectedHMAC([]byte(""), "reference", 1), - wantErr: false, - }, - { - name: "empty reference", - pubSharedSecret: []byte("shared_secret"), - reference: "", - idx: 1, - expected: computeExpectedHMAC([]byte("shared_secret"), "", 1), - wantErr: false, - }, - { - name: "both empty", - pubSharedSecret: []byte(""), - reference: "", - idx: 1, - expected: computeExpectedHMAC([]byte(""), "", 1), - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := calculateHMAC(tt.pubSharedSecret, fmt.Sprintf("%s-%d", tt.reference, tt.idx)) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.expected, got) - } - }) - } -} - func TestDeriveLinkedKey(t *testing.T) { sourcePubKeyHex := "027c1404c3ecb034053e6dd90bc68f7933284559c7d0763367584195a8796d9b0e" sourcePubKeyBytes, err := hex.DecodeString(sourcePubKeyHex) @@ -86,8 +21,10 @@ func TestDeriveLinkedKey(t *testing.T) { linkPubKey, err := bec.ParsePubKey(linkPubKeyBytes, bec.S256()) assert.NoError(t, err) - validHMAC, _ := calculateHMAC(sourcePubKey.SerialiseCompressed(), "valid-invoice") - validDerivedKey, _ := calculateDedicatedPublicKey(validHMAC, linkPubKey) + validHMAC, err := calculateHMAC(sourcePubKey.SerialiseCompressed(), "valid-invoice") + assert.NoError(t, err) + validDerivedKey, err := calculateLinkedPublicKey(validHMAC, linkPubKey) + assert.NoError(t, err) validTests := []struct { name string @@ -105,6 +42,17 @@ func TestDeriveLinkedKey(t *testing.T) { }, } + t.Run("Valid Cases", func(t *testing.T) { + for _, tt := range validTests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + result, err := DeriveLinkedKey(&tt.source, &tt.linkPubKey, tt.invoiceNumber) + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, result) + }) + } + }) + errorTests := []struct { name string source bec.PublicKey @@ -125,22 +73,11 @@ func TestDeriveLinkedKey(t *testing.T) { }, } - t.Run("Valid Cases", func(t *testing.T) { - for _, tt := range validTests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - result, err := DeriveLinkedKey(tt.source, tt.linkPubKey, tt.invoiceNumber) - assert.NoError(t, err) - assert.Equal(t, tt.expectedResult, result) - }) - } - }) - t.Run("Error Cases", func(t *testing.T) { for _, tt := range errorTests { tt := tt t.Run(tt.name, func(t *testing.T) { - result, err := DeriveLinkedKey(tt.source, tt.linkPubKey, tt.invoiceNumber) + result, err := DeriveLinkedKey(&tt.source, &tt.linkPubKey, tt.invoiceNumber) assert.Error(t, err) assert.Nil(t, result) })