mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-14 20:15:18 +00:00
ab3cf7c994
* feat!(precompile): Add registry and genesis tests Based on evgeniy-scherbina's work, this adds a new precompile module which defines a contract moudule with an example noop contract that will be will be used for implementing test functions. In addition, it defines a registry module that instantiates stateful precompile contracts and associates them with an address in a global registry defined in kava-labs/go-ethereum. See precompile/README.md for more information. The kava-labs/go-ethereum and kava-labs/etheremint replace statements are updated to support these changes as well as an update to kvtool which includes genesis state for the registry.NoopContractAddress and initializes the contract's EthAccount with a non-zero sequence and codehash set to keccak256(0x01), and sets the contract code to 0x01. See tests/e2e/e2e_precompile_genesis_test.go for an overview of the expected genesis state for an enabled precompile. Co-authored-by: evgeniy-scherbina <evgeniy.shcherbina.es@gmail.com> * chore: Precompile readme improvements This fixes a typo (import -> important) and uses package terminology instead of unclear module terminology. This aligns best with golang terminology were modules and packages are distinctly different and modules are defined using go.mod. * chore: Improve noop contract godoc Add a more meaningful godoc where the noop contract is constructed. * chore(e2e): Improve comments around query checks Improve the clarity of comments around where the error is checked for accounts and why it is not checked directly. In addition, improve comment on why both grpc and rpc code is fetched and where they are used. --------- Co-authored-by: evgeniy-scherbina <evgeniy.shcherbina.es@gmail.com>
172 lines
5.6 KiB
Go
172 lines
5.6 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
db "github.com/cometbft/cometbft-db"
|
|
abci "github.com/cometbft/cometbft/abci/types"
|
|
"github.com/cometbft/cometbft/libs/log"
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/testutil/sims"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
|
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
|
solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine"
|
|
ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint"
|
|
"github.com/ethereum/go-ethereum/precompile/modules"
|
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewApp(t *testing.T) {
|
|
SetSDKConfig()
|
|
NewApp(
|
|
log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
|
db.NewMemDB(),
|
|
DefaultNodeHome,
|
|
nil,
|
|
MakeEncodingConfig(),
|
|
DefaultOptions,
|
|
)
|
|
}
|
|
|
|
func TestExport(t *testing.T) {
|
|
SetSDKConfig()
|
|
db := db.NewMemDB()
|
|
app := NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, DefaultNodeHome, nil, MakeEncodingConfig(), DefaultOptions, baseapp.SetChainID(TestChainId))
|
|
|
|
genesisState := GenesisStateWithSingleValidator(&TestApp{App: *app}, NewDefaultGenesisState())
|
|
|
|
stateBytes, err := json.Marshal(genesisState)
|
|
require.NoError(t, err)
|
|
|
|
initRequest := abci.RequestInitChain{
|
|
Time: time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
ChainId: TestChainId,
|
|
InitialHeight: 1,
|
|
ConsensusParams: sims.DefaultConsensusParams,
|
|
Validators: nil,
|
|
AppStateBytes: stateBytes,
|
|
}
|
|
app.InitChain(initRequest)
|
|
app.Commit()
|
|
|
|
exportedApp, err := app.ExportAppStateAndValidators(false, []string{}, []string{})
|
|
require.NoError(t, err)
|
|
|
|
// Assume each module is exported correctly, so only check modules in genesis are present in export
|
|
initialModules, err := unmarshalJSONKeys(initRequest.AppStateBytes)
|
|
require.NoError(t, err)
|
|
// note ibctm is only registered in the BasicManager and not module manager so can be ignored
|
|
initialModules = removeIbcTmModule(initialModules)
|
|
exportedModules, err := unmarshalJSONKeys(exportedApp.AppState)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, initialModules, exportedModules)
|
|
|
|
assert.Equal(t, initRequest.InitialHeight+1, exportedApp.Height) // app.Commit() increments height
|
|
assert.Equal(t, initRequest.ConsensusParams, exportedApp.ConsensusParams)
|
|
assert.Len(t, exportedApp.Validators, 1) // no validators set in default genesis
|
|
}
|
|
|
|
// TestLegacyMsgAreAminoRegistered checks if all known msg types are registered on the app's amino codec.
|
|
// It doesn't check if they are registered on the module codecs used for signature checking.
|
|
func TestLegacyMsgAreAminoRegistered(t *testing.T) {
|
|
tApp := NewTestApp()
|
|
|
|
lcdc := tApp.LegacyAmino()
|
|
|
|
// Use the proto codec as the canonical list of msg types.
|
|
protoCodec := tApp.AppCodec().(*codec.ProtoCodec)
|
|
protoRegisteredMsgs := protoCodec.InterfaceRegistry().ListImplementations(sdk.MsgInterfaceProtoName)
|
|
|
|
for i, msgName := range protoRegisteredMsgs {
|
|
// Skip msgs from dependencies that were never amino registered.
|
|
if msgName == sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}) ||
|
|
msgName == sdk.MsgTypeURL(&vestingtypes.MsgCreatePeriodicVestingAccount{}) {
|
|
continue
|
|
}
|
|
|
|
// Create an encoded json msg, then unmarshal it to instantiate the msg type.
|
|
jsonMsg := []byte(fmt.Sprintf(`{"@type": "%s"}`, msgName))
|
|
|
|
var msg sdk.Msg
|
|
err := protoCodec.UnmarshalInterfaceJSON(jsonMsg, &msg)
|
|
require.NoError(t, err)
|
|
|
|
// Only check legacy msgs for amino registration.
|
|
// Only legacy msg can be signed with amino.
|
|
_, ok := msg.(legacytx.LegacyMsg)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Check the msg is registered in amino by checking a repeat registration call panics.
|
|
panicValue, ok := catchPanic(func() {
|
|
lcdc.RegisterConcrete(interface{}(msg), fmt.Sprintf("aUniqueRegistrationName%d", i), nil)
|
|
})
|
|
assert.True(t, ok, "registration did not panic, msg %s is not registered in amino", msgName)
|
|
if ok {
|
|
require.IsTypef(t, "", panicValue, "msg %s amino registration panicked with unexpected type", msgName)
|
|
aminoErrMsgPrefix := "TypeInfo already exists for"
|
|
require.Containsf(t, panicValue, aminoErrMsgPrefix, "msg %s amino registration panicked for unexpected reason", msgName)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPrecompilesAreRegistered asserts that we have loaded the global precompile registry
|
|
// by checking if at least one precompile is set
|
|
//
|
|
// If this test fails then has '_ "github.com/kava-labs/kava/precompile/registry"' been imported?
|
|
func TestPrecompilesAreRegistered(t *testing.T) {
|
|
assert.Greater(t, len(modules.RegisteredModules()), 0,
|
|
"expected precompile registry to be imported and have at least one registered precompile",
|
|
)
|
|
}
|
|
|
|
// catchPanic returns the panic value of the passed function. The second return indicates if the function panicked.
|
|
func catchPanic(f func()) (panicValue interface{}, didPanic bool) {
|
|
didPanic = true
|
|
|
|
defer func() {
|
|
panicValue = recover()
|
|
}()
|
|
|
|
f()
|
|
didPanic = false
|
|
return
|
|
}
|
|
|
|
// unmarshalJSONKeys extracts keys from the top level of a json blob.
|
|
func unmarshalJSONKeys(jsonBytes []byte) ([]string, error) {
|
|
var jsonMap map[string]json.RawMessage
|
|
err := json.Unmarshal(jsonBytes, &jsonMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
keys := make([]string, 0, len(jsonMap))
|
|
for k := range jsonMap {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
return keys, nil
|
|
}
|
|
|
|
func removeIbcTmModule(modules []string) []string {
|
|
var result []string
|
|
for _, str := range modules {
|
|
if str != ibctm.ModuleName && str != solomachine.ModuleName {
|
|
result = append(result, str)
|
|
}
|
|
}
|
|
return result
|
|
}
|