Add tools to verify signatures

This commit is contained in:
gagliardetto 2022-02-09 17:13:49 +01:00
parent 412bf5bb24
commit 851de7256b
6 changed files with 213 additions and 7 deletions

View File

@ -110,8 +110,8 @@ func (slice *AccountMetaSlice) SetAccounts(accounts []*AccountMeta) error {
return nil
}
func (slice AccountMetaSlice) GetAccounts() (out []*AccountMeta) {
out = make([]*AccountMeta, 0)
func (slice AccountMetaSlice) GetAccounts() []*AccountMeta {
out := make([]*AccountMeta, 0, len(slice))
for i := range slice {
if slice[i] != nil {
out = append(out, slice[i])
@ -131,7 +131,7 @@ func (slice AccountMetaSlice) Get(index int) *AccountMeta {
// GetSigners returns the accounts that are signers.
func (slice AccountMetaSlice) GetSigners() []*AccountMeta {
signers := make([]*AccountMeta, 0)
signers := make([]*AccountMeta, 0, len(slice))
for _, ac := range slice {
if ac.IsSigner {
signers = append(signers, ac)
@ -140,6 +140,15 @@ func (slice AccountMetaSlice) GetSigners() []*AccountMeta {
return signers
}
// GetKeys returns the pubkeys of all AccountMeta.
func (slice AccountMetaSlice) GetKeys() PublicKeySlice {
keys := make(PublicKeySlice, 0, len(slice))
for _, ac := range slice {
keys = append(keys, ac.PublicKey)
}
return keys
}
func (slice AccountMetaSlice) Len() int {
return len(slice)
}

View File

@ -186,13 +186,35 @@ func (mx *Message) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) {
return nil
}
func (m *Message) AccountMetaList() (out []*AccountMeta) {
for _, a := range m.AccountKeys {
out = append(out, &AccountMeta{
func (m *Message) AccountMetaList() AccountMetaSlice {
out := make(AccountMetaSlice, len(m.AccountKeys))
for i, a := range m.AccountKeys {
out[i] = &AccountMeta{
PublicKey: a,
IsSigner: m.IsSigner(a),
IsWritable: m.IsWritable(a),
})
}
}
return out
}
// Signers returns the pubkeys of all accounts that are signers.
func (m *Message) Signers() PublicKeySlice {
out := make(PublicKeySlice, 0, len(m.AccountKeys))
for _, a := range m.AccountKeys {
if m.IsSigner(a) {
out = append(out, a)
}
}
return out
}
// Writable returns the pubkeys of all accounts that are writable.
func (m *Message) Writable() (out PublicKeySlice) {
for _, a := range m.AccountKeys {
if m.IsWritable(a) {
out = append(out, a)
}
}
return out
}

View File

@ -15,6 +15,7 @@
package solana
import (
"encoding/base64"
"strconv"
"testing"
@ -278,3 +279,76 @@ func TestMustPublicKeyFromBase58(t *testing.T) {
MustPublicKeyFromBase58("toto")
})
}
func TestSignatureVerify(t *testing.T) {
type signerSignature struct {
Sig Signature
Signer PublicKey
}
type testCase struct {
Message string
Signers []signerSignature
}
testCases := []testCase{
{
Signers: []signerSignature{
{
Sig: MustSignatureFromBase58("3APVUYY2Qcq9WwPSciQ1xicshQcsXJWhgD1x5YEMNnNnKtpaAJtRhDVMWcvePosamk3JfSKpBM8qt5ZkAQgRNSzo"),
Signer: MustPublicKeyFromBase58("2V7t5NaKY7aGkwytCWQgvUYZfEr9XMwNChhJEakTExk6"),
},
},
Message: "AQACBBYPusE6993YBdMXCj3gxr2XEmoeAsDSWdCobvgh1uXHoaZGX0wuvyRMMdgLyVwnNFo0JOQowt7zPs7Z6Q0/cBsGp9UXGMd0yShWY5hpHV62i164o5tLbVxzVVshAAAAANzl6+HknDufEUy1VExQqZ7A1pLWP1Z5WuAprIPZ6ovia//gAIGoVfMJgSuJp8/VsCUq8qvMdiHNGrMrQAxoS2QBAwMAAQIoAgAAAAcAAAABAAAAAAAAAM/ncrkCAAAACJ9fAQAAAAD7FSgHAAAAAA==",
},
{
Signers: []signerSignature{
{
Sig: MustSignatureFromBase58("5TPaoKhkhRck3TTjyEQe1TbgXHATggx9iyWaXqj7foCUSuXnVUh3iqMrKKndWZn31cV3KEiBDiqg9wM8ijk41eyQ"),
Signer: MustPublicKeyFromBase58("G2fzpkX69kmaYtDtMfUkQHykSvgV24wze9ikb911FHT"),
},
},
Message: "AQADBQPZmmOEu9u2f5Tkm9PJx3oVRbAt9pEyjDD4zyadddWyjNBZdQ4NOL1dsfZy9kwqkh5o0/MYOsFAM42WtfumAgUGp9UXGS8Kr8byZeP7d8x62oLFKdC+OxNuLQBVIAAAAAan1RcYx3TJKFZjmGkdXraLXrijm0ttXHNVWyEAAAAAB2FIHTV0dLt8TXYk69O9s9g1XnPREEP8DaNTgAAAAACQ0H7JStckBZBqg11runLYUTbmBpWo2DDpraEiQeoZjwEEBAECAwBNAgAAAAMAAAAAAAAA+RUoBwAAAAD6FSgHAAAAAPsVKAcAAAAAxAjCTj1+3u3zheR66vcYXXIdlKoYPL0DmMmTP+lffN4B0NIDYgAAAAA=",
},
{
Signers: []signerSignature{
{
Sig: MustSignatureFromBase58("T1BbonhT8wdLpRMPN8UAtshjukRwot8Z5ta7wSaHujSfrqKuFpQD56iwa5hjESMzsNKFpzPVETCVZ54puCiZh2y"),
Signer: MustPublicKeyFromBase58("3jBp2LRCFgSmBS1Qemji1rHWS9ytSoN5pYpWzK5o9CDY"),
},
},
Message: "AQACBSiF/dgUa5IBH6yxFwL17cOzoDz4R1PutgvAweUD6DIDUmyl4Hbm9LK+2Kdzb3a7Kekg6CeIHaMkWndgFfiWqY9HUbWFuDsRO/Bb0WU3qhxctjMv5tdv2nxiD0vIlJzBpQan1RcYx3TJKFZjmGkdXraLXrijm0ttXHNVWyEAAAAAv47fHJRrVrtD0l+YuHtr5REXMAphJhr5+flO7XpagIiuIXQDe/BrXhY6rSa8S18aVTSrLtZX3XBT12vynIOHuQEEBAEAAwInJjIkEAIhro89MaE0EkAoADDzq6A5ORdIUPwYMxJAQb1SliGONRJA",
},
{
Signers: []signerSignature{
{
Sig: MustSignatureFromBase58("2c5u22N6Yyjj7qQdGtEshp4n9r4akLyRLJijnXL9HnQAXqq7S9cfnGdi5mwnfQ5kJHQuRv62T366SKaztHiUUqnK"),
Signer: MustPublicKeyFromBase58("AXUChvpRwUUPMJhA4d23WcoyAL7W8zgAeo7KoH57c75F"),
},
},
Message: "AQACBo2HXSJ3fw0pdiiQj9fBoRA5Wicjyla4ARhrK90xeo0mD4K9wHSEzSq9lEFUOSPloeUrUL/2uV3S6+lTeGNtTMtTP2NRTSucECyiWtS2HzfxwLnbnxVbXNH0T0egDFCfVK5ESO7I2Pz56XFyxewO0gbya1rvqPPu0CGg/LC/Wl5BhQ8tbgKkevgk0Jq2ncQtcMsoy/okn7fuV7nSVsEnYu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIvotbwkk/hzTtAxHIaRCSJjB4WdQUXrn+6aEapl6XgPAgQFAQIDAgIHAAMAAAABAAUCAAAMAgAAAOsNAAAAAAAA",
},
{
Signers: []signerSignature{
{
Sig: MustSignatureFromBase58("2amhinnUjRMef8aNMKKhGCf4odSR3q9Tq2SjYjvFePN6ZmStF5EbNmfdsqssXe4XjXn6Nidu1Sg4MKyo5UPVWQgf"),
Signer: MustPublicKeyFromBase58("2vKtu3nW1TS6iPvJPK8R88B5QfDrwJDwwB11Uu1CN9o7"),
},
{
Sig: MustSignatureFromBase58("5qzhXaDMhcVZA2y6KDK218noKdX7cd7aRCC1XvFFUTEievDMt8EbCC1oCKyqGvGmZs5UzE34v9JR8846HtJVF6qe"),
Signer: MustPublicKeyFromBase58("96i77Hg7RL7J4Lrc1d6MXFofCKwEFZe84DejFUMhUs19"),
},
},
Message: "AgEBBByE1Y6EqCJKsr7iEupU6lsBHtBdtI4SK3yWMCFA0iEKeFPgnGmtp+1SIX1Ak+sN65iBaR7v4Iim5m1OEuFQTgi9N57UnhNpCNuUePaTt7HJaFBmyeZB3deXeKWVudpY3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWVECK/n3a7QR6OKWYR4DuAVjS6FXgZj82W0dJpSIPnEBAwQAAgEDDAIAAABAQg8AAAAAAA==",
},
}
for _, tc := range testCases {
msg, err := base64.StdEncoding.DecodeString(tc.Message)
require.NoError(t, err)
for _, tcs := range tc.Signers {
require.True(t, tcs.Sig.Verify(tcs.Signer, msg))
require.False(t, tcs.Sig.Verify(BPFLoaderDeprecatedProgramID, msg))
}
}
}

View File

@ -18,6 +18,7 @@
package solana
import (
"crypto/ed25519"
"encoding/base64"
"fmt"
"io"
@ -153,6 +154,10 @@ func (p *Signature) UnmarshalJSON(data []byte) (err error) {
return
}
func (s Signature) Verify(pubkey PublicKey, msg []byte) bool {
return ed25519.Verify(pubkey[:], msg, s[:])
}
func (p Signature) String() string {
return base58.Encode(p[:])
}

View File

@ -463,3 +463,30 @@ func (tx *Transaction) EncodeToTree(parent treeout.Branches) {
}
})
}
// VerifySignatures verifies all the signatures in the transaction
// against the pubkeys of the signers.
func (tx *Transaction) VerifySignatures() error {
msg, err := tx.Message.MarshalBinary()
if err != nil {
return err
}
signers := tx.Message.Signers()
if len(signers) != len(tx.Signatures) {
return fmt.Errorf(
"got %v signers, but %v signatures",
len(signers),
len(tx.Signatures),
)
}
for i, sig := range tx.Signatures {
if !sig.Verify(signers[i], msg) {
return fmt.Errorf("invalid signature by %s", signers[i].String())
}
}
return nil
}

View File

@ -166,5 +166,74 @@ func TestTransactionDecode(t *testing.T) {
},
tx.Message.Instructions,
)
}
func TestTransactionVerifySignatures(t *testing.T) {
type testCase struct {
Transaction string
}
testCases := []testCase{
{
Transaction: "AVBFwRrn4wroV9+NVQfgg/GbjFtQFodLnNI5oTpDMQiQ4HfZNyFzcFamHSSFW4p5wc3efeEKvykbmk8jzf2LCQwBAAIGjYddInd/DSl2KJCP18GhEDlaJyPKVrgBGGsr3TF6jSYPgr3AdITNKr2UQVQ5I+Wh5StQv/a5XdLr6VN4Y21My1M/Y1FNK5wQLKJa1LYfN/HAudufFVtc0fRPR6AMUJ9UrkRI7sjY/PnpcXLF7A7SBvJrWu+o8+7QIaD8sL9aXkGFDy1uAqR6+CTQmradxC1wyyjL+iSft+5XudJWwSdi7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi+i1vCST+HNO0DEchpEJImMHhZ1BReuf7poRqmXpeA8CBAUBAgMCAgcAAwAAAAEABQIAAAwCAAAA6w0AAAAAAAA=",
},
{
Transaction: "AWwhMTxKhl9yZOlidY0u3gYmy+J/6V3kFSXU7GgK5zwN+SwljR2dOlHgKtUDRX8uee2HtfeyL3t4lB3n749L4QQBAAIEFg+6wTr33dgF0xcKPeDGvZcSah4CwNJZ0Khu+CHW5cehpkZfTC6/JEwx2AvJXCc0WjQk5CjC3vM+ztnpDT9wGwan1RcYx3TJKFZjmGkdXraLXrijm0ttXHNVWyEAAAAA3OXr4eScO58RTLVUTFCpnsDWktY/Vnla4Cmsg9nqi+Jr/+AAgahV8wmBK4mnz9WwJSryq8x2Ic0asytADGhLZAEDAwABAigCAAAABwAAAAEAAAAAAAAAz+dyuQIAAAAIn18BAAAAAPsVKAcAAAAA",
},
{
Transaction: "ARZsk8+AvvT9onUT8FU1VRaiC8Sp+FKveOwhdPoigWHA+MGNcIOqbow6mwSILEYvvyOB/fi3UQ/xKQCjEtxBRgIBAAIFKIX92BRrkgEfrLEXAvXtw7OgPPhHU+62C8DB5QPoMgNSbKXgdub0sr7Yp3Nvdrsp6SDoJ4gdoyRad2AV+Japj0dRtYW4OxE78FvRZTeqHFy2My/m12/afGIPS8iUnMGlBqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAC/jt8clGtWu0PSX5i4e2vlERcwCmEmGvn5+U7telqAiK4hdAN78GteFjqtJrxLXxpVNKsu1lfdcFPXa/Kcg4e5AQQEAQADAicmMiQQAiGujz0xoTQSQCgAMPOroDk5F0hQ/BgzEkBBvVKWIY41EkA=",
},
{
Transaction: "Ad7TPpYTvSpO//KNA5YTZVojVwz4NlH4gH9ktl+rTObJcgo8QkqmHK4t6DQr9dD58B/A/5/N7v9K+0j6y1TVCAsBAAMFA9maY4S727Z/lOSb08nHehVFsC32kTKMMPjPJp111bKM0Fl1Dg04vV2x9nL2TCqSHmjT8xg6wUAzjZa1+6YCBQan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAJDQfslK1yQFkGqDXWu6cthRNuYGlajYMOmtoSJB6hmPAQQEAQIDAE0CAAAAAwAAAAAAAAD5FSgHAAAAAPoVKAcAAAAA+xUoBwAAAADECMJOPX7e7fOF5Hrq9xhdch2Uqhg8vQOYyZM/6V983gHQ0gNiAAAAAA==",
},
{
Transaction: "Ak8jvC3ch5hq3lhOHPkACoFepIUON2zEN4KRcw4lDS6GBsQfnSdzNGPETm/yi0hPKk75/i2VXFj0FLUWnGR64ADyUbqnirFjFtaSNgcGi02+Tm7siT4CPpcaTq0jxfYQK/h9FdxXXPnLry74J+RE8yji/BtJ/Cjxbx+TIHigeIYJAgEBBByE1Y6EqCJKsr7iEupU6lsBHtBdtI4SK3yWMCFA0iEKeFPgnGmtp+1SIX1Ak+sN65iBaR7v4Iim5m1OEuFQTgi9N57UnhNpCNuUePaTt7HJaFBmyeZB3deXeKWVudpY3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWVECK/n3a7QR6OKWYR4DuAVjS6FXgZj82W0dJpSIPnEBAwQAAgEDDAIAAABAQg8AAAAAAA==",
},
}
for _, tc := range testCases {
txBin, err := base64.StdEncoding.DecodeString(tc.Transaction)
require.NoError(t, err)
tx, err := TransactionFromDecoder(bin.NewBinDecoder(txBin))
require.NoError(t, err)
require.NoError(t, tx.VerifySignatures())
require.Equal(t, len(tx.Signatures), len(tx.Message.Signers()))
}
}
func BenchmarkTransactionFromDecoder(b *testing.B) {
txString := "Ak8jvC3ch5hq3lhOHPkACoFepIUON2zEN4KRcw4lDS6GBsQfnSdzNGPETm/yi0hPKk75/i2VXFj0FLUWnGR64ADyUbqnirFjFtaSNgcGi02+Tm7siT4CPpcaTq0jxfYQK/h9FdxXXPnLry74J+RE8yji/BtJ/Cjxbx+TIHigeIYJAgEBBByE1Y6EqCJKsr7iEupU6lsBHtBdtI4SK3yWMCFA0iEKeFPgnGmtp+1SIX1Ak+sN65iBaR7v4Iim5m1OEuFQTgi9N57UnhNpCNuUePaTt7HJaFBmyeZB3deXeKWVudpY3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWVECK/n3a7QR6OKWYR4DuAVjS6FXgZj82W0dJpSIPnEBAwQAAgEDDAIAAABAQg8AAAAAAA=="
txBin, err := base64.StdEncoding.DecodeString(txString)
if err != nil {
b.Error(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := TransactionFromDecoder(bin.NewBinDecoder(txBin))
if err != nil {
b.Error(err)
}
}
}
func BenchmarkTransactionVerifySignatures(b *testing.B) {
txString := "Ak8jvC3ch5hq3lhOHPkACoFepIUON2zEN4KRcw4lDS6GBsQfnSdzNGPETm/yi0hPKk75/i2VXFj0FLUWnGR64ADyUbqnirFjFtaSNgcGi02+Tm7siT4CPpcaTq0jxfYQK/h9FdxXXPnLry74J+RE8yji/BtJ/Cjxbx+TIHigeIYJAgEBBByE1Y6EqCJKsr7iEupU6lsBHtBdtI4SK3yWMCFA0iEKeFPgnGmtp+1SIX1Ak+sN65iBaR7v4Iim5m1OEuFQTgi9N57UnhNpCNuUePaTt7HJaFBmyeZB3deXeKWVudpY3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWVECK/n3a7QR6OKWYR4DuAVjS6FXgZj82W0dJpSIPnEBAwQAAgEDDAIAAABAQg8AAAAAAA=="
txBin, err := base64.StdEncoding.DecodeString(txString)
if err != nil {
b.Error(err)
}
tx, err := TransactionFromDecoder(bin.NewBinDecoder(txBin))
if err != nil {
b.Error(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tx.VerifySignatures()
}
}