From 62ee8f90498188ca9a4d0f2ae3060244bcbf0111 Mon Sep 17 00:00:00 2001 From: krehermann Date: Fri, 2 Jun 2023 15:50:41 -0600 Subject: [PATCH] Simply KeystoreAdapter interface (#297) --- relayer/pkg/chainlink/txm/keystore.go | 26 +++++------ relayer/pkg/chainlink/txm/keystore_test.go | 51 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 relayer/pkg/chainlink/txm/keystore_test.go diff --git a/relayer/pkg/chainlink/txm/keystore.go b/relayer/pkg/chainlink/txm/keystore.go index 59607c54c..6881ac77d 100644 --- a/relayer/pkg/chainlink/txm/keystore.go +++ b/relayer/pkg/chainlink/txm/keystore.go @@ -2,6 +2,7 @@ package txm import ( "context" + "errors" "fmt" "math/big" @@ -13,42 +14,37 @@ import ( // KeystoreAdapter is a starknet-specific adaption layer to translate between the generic Loop Keystore (bytes) and // the type specific caigo Keystore (big.Int) -// The loop.Keystore must be produce a byte representation that can be parsed by the Decode func implementation -// Users of the interface are responsible to ensure this compatibility. type KeystoreAdapter interface { caigo.Keystore - // Loopp must return a LOOPp Keystore implementation whose Sign func - // is compatible with the [Decode] func implementation Loopp() loop.Keystore - // Decode translates from the raw signature of the LOOPp Keystore to that of the Caigo Keystore - Decode(ctx context.Context, rawSignature []byte) (*big.Int, *big.Int, error) } -// keystoreAdapter implements [KeystoreAdapter] +// keystoreAdapter implements [KeystoreAdapter]. type keystoreAdapter struct { looppKs loop.Keystore } // NewKeystoreAdapter instantiates the KeystoreAdapter interface +// The implementation requires that the given [looppKs] produces a signature [loop.Keystore.Sign] +// that is []byte representation of [adapters.Signature] // Callers are responsible for ensuring that the given LOOPp Keystore encodes -// signatures that can be parsed by the Decode function +// signatures correctly. func NewKeystoreAdapter(lk loop.Keystore) KeystoreAdapter { return &keystoreAdapter{looppKs: lk} } -// Sign implements the caigo Keystore Sign func. +var ErrBadAdapterEncoding = errors.New("failed to decode raw signature as adapter signature") + +// Sign implements the caigo Keystore Sign func. Returns [ErrBadAdapterSignature] if the signature cannot be +// decoded from the [loop.Keystore] implementation func (ca *keystoreAdapter) Sign(ctx context.Context, senderAddress string, hash *big.Int) (*big.Int, *big.Int, error) { raw, err := ca.looppKs.Sign(ctx, senderAddress, hash.Bytes()) if err != nil { return nil, nil, fmt.Errorf("error computing loopp keystore signature: %w", err) } - return ca.Decode(ctx, raw) -} - -func (ca *keystoreAdapter) Decode(ctx context.Context, rawSignature []byte) (x *big.Int, y *big.Int, err error) { - starknetSig, serr := adapters.SignatureFromBytes(rawSignature) + starknetSig, serr := adapters.SignatureFromBytes(raw) if serr != nil { - return nil, nil, fmt.Errorf("error creating starknet signature from raw signature: %w", serr) + return nil, nil, fmt.Errorf("%w: %w", ErrBadAdapterEncoding, serr) } return starknetSig.Ints() } diff --git a/relayer/pkg/chainlink/txm/keystore_test.go b/relayer/pkg/chainlink/txm/keystore_test.go new file mode 100644 index 000000000..f68353380 --- /dev/null +++ b/relayer/pkg/chainlink/txm/keystore_test.go @@ -0,0 +1,51 @@ +package txm_test + +import ( + "context" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-relay/pkg/loop" + adapters "github.com/smartcontractkit/chainlink-relay/pkg/loop/adapters/starknet" + "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/txm" +) + +func TestKeystoreAdapterImpl(t *testing.T) { + t.Run("valid loop keystore impl", func(t *testing.T) { + goodLoopSignFn := func(ctx context.Context, account string, data []byte) (signed []byte, err error) { + sig, err := adapters.SignatureFromBigInts(big.NewInt(7), big.NewInt(11)) + require.NoError(t, err) + return sig.Bytes() + } + ksa := txm.NewKeystoreAdapter(&testLoopKeystore{signFn: goodLoopSignFn}) + + _, _, err := ksa.Sign(context.Background(), "anything", big.NewInt(42)) + require.NoError(t, err) + }) + t.Run("invalid loop keystore impl", func(t *testing.T) { + badLoopSignFn := func(ctx context.Context, account string, data []byte) (signed []byte, err error) { + return []byte("not an adapter signature"), nil + } + ksa := txm.NewKeystoreAdapter(&testLoopKeystore{signFn: badLoopSignFn}) + + _, _, err := ksa.Sign(context.Background(), "anything", big.NewInt(42)) + require.ErrorIs(t, err, txm.ErrBadAdapterEncoding) + }) + +} + +type testLoopKeystore struct { + signFn func(ctx context.Context, account string, data []byte) (signed []byte, err error) +} + +var _ loop.Keystore = &testLoopKeystore{} + +func (lk *testLoopKeystore) Sign(ctx context.Context, account string, data []byte) (signed []byte, err error) { + return lk.signFn(ctx, account, data) +} + +func (lk *testLoopKeystore) Accounts(ctx context.Context) (accounts []string, err error) { + return nil, nil +}