Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions pkg/keys/v2/core_keystore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package keys

import (
"context"
"errors"

"github.com/smartcontractkit/chainlink-common/keystore"
"github.com/smartcontractkit/chainlink-common/pkg/types/core"
)

var _ core.Keystore = &TxKeyCoreKeystore{}

type TxKeyCoreKeystore struct {
ks keystore.Keystore
cache map[string]string
}

// NewTxKeyCoreKeystore creates a new CoreKeystore for transaction keys.
// This wrapper is required for using TxKeys with the txm
// which requires address based lookups.
func NewTxKeyCoreKeystore(ks keystore.Keystore) *TxKeyCoreKeystore {
return &TxKeyCoreKeystore{
ks: ks,
cache: make(map[string]string),
}
}

func (s *TxKeyCoreKeystore) GetCache() map[string]string {
return s.cache
}

func (s *TxKeyCoreKeystore) Accounts(ctx context.Context) ([]string, error) {
keys, err := GetTxKeys(ctx, s.ks, []string{})
if err != nil {
return nil, err
}
accounts := make([]string, 0, len(keys))
for _, key := range keys {
accounts = append(accounts, key.Address().String())
}
return accounts, nil
}

func (s *TxKeyCoreKeystore) Sign(ctx context.Context, account string, data []byte) ([]byte, error) {
if addr, ok := s.cache[account]; ok {
resp, err := s.ks.Sign(ctx, keystore.SignRequest{
KeyName: addr,
Data: data,
})
if err != nil {
return nil, err
}
return resp.Signature, nil
}
// Otherwise do the first time lookup to find the key by address.
keys, err := GetTxKeys(ctx, s.ks, []string{})
if err != nil {
return nil, err
}
if len(keys) == 0 {
return nil, errors.New("no keys found")
}
for _, key := range keys {
if key.Address().String() == account {
s.cache[account] = key.KeyPath().String()
resp, err := key.SignRaw(ctx, SignRawDataRequest{Data: data})
if err != nil {
return nil, err
}
return resp.Signature, nil
}
}
return nil, errors.New("key not found")
}

func (s *TxKeyCoreKeystore) Decrypt(ctx context.Context, account string, data []byte) ([]byte, error) {
return nil, errors.New("not implemented")
}
65 changes: 65 additions & 0 deletions pkg/keys/v2/core_keystore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package keys

import (
"context"
"os"
"testing"

"github.com/ethereum/go-ethereum/crypto"
"github.com/smartcontractkit/chainlink-common/keystore"
"github.com/stretchr/testify/require"
)

func TestCoreKeystore(t *testing.T) {
ctx := context.Background()
tmpfile, err := os.CreateTemp("", "keystore.json")
require.NoError(t, err)
defer os.Remove(tmpfile.Name())
ks, err := keystore.LoadKeystore(ctx, keystore.NewFileStorage(tmpfile.Name()), "password")
require.NoError(t, err)

coreKs := NewTxKeyCoreKeystore(ks)
accounts, err := coreKs.Accounts(ctx)
require.NoError(t, err)
require.Len(t, accounts, 0)

txKey, err := CreateTxKey(ks, "key1")
require.NoError(t, err)

keys, err := ks.GetKeys(ctx, keystore.GetKeysRequest{
KeyNames: []string{txKey.KeyPath().String()},
})
require.NoError(t, err)
require.Len(t, keys.Keys, 1)

data := crypto.Keccak256([]byte("data"))
signature, err := coreKs.Sign(ctx, txKey.Address().String(), data)
require.NoError(t, err)
require.NotNil(t, signature)

resp, err := ks.Verify(ctx, keystore.VerifyRequest{
KeyType: keystore.ECDSA_S256,
PublicKey: keys.Keys[0].KeyInfo.PublicKey,
Data: data,
Signature: signature,
})
require.NoError(t, err)
require.True(t, resp.Valid)

// Make sure the cache populated.
cache := coreKs.GetCache()
require.Equal(t, txKey.KeyPath().String(), cache[txKey.Address().String()])

// Sign again with cached.
signature, err = coreKs.Sign(ctx, txKey.Address().String(), data)
require.NoError(t, err)
require.NotNil(t, signature)
resp, err = ks.Verify(ctx, keystore.VerifyRequest{
KeyType: keystore.ECDSA_S256,
PublicKey: keys.Keys[0].KeyInfo.PublicKey,
Data: data,
Signature: signature,
})
require.NoError(t, err)
require.True(t, resp.Valid)
}
22 changes: 22 additions & 0 deletions pkg/keys/v2/evm_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ type SignTxResponse struct {
Tx *gethtypes.Transaction
}

// SignRawDataRequest contains the request to sign raw data.
type SignRawDataRequest struct {
Data []byte
}

// SignRawDataResponse contains the signed raw data.
type SignRawDataResponse struct {
Signature []byte
}

// KeyPath returns the key path for this transaction key.
func (k *TxKey) KeyPath() keystore.KeyPath {
return k.keyPath
Expand Down Expand Up @@ -70,6 +80,18 @@ func (k *TxKey) SignTx(ctx context.Context, req SignTxRequest) (SignTxResponse,
return SignTxResponse{Tx: req.Tx}, nil
}

func (k *TxKey) SignRaw(ctx context.Context, req SignRawDataRequest) (SignRawDataResponse, error) {
signReq := keystore.SignRequest{
KeyName: k.keyPath.String(),
Data: req.Data,
}
signResp, err := k.ks.Sign(ctx, signReq)
if err != nil {
return SignRawDataResponse{}, err
}
return SignRawDataResponse{Signature: signResp.Signature}, nil
}

// GetTransactOpts returns transaction options for this key.
func (k *TxKey) GetTransactOpts(ctx context.Context, chainID *big.Int) (*bind.TransactOpts, error) {
if chainID == nil {
Expand Down
Loading