@@ -5,43 +5,34 @@ import (
55 "context"
66 "fmt"
77 "os"
8- "sync"
98 "time"
109
11- sdkcontext "github.com/cosmos/cosmos-sdk/client/context"
10+ "github.com/spf13/viper"
11+
1212 "github.com/cosmos/cosmos-sdk/client/flags"
1313 "github.com/cosmos/cosmos-sdk/crypto/keys"
14+ "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
1415 sdk "github.com/cosmos/cosmos-sdk/types"
15- emintcrypto "github.com/cosmos/ethermint/crypto"
16- params "github.com/cosmos/ethermint/rpc/args"
17- "github.com/spf13/viper"
18- "github.com/tendermint/tendermint/libs/log"
1916
2017 "github.com/ethereum/go-ethereum/accounts"
2118 "github.com/ethereum/go-ethereum/common"
2219 "github.com/ethereum/go-ethereum/common/hexutil"
2320 "github.com/ethereum/go-ethereum/crypto"
21+
22+ emintcrypto "github.com/cosmos/ethermint/crypto"
23+ params "github.com/cosmos/ethermint/rpc/args"
2424)
2525
26- // PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
26+ // PersonalEthAPI is the personal_ prefixed set of APIs in the Web3 JSON-RPC spec.
2727type PersonalEthAPI struct {
28- logger log.Logger
29- cliCtx sdkcontext.CLIContext
30- ethAPI * PublicEthAPI
31- nonceLock * AddrLocker
32- keys []emintcrypto.PrivKeySecp256k1
33- keyInfos []keys.Info
34- keybaseLock sync.Mutex
28+ ethAPI * PublicEthAPI
29+ keyInfos []keys.Info // all keys, both locked and unlocked. unlocked keys are stored in ethAPI.keys
3530}
3631
37- // NewPersonalEthAPI creates an instance of the public ETH Web3 API.
38- func NewPersonalEthAPI (cliCtx sdkcontext. CLIContext , ethAPI * PublicEthAPI , nonceLock * AddrLocker , keys []emintcrypto. PrivKeySecp256k1 ) * PersonalEthAPI {
32+ // NewPersonalEthAPI creates an instance of the public Personal Eth API.
33+ func NewPersonalEthAPI (ethAPI * PublicEthAPI ) * PersonalEthAPI {
3934 api := & PersonalEthAPI {
40- logger : log .NewTMLogger (log .NewSyncWriter (os .Stdout )).With ("module" , "json-rpc" ),
41- cliCtx : cliCtx ,
42- ethAPI : ethAPI ,
43- nonceLock : nonceLock ,
44- keys : keys ,
35+ ethAPI : ethAPI ,
4536 }
4637
4738 infos , err := api .getKeybaseInfo ()
@@ -54,43 +45,68 @@ func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, ethAPI *PublicEthAPI, nonce
5445}
5546
5647func (e * PersonalEthAPI ) getKeybaseInfo () ([]keys.Info , error ) {
57- e .keybaseLock .Lock ()
58- defer e .keybaseLock .Unlock ()
48+ e .ethAPI . keybaseLock .Lock ()
49+ defer e .ethAPI . keybaseLock .Unlock ()
5950
60- if e .cliCtx .Keybase == nil {
51+ if e .ethAPI . cliCtx .Keybase == nil {
6152 keybase , err := keys .NewKeyring (
6253 sdk .KeyringServiceName (),
6354 viper .GetString (flags .FlagKeyringBackend ),
6455 viper .GetString (flags .FlagHome ),
65- e .cliCtx .Input ,
56+ e .ethAPI . cliCtx .Input ,
6657 emintcrypto .EthSecp256k1Options ()... ,
6758 )
6859 if err != nil {
6960 return nil , err
7061 }
7162
72- e .cliCtx .Keybase = keybase
63+ e .ethAPI . cliCtx .Keybase = keybase
7364 }
7465
75- return e .cliCtx .Keybase .List ()
66+ return e .ethAPI . cliCtx .Keybase .List ()
7667}
7768
78- // ImportRawKey stores the given hex encoded ECDSA key into the key directory,
79- // encrypting it with the passphrase.
80- // Currently, this is not implemented since the feature is not supported by the keys.
69+ // ImportRawKey armors and encrypts a given raw hex encoded ECDSA key and stores it into the key directory.
70+ // The name of the key will have the format "personal_<length-keys>", where <length-keys> is the total number of
71+ // keys stored on the keyring.
72+ // NOTE: The key will be both armored and encrypted using the same passphrase.
8173func (e * PersonalEthAPI ) ImportRawKey (privkey , password string ) (common.Address , error ) {
82- e .logger .Debug ("personal_importRawKey" , "error" , "not implemented " )
83- _ , err := crypto .HexToECDSA (privkey )
74+ e .ethAPI . logger .Debug ("personal_importRawKey" )
75+ priv , err := crypto .HexToECDSA (privkey )
8476 if err != nil {
8577 return common.Address {}, err
8678 }
8779
88- return common.Address {}, nil
80+ privKey := emintcrypto .PrivKeySecp256k1 (crypto .FromECDSA (priv ))
81+
82+ armor := mintkey .EncryptArmorPrivKey (privKey , password , emintcrypto .EthSecp256k1Type )
83+
84+ // ignore error as we only care about the length of the list
85+ list , _ := e .ethAPI .cliCtx .Keybase .List ()
86+ privKeyName := fmt .Sprintf ("personal_%d" , len (list ))
87+
88+ if err := e .ethAPI .cliCtx .Keybase .ImportPrivKey (privKeyName , armor , password ); err != nil {
89+ return common.Address {}, err
90+ }
91+
92+ addr := common .BytesToAddress (privKey .PubKey ().Address ().Bytes ())
93+
94+ info , err := e .ethAPI .cliCtx .Keybase .Get (privKeyName )
95+ if err != nil {
96+ return common.Address {}, err
97+ }
98+
99+ // append key and info to be able to lock and list the account
100+ //e.ethAPI.keys = append(e.ethAPI.keys, privKey)
101+ e .keyInfos = append (e .keyInfos , info )
102+ e .ethAPI .logger .Info ("key successfully imported" , "name" , privKeyName , "address" , addr .String ())
103+
104+ return addr , nil
89105}
90106
91107// ListAccounts will return a list of addresses for accounts this node manages.
92108func (e * PersonalEthAPI ) ListAccounts () ([]common.Address , error ) {
93- e .logger .Debug ("personal_listAccounts" )
109+ e .ethAPI . logger .Debug ("personal_listAccounts" )
94110 addrs := []common.Address {}
95111 for _ , info := range e .keyInfos {
96112 addressBytes := info .GetPubKey ().Address ().Bytes ()
@@ -103,18 +119,7 @@ func (e *PersonalEthAPI) ListAccounts() ([]common.Address, error) {
103119// LockAccount will lock the account associated with the given address when it's unlocked.
104120// It removes the key corresponding to the given address from the API's local keys.
105121func (e * PersonalEthAPI ) LockAccount (address common.Address ) bool {
106- e .logger .Debug ("personal_lockAccount" , "address" , address )
107- for i , key := range e .keys {
108- if ! bytes .Equal (key .PubKey ().Address ().Bytes (), address .Bytes ()) {
109- continue
110- }
111-
112- tmp := make ([]emintcrypto.PrivKeySecp256k1 , len (e .keys )- 1 )
113- copy (tmp [:i ], e .keys [:i ])
114- copy (tmp [i :], e .keys [i + 1 :])
115- e .keys = tmp
116- return true
117- }
122+ e .ethAPI .logger .Debug ("personal_lockAccount" , "address" , address .String ())
118123
119124 for i , key := range e .ethAPI .keys {
120125 if ! bytes .Equal (key .PubKey ().Address ().Bytes (), address .Bytes ()) {
@@ -125,6 +130,8 @@ func (e *PersonalEthAPI) LockAccount(address common.Address) bool {
125130 copy (tmp [:i ], e .ethAPI .keys [:i ])
126131 copy (tmp [i :], e .ethAPI .keys [i + 1 :])
127132 e .ethAPI .keys = tmp
133+
134+ e .ethAPI .logger .Debug ("account unlocked" , "address" , address .String ())
128135 return true
129136 }
130137
@@ -133,62 +140,55 @@ func (e *PersonalEthAPI) LockAccount(address common.Address) bool {
133140
134141// NewAccount will create a new account and returns the address for the new account.
135142func (e * PersonalEthAPI ) NewAccount (password string ) (common.Address , error ) {
136- e .logger .Debug ("personal_newAccount" )
143+ e .ethAPI . logger .Debug ("personal_newAccount" )
137144 _ , err := e .getKeybaseInfo ()
138145 if err != nil {
139146 return common.Address {}, err
140147 }
141148
142149 name := "key_" + time .Now ().UTC ().Format (time .RFC3339 )
143- info , _ , err := e .cliCtx .Keybase .CreateMnemonic (name , keys .English , password , emintcrypto .EthSecp256k1 )
150+ info , _ , err := e .ethAPI . cliCtx .Keybase .CreateMnemonic (name , keys .English , password , emintcrypto .EthSecp256k1 )
144151 if err != nil {
145152 return common.Address {}, err
146153 }
147154
148155 e .keyInfos = append (e .keyInfos , info )
149156
150- // update ethAPI
151- privKey , err := e .cliCtx .Keybase .ExportPrivateKeyObject (name , password )
152- if err != nil {
153- return common.Address {}, err
154- }
155-
156- emintKey , ok := privKey .(emintcrypto.PrivKeySecp256k1 )
157- if ! ok {
158- return common.Address {}, fmt .Errorf ("invalid private key type: %T" , privKey )
159- }
160- e .ethAPI .keys = append (e .ethAPI .keys , emintKey )
161- e .logger .Debug ("personal_newAccount" , "address" , fmt .Sprintf ("0x%x" , emintKey .PubKey ().Address ().Bytes ()))
162-
163157 addr := common .BytesToAddress (info .GetPubKey ().Address ().Bytes ())
164- e .logger .Info ("Your new key was generated" , "address" , addr )
165- e .logger .Info ("Please backup your key file!" , "path" , os .Getenv ("HOME" )+ "/.ethermintcli/" + name )
166- e .logger .Info ("Please remember your password!" )
158+ e .ethAPI . logger .Info ("Your new key was generated" , "address" , addr . String () )
159+ e .ethAPI . logger .Info ("Please backup your key file!" , "path" , os .Getenv ("HOME" )+ "/.ethermintcli/" + name )
160+ e .ethAPI . logger .Info ("Please remember your password!" )
167161 return addr , nil
168162}
169163
170164// UnlockAccount will unlock the account associated with the given address with
171165// the given password for duration seconds. If duration is nil it will use a
172166// default of 300 seconds. It returns an indication if the account was unlocked.
173167// It exports the private key corresponding to the given address from the keyring and stores it in the API's local keys.
174- func (e * PersonalEthAPI ) UnlockAccount (ctx context.Context , addr common.Address , password string , _ * uint64 ) (bool , error ) {
175- e .logger .Debug ("personal_unlockAccount" , "address" , addr )
168+ func (e * PersonalEthAPI ) UnlockAccount (_ context.Context , addr common.Address , password string , _ * uint64 ) (bool , error ) { // nolint: interfacer
169+ e .ethAPI . logger .Debug ("personal_unlockAccount" , "address" , addr . String () )
176170 // TODO: use duration
177171
178- name := ""
172+ var keyInfo keys.Info
173+
179174 for _ , info := range e .keyInfos {
180175 addressBytes := info .GetPubKey ().Address ().Bytes ()
181176 if bytes .Equal (addressBytes , addr [:]) {
182- name = info .GetName ()
177+ keyInfo = info
178+ break
183179 }
184180 }
185181
186- if name == "" {
187- return false , fmt .Errorf ("cannot find key with given address" )
182+ if keyInfo == nil {
183+ return false , fmt .Errorf ("cannot find key with given address %s" , addr . String () )
188184 }
189185
190- // TODO: this only works on local keys
191- privKey , err := e .cliCtx .Keybase .ExportPrivateKeyObject (name , password )
186+ // exporting private key only works on local keys
187+ if keyInfo .GetType () != keys .TypeLocal {
188+ return false , fmt .Errorf ("key type must be %s, got %s" , keys .TypeLedger .String (), keyInfo .GetType ().String ())
189+ }
190+
191+ privKey , err := e .ethAPI .cliCtx .Keybase .ExportPrivateKeyObject (keyInfo .GetName (), password )
192192 if err != nil {
193193 return false , err
194194 }
@@ -198,17 +198,15 @@ func (e *PersonalEthAPI) UnlockAccount(ctx context.Context, addr common.Address,
198198 return false , fmt .Errorf ("invalid private key type: %T" , privKey )
199199 }
200200
201- e .keys = append (e .keys , emintKey )
202201 e .ethAPI .keys = append (e .ethAPI .keys , emintKey )
203- e .logger .Debug ("personal_unlockAccount" , "address" , fmt .Sprintf ("0x%x" , emintKey .PubKey ().Address ().Bytes ()))
204-
202+ e .ethAPI .logger .Debug ("account unlocked" , "address" , addr .String ())
205203 return true , nil
206204}
207205
208206// SendTransaction will create a transaction from the given arguments and
209207// tries to sign it with the key associated with args.To. If the given password isn't
210208// able to decrypt the key it fails.
211- func (e * PersonalEthAPI ) SendTransaction (ctx context.Context , args params.SendTxArgs , passwd string ) (common.Hash , error ) {
209+ func (e * PersonalEthAPI ) SendTransaction (_ context.Context , args params.SendTxArgs , _ string ) (common.Hash , error ) {
212210 return e .ethAPI .SendTransaction (args )
213211}
214212
@@ -221,12 +219,12 @@ func (e *PersonalEthAPI) SendTransaction(ctx context.Context, args params.SendTx
221219// The key used to calculate the signature is decrypted with the given password.
222220//
223221// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
224- func (e * PersonalEthAPI ) Sign (ctx context.Context , data hexutil.Bytes , addr common.Address , passwd string ) (hexutil.Bytes , error ) {
225- e .logger .Debug ("personal_sign" , "data" , data , "address" , addr )
222+ func (e * PersonalEthAPI ) Sign (_ context.Context , data hexutil.Bytes , addr common.Address , _ string ) (hexutil.Bytes , error ) {
223+ e .ethAPI . logger .Debug ("personal_sign" , "data" , data , "address" , addr . String () )
226224
227- key , ok := checkKeyInKeyring (e .keys , addr )
225+ key , ok := checkKeyInKeyring (e .ethAPI . keys , addr )
228226 if ! ok {
229- return nil , fmt .Errorf ("cannot find key with given address" )
227+ return nil , fmt .Errorf ("cannot find key with address %s" , addr . String () )
230228 }
231229
232230 sig , err := crypto .Sign (accounts .TextHash (data ), key .ToECDSA ())
@@ -248,8 +246,8 @@ func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr comm
248246// the V value must be 27 or 28 for legacy reasons.
249247//
250248// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecove
251- func (e * PersonalEthAPI ) EcRecover (ctx context.Context , data , sig hexutil.Bytes ) (common.Address , error ) {
252- e .logger .Debug ("personal_ecRecover" , "data" , data , "sig" , sig )
249+ func (e * PersonalEthAPI ) EcRecover (_ context.Context , data , sig hexutil.Bytes ) (common.Address , error ) {
250+ e .ethAPI . logger .Debug ("personal_ecRecover" , "data" , data , "sig" , sig )
253251
254252 if len (sig ) != crypto .SignatureLength {
255253 return common.Address {}, fmt .Errorf ("signature must be %d bytes long" , crypto .SignatureLength )
@@ -259,9 +257,9 @@ func (e *PersonalEthAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes)
259257 }
260258 sig [crypto .RecoveryIDOffset ] -= 27 // Transform yellow paper V from 27/28 to 0/1
261259
262- rpk , err := crypto .SigToPub (accounts .TextHash (data ), sig )
260+ pubkey , err := crypto .SigToPub (accounts .TextHash (data ), sig )
263261 if err != nil {
264262 return common.Address {}, err
265263 }
266- return crypto .PubkeyToAddress (* rpk ), nil
264+ return crypto .PubkeyToAddress (* pubkey ), nil
267265}
0 commit comments