Skip to content

Commit cebb824

Browse files
authored
Txm compatibility with keystore lib (#318)
* Txm compatibility * Comments
1 parent 4b93f62 commit cebb824

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

pkg/keys/v2/core_keystore.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package keys
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/smartcontractkit/chainlink-common/keystore"
8+
"github.com/smartcontractkit/chainlink-common/pkg/types/core"
9+
)
10+
11+
var _ core.Keystore = &TxKeyCoreKeystore{}
12+
13+
type TxKeyCoreKeystore struct {
14+
ks keystore.Keystore
15+
cache map[string]string
16+
}
17+
18+
// NewTxKeyCoreKeystore creates a new CoreKeystore for transaction keys.
19+
// This wrapper is required for using TxKeys with the txm
20+
// which requires address based lookups.
21+
func NewTxKeyCoreKeystore(ks keystore.Keystore) *TxKeyCoreKeystore {
22+
return &TxKeyCoreKeystore{
23+
ks: ks,
24+
cache: make(map[string]string),
25+
}
26+
}
27+
28+
func (s *TxKeyCoreKeystore) Accounts(ctx context.Context) ([]string, error) {
29+
keys, err := GetTxKeys(ctx, s.ks, []string{})
30+
if err != nil {
31+
return nil, err
32+
}
33+
accounts := make([]string, 0, len(keys))
34+
for _, key := range keys {
35+
accounts = append(accounts, key.Address().String())
36+
}
37+
return accounts, nil
38+
}
39+
40+
func (s *TxKeyCoreKeystore) Sign(ctx context.Context, account string, data []byte) ([]byte, error) {
41+
if addr, ok := s.cache[account]; ok {
42+
resp, err := s.ks.Sign(ctx, keystore.SignRequest{
43+
KeyName: addr,
44+
Data: data,
45+
})
46+
if err != nil {
47+
return nil, err
48+
}
49+
return resp.Signature, nil
50+
}
51+
// Otherwise do the first time lookup to find the key by address.
52+
keys, err := GetTxKeys(ctx, s.ks, []string{})
53+
if err != nil {
54+
return nil, err
55+
}
56+
if len(keys) == 0 {
57+
return nil, errors.New("no keys found")
58+
}
59+
for _, key := range keys {
60+
if key.Address().String() == account {
61+
s.cache[account] = key.KeyPath().String()
62+
resp, err := key.SignRaw(ctx, SignRawDataRequest{Data: data})
63+
if err != nil {
64+
return nil, err
65+
}
66+
return resp.Signature, nil
67+
}
68+
}
69+
return nil, errors.New("key not found")
70+
}
71+
72+
func (s *TxKeyCoreKeystore) Decrypt(ctx context.Context, account string, data []byte) ([]byte, error) {
73+
return nil, errors.New("not implemented")
74+
}

pkg/keys/v2/core_keystore_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package keys
2+
3+
import (
4+
"context"
5+
"os"
6+
"testing"
7+
8+
"github.com/ethereum/go-ethereum/crypto"
9+
"github.com/smartcontractkit/chainlink-common/keystore"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestCoreKeystore(t *testing.T) {
14+
ctx := context.Background()
15+
tmpfile, err := os.CreateTemp("", "keystore.json")
16+
require.NoError(t, err)
17+
defer os.Remove(tmpfile.Name())
18+
ks, err := keystore.LoadKeystore(ctx, keystore.NewFileStorage(tmpfile.Name()), "password")
19+
require.NoError(t, err)
20+
21+
coreKs := NewTxKeyCoreKeystore(ks)
22+
accounts, err := coreKs.Accounts(ctx)
23+
require.NoError(t, err)
24+
require.Len(t, accounts, 0)
25+
26+
txKey, err := CreateTxKey(ks, "key1")
27+
require.NoError(t, err)
28+
29+
keys, err := ks.GetKeys(ctx, keystore.GetKeysRequest{
30+
KeyNames: []string{txKey.KeyPath().String()},
31+
})
32+
require.NoError(t, err)
33+
require.Len(t, keys.Keys, 1)
34+
35+
data := crypto.Keccak256([]byte("data"))
36+
signature, err := coreKs.Sign(ctx, txKey.Address().String(), data)
37+
require.NoError(t, err)
38+
require.NotNil(t, signature)
39+
40+
resp, err := ks.Verify(ctx, keystore.VerifyRequest{
41+
KeyType: keystore.ECDSA_S256,
42+
PublicKey: keys.Keys[0].KeyInfo.PublicKey,
43+
Data: data,
44+
Signature: signature,
45+
})
46+
require.NoError(t, err)
47+
require.True(t, resp.Valid)
48+
49+
// Make sure the cache populated.
50+
require.Equal(t, txKey.KeyPath().String(), coreKs.cache[txKey.Address().String()])
51+
52+
// Sign again with cached.
53+
signature, err = coreKs.Sign(ctx, txKey.Address().String(), data)
54+
require.NoError(t, err)
55+
require.NotNil(t, signature)
56+
resp, err = ks.Verify(ctx, keystore.VerifyRequest{
57+
KeyType: keystore.ECDSA_S256,
58+
PublicKey: keys.Keys[0].KeyInfo.PublicKey,
59+
Data: data,
60+
Signature: signature,
61+
})
62+
require.NoError(t, err)
63+
require.True(t, resp.Valid)
64+
}

pkg/keys/v2/evm_tx.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ type SignTxResponse struct {
3838
Tx *gethtypes.Transaction
3939
}
4040

41+
// SignRawDataRequest contains the request to sign raw data.
42+
type SignRawDataRequest struct {
43+
Data []byte
44+
}
45+
46+
// SignRawDataResponse contains the signed raw data.
47+
type SignRawDataResponse struct {
48+
Signature []byte
49+
}
50+
4151
// KeyPath returns the key path for this transaction key.
4252
func (k *TxKey) KeyPath() keystore.KeyPath {
4353
return k.keyPath
@@ -70,6 +80,18 @@ func (k *TxKey) SignTx(ctx context.Context, req SignTxRequest) (SignTxResponse,
7080
return SignTxResponse{Tx: req.Tx}, nil
7181
}
7282

83+
func (k *TxKey) SignRaw(ctx context.Context, req SignRawDataRequest) (SignRawDataResponse, error) {
84+
signReq := keystore.SignRequest{
85+
KeyName: k.keyPath.String(),
86+
Data: req.Data,
87+
}
88+
signResp, err := k.ks.Sign(ctx, signReq)
89+
if err != nil {
90+
return SignRawDataResponse{}, err
91+
}
92+
return SignRawDataResponse{Signature: signResp.Signature}, nil
93+
}
94+
7395
// GetTransactOpts returns transaction options for this key.
7496
func (k *TxKey) GetTransactOpts(ctx context.Context, chainID *big.Int) (*bind.TransactOpts, error) {
7597
if chainID == nil {

0 commit comments

Comments
 (0)