diff --git a/sign/signed_transaction.go b/sign/signed_transaction.go index a267f68..4f61b74 100644 --- a/sign/signed_transaction.go +++ b/sign/signed_transaction.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha256" "encoding/hex" + "fmt" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcutil" @@ -82,3 +83,42 @@ func (tx *SignedTransaction) Sign(wifs []string, chain *Chain) error { tx.Transaction.Signatures = sigsHex return nil } + +func (tx *SignedTransaction) Verify(chain *Chain, keys [][]byte) (bool, error) { + dig, err := tx.Digest(chain) + if err != nil { + return false, fmt.Errorf("failed to get digest: %w", err) + } + + pubKeysFound := make([]*btcec.PublicKey, 0, len(tx.Signatures)) + for _, signature := range tx.Signatures { + sig, err := hex.DecodeString(signature) + if err != nil { + return false, fmt.Errorf("failed to decode signature: %w", err) + } + + p, _, err := btcec.RecoverCompact(btcec.S256(), sig, dig) + if err != nil { + return false, fmt.Errorf("failed to RecoverCompact: %w", err) + } + + pubKeysFound = append(pubKeysFound, p) + } + +find: + for _, pub := range pubKeysFound { + for _, v := range keys { + pb, err := btcec.ParsePubKey(v, btcec.S256()) + if err != nil { + return false, fmt.Errorf("failed to parse pub key: %w", err) + } + + if pub.IsEqual(pb) { + continue find + } + } + return false, nil + } + + return true, nil +} diff --git a/sign/signed_transaction_test.go b/sign/signed_transaction_test.go index 84d48a7..c894eb6 100644 --- a/sign/signed_transaction_test.go +++ b/sign/signed_transaction_test.go @@ -2,9 +2,11 @@ package sign import ( "encoding/hex" + "encoding/json" "testing" "time" + "github.com/btcsuite/btcutil/base58" "github.com/scorum/scorum-go/types" "github.com/stretchr/testify/require" ) @@ -35,3 +37,40 @@ func TestTransaction_Digest(t *testing.T) { require.NoError(t, err) require.Equal(t, expected, hex.EncodeToString(digest)) } + +func TestTransaction_Verify(t *testing.T) { + txStr := ` + { + "ref_block_num": 57753, + "ref_block_prefix": 1882698764, + "expiration": "2021-12-01T17:26:00", + "operations": [ + [ + "transfer", + { + "from": "azucena", + "to": "leonarda", + "amount": "0.000009000 SCR", + "memo": "{\"bet_id\":\"8b022219-3825-413e-a6ae-1cc3154bdb7f\",\"game_id\":\"17ff54ad-8472-4e4f-9e96-2b6ccbfaef33\"}" + } + ] + ], + "extensions": [], + "signatures": [ + "204da1d0e0c5cb39978d1ad4fe4cd4446718ad0e10a19d662ac7bc9e0bba708f2c2116279fdc512d03fe2c97cdfc00804a4b958a8078f9b3b28e4f4b22f798f032" + ], + "transaction_id": "825d2733272648f95c21ae97be9f8c3d8edaf8f9", + "block_num": 31449514, + "transaction_num": 0 + }` + + var tx types.Transaction + err := json.Unmarshal([]byte(txStr), &tx) + stx := NewSignedTransaction(&tx) + + keyWithChecksum := base58.Decode("7cTf2Dx9rxffs6E2z2pdn5cLMneo3AAFSsF9g4SaVviCYdfQ63") + + res, err := stx.Verify(TestChain, [][]byte{keyWithChecksum[:len(keyWithChecksum)-4]}) + require.NoError(t, err) + require.True(t, res) +}