diff --git a/CHANGELOG.md b/CHANGELOG.md index 41656c54a0..0a41695c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking +* (app, rpc) [tharsis#447](https://github.com/tharsis/ethermint/pull/447) Chain ID format has been changed from `-` to `_-` +in order to clearly distinguish permanent vs impermanent components. * (app, evm) [tharsis#434](https://github.com/tharsis/ethermint/pull/434) EVM `Keeper` struct and `NewEVM` function now have a new `trace` field to define the Tracer type used to collect execution traces from the EVM transaction execution. * (evm) [tharsis#175](https://github.com/tharsis/ethermint/issues/175) The msg `TxData` field is now represented as a `*proto.Any`. diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index f5faab95e5..ad23133234 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -44,7 +44,7 @@ type AnteTestSuite struct { func (suite *AnteTestSuite) SetupTest() { checkTx := false suite.app = app.Setup(checkTx) - suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 2, ChainID: "ethermint-1", Time: time.Now().UTC()}) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 2, ChainID: "ethermint_9000-1", Time: time.Now().UTC()}) suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(evmtypes.DefaultEVMDenom, sdk.OneInt()))) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1000000000000000000)) suite.app.EvmKeeper.WithChainID(suite.ctx) diff --git a/app/app_test.go b/app/app_test.go index 4dbb4f9e5d..6ddf51284f 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -27,7 +27,7 @@ func TestEthermintAppExport(t *testing.T) { // Initialize the chain app.InitChain( abci.RequestInitChain{ - ChainId: "ethermint-1", + ChainId: "ethermint_9000-1", Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes, }, diff --git a/app/test_helpers.go b/app/test_helpers.go index 0c01a604a6..6b80157aa8 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -48,7 +48,7 @@ func Setup(isCheckTx bool) *EthermintApp { // Initialize the chain app.InitChain( abci.RequestInitChain{ - ChainId: "ethermint-1", + ChainId: "ethermint_9000-1", Validators: []abci.ValidatorUpdate{}, ConsensusParams: DefaultConsensusParams, AppStateBytes: stateBytes, diff --git a/client/testnet.go b/client/testnet.go index 7dcaa9c189..b3353b85b5 100644 --- a/client/testnet.go +++ b/client/testnet.go @@ -130,7 +130,7 @@ func InitTestnet( ) error { if chainID == "" { - chainID = fmt.Sprintf("ethermint-%d", tmrand.Int63n(9999999999999)+1) + chainID = fmt.Sprintf("ethermint_%d-1", tmrand.Int63n(9999999999999)+1) } if !ethermint.IsValidChainID(chainID) { diff --git a/cmd/ethermintd/cmd_test.go b/cmd/ethermintd/cmd_test.go index 398e3063f2..3fe91e5adf 100644 --- a/cmd/ethermintd/cmd_test.go +++ b/cmd/ethermintd/cmd_test.go @@ -17,10 +17,10 @@ import ( func TestInitCmd(t *testing.T) { rootCmd, _ := ethermintd.NewRootCmd() rootCmd.SetArgs([]string{ - "init", // Test the init cmd - "ethermint-test", // Moniker + "init", // Test the init cmd + "etherminttest", // Moniker fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists - fmt.Sprintf("--%s=%s", flags.FlagChainID, "ethermint-1"), + fmt.Sprintf("--%s=%s", flags.FlagChainID, "ethermint_9000-1"), }) err := svrcmd.Execute(rootCmd, app.DefaultNodeHome) diff --git a/config.yml b/config.yml index 32ffb0f75f..1d6d724cda 100644 --- a/config.yml +++ b/config.yml @@ -18,7 +18,7 @@ init: address: "0.0.0.0:8545" # change the JSON-RPC address and port ws-address: "0.0.0.0:8546" # change the JSON-RPC websocket address and port genesis: - chain_id: "ethermint-2" + chain_id: "ethermint_9000-1" app_state: staking: params: diff --git a/docs/basics/README.md b/docs/basics/README.md index 824f10f5f6..c688281ae7 100644 --- a/docs/basics/README.md +++ b/docs/basics/README.md @@ -8,9 +8,10 @@ parent: This repository contains reference documentation on the basic concepts of Ethermint. +1. [Chain ID](./chain_id) 1. [Accounts](./accounts) -2. [Gas and Fees](./gas) -3. [Lifecycle of a transaction](./transactions) -4. [Tokens](./tokens) +1. [Gas and Fees](./gas) +1. [Lifecycle of a transaction](./transactions) +1. [Tokens](./tokens) After reading the basics, head on to the [Core Reference](../core/README) for more advanced material. diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md index ddccbe3fc7..a49671054e 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -1,5 +1,5 @@ # Accounts diff --git a/docs/basics/chain_id.md b/docs/basics/chain_id.md new file mode 100644 index 0000000000..17f08903a3 --- /dev/null +++ b/docs/basics/chain_id.md @@ -0,0 +1,36 @@ + + +# Chain ID + +Learn about the Ethermint chain-id format {synopsis} + +## The Chain Identifier + +Every chain must have a unique identifier or `chain-id`. Tendermint requires each application to +define its own `chain-id` in the [genesis.json fields](https://docs.tendermint.com/master/spec/core/genesis.html#genesis-fields). However, in order to comply with both EIP155 and Cosmos standard for chain upgrades, Ethermint-compatible chains must implement a special structure for their chain identifiers. + +## Structure + +The Ethermint Chain ID contains 3 main components + +- **Identifier**: Unstructured string that defines the name of the application. +- **EIP155 Number**: Immutable [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) `CHAIN_ID` that defines the replay attack protection number. +- **Version Number**: Is the version number (always positive) that the chain is currently running. +This number **MUST** be incremented every time the chain is upgraded or forked in order to avoid network or consensus errors. + +### Format + +The format for specifying and Ethermint compatible chain-id in genesis is the following: + +```bash +{identifier}_{EIP155}-{version} +``` + +The following table provides an example where the second row corresponds to an upgrade from the first one: + +| ChainID | Identifier | EIP155 Number | Version Number | +|--------------------|------------|---------------|----------------| +| `ethermint_9000-1` | ethermint | 9000 | 1 | +| `ethermint_9000-2` | ethermint | 9000 | 2 | diff --git a/docs/basics/gas.md b/docs/basics/gas.md index 29d1fe9876..2de89687aa 100644 --- a/docs/basics/gas.md +++ b/docs/basics/gas.md @@ -1,5 +1,5 @@ # Gas and Fees diff --git a/docs/basics/tokens.md b/docs/basics/tokens.md index 0e1dd1579b..959f7e239b 100644 --- a/docs/basics/tokens.md +++ b/docs/basics/tokens.md @@ -1,5 +1,5 @@ # Tokens diff --git a/docs/basics/transactions.md b/docs/basics/transactions.md index 3c5439db7d..bdbe392582 100644 --- a/docs/basics/transactions.md +++ b/docs/basics/transactions.md @@ -1,5 +1,5 @@ # Transactions diff --git a/docs/guides/keys-wallets/metamask.md b/docs/guides/keys-wallets/metamask.md index 125ce99b0e..125a68e144 100644 --- a/docs/guides/keys-wallets/metamask.md +++ b/docs/guides/keys-wallets/metamask.md @@ -15,8 +15,8 @@ are not already. Then click the top right circle and go to `Settings` > `Network Network` button and fill the form as shown below with your application `ChainID`. ::: tip -To find your full `ChainID`, got your genesis.json file. To get the Ethereum chain ID from the Cosmos chain ID, you need to consider only the last digit in the string value. For example -if your chain id on ethermint is `"chain_id": "ethermint-1337"`, then you will have to use the value `1337` on Metamask. +To find your full `ChainID`, got your genesis.json file. To get the [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) chain ID from the Cosmos chain ID, you need to consider only the second number in the string value. For example +if your chain id on ethermint is `"chain_id": "ethermint_9000-1"`, then you will have to use the value `9000` on Metamask. ::: ![metamask networks settings](./../img/metamask_network_settings.png) diff --git a/docs/guides/localnet/single_node.md b/docs/guides/localnet/single_node.md index bfdf090f8d..26b561dd5c 100644 --- a/docs/guides/localnet/single_node.md +++ b/docs/guides/localnet/single_node.md @@ -24,7 +24,7 @@ This guide helps you create a single validator node that runs a network locally ```bash $MONIKER=testing $KEY=mykey -$CHAINID="ethermint-777" +$CHAINID="ethermint_9000-1" ethermintd init $MONIKER --chain-id=$CHAINID ``` diff --git a/docs/quickstart/binary.md b/docs/quickstart/binary.md index 1ad5037dc6..b20dff5141 100644 --- a/docs/quickstart/binary.md +++ b/docs/quickstart/binary.md @@ -63,13 +63,13 @@ ethermintd config We can make changes to the default settings upon our choices, so it allows users to set the configuration beforehand all at once, so it would be ready with the same config afterward. -For example, the chain identifier can be changed to `ethermint-777` from a blank name by using: +For example, the chain identifier can be changed to `ethermint_9000-1` from a blank name by using: ```bash -ethermintd config "chain-id" ethermint-777 +ethermintd config "chain-id" ethermint_9000-1 ethermintd config { - "chain-id": "ethermint-777", + "chain-id": "ethermint_9000-1", "keyring-backend": "os", "output": "text", "node": "tcp://localhost:26657", @@ -89,7 +89,7 @@ Alternatively, we can directly make the changes to the config values in one plac # The network chain ID -chain-id = "ethermint-777" +chain-id = "ethermint_9000-1" # The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory) @@ -108,12 +108,12 @@ node = "tcp://localhost:26657" broadcast-mode = "sync" ``` -After the necessary changes are made in the `client.toml`, then save. For example, if we directly change the chain-id from `ethermint-0` to `etherminttest-1`, and output to number, it would change instantly as shown below. +After the necessary changes are made in the `client.toml`, then save. For example, if we directly change the chain-id from `ethermint_9000-1` to `etherminttest_9000-1`, and output to number, it would change instantly as shown below. ```bash ethermintd config { - "chain-id": "etherminttest-1", + "chain-id": "etherminttest_9000-1", "keyring-backend": "os", "output": "number", "node": "tcp://localhost:26657", @@ -126,7 +126,7 @@ ethermintd config A list of commonly used flags of `ethermintd` is listed below: | Option | Description | Type | Default Value | -| ------------------- | ----------------------------- | ------------ | --------------- | +|---------------------|-------------------------------|--------------|-----------------| | `--chain-id` | Full Chain ID | String | --- | | `--home` | Directory for config and data | string | `~/.ethermintd` | | `--keyring-backend` | Select keyring's backend | os/file/test | os | @@ -136,13 +136,13 @@ A list of commonly used flags of `ethermintd` is listed below: A list of commonly used `ethermintd` commands. You can obtain the full list by using the `ethermintd -h` command. -| Command | Description | Subcommands (example) | -| --------------- | ------------------------ | ------------------------------------------------------------ | -| `keys` | Keys management | `list`, `show`, `add`, `add --recover`, `delete` | -| `tx` | Transactions subcommands | `bank send`, `ibc-transfer transfer`, `distribution withdraw-all-rewards` | -| `query` | Query subcommands | `bank balance`, `staking validators`, `gov proposals` | -| `tendermint` | Tendermint subcommands | `show-address`, `show-node-id`, `version` | -| `config` | Client configuration | | -| `init` | Initialize full node | | -| `start` | Run full node | | -| `version` | Ethermint version | | +| Command | Description | Subcommands (example) | +|--------------|--------------------------|---------------------------------------------------------------------------| +| `keys` | Keys management | `list`, `show`, `add`, `add --recover`, `delete` | +| `tx` | Transactions subcommands | `bank send`, `ibc-transfer transfer`, `distribution withdraw-all-rewards` | +| `query` | Query subcommands | `bank balance`, `staking validators`, `gov proposals` | +| `tendermint` | Tendermint subcommands | `show-address`, `show-node-id`, `version` | +| `config` | Client configuration | | +| `init` | Initialize full node | | +| `start` | Run full node | | +| `version` | Ethermint version | | diff --git a/init.bat b/init.bat index 08c06e4094..54244d0edb 100644 --- a/init.bat +++ b/init.bat @@ -9,7 +9,7 @@ rem 3. add path C:\msys64\mingw64\bin rem C:\msys64\usr\bin set KEY="mykey" -set CHAINID="ethermint-2" +set CHAINID="ethermint_9000-1" set MONIKER="localtestnet" set KEYRING="test" set KEYALGO="eth_secp256k1" diff --git a/init.sh b/init.sh index e4de72723f..96eb639f0a 100755 --- a/init.sh +++ b/init.sh @@ -1,6 +1,6 @@ KEY="mykey" -CHAINID="ethermint-2" +CHAINID="ethermint_9000-1" MONIKER="localtestnet" KEYRING="test" KEYALGO="eth_secp256k1" diff --git a/scripts/contract-test.sh b/scripts/contract-test.sh index 76c9c88e5c..951a4c20a4 100644 --- a/scripts/contract-test.sh +++ b/scripts/contract-test.sh @@ -1,7 +1,7 @@ #!/bin/bash KEY="mykey" -CHAINID="ethermint-100" +CHAINID="ethermint_9000-1" MONIKER="localtestnet" # stop and remove existing daemon and client data and process(es) diff --git a/scripts/integration-test-all.sh b/scripts/integration-test-all.sh index 95eb32311d..6542c4a136 100755 --- a/scripts/integration-test-all.sh +++ b/scripts/integration-test-all.sh @@ -17,7 +17,7 @@ RPC_PORT="854" IP_ADDR="0.0.0.0" KEY="mykey" -CHAINID="ethermint-2" +CHAINID="ethermint_9000-1" MONIKER="mymoniker" ## default port prefixes for ethermintd diff --git a/scripts/run-solidity-tests.sh b/scripts/run-solidity-tests.sh index 05765314d3..94e6c7ba01 100755 --- a/scripts/run-solidity-tests.sh +++ b/scripts/run-solidity-tests.sh @@ -10,7 +10,7 @@ localKeyAddr=0x7cb61d4117ae31a12e393a1cfa3bac666481d02e user1Addr=0xc6fe5d33615a1c52c08018c47e8bc53646a0e101 user2Addr=0x963ebdf2e1f8db8707d05fc75bfeffba1b5bac17 -CHAINID="ethermint-1337" +CHAINID="ethermint_9000-1" # build ethermint binary make install diff --git a/scripts/start.sh b/scripts/start.sh index fbe2d919cc..c5e8472983 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -15,7 +15,7 @@ IP_ADDR="0.0.0.0" MODE="rpc" KEY="mykey" -CHAINID="ethermint-2" +CHAINID="ethermint_9000-1" MONIKER="mymoniker" ## default port prefixes for ethermintd @@ -47,14 +47,14 @@ done set -euxo pipefail -DATA_DIR=$(mktemp -d -t ethermint-datadir.XXXXX) +DATA_DIR=$(mktemp -d -t ethermint_9000-datadir.XXXXX) if [[ ! "$DATA_DIR" ]]; then echo "Could not create $DATA_DIR" exit 1 fi -DATA_CLI_DIR=$(mktemp -d -t ethermint-cli-datadir.XXXXX) +DATA_CLI_DIR=$(mktemp -d -t ethermint_9000-cli-datadir.XXXXX) if [[ ! "$DATA_CLI_DIR" ]]; then echo "Could not create $DATA_CLI_DIR" diff --git a/tests/solidity/init-test-node.sh b/tests/solidity/init-test-node.sh index 5006f38c7f..38440abf69 100755 --- a/tests/solidity/init-test-node.sh +++ b/tests/solidity/init-test-node.sh @@ -1,6 +1,6 @@ #!/bin/bash -CHAINID="ethermint-1337" +CHAINID="ethermint_9000-1" MONIKER="localtestnet" # localKey address 0x7cb61d4117ae31a12e393a1cfa3bac666481d02e diff --git a/testutil/network/network.go b/testutil/network/network.go index c7a41f0a5e..862874ac12 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -117,7 +117,7 @@ func DefaultConfig() Config { AppConstructor: NewAppConstructor(encCfg), GenesisState: app.NewDefaultGenesisState(), TimeoutCommit: 2 * time.Second, - ChainID: "ethermint-" + fmt.Sprintf("%d", tmrand.NewRand().Int63n(1000)), + ChainID: "ethermint_9000-" + fmt.Sprintf("%d", tmrand.NewRand().Int63n(1000)), NumValidators: 4, BondDenom: ethermint.AttoPhoton, MinGasPrices: fmt.Sprintf("0.000006%s", ethermint.AttoPhoton), diff --git a/types/chain_id.go b/types/chain_id.go index 7bc316b12f..c56843ee13 100644 --- a/types/chain_id.go +++ b/types/chain_id.go @@ -10,10 +10,12 @@ import ( ) var ( - regexChainID = `[a-z]*` - regexSeparator = `-{1}` - regexEpoch = `[1-9][0-9]*` - ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, regexChainID, regexSeparator, regexEpoch)) + regexChainID = `[a-z]{1,}` + regexEIP155Separator = `_{1}` + regexEIP155 = `[1-9][0-9]*` + regexEpochSeparator = `-{1}` + regexEpoch = `[1-9][0-9]*` + ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)%s(%s)$`, regexChainID, regexEIP155Separator, regexEIP155, regexEpochSeparator, regexEpoch)) ) // IsValidChainID returns false if the given chain identifier is incorrectly formatted. @@ -34,8 +36,8 @@ func ParseChainID(chainID string) (*big.Int, error) { } matches := ethermintChainID.FindStringSubmatch(chainID) - if matches == nil || len(matches) != 3 || matches[1] == "" { - return nil, sdkerrors.Wrap(ErrInvalidChainID, chainID) + if matches == nil || len(matches) != 4 || matches[1] == "" { + return nil, sdkerrors.Wrapf(ErrInvalidChainID, "%s: %v", chainID, matches) } // verify that the chain-id entered is a base 10 integer diff --git a/types/chain_id_test.go b/types/chain_id_test.go index 63a6b90250..a527ac76a3 100644 --- a/types/chain_id_test.go +++ b/types/chain_id_test.go @@ -16,40 +16,46 @@ func TestParseChainID(t *testing.T) { expInt *big.Int }{ { - "valid chain-id, single digit", "ethermint-1", false, big.NewInt(1), + "valid chain-id, single digit", "ethermint_1-1", false, big.NewInt(1), }, { - "valid chain-id, multiple digits", "aragonchain-256", false, big.NewInt(256), + "valid chain-id, multiple digits", "aragonchain_256-1", false, big.NewInt(256), }, { - "invalid chain-id, double dash", "aragon-chain-1", true, nil, + "invalid chain-id, double dash", "aragonchain-1-1", true, nil, + }, + { + "invalid chain-id, double underscore", "aragonchain_1_1", true, nil, }, { "invalid chain-id, dash only", "-", true, nil, }, { - "invalid chain-id, undefined", "-1", true, nil, + "invalid chain-id, undefined identifier and EIP155", "-1", true, nil, + }, + { + "invalid chain-id, undefined identifier", "_1-1", true, nil, }, { - "invalid chain-id, uppercases", "ETHERMINT-1", true, nil, + "invalid chain-id, uppercases", "ETHERMINT_1-1", true, nil, }, { - "invalid chain-id, mixed cases", "Ethermint-1", true, nil, + "invalid chain-id, mixed cases", "Ethermint_1-1", true, nil, }, { - "invalid chain-id, special chars", "$&*#!-1", true, nil, + "invalid chain-id, special chars", "$&*#!_1-1", true, nil, }, { - "invalid epoch, cannot start with 0", "ethermint-001", true, nil, + "invalid eip155 chain-id, cannot start with 0", "ethermint_001-1", true, nil, }, { - "invalid epoch, cannot invalid base", "ethermint-0x212", true, nil, + "invalid eip155 chain-id, cannot invalid base", "ethermint_0x212-1", true, nil, }, { - "invalid epoch, non-integer", "ethermint-ethermint", true, nil, + "invalid eip155 chain-id, non-integer", "ethermint_ethermint_9000-1", true, nil, }, { - "invalid epoch, undefined", "ethermint-", true, nil, + "invalid epoch, undefined", "ethermint_-", true, nil, }, { "blank chain ID", " ", true, nil, @@ -58,7 +64,10 @@ func TestParseChainID(t *testing.T) { "empty chain ID", "", true, nil, }, { - "long chain-id", "ethermint-" + strings.Repeat("1", 40), true, nil, + "empty content for chain id, eip155 and epoch numbers", "_-", true, nil, + }, + { + "long chain-id", "ethermint_" + strings.Repeat("1", 40) + "-1", true, nil, }, } @@ -67,9 +76,12 @@ func TestParseChainID(t *testing.T) { if tc.expError { require.Error(t, err, tc.name) require.Nil(t, chainIDEpoch) + + require.False(t, IsValidChainID(tc.chainID), tc.name) } else { require.NoError(t, err, tc.name) require.Equal(t, tc.expInt, chainIDEpoch, tc.name) + require.True(t, IsValidChainID(tc.chainID)) } } } diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index 0a1e143f4c..ed063d8dcb 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -41,7 +41,7 @@ func (suite *EvmTestSuite) SetupTest() { checkTx := false suite.app = app.Setup(checkTx) - suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()}) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint_9000-1", Time: time.Now().UTC()}) suite.app.EvmKeeper.WithContext(suite.ctx) suite.handler = evm.NewHandler(suite.app.EvmKeeper) suite.codec = suite.app.AppCodec() diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index 44917cac9b..abe1319969 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -69,7 +69,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.app = app.Setup(checkTx) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ Height: 1, - ChainID: "ethermint-1", + ChainID: "ethermint_9000-1", Time: time.Now().UTC(), ProposerAddress: suite.consAddress.Bytes(), }) diff --git a/x/evm/types/logs.go b/x/evm/types/logs.go index 4048ae8573..47f2e0d6ea 100644 --- a/x/evm/types/logs.go +++ b/x/evm/types/logs.go @@ -68,7 +68,7 @@ func (log *Log) Validate() error { return nil } -// ToEthereum returns the Ethereum type Log from a Ethermint-proto compatible Log. +// ToEthereum returns the Ethereum type Log from a Ethermint proto compatible Log. func (log *Log) ToEthereum() *ethtypes.Log { var topics []ethcmn.Hash // nolint: prealloc for i := range log.Topics {