forked from cerc-io/laconicd-deprecated
Implements eth_call (#127)
* Fixed tx receipt error on failed transaction * Add returnData to failed transaction for logs bloom * Added simulate call option, without returning evm data * Added encoding and decoding of data from EVM execution for usability * Remove unused context parameter * Fix function comment and remove unnecessary logging on eth_call
This commit is contained in:
committed by
Dustin Brickwood
parent
475919274e
commit
2b4d2bea82
+4
-2
@@ -20,8 +20,8 @@ func NewHandler(keeper Keeper) sdk.Handler {
|
||||
switch msg := msg.(type) {
|
||||
case types.EthereumTxMsg:
|
||||
return handleETHTxMsg(ctx, keeper, msg)
|
||||
case types.EmintMsg:
|
||||
return handleEmintMsg(ctx, keeper, msg)
|
||||
case *types.EmintMsg:
|
||||
return handleEmintMsg(ctx, keeper, *msg)
|
||||
default:
|
||||
errMsg := fmt.Sprintf("Unrecognized ethermint Msg type: %v", msg.Type())
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
@@ -67,6 +67,7 @@ func handleETHTxMsg(ctx sdk.Context, keeper Keeper, msg types.EthereumTxMsg) sdk
|
||||
Csdb: keeper.csdb.WithContext(ctx),
|
||||
ChainID: intChainID,
|
||||
THash: ðHash,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
// Prepare db for logs
|
||||
keeper.csdb.Prepare(ethHash, common.Hash{}, keeper.txCount.get())
|
||||
@@ -97,6 +98,7 @@ func handleEmintMsg(ctx sdk.Context, keeper Keeper, msg types.EmintMsg) sdk.Resu
|
||||
Payload: msg.Payload,
|
||||
Csdb: keeper.csdb.WithContext(ctx),
|
||||
ChainID: intChainID,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
|
||||
if msg.Recipient != nil {
|
||||
|
||||
+1
-1
@@ -391,7 +391,7 @@ func GenerateFromArgs(args args.SendTxArgs, ctx context.CLIContext) (msg *Ethere
|
||||
if args.GasPrice == nil {
|
||||
// Set default gas price
|
||||
// TODO: Change to min gas price from context once available through server/daemon
|
||||
gasPrice = big.NewInt(20)
|
||||
gasPrice = big.NewInt(types.DefaultGasPrice)
|
||||
}
|
||||
|
||||
if args.Nonce == nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ type StateTransition struct {
|
||||
Csdb *CommitStateDB
|
||||
ChainID *big.Int
|
||||
THash *common.Hash
|
||||
Simulate bool
|
||||
}
|
||||
|
||||
// TransitionCSDB performs an evm state transition from a transaction
|
||||
@@ -60,6 +61,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (sdk.Result, *big.Int)
|
||||
)
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
leftOverGas uint64
|
||||
addr common.Address
|
||||
vmerr error
|
||||
@@ -67,11 +69,11 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (sdk.Result, *big.Int)
|
||||
)
|
||||
|
||||
if contractCreation {
|
||||
_, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
|
||||
ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
st.Csdb.SetNonce(st.Sender, st.Csdb.GetNonce(st.Sender)+1)
|
||||
_, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
|
||||
ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
|
||||
}
|
||||
|
||||
// Generate bloom filter to be saved in tx receipt data
|
||||
@@ -83,8 +85,8 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (sdk.Result, *big.Int)
|
||||
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
|
||||
}
|
||||
|
||||
// TODO: coniditionally add either/ both of these to return data
|
||||
returnData := append(addr.Bytes(), bloomFilter.Bytes()...)
|
||||
// Encode all necessary data into slice of bytes to return in sdk result
|
||||
returnData := EncodeReturnData(addr, bloomFilter, ret)
|
||||
|
||||
// handle errors
|
||||
if vmerr != nil {
|
||||
@@ -105,12 +107,15 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (sdk.Result, *big.Int)
|
||||
}
|
||||
|
||||
func (st *StateTransition) checkNonce() sdk.Result {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
nonce := st.Csdb.GetNonce(st.Sender)
|
||||
if nonce < st.AccountNonce {
|
||||
return emint.ErrInvalidNonce("nonce too high").Result()
|
||||
} else if nonce > st.AccountNonce {
|
||||
return emint.ErrInvalidNonce("nonce too low").Result()
|
||||
// If simulated transaction, don't verify nonce
|
||||
if !st.Simulate {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
nonce := st.Csdb.GetNonce(st.Sender)
|
||||
if nonce < st.AccountNonce {
|
||||
return emint.ErrInvalidNonce("nonce too high").Result()
|
||||
} else if nonce > st.AccountNonce {
|
||||
return emint.ErrInvalidNonce("nonce too low").Result()
|
||||
}
|
||||
}
|
||||
|
||||
return sdk.Result{}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/cosmos/ethermint/crypto"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
||||
@@ -12,6 +13,11 @@ import (
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
bloomIdx = ethcmn.AddressLength
|
||||
returnIdx = bloomIdx + ethtypes.BloomByteLength
|
||||
)
|
||||
|
||||
// GenerateEthAddress generates an Ethereum address.
|
||||
func GenerateEthAddress() ethcmn.Address {
|
||||
priv, err := crypto.GenerateKey()
|
||||
@@ -46,3 +52,24 @@ func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
// EncodeReturnData takes all of the necessary data from the EVM execution
|
||||
// and returns the data as a byte slice
|
||||
func EncodeReturnData(addr ethcmn.Address, bloom ethtypes.Bloom, evmRet []byte) []byte {
|
||||
// Append address, bloom, evm return bytes in that order
|
||||
returnData := append(addr.Bytes(), bloom.Bytes()...)
|
||||
return append(returnData, evmRet...)
|
||||
}
|
||||
|
||||
// DecodeReturnData decodes the byte slice of values to their respective types
|
||||
func DecodeReturnData(bytes []byte) (addr ethcmn.Address, bloom ethtypes.Bloom, ret []byte, err error) {
|
||||
if len(bytes) >= returnIdx {
|
||||
addr = ethcmn.BytesToAddress(bytes[:bloomIdx])
|
||||
bloom = ethtypes.BytesToBloom(bytes[bloomIdx:returnIdx])
|
||||
ret = bytes[returnIdx:]
|
||||
} else {
|
||||
err = fmt.Errorf("Invalid format for encoded data, message must be an EVM state transition")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEvmDataEncoding(t *testing.T) {
|
||||
addr := ethcmn.HexToAddress("0x12345")
|
||||
bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||
ret := []byte{0x5, 0x8}
|
||||
|
||||
encoded := EncodeReturnData(addr, bloom, ret)
|
||||
|
||||
decAddr, decBloom, decRet, err := DecodeReturnData(encoded)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr, decAddr)
|
||||
require.Equal(t, bloom, decBloom)
|
||||
require.Equal(t, ret, decRet)
|
||||
}
|
||||
Reference in New Issue
Block a user