mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 16:25:21 +00:00
174 lines
6.1 KiB
Go
174 lines
6.1 KiB
Go
|
package e2e_test
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"math/big"
|
||
|
|
||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||
|
"github.com/ethereum/go-ethereum/common"
|
||
|
"github.com/ethereum/go-ethereum/crypto"
|
||
|
ethermint "github.com/evmos/ethermint/types"
|
||
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||
|
|
||
|
"github.com/kava-labs/kava/precompile/registry"
|
||
|
)
|
||
|
|
||
|
// TestPrecompileGenesis tests that the the following is true for enabled precompiles:
|
||
|
//
|
||
|
// - An enabled precompile has an EthAccount with matching code hash,
|
||
|
// sequence of 1, and no public key
|
||
|
// - An enabled precompile has code equal to 0x01
|
||
|
// - An enabled precompile has a nonce of 1
|
||
|
//
|
||
|
// This is important to ensure the genesis setup for precompiles is correct.
|
||
|
func (suite *IntegrationTestSuite) TestPrecompileGenesis() {
|
||
|
type fixture struct {
|
||
|
address string
|
||
|
expectIsEnabled bool
|
||
|
expectIsEthAccount bool
|
||
|
expectCode []byte
|
||
|
expectNonce uint64
|
||
|
}
|
||
|
|
||
|
// enabled represnets the expected state for a registered precompile
|
||
|
// that is enabled at genesis
|
||
|
enabled := func(address string) func() fixture {
|
||
|
return func() fixture {
|
||
|
return fixture{
|
||
|
address: address,
|
||
|
expectIsEnabled: true,
|
||
|
expectIsEthAccount: true,
|
||
|
expectCode: []byte{0x01},
|
||
|
expectNonce: uint64(1),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// disabled represents the expected state for a registered precompile
|
||
|
// that is not enabled at genesis
|
||
|
disabled := func(address string) func() fixture {
|
||
|
return func() fixture {
|
||
|
return fixture{
|
||
|
address: address,
|
||
|
expectIsEnabled: false,
|
||
|
expectIsEthAccount: false,
|
||
|
expectCode: []byte{},
|
||
|
expectNonce: uint64(0),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
testCases := []struct {
|
||
|
name string
|
||
|
genFixture func() fixture
|
||
|
}{
|
||
|
{
|
||
|
name: "noop contract address is enabled and initialized",
|
||
|
genFixture: enabled(registry.NoopContractAddress),
|
||
|
},
|
||
|
{
|
||
|
name: "noop contract address second address is disabled and not initialized",
|
||
|
genFixture: disabled(registry.NoopContractAddress2),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range testCases {
|
||
|
suite.Run(tc.name, func() {
|
||
|
tf := tc.genFixture()
|
||
|
|
||
|
//
|
||
|
// Addresses
|
||
|
//
|
||
|
evmAddress := common.HexToAddress(tf.address)
|
||
|
sdkAddress := sdk.AccAddress(evmAddress.Bytes())
|
||
|
|
||
|
//
|
||
|
// Heights
|
||
|
//
|
||
|
// We ensure all queries happen at block 1 after genesis
|
||
|
// and help ensure determisitc behavior
|
||
|
genesisHeight := big.NewInt(1)
|
||
|
grpcGenesisContext := suite.Kava.Grpc.CtxAtHeight(genesisHeight.Int64())
|
||
|
|
||
|
//
|
||
|
// Queries
|
||
|
//
|
||
|
evmParamsResp, err := suite.Kava.Grpc.Query.Evm.Params(grpcGenesisContext, &evmtypes.QueryParamsRequest{})
|
||
|
suite.Require().NoError(err)
|
||
|
|
||
|
// accountErr is checked during in the assertions below if tf.expectIsEthAccount is true
|
||
|
// This is due to the service returning a not found error for address that are not enabled,
|
||
|
// which can be ignored when we do not expect an eth account to exist.
|
||
|
accountResponse, accountErr := suite.Kava.Grpc.Query.Auth.Account(
|
||
|
grpcGenesisContext, &authtypes.QueryAccountRequest{Address: sdkAddress.String()})
|
||
|
var account authtypes.AccountI
|
||
|
if accountErr == nil {
|
||
|
err = suite.Kava.EncodingConfig.Marshaler.UnpackAny(accountResponse.Account, &account)
|
||
|
suite.Require().NoError(err)
|
||
|
}
|
||
|
|
||
|
// We ensure both the evm json rpc and x/evm grpc code endpoints return the same value
|
||
|
// in the assertions below
|
||
|
grpcCodeResponse, err := suite.Kava.Grpc.Query.Evm.Code(grpcGenesisContext,
|
||
|
&evmtypes.QueryCodeRequest{Address: evmAddress.String()})
|
||
|
suite.Require().NoError(err)
|
||
|
rpcCode, err := suite.Kava.EvmClient.CodeAt(context.Background(), evmAddress, genesisHeight)
|
||
|
suite.Require().NoError(err)
|
||
|
|
||
|
nonce, err := suite.Kava.EvmClient.NonceAt(context.Background(), evmAddress, genesisHeight)
|
||
|
suite.Require().NoError(err)
|
||
|
|
||
|
//
|
||
|
// Assertions
|
||
|
//
|
||
|
if tf.expectIsEnabled {
|
||
|
suite.Containsf(evmParamsResp.Params.EnabledPrecompiles, tf.address,
|
||
|
"expected %s to be enabled in evm params", tf.address)
|
||
|
} else {
|
||
|
suite.NotContainsf(evmParamsResp.Params.EnabledPrecompiles, tf.address,
|
||
|
"expected %s to not be enabled in evm params", tf.address)
|
||
|
}
|
||
|
|
||
|
// Fail if fixuture configuration is invalid
|
||
|
if tf.expectIsEthAccount && len(tf.expectCode) == 0 {
|
||
|
suite.Failf("an eth account must have expected code for address %s", tf.address)
|
||
|
}
|
||
|
|
||
|
// Run addition EthAccount assertions
|
||
|
if tf.expectIsEthAccount {
|
||
|
suite.Require().NoErrorf(accountErr, "expected account query to not error for address %s", tf.address)
|
||
|
|
||
|
// All contracts including precompiles must be EthAccount's
|
||
|
ethAccount, isEthAccount := account.(*ethermint.EthAccount)
|
||
|
suite.Require().Truef(isEthAccount, "expected account at address %s to be an eth account", tf.address)
|
||
|
|
||
|
// Code hash must always match the EthAccount
|
||
|
codeHash := ethAccount.GetCodeHash()
|
||
|
suite.Equalf(crypto.Keccak256Hash(tf.expectCode), codeHash,
|
||
|
"expected codehash for account %s to match expected code", tf.address)
|
||
|
|
||
|
// A precompile (and contract) should never have a public key set
|
||
|
suite.Nilf(ethAccount.PubKey, "expected account %s to have no public key", tf.address)
|
||
|
|
||
|
// Assert the account sequence matches the expected nonce
|
||
|
// This a duplicate of the nonce assertion below, but also helps ensure these
|
||
|
// two sources agree
|
||
|
suite.Equal(tf.expectNonce, ethAccount.GetSequence())
|
||
|
}
|
||
|
|
||
|
// We assert both methods of code retrieval report the same value
|
||
|
suite.Equalf(tf.expectCode, rpcCode, "expected code for address %s to match expected", tf.address)
|
||
|
// The GRPC endpoint returns []byte(nil) when the code is not in state, which is different from
|
||
|
// the rpc endpoint that returns []byte{}.
|
||
|
grpcExpectedCode := tf.expectCode
|
||
|
if len(grpcExpectedCode) == 0 {
|
||
|
grpcExpectedCode = []byte(nil)
|
||
|
}
|
||
|
suite.Equalf(grpcExpectedCode, grpcCodeResponse.Code, "expected code for address %s to match expected", tf.address)
|
||
|
// We assert this outside of the account context since the evm rpc always returns a nonce
|
||
|
suite.Equalf(tf.expectNonce, nonce, "expected nonce for address %s to match expected", tf.address)
|
||
|
})
|
||
|
}
|
||
|
}
|