diff --git a/bcm/blockchain.go b/bcm/blockchain.go index 5c4b59d06..28fde43fc 100644 --- a/bcm/blockchain.go +++ b/bcm/blockchain.go @@ -183,7 +183,7 @@ func (bc *Blockchain) GenesisDoc() genesis.GenesisDoc { } func (bc *Blockchain) ChainID() string { - return bc.genesisDoc.ChainID() + return bc.genesisDoc.GetChainID() } func (bc *Blockchain) LastBlockHeight() uint64 { diff --git a/cmd/burrow/commands/config_options.go b/cmd/burrow/commands/config_options.go index a840711a2..5171c9cd4 100644 --- a/cmd/burrow/commands/config_options.go +++ b/cmd/burrow/commands/config_options.go @@ -115,8 +115,8 @@ func (opts *configOptions) obtainBurrowConfig() (*config.BurrowConfig, error) { } if *opts.initMonikerOpt == "" { chainIDHeader := "" - if conf.GenesisDoc != nil && conf.GenesisDoc.ChainID() != "" { - chainIDHeader = conf.GenesisDoc.ChainID() + "_" + if conf.GenesisDoc != nil && conf.GenesisDoc.GetChainID() != "" { + chainIDHeader = conf.GenesisDoc.GetChainID() + "_" } if conf.ValidatorAddress != nil { // Set a default moniker... since we can at this stage of config completion and it is required for start diff --git a/config/config.go b/config/config.go index 9cb6e1746..421a8f75e 100644 --- a/config/config.go +++ b/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" + "github.com/alecthomas/jsonschema" "github.com/hyperledger/burrow/config/source" "github.com/hyperledger/burrow/consensus/tendermint" "github.com/hyperledger/burrow/crypto" @@ -32,6 +33,8 @@ type BurrowConfig struct { Logging *logconfig.LoggingConfig `json:",omitempty" toml:",omitempty"` } +var burrowConfigSchema = jsonschema.Reflect(&BurrowConfig{}) + func DefaultBurrowConfig() *BurrowConfig { return &BurrowConfig{ BurrowDir: ".burrow", diff --git a/config/config_test.go b/config/config_test.go index 16a2d9f0a..2569f9a72 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,10 +1,11 @@ package config import ( - "fmt" "testing" + "github.com/hyperledger/burrow/config/source" "github.com/hyperledger/burrow/genesis" + "github.com/stretchr/testify/require" ) func TestBurrowConfigSerialise(t *testing.T) { @@ -13,5 +14,9 @@ func TestBurrowConfigSerialise(t *testing.T) { ChainName: "Foo", }, } - fmt.Println(conf.JSONString()) + confOut := new(BurrowConfig) + jsonString := conf.JSONString() + err := source.FromJSONString(jsonString, confOut) + require.NoError(t, err) + require.Equal(t, jsonString, confOut.JSONString()) } diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go index d31acf0ce..512a57a10 100644 --- a/consensus/tendermint/tendermint.go +++ b/consensus/tendermint/tendermint.go @@ -102,7 +102,7 @@ func DeriveGenesisDoc(burrowGenesisDoc *genesis.GenesisDoc, appHash []byte) *tmT consensusParams.Block.TimeIotaMs = 1 return &tmTypes.GenesisDoc{ - ChainID: burrowGenesisDoc.ChainID(), + ChainID: burrowGenesisDoc.GetChainID(), GenesisTime: burrowGenesisDoc.GenesisTime, Validators: validators, AppHash: appHash, diff --git a/execution/exec/stream_event_test.go b/execution/exec/stream_event_test.go index b55fd3134..c37790bb9 100644 --- a/execution/exec/stream_event_test.go +++ b/execution/exec/stream_event_test.go @@ -17,7 +17,7 @@ import ( var genesisDoc, accounts, _ = genesis.NewDeterministicGenesis(345234523).GenesisDoc(10, 0) func TestTxExecution(t *testing.T) { - txe := NewTxExecution(txs.Enclose(genesisDoc.ChainID(), newCallTx(0, 1))) + txe := NewTxExecution(txs.Enclose(genesisDoc.GetChainID(), newCallTx(0, 1))) stack := new(TxStack) var txeOut *TxExecution @@ -38,7 +38,7 @@ func TestConsumeBlockExecution(t *testing.T) { height := int64(234242) be := &BlockExecution{ Header: &tmproto.Header{ - ChainID: genesisDoc.ChainID(), + ChainID: genesisDoc.GetChainID(), AppHash: crypto.Keccak256([]byte("hashily")), Time: time.Now(), Height: height, @@ -46,9 +46,9 @@ func TestConsumeBlockExecution(t *testing.T) { Height: uint64(height), } be.AppendTxs( - NewTxExecution(txs.Enclose(genesisDoc.ChainID(), newCallTx(0, 3))), - NewTxExecution(txs.Enclose(genesisDoc.ChainID(), newCallTx(0, 2))), - NewTxExecution(txs.Enclose(genesisDoc.ChainID(), newCallTx(2, 1))), + NewTxExecution(txs.Enclose(genesisDoc.GetChainID(), newCallTx(0, 3))), + NewTxExecution(txs.Enclose(genesisDoc.GetChainID(), newCallTx(0, 2))), + NewTxExecution(txs.Enclose(genesisDoc.GetChainID(), newCallTx(2, 1))), ) stack := NewBlockAccumulator() diff --git a/execution/execution.go b/execution/execution.go index ff1023603..e5ddd16f7 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -97,7 +97,7 @@ type Params struct { func ParamsFromGenesis(genesisDoc *genesis.GenesisDoc) Params { return Params{ - ChainID: genesisDoc.ChainID(), + ChainID: genesisDoc.GetChainID(), ProposalThreshold: genesisDoc.Params.ProposalThreshold, } } diff --git a/execution/execution_test.go b/execution/execution_test.go index 3902d9bcf..7954998c5 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -53,7 +53,7 @@ var logger = logging.NewNoopLogger() var deterministicGenesis = genesis.NewDeterministicGenesis(34059836243380576) var testGenesisDoc, testPrivAccounts, _ = deterministicGenesis. GenesisDoc(3, 1) -var testChainID = testGenesisDoc.ChainID() +var testChainID = testGenesisDoc.GetChainID() func TestSendFails(t *testing.T) { stateDB, err := dbm.NewDB("state", dbBackend, dbDir) diff --git a/genesis/genesis.go b/genesis/genesis.go index 8255ff1b6..387d78c7e 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -54,17 +54,18 @@ type params struct { } type GenesisDoc struct { - GenesisTime time.Time - ChainName string - AppHash binary.HexBytes `json:",omitempty" toml:",omitempty"` - Params params `json:",omitempty" toml:",omitempty"` - Salt []byte `json:",omitempty" toml:",omitempty"` + GenesisTime time.Time + ChainName string + // Ordinarily we derive this from the genesis hash but to support explicit Ethereum ChainID it may be set + ChainID string `json:",omitempty" toml:",omitempty"` + AppHash binary.HexBytes + Params params `json:",omitempty" toml:",omitempty"` + Salt []byte `json:",omitempty" toml:",omitempty"` GlobalPermissions permission.AccountPermissions Accounts []Account Validators []Validator // memo - hash []byte - chainID string + hash []byte } func (genesisDoc *GenesisDoc) GlobalPermissionsAccount() *acm.Account { @@ -115,11 +116,11 @@ func (genesisDoc *GenesisDoc) ShortHash() []byte { return genesisDoc.Hash()[:ShortHashSuffixBytes] } -func (genesisDoc *GenesisDoc) ChainID() string { - if genesisDoc.chainID == "" { - genesisDoc.chainID = fmt.Sprintf("%s-%X", genesisDoc.ChainName, genesisDoc.ShortHash()) +func (genesisDoc *GenesisDoc) GetChainID() string { + if genesisDoc.ChainID == "" { + genesisDoc.ChainID = fmt.Sprintf("%s-%X", genesisDoc.ChainName, genesisDoc.ShortHash()) } - return genesisDoc.chainID + return genesisDoc.ChainID } //------------------------------------------------------------ diff --git a/integration/governance/governance_test.go b/integration/governance/governance_test.go index a0fdca0cb..ff6f4fc97 100644 --- a/integration/governance/governance_test.go +++ b/integration/governance/governance_test.go @@ -226,12 +226,12 @@ func TestGovernance(t *testing.T) { }) setSequence(t, qcli, tx) - _, err := localSignAndBroadcastSync(t, tcli1, genesisDoc.ChainID(), genesisAccounts[0], tx) + _, err := localSignAndBroadcastSync(t, tcli1, genesisDoc.GetChainID(), genesisAccounts[0], tx) require.NoError(t, err) // Make it a different Tx hash so it can enter cache but keep sequence number tx.AccountUpdates[0].Amounts = balance.New().Power(power).Native(1) - _, err = localSignAndBroadcastSync(t, tcli2, genesisDoc.ChainID(), genesisAccounts[0], tx) + _, err = localSignAndBroadcastSync(t, tcli2, genesisDoc.GetChainID(), genesisAccounts[0], tx) require.Error(t, err) assert.Contains(t, err.Error(), "invalid sequence") }) diff --git a/integration/rpcinfo/info_server_test.go b/integration/rpcinfo/info_server_test.go index de6051f35..1c216fbd9 100644 --- a/integration/rpcinfo/info_server_test.go +++ b/integration/rpcinfo/info_server_test.go @@ -54,7 +54,7 @@ func TestInfoServer(t *testing.T) { resp, err := infoclient.Status(rpcClient) require.NoError(t, err) assert.Contains(t, resp.GetNodeInfo().GetMoniker(), "node") - assert.Equal(t, rpctest.GenesisDoc.ChainID(), resp.NodeInfo.Network, + assert.Equal(t, rpctest.GenesisDoc.GetChainID(), resp.NodeInfo.Network, "ChainID should match NodeInfo.Network") }) diff --git a/integration/rpcquery/query_server_test.go b/integration/rpcquery/query_server_test.go index 5bf6e1fee..4914c2ad1 100644 --- a/integration/rpcquery/query_server_test.go +++ b/integration/rpcquery/query_server_test.go @@ -31,7 +31,7 @@ func TestQueryServer(t *testing.T) { status, err := cli.Status(context.Background(), &rpcquery.StatusParam{}) require.NoError(t, err) assert.Equal(t, rpctest.PrivateAccounts[0].GetPublicKey(), status.ValidatorInfo.PublicKey) - assert.Equal(t, rpctest.GenesisDoc.ChainID(), status.ChainID) + assert.Equal(t, rpctest.GenesisDoc.GetChainID(), status.ChainID) for i := 0; i < 3; i++ { // Unless we get lucky this is an error _, err = cli.Status(context.Background(), &rpcquery.StatusParam{ diff --git a/integration/rpctest/helpers.go b/integration/rpctest/helpers.go index d25f4a260..dc9064a58 100644 --- a/integration/rpctest/helpers.go +++ b/integration/rpctest/helpers.go @@ -154,7 +154,7 @@ func MakeDefaultCallTx(t *testing.T, client rpc.Client, addr *crypto.Address, co fee uint64) *txs.Envelope { sequence := GetSequence(t, client, PrivateAccounts[0].GetAddress()) tx := payload.NewCallTxWithSequence(PrivateAccounts[0].GetPublicKey(), addr, code, amt, gasLim, fee, sequence+1) - txEnv := txs.Enclose(GenesisDoc.ChainID(), tx) + txEnv := txs.Enclose(GenesisDoc.GetChainID(), tx) require.NoError(t, txEnv.Sign(PrivateAccounts[0])) return txEnv } diff --git a/integration/rpctransact/transact_server_test.go b/integration/rpctransact/transact_server_test.go index a66d77c13..826a4cdef 100644 --- a/integration/rpctransact/transact_server_test.go +++ b/integration/rpctransact/transact_server_test.go @@ -62,7 +62,7 @@ func TestTransactServer(t *testing.T) { }) require.NoError(t, err) amount := uint64(2123) - txEnv := txs.Enclose(rpctest.GenesisDoc.ChainID(), &payload.SendTx{ + txEnv := txs.Enclose(rpctest.GenesisDoc.GetChainID(), &payload.SendTx{ Inputs: []*payload.TxInput{{ Address: inputAddress, Sequence: acc.Sequence + 1, @@ -120,7 +120,7 @@ func TestTransactServer(t *testing.T) { // We should see the sign bytes embedded if !assert.Contains(t, string(bs), fmt.Sprintf("{\"ChainID\":\"%s\",\"Type\":\"CallTx\","+ "\"Payload\":{\"Input\":{\"Address\":\"E80BB91C2F0F4C3C39FC53E89BF8416B219BE6E4\",\"Amount\":230},"+ - "\"Data\":\"0203060403\"}}", rpctest.GenesisDoc.ChainID())) { + "\"Data\":\"0203060403\"}}", rpctest.GenesisDoc.GetChainID())) { fmt.Println(string(bs)) } }) diff --git a/rpc/web3/eth_service.go b/rpc/web3/eth_service.go index 5ae15e8d3..75362739e 100644 --- a/rpc/web3/eth_service.go +++ b/rpc/web3/eth_service.go @@ -45,6 +45,7 @@ type EthService struct { keyClient keys.KeyClient keyStore *keys.FilesystemKeyStore config *tmConfig.Config + chainID *big.Int logger *logging.Logger } @@ -63,16 +64,18 @@ func NewEthService( keyClient := keys.NewLocalKeyClient(keyStore, logger) return &EthService{ - accounts, - events, - blockchain, - validators, - nodeView, - trans, - keyClient, - keyStore, - tmConfig.DefaultConfig(), - logger, + accounts: accounts, + events: events, + blockchain: blockchain, + validators: validators, + nodeView: nodeView, + trans: trans, + keyClient: keyClient, + keyStore: keyStore, + config: tmConfig.DefaultConfig(), + // Ethereum expects ChainID to be an integer value + chainID: crypto.GetEthChainID(blockchain.ChainID()), + logger: logger, } } @@ -119,7 +122,7 @@ func (srv *EthService) NetPeerCount() (*NetPeerCountResult, error) { // this is typically a small int (where 1 == Ethereum mainnet) func (srv *EthService) NetVersion() (*NetVersionResult, error) { return &NetVersionResult{ - ChainID: crypto.GetEthChainID(srv.blockchain.ChainID()).String(), + ChainID: HexEncoder.BigInt(srv.chainID), }, nil } @@ -133,7 +136,7 @@ func (srv *EthService) EthProtocolVersion() (*EthProtocolVersionResult, error) { // EthChainId returns the chainID func (srv *EthService) EthChainId() (*EthChainIdResult, error) { return &EthChainIdResult{ - ChainId: srv.blockchain.ChainID(), + ChainId: HexEncoder.BigInt(srv.chainID), }, nil } @@ -568,7 +571,7 @@ func (srv *EthService) EthSendRawTransaction(req *EthSendRawTransactionParams) ( return nil, d.Err() } - rawTx := txs.NewEthRawTx(srv.blockchain.ChainID()) + rawTx := txs.NewEthRawTx(srv.chainID) err := rlp.Decode(data, rawTx) if err != nil { return nil, err diff --git a/rpc/web3/eth_service_test.go b/rpc/web3/eth_service_test.go index e8bd75c2b..e3881f9cb 100644 --- a/rpc/web3/eth_service_test.go +++ b/rpc/web3/eth_service_test.go @@ -8,10 +8,6 @@ import ( "testing" "time" - "github.com/hyperledger/burrow/rpc/web3" - "github.com/hyperledger/burrow/txs" - "github.com/hyperledger/burrow/txs/payload" - "github.com/hyperledger/burrow/acm/balance" "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/execution/evm/abi" @@ -20,17 +16,23 @@ import ( "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/project" "github.com/hyperledger/burrow/rpc" + "github.com/hyperledger/burrow/rpc/web3" + "github.com/hyperledger/burrow/txs" + "github.com/hyperledger/burrow/txs/payload" "github.com/stretchr/testify/require" ) var d = new(web3.HexDecoder).Must() +// Check we can force set a decimal ChainID +const chainID = "15321" + func TestWeb3Service(t *testing.T) { ctx := context.Background() genesisAccounts := integration.MakePrivateAccounts("burrow", 1) genesisAccounts = append(genesisAccounts, integration.MakeEthereumAccounts("ethereum", 3)...) genesisDoc := integration.TestGenesisDoc(genesisAccounts, 0) - + genesisDoc.ChainID = chainID config, _ := integration.NewTestConfig(genesisDoc) logger := logging.NewNoopLogger() kern, err := integration.TestKernel(genesisAccounts[0], genesisAccounts, config) @@ -91,7 +93,7 @@ func TestWeb3Service(t *testing.T) { t.Run("NetVersion", func(t *testing.T) { result, err := eth.NetVersion() require.NoError(t, err) - require.Equal(t, crypto.GetEthChainID(genesisDoc.ChainID()).String(), result.ChainID) + require.Equal(t, web3.HexEncoder.BigInt(crypto.GetEthChainID(genesisDoc.GetChainID())), result.ChainID) }) t.Run("EthProtocolVersion", func(t *testing.T) { @@ -104,7 +106,7 @@ func TestWeb3Service(t *testing.T) { result, err := eth.EthChainId() require.NoError(t, err) doc := config.GenesisDoc - require.Equal(t, doc.ChainID(), result.ChainId) + require.Equal(t, web3.HexEncoder.BigInt(crypto.GetEthChainID(doc.GetChainID())), result.ChainId) }) }) @@ -118,7 +120,7 @@ func TestWeb3Service(t *testing.T) { before := acc.GetBalance() t.Run("EthSendRawTransaction", func(t *testing.T) { - txEnv := txs.Enclose(genesisDoc.ChainID(), &payload.CallTx{ + txEnv := txs.Enclose(chainID, &payload.CallTx{ Input: &payload.TxInput{ Address: sender.GetAddress(), Amount: 1, diff --git a/rpc/web3/hex.go b/rpc/web3/hex.go index 4822ce0a8..24bbe6792 100644 --- a/rpc/web3/hex.go +++ b/rpc/web3/hex.go @@ -99,7 +99,7 @@ func (e *hexEncoder) BytesTrim(bs []byte) string { return "0x" + str } -func (e *hexEncoder) BigInt(x big.Int) string { +func (e *hexEncoder) BigInt(x *big.Int) string { return e.BytesTrim(x.Bytes()) } diff --git a/txs/ethereum_tx.go b/txs/ethereum_tx.go index 070a22929..79ca23918 100644 --- a/txs/ethereum_tx.go +++ b/txs/ethereum_tx.go @@ -26,8 +26,8 @@ type EthRawTx struct { chainID *big.Int } -func NewEthRawTx(chainID string) *EthRawTx { - return &EthRawTx{chainID: crypto.GetEthChainID(chainID)} +func NewEthRawTx(chainID *big.Int) *EthRawTx { + return &EthRawTx{chainID: chainID} } func EthRawTxFromEnvelope(txEnv *Envelope) (*EthRawTx, error) {