diff --git a/app/ante/ante.go b/app/ante/ante.go index 58160150..8015b152 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -37,6 +37,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK case auth.StdTx: anteHandler = sdk.ChainAnteDecorators( authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + NewAccountSetupDecorator(ak), authante.NewMempoolFeeDecorator(), authante.NewValidateBasicDecorator(), authante.NewValidateMemoDecorator(ak), @@ -126,3 +127,33 @@ func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim return next(ctx, tx, simulate) } + +type AccountSetupDecorator struct { + ak auth.AccountKeeper +} + +func NewAccountSetupDecorator(ak auth.AccountKeeper) AccountSetupDecorator { + return AccountSetupDecorator{ + ak: ak, + } +} + +func (asd AccountSetupDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + msgs := tx.GetMsgs() + if len(msgs) == 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no messages included in transaction") + } + + msg, ok := msgs[0].(evmtypes.MsgEthermint) + if !ok { + return next(ctx, tx, simulate) + } + + acc := asd.ak.GetAccount(ctx, msg.From) + if acc == nil { + info := asd.ak.NewAccountWithAddress(ctx, msg.From) + asd.ak.SetAccount(ctx, info) + } + + return next(ctx, tx, simulate) +} diff --git a/app/ante/eth.go b/app/ante/eth.go index 0cd6b437..424f4acf 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -191,7 +191,8 @@ func (avd AccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s acc := avd.ak.GetAccount(ctx, address) if acc == nil { - return ctx, fmt.Errorf("account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address) + acc = avd.ak.NewAccountWithAddress(ctx, address) + avd.ak.SetAccount(ctx, acc) } // on InitChain make sure account number == 0 diff --git a/rpc/eth_api.go b/rpc/eth_api.go index 3974c066..f0c10989 100644 --- a/rpc/eth_api.go +++ b/rpc/eth_api.go @@ -54,13 +54,41 @@ type PublicEthAPI struct { func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker, key []crypto.PrivKeySecp256k1) *PublicEthAPI { - return &PublicEthAPI{ + api := &PublicEthAPI{ cliCtx: cliCtx, logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "json-rpc"), backend: backend, keys: key, nonceLock: nonceLock, } + err := api.getKeybaseInfo() + if err != nil { + api.logger.Error("failed to get keybase info", "error", err) + } + + return api +} + +func (e *PublicEthAPI) getKeybaseInfo() error { + e.keybaseLock.Lock() + defer e.keybaseLock.Unlock() + + if e.cliCtx.Keybase == nil { + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + e.cliCtx.Input, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return err + } + + e.cliCtx.Keybase = keybase + } + + return nil } // ProtocolVersion returns the supported Ethereum protocol version. @@ -349,8 +377,13 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err e.logger.Debug("eth_sendTransaction", "args", args) // TODO: Change this functionality to find an unlocked account by address + for _, key := range e.keys { + e.logger.Debug("eth_sendTransaction", "key", fmt.Sprintf("0x%x", key.PubKey().Address().Bytes())) + } + key, exist := checkKeyInKeyring(e.keys, args.From) if !exist { + e.logger.Debug("failed to find key in keyring", "key", args.From) return common.Hash{}, keystore.ErrLocked } @@ -363,6 +396,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err // Assemble transaction from fields tx, err := e.generateFromArgs(args) if err != nil { + e.logger.Debug("failed to generate tx", "error", err) return common.Hash{}, err } @@ -376,6 +410,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err // Sign transaction if err := tx.Sign(intChainID, key.ToECDSA()); err != nil { + e.logger.Debug("failed to sign tx", "error", err) return common.Hash{}, err } @@ -967,6 +1002,10 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*evmtypes.MsgEt from := sdk.AccAddress(args.From.Bytes()) accRet := authtypes.NewAccountRetriever(e.cliCtx) + if e.cliCtx.Keybase == nil { + return nil, fmt.Errorf("cliCtx.Keybase is nil") + } + err = accRet.EnsureExists(from) if err != nil { // account doesn't exist diff --git a/rpc/personal_api.go b/rpc/personal_api.go index 3de537dc..2b783dd1 100644 --- a/rpc/personal_api.go +++ b/rpc/personal_api.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "log" "os" "sync" "time" @@ -16,6 +15,7 @@ import ( emintcrypto "github.com/cosmos/ethermint/crypto" params "github.com/cosmos/ethermint/rpc/args" "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/log" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -25,6 +25,7 @@ import ( // PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. type PersonalEthAPI struct { + logger log.Logger cliCtx sdkcontext.CLIContext ethAPI *PublicEthAPI nonceLock *AddrLocker @@ -36,6 +37,7 @@ type PersonalEthAPI struct { // NewPersonalEthAPI creates an instance of the public ETH Web3 API. func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, ethAPI *PublicEthAPI, nonceLock *AddrLocker, keys []emintcrypto.PrivKeySecp256k1) *PersonalEthAPI { api := &PersonalEthAPI{ + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "json-rpc"), cliCtx: cliCtx, ethAPI: ethAPI, nonceLock: nonceLock, @@ -77,6 +79,7 @@ func (e *PersonalEthAPI) getKeybaseInfo() ([]keys.Info, error) { // encrypting it with the passphrase. // Currently, this is not implemented since the feature is not supported by the keys. func (e *PersonalEthAPI) ImportRawKey(privkey, password string) (common.Address, error) { + e.logger.Debug("personal_importRawKey", "error", "not implemented") _, err := crypto.HexToECDSA(privkey) if err != nil { return common.Address{}, err @@ -87,6 +90,7 @@ func (e *PersonalEthAPI) ImportRawKey(privkey, password string) (common.Address, // ListAccounts will return a list of addresses for accounts this node manages. func (e *PersonalEthAPI) ListAccounts() ([]common.Address, error) { + e.logger.Debug("personal_listAccounts") addrs := []common.Address{} for _, info := range e.keyInfos { addressBytes := info.GetPubKey().Address().Bytes() @@ -99,6 +103,7 @@ func (e *PersonalEthAPI) ListAccounts() ([]common.Address, error) { // LockAccount will lock the account associated with the given address when it's unlocked. // It removes the key corresponding to the given address from the API's local keys. func (e *PersonalEthAPI) LockAccount(address common.Address) bool { + e.logger.Debug("personal_lockAccount", "address", address) for i, key := range e.keys { if !bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) { continue @@ -111,11 +116,24 @@ func (e *PersonalEthAPI) LockAccount(address common.Address) bool { return true } + for i, key := range e.ethAPI.keys { + if !bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) { + continue + } + + tmp := make([]emintcrypto.PrivKeySecp256k1, len(e.ethAPI.keys)-1) + copy(tmp[:i], e.ethAPI.keys[:i]) + copy(tmp[i:], e.ethAPI.keys[i+1:]) + e.ethAPI.keys = tmp + return true + } + return false } // NewAccount will create a new account and returns the address for the new account. func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) { + e.logger.Debug("personal_newAccount") _, err := e.getKeybaseInfo() if err != nil { return common.Address{}, err @@ -129,10 +147,23 @@ func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) { e.keyInfos = append(e.keyInfos, info) + // update ethAPI + privKey, err := e.cliCtx.Keybase.ExportPrivateKeyObject(name, password) + if err != nil { + return common.Address{}, err + } + + emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1) + if !ok { + return common.Address{}, fmt.Errorf("invalid private key type: %T", privKey) + } + e.ethAPI.keys = append(e.ethAPI.keys, emintKey) + e.logger.Debug("personal_newAccount", "address", fmt.Sprintf("0x%x", emintKey.PubKey().Address().Bytes())) + addr := common.BytesToAddress(info.GetPubKey().Address().Bytes()) - log.Printf("Your new key was generated\t\taddress=0x%x", addr) - log.Printf("Please backup your key file!\tpath=%s", os.Getenv("HOME")+"/.ethermintcli/"+name) - log.Println("Please remember your password!") + e.logger.Info("Your new key was generated", "address", addr) + e.logger.Info("Please backup your key file!", "path", os.Getenv("HOME")+"/.ethermintcli/"+name) + e.logger.Info("Please remember your password!") return addr, nil } @@ -141,6 +172,7 @@ func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) { // default of 300 seconds. It returns an indication if the account was unlocked. // It exports the private key corresponding to the given address from the keyring and stores it in the API's local keys. func (e *PersonalEthAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, _ *uint64) (bool, error) { + e.logger.Debug("personal_unlockAccount", "address", addr) // TODO: use duration name := "" @@ -167,6 +199,9 @@ func (e *PersonalEthAPI) UnlockAccount(ctx context.Context, addr common.Address, } e.keys = append(e.keys, emintKey) + e.ethAPI.keys = append(e.ethAPI.keys, emintKey) + e.logger.Debug("personal_unlockAccount", "address", fmt.Sprintf("0x%x", emintKey.PubKey().Address().Bytes())) + return true, nil } @@ -187,6 +222,8 @@ func (e *PersonalEthAPI) SendTransaction(ctx context.Context, args params.SendTx // // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { + e.logger.Debug("personal_sign", "data", data, "address", addr) + key, ok := checkKeyInKeyring(e.keys, addr) if !ok { return nil, fmt.Errorf("cannot find key with given address") @@ -212,6 +249,8 @@ func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr comm // // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecove func (e *PersonalEthAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { + e.logger.Debug("personal_ecRecover", "data", data, "sig", sig) + if len(sig) != crypto.SignatureLength { return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) } diff --git a/tests/personal_test.go b/tests/personal_test.go index 2ee7b76e..914183e2 100644 --- a/tests/personal_test.go +++ b/tests/personal_test.go @@ -20,7 +20,7 @@ func TestPersonal_ListAccounts(t *testing.T) { } func TestPersonal_NewAccount(t *testing.T) { - rpcRes := call(t, "personal_newAccount", []string{""}) + rpcRes := call(t, "personal_newAccount", []string{"password"}) var addr common.Address err := json.Unmarshal(rpcRes.Result, &addr) require.NoError(t, err) diff --git a/tests/rpc_test.go b/tests/rpc_test.go index 032192ae..383c7225 100644 --- a/tests/rpc_test.go +++ b/tests/rpc_test.go @@ -758,7 +758,7 @@ func TestEth_EstimateGas(t *testing.T) { err := json.Unmarshal(rpcRes.Result, &gas) require.NoError(t, err, string(rpcRes.Result)) - require.Equal(t, "0xef7e", gas) + require.Equal(t, "0xf552", gas) } func TestEth_EstimateGas_ContractDeployment(t *testing.T) {