diff --git a/accounts/accounts.go b/accounts/accounts.go index 7a14e4e3e5d5..b95049ecae50 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -39,6 +39,7 @@ const ( MimetypeDataWithValidator = "data/validator" MimetypeTypedData = "data/typed" MimetypeClique = "application/x-clique-header" + MimetypeAura = "application/x-aura-header" MimetypeTextPlain = "text/plain" ) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index b98597e307e6..02331d3c1621 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -163,6 +163,7 @@ The export-preimages command export hash preimages to an RLP encoded stream`, utils.RinkebyFlag, utils.TxLookupLimitFlag, utils.GoerliFlag, + utils.LuksoFlag, utils.YoloV1Flag, utils.LegacyTestnetFlag, }, diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go index e2f733f844a4..74282491496f 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -138,6 +138,8 @@ func remoteConsole(ctx *cli.Context) error { path = filepath.Join(path, "goerli") } else if ctx.GlobalBool(utils.YoloV1Flag.Name) { path = filepath.Join(path, "yolo-v1") + } else if ctx.GlobalBool(utils.LuksoFlag.Name) { + path = filepath.Join(path, "lukso") } } endpoint = fmt.Sprintf("%s/geth.ipc", path) diff --git a/cmd/geth/genesis_test.go b/cmd/geth/genesis_test.go index ee3991acd1bc..8ba898c473e3 100644 --- a/cmd/geth/genesis_test.go +++ b/cmd/geth/genesis_test.go @@ -68,6 +68,61 @@ var customGenesisTests = []struct { }, } +var customAuraGenesisTests = []struct { + genesis string + query string + result string +}{ + // Aura Genesis file with specific chain configurations to test + { + genesis: ` + { + "name": "AuthorityRound", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators" : { + "list": [ + "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "0xafe443af9d1504de4c2d486356c421c160fdd7b1" + ] + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2323" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + } + }`, + query: "eth.getBlock(0).hash", + result: "0x2778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788c", + }, +} + // Tests that initializing Geth with a custom genesis block and chain definitions // work properly. func TestCustomGenesis(t *testing.T) { @@ -92,3 +147,27 @@ func TestCustomGenesis(t *testing.T) { geth.ExpectExit() } } + +// Tests that initializing Geth with a custom aura genesis block and chain definitions +// work properly. +func TestAuraGenesisBlock(t *testing.T) { + for i, tt := range customAuraGenesisTests { + // Create a temporary data directory to use and inspect later + datadir := tmpdir(t) + defer os.RemoveAll(datadir) + + // Initialize the data directory with the custom genesis block + json := filepath.Join(datadir, "genesis.json") + if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil { + t.Fatalf("test %d: failed to write aura genesis file: %v", i, err) + } + runGeth(t, "--nousb", "--datadir", datadir, "init", json).WaitExit() + // Query the custom genesis block + geth := runGeth(t, "--nousb", + "--datadir", datadir, "--maxpeers", "0", "--port", "0", + "--nodiscover", "--nat", "none", "--ipcdisable", + "--exec", tt.query, "console") + geth.ExpectRegexp(tt.result) + geth.ExpectExit() + } +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 677a19eed2cd..eb10e945b7ac 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -144,6 +144,7 @@ var ( utils.RopstenFlag, utils.RinkebyFlag, utils.GoerliFlag, + utils.LuksoFlag, utils.YoloV1Flag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, @@ -295,6 +296,9 @@ func prepare(ctx *cli.Context) { case ctx.GlobalIsSet(utils.GoerliFlag.Name): log.Info("Starting Geth on Görli testnet...") + case ctx.GlobalIsSet(utils.LuksoFlag.Name): + log.Info("Starting Geth on Lukso testnet...") + case ctx.GlobalIsSet(utils.DeveloperFlag.Name): log.Info("Starting Geth in ephemeral dev mode...") @@ -304,7 +308,7 @@ func prepare(ctx *cli.Context) { // If we're a full node on mainnet without --cache specified, bump default cache allowance if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) { // Make sure we're not on any supported preconfigured testnet either - if !ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name) && !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) { + if !ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name) && !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.LuksoFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) { // Nope, we're really on mainnet. Bump that cache up! log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096) ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096)) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 334a729c24d8..87ea31ec0bd1 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -41,6 +41,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.SmartCardDaemonPathFlag, utils.NetworkIdFlag, utils.GoerliFlag, + utils.LuksoFlag, utils.RinkebyFlag, utils.YoloV1Flag, utils.RopstenFlag, diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go index 5d9ef46523e0..143ea91a2c4d 100644 --- a/cmd/puppeth/module_node.go +++ b/cmd/puppeth/module_node.go @@ -32,7 +32,7 @@ import ( // nodeDockerfile is the Dockerfile required to run an Ethereum node. var nodeDockerfile = ` -FROM ethereum/client-go:latest +FROM {{.DockerImage}} ADD genesis.json /genesis.json {{if .Unlock}} @@ -82,7 +82,14 @@ services: // deployNode deploys a new Ethereum node container to a remote machine via SSH, // docker and docker-compose. If an instance with the specified network name // already exists there, it will be overwritten! -func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos, nocache bool) ([]byte, error) { +func deployNode( + client *sshClient, + network string, + bootnodes []string, + config *nodeInfos, + nocache bool, + dockerImage string, +) ([]byte, error) { kind := "sealnode" if config.keyJSON == "" && config.etherbase == "" { kind = "bootnode" @@ -98,18 +105,19 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n } dockerfile := new(bytes.Buffer) template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{ - "NetworkID": config.network, - "Port": config.port, - "IP": client.address, - "Peers": config.peersTotal, - "LightFlag": lightFlag, - "Bootnodes": strings.Join(bootnodes, ","), - "Ethstats": config.ethstats, - "Etherbase": config.etherbase, - "GasTarget": uint64(1000000 * config.gasTarget), - "GasLimit": uint64(1000000 * config.gasLimit), - "GasPrice": uint64(1000000000 * config.gasPrice), - "Unlock": config.keyJSON != "", + "DockerImage": dockerImage, + "NetworkID": config.network, + "Port": config.port, + "IP": client.address, + "Peers": config.peersTotal, + "LightFlag": lightFlag, + "Bootnodes": strings.Join(bootnodes, ","), + "Ethstats": config.ethstats, + "Etherbase": config.etherbase, + "GasTarget": uint64(1000000 * config.gasTarget), + "GasLimit": uint64(1000000 * config.gasLimit), + "GasPrice": uint64(1000000000 * config.gasPrice), + "Unlock": config.keyJSON != "", }) files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go index 40327d25d226..f151ba4c355f 100644 --- a/cmd/puppeth/wizard_genesis.go +++ b/cmd/puppeth/wizard_genesis.go @@ -59,6 +59,21 @@ func (w *wizard) makeGenesis() { fmt.Println("Which consensus engine to use? (default = clique)") fmt.Println(" 1. Ethash - proof-of-work") fmt.Println(" 2. Clique - proof-of-authority") + fmt.Println(" 3. Aura - proof-of-authority") + + readSigners := func() (signers []common.Address) { + for { + if address := w.readAddress(); address != nil { + signers = append(signers, *address) + continue + } + if len(signers) > 0 { + break + } + } + + return + } choice := w.read() switch { @@ -83,15 +98,8 @@ func (w *wizard) makeGenesis() { fmt.Println("Which accounts are allowed to seal? (mandatory at least one)") var signers []common.Address - for { - if address := w.readAddress(); address != nil { - signers = append(signers, *address) - continue - } - if len(signers) > 0 { - break - } - } + signers = readSigners() + // Sort the signers and embed into the extra-data section for i := 0; i < len(signers); i++ { for j := i + 1; j < len(signers); j++ { @@ -104,7 +112,27 @@ func (w *wizard) makeGenesis() { for i, signer := range signers { copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) } + case "3" == choice: + genesis.Difficulty = big.NewInt(1) + genesis.Config.Aura = ¶ms.AuraConfig{ + Period: 5, + Epoch: 30000, + } + fmt.Println() + fmt.Println("How many seconds should round take? (default = 5)") + genesis.Config.Aura.Period = uint64(w.readDefaultInt(5)) + + // We also need the initial list of signers + fmt.Println() + fmt.Println("Which accounts are allowed to seal? (mandatory at least one)") + + signers := readSigners() + auraConfig := genesis.Config.Aura + auraConfig.Authorities = make([]common.Address, len(signers)) + for i, signer := range signers { + genesis.Config.Aura.Authorities[i] = signer + } default: log.Crit("Invalid consensus engine choice", "choice", choice) } diff --git a/cmd/puppeth/wizard_node.go b/cmd/puppeth/wizard_node.go index 2bae33214283..69846e326de2 100644 --- a/cmd/puppeth/wizard_node.go +++ b/cmd/puppeth/wizard_node.go @@ -118,7 +118,8 @@ func (w *wizard) deployNode(boot bool) { fmt.Printf("What address should the miner use? (default = %s)\n", infos.etherbase) infos.etherbase = w.readDefaultAddress(common.HexToAddress(infos.etherbase)).Hex() } - } else if w.conf.Genesis.Config.Clique != nil { + } + if w.conf.Genesis.Config.Clique != nil || w.conf.Genesis.Config.Aura != nil { // If a previous signer was already set, offer to reuse it if infos.keyJSON != "" { if key, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil { @@ -167,7 +168,13 @@ func (w *wizard) deployNode(boot bool) { fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n") nocache = w.readDefaultYesNo(false) } - if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { + + dockerImage := "ethereum/client-go:latest" + fmt.Println() + fmt.Printf("Please provide geth docker image you would like to use (y/n)? (default = %s)\n", dockerImage) + dockerImage = w.readDefaultString(dockerImage) + + if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache, dockerImage); err != nil { log.Error("Failed to deploy Ethereum node container", "err", err) if len(out) > 0 { fmt.Printf("%s\n", out) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2c57e533edd7..344d9c132723 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -20,6 +20,7 @@ package utils import ( "crypto/ecdsa" "fmt" + "github.com/ethereum/go-ethereum/consensus/aura" "io" "io/ioutil" "math/big" @@ -135,6 +136,10 @@ var ( Name: "goerli", Usage: "Görli network: pre-configured proof-of-authority test network", } + LuksoFlag = cli.BoolFlag{ + Name: "luksoAura", + Usage: "Lukso aura network: pre-configured proof-of-authority(Aura) test network", + } YoloV1Flag = cli.BoolFlag{ Name: "yolov1", Usage: "YOLOv1 network: pre-configured proof-of-authority shortlived test network.", @@ -744,6 +749,9 @@ func MakeDataDir(ctx *cli.Context) string { if ctx.GlobalBool(GoerliFlag.Name) { return filepath.Join(path, "goerli") } + if ctx.GlobalBool(LuksoFlag.Name) { + return filepath.Join(path, "lukso") + } if ctx.GlobalBool(YoloV1Flag.Name) { return filepath.Join(path, "yolo-v1") } @@ -803,6 +811,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls = params.RinkebyBootnodes case ctx.GlobalBool(GoerliFlag.Name): urls = params.GoerliBootnodes + case ctx.GlobalBool(LuksoFlag.Name): + urls = params.LuksoBootnodes case ctx.GlobalBool(YoloV1Flag.Name): urls = params.YoloV1Bootnodes case cfg.BootstrapNodes != nil: @@ -839,6 +849,8 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { urls = params.RinkebyBootnodes case ctx.GlobalBool(GoerliFlag.Name): urls = params.GoerliBootnodes + case ctx.GlobalBool(LuksoFlag.Name): + urls = params.LuksoBootnodes case ctx.GlobalBool(YoloV1Flag.Name): urls = params.YoloV1Bootnodes case cfg.BootstrapNodesV5 != nil: @@ -1270,6 +1282,8 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) { cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir(): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") + case ctx.GlobalBool(LuksoFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "lukso") case ctx.GlobalBool(YoloV1Flag.Name) && cfg.DataDir == node.DefaultDataDir(): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v1") } @@ -1484,7 +1498,7 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { // Avoid conflicting network flags - CheckExclusive(ctx, DeveloperFlag, LegacyTestnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV1Flag) + CheckExclusive(ctx, DeveloperFlag, LegacyTestnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, LuksoFlag, YoloV1Flag) CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light") CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag) @@ -1604,6 +1618,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { } cfg.Genesis = core.DefaultGoerliGenesisBlock() setDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) + case ctx.GlobalBool(LuksoFlag.Name): + if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + cfg.NetworkId = 5 + } + cfg.Genesis = core.DefaultLuksoGenesisBlock() + setDNSDiscoveryDefaults(cfg, params.LuksoGenesisHash) case ctx.GlobalBool(YoloV1Flag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { cfg.NetworkId = 133519467574833 // "yolov1" @@ -1792,6 +1812,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis { genesis = core.DefaultRinkebyGenesisBlock() case ctx.GlobalBool(GoerliFlag.Name): genesis = core.DefaultGoerliGenesisBlock() + case ctx.GlobalBool(LuksoFlag.Name): + genesis = core.DefaultLuksoGenesisBlock() case ctx.GlobalBool(YoloV1Flag.Name): genesis = core.DefaultYoloV1GenesisBlock() case ctx.GlobalBool(DeveloperFlag.Name): @@ -1811,6 +1833,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.B var engine consensus.Engine if config.Clique != nil { engine = clique.New(config.Clique, chainDb) + } else if config.Aura != nil { + engine = aura.New(config.Aura, chainDb) } else { engine = ethash.NewFaker() if !ctx.GlobalBool(FakePoWFlag.Name) { diff --git a/common/bindings/aura_test.go b/common/bindings/aura_test.go new file mode 100644 index 000000000000..5787f9190109 --- /dev/null +++ b/common/bindings/aura_test.go @@ -0,0 +1,50 @@ +package bindings + +import ( + "encoding/json" + "github.com/ethereum/go-ethereum/core" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" +) + +func TestNewParityChainSpec(t *testing.T) { + parityFixture, err := ioutil.ReadFile("./fixtures/block-0-parity.json") + assert.Nil(t, err) + + // Other stuff is not needed, I guess hash is really what matters for now + // If you want to strict compare you can compare indented bytes instead + blockStruct := struct { + Hash string `json:"hash"` + }{} + + err = json.Unmarshal(parityFixture, &blockStruct) + assert.Nil(t, err) + + parityGenesis, err := ioutil.ReadFile("./fixtures/parity-aura.json") + assert.Nil(t, err) + var parityChainSpec ParityChainSpec + err = json.Unmarshal(parityGenesis, &parityChainSpec) + + t.Run("Genesis file from geth should produce proper spec in openethereum", func(t *testing.T) { + var genesisGeth core.Genesis + gethGenesisFixture, err := ioutil.ReadFile("./fixtures/geth-aura.json") + assert.Nil(t, err) + err = json.Unmarshal(gethGenesisFixture, &genesisGeth) + assert.Nil(t, err) + spec, err := NewParityChainSpec("AuthorityRound", &genesisGeth, nil) + assert.Nil(t, err) + assert.NotNil(t, spec.Genesis) + assert.NotNil(t, spec.Name) + assert.NotNil(t, spec.Accounts) + assert.NotNil(t, spec.Engine) + assert.NotNil(t, spec.Params) + assert.NotNil(t, spec.Engine.AuthorityRound) + chainSpec, err := json.Marshal(spec) + assert.Nil(t, err) + // This little guy can be used to print the output: + //assert.Equal(t, "", fmt.Sprintf("%s", chainSpec)) + assert.NotEqual(t, "", chainSpec) + assert.Equal(t, parityChainSpec, *spec) + }) +} diff --git a/common/bindings/fixtures/block-0-parity.json b/common/bindings/fixtures/block-0-parity.json new file mode 100644 index 000000000000..6ba199a63ee9 --- /dev/null +++ b/common/bindings/fixtures/block-0-parity.json @@ -0,0 +1,24 @@ +{ + "author": "0x0000000000000000000000000000000000000000", + "difficulty": 131072, + "extraData": "0x", + "gasLimit": 2236962, + "gasUsed": 0, + "hash": "0x9644f48c969c5d4fb16d24444b10cac9a3d0a4684d0170d46739700de25edae4", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "number": 0, + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields": ["0xa00000000000000000000000000000000000000000000000000000000000000000", "0x880000000000000000"], + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature": "", + "size": 507, + "stateRoot": "0x40cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133b", + "step": "", + "timestamp": 1, + "totalDifficulty": 131072, + "transactions": [], + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] +} \ No newline at end of file diff --git a/common/bindings/fixtures/geth-aura.json b/common/bindings/fixtures/geth-aura.json new file mode 100644 index 000000000000..a69ea8383ba7 --- /dev/null +++ b/common/bindings/fixtures/geth-aura.json @@ -0,0 +1,43 @@ +{ + "name": "AuthorityRound", + "config": { + "chainId": 8995, + "aura": { + "period": 5, + "authorities": [ + "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "0xafe443af9d1504de4c2d486356c421c160fdd7b1" + ] + } + }, + "params": { + "gasLimitBoundDivisor": 1024, + "maximumExtraDataSize": 32, + "minGasLimit": 5000, + "networkID": 8995 + }, + "seal": { + "step": "0x", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "difficulty": "0x20000", + "coinbase": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222", + "alloc": { + "0x0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000004": { + "balance": "0x1" + } + } +} \ No newline at end of file diff --git a/common/bindings/fixtures/parity-aura.json b/common/bindings/fixtures/parity-aura.json new file mode 100644 index 000000000000..e1dab4894306 --- /dev/null +++ b/common/bindings/fixtures/parity-aura.json @@ -0,0 +1,86 @@ +{ + "name": "AuthorityRound", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "list": [ + "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "0xafe443af9d1504de4c2d486356c421c160fdd7b1" + ] + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x2323" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x00", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } +} \ No newline at end of file diff --git a/common/bindings/parity.go b/common/bindings/parity.go new file mode 100644 index 000000000000..105b97eb7da1 --- /dev/null +++ b/common/bindings/parity.go @@ -0,0 +1,220 @@ +package bindings + +import ( + "bytes" + "errors" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/params" + "math" + "math/big" + "strconv" +) + +// ParityChainSpec is the chain specification format used by Parity. +type ParityChainSpec struct { + Name string `json:"name"` + Engine struct { + Ethash *Ethash `json:"ethash,omitempty"` + AuthorityRound *AuthorityRound `json:"authorityRound,omitempty"` + } `json:"engine"` + + Params struct { + MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` + MinGasLimit hexutil.Uint64 `json:"minGasLimit"` + GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"` + NetworkID hexutil.Uint64 `json:"networkID"` + MaxCodeSize *big.Int `json:"maxCodeSize"` + EIP155Transition *big.Int `json:"eip155Transition, omitempty"` + EIP98Transition *big.Float `json:"eip98Transition, omitempty"` + EIP140Transition *big.Int `json:"eip140Transition, omitempty"` + EIP211Transition *big.Int `json:"eip211Transition, omitempty"` + EIP214Transition *big.Int `json:"eip214Transition, omitempty"` + EIP658Transition *big.Int `json:"eip658Transition, omitempty"` + } `json:"params"` + + Genesis struct { + Seal struct { + AuthorityRound core.Seal `json:"authorityRound"` + } `json:"seal"` + + Difficulty *hexutil.Big `json:"difficulty"` + Author common.Address `json:"author"` + Timestamp uint64 `json:"timestamp"` + ParentHash common.Hash `json:"parentHash"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + } `json:"genesis"` + + Nodes []string `json:"nodes"` + Accounts map[common.Address]*parityChainSpecAccount `json:"accounts"` +} + +type Ethash struct { + Params struct { + MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` + DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"` + DurationLimit *hexutil.Big `json:"durationLimit"` + BlockReward *hexutil.Big `json:"blockReward"` + HomesteadTransition *big.Int `json:"homesteadTransition, omitempty"` + EIP150Transition *big.Int `json:"eip150Transition, omitempty"` + EIP160Transition *big.Int `json:"eip160Transition, omitempty"` + EIP161abcTransition *big.Int `json:"eip161abcTransition, omitempty"` + EIP161dTransition *big.Int `json:"eip161dTransition, omitempty"` + EIP649Reward *hexutil.Big `json:"eip649Reward, omitempty"` + EIP100bTransition *big.Int `json:"eip100bTransition, omitempty"` + EIP649Transition *big.Int `json:"eip649Transition, omitempty"` + } `json:"params"` +} + +type AuthorityRound struct { + Params struct { + StepDuration string `json:"stepDuration, omitempty"` + Validators struct { + List []common.Address `json:"list, omitempty"` + } `json:"validators, omitempty"` + } `json:"params, omitempty"` +} + +// parityChainSpecAccount is the prefunded genesis account and/or precompiled +// contract definition. +type parityChainSpecAccount struct { + Balance string `json:"balance"` + Nonce uint64 `json:"nonce,omitempty"` + Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"` +} + +// parityChainSpecBuiltin is the precompiled contract definition. +type parityChainSpecBuiltin struct { + Name string `json:"name,omitempty"` + ActivateAt uint64 `json:"activate_at,omitempty"` + Pricing *parityChainSpecPricing `json:"pricing,omitempty"` +} + +// parityChainSpecPricing represents the different pricing models that builtin +// contracts might advertise using. +type parityChainSpecPricing struct { + Linear *parityChainSpecLinearPricing `json:"linear,omitempty"` + ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"` + AltBnPairing *parityChainSpecAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"` +} + +type parityChainSpecLinearPricing struct { + Base uint64 `json:"base"` + Word uint64 `json:"word"` +} + +type parityChainSpecModExpPricing struct { + Divisor uint64 `json:"divisor"` +} + +type parityChainSpecAltBnPairingPricing struct { + Base uint64 `json:"base"` + Pair uint64 `json:"pair"` +} + +// newParityChainSpec converts a go-ethereum genesis block into a Parity specific +// chain specification format. +func NewParityChainSpec(network string, genesis *core.Genesis, bootnodes []string) (*ParityChainSpec, error) { + // Only ethash is currently supported between go-ethereum and Parity + if genesis.Config.Ethash == nil && nil == genesis.Config.Aura { + return nil, errors.New("unsupported consensus engine") + } + // Reconstruct the chain spec in Parity's format + spec := &ParityChainSpec{ + Name: network, + Nodes: bootnodes, + } + + if nil != genesis.Config.Ethash { + spec.Engine.Ethash = &Ethash{} + spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) + spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor) + spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit) + spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward) + spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock + spec.Engine.Ethash.Params.EIP150Transition = genesis.Config.EIP150Block + spec.Engine.Ethash.Params.EIP160Transition = genesis.Config.EIP155Block + spec.Engine.Ethash.Params.EIP161abcTransition = genesis.Config.EIP158Block + spec.Engine.Ethash.Params.EIP161dTransition = genesis.Config.EIP158Block + spec.Engine.Ethash.Params.EIP649Reward = (*hexutil.Big)(ethash.ByzantiumBlockReward) + spec.Engine.Ethash.Params.EIP100bTransition = genesis.Config.ByzantiumBlock + spec.Engine.Ethash.Params.EIP649Transition = genesis.Config.ByzantiumBlock + } + + if nil != genesis.Config.Aura { + spec.Engine.AuthorityRound = &AuthorityRound{} + authorityRoundEngine := spec.Engine.AuthorityRound + authorityRoundEngine.Params.Validators.List = genesis.Config.Aura.Authorities + authorityRoundEngine.Params.StepDuration = strconv.FormatUint(genesis.Config.Aura.Period, 10) + } + + spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) + spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) + spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor) + spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) + if nil != genesis.Config.EIP155Block { + spec.Params.EIP155Transition = genesis.Config.EIP155Block + } + if nil != genesis.Config.Ethash { + spec.Params.EIP98Transition = big.NewFloat(math.MaxUint64) + } + if nil != genesis.Config.ByzantiumBlock { + spec.Params.EIP140Transition = genesis.Config.ByzantiumBlock + spec.Params.EIP211Transition = genesis.Config.ByzantiumBlock + spec.Params.EIP214Transition = genesis.Config.ByzantiumBlock + spec.Params.EIP658Transition = genesis.Config.ByzantiumBlock + } + + spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty) + spec.Genesis.Author = genesis.Coinbase + spec.Genesis.Timestamp = genesis.Timestamp + spec.Genesis.ParentHash = genesis.ParentHash + spec.Genesis.ExtraData = hexutil.Bytes{} + + if nil != genesis.ExtraData { + spec.Genesis.ExtraData = genesis.ExtraData + } + + spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit) + + spec.Accounts = make(map[common.Address]*parityChainSpecAccount) + for address, account := range genesis.Alloc { + spec.Accounts[address] = &parityChainSpecAccount{ + Balance: account.Balance.String(), + } + } + spec.Accounts[common.BytesToAddress([]byte{1})].Builtin = &parityChainSpecBuiltin{ + Name: "ecrecover", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}, + } + spec.Accounts[common.BytesToAddress([]byte{2})].Builtin = &parityChainSpecBuiltin{ + Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}}, + } + spec.Accounts[common.BytesToAddress([]byte{3})].Builtin = &parityChainSpecBuiltin{ + Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}}, + } + spec.Accounts[common.BytesToAddress([]byte{4})].Builtin = &parityChainSpecBuiltin{ + Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}}, + } + if genesis.Config.ByzantiumBlock != nil { + spec.Accounts[common.BytesToAddress([]byte{5})].Builtin = &parityChainSpecBuiltin{ + Name: "modexp", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}}, + } + spec.Accounts[common.BytesToAddress([]byte{6})].Builtin = &parityChainSpecBuiltin{ + Name: "alt_bn128_add", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}}, + } + spec.Accounts[common.BytesToAddress([]byte{7})].Builtin = &parityChainSpecBuiltin{ + Name: "alt_bn128_mul", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}}, + } + spec.Accounts[common.BytesToAddress([]byte{8})].Builtin = &parityChainSpecBuiltin{ + Name: "alt_bn128_pairing", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}}, + } + } + spec.Genesis.Seal.AuthorityRound = genesis.Seal + if bytes.Equal(genesis.Seal.Step, []byte{}) { + spec.Genesis.Seal.AuthorityRound.Step = []byte{0} + } + return spec, nil +} diff --git a/consensus/aura/api.go b/consensus/aura/api.go new file mode 100644 index 000000000000..190ab6d27aeb --- /dev/null +++ b/consensus/aura/api.go @@ -0,0 +1,177 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package aura + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" +) + +// API is a user facing RPC API to allow controlling the signer and voting +// mechanisms of the proof-of-authority scheme. +type API struct { + chain consensus.ChainHeaderReader + aura *Aura +} + +// GetSnapshot retrieves the state snapshot at a given block. +func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else { + header = api.chain.GetHeaderByNumber(uint64(number.Int64())) + } + // Ensure we have an actually valid block and return its snapshot + if header == nil { + return nil, errUnknownBlock + } + return api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) +} + +// GetSnapshotAtHash retrieves the state snapshot at a given block. +func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { + header := api.chain.GetHeaderByHash(hash) + if header == nil { + return nil, errUnknownBlock + } + return api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) +} + +// GetSigners retrieves the list of authorized signers at the specified block. +func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else { + header = api.chain.GetHeaderByNumber(uint64(number.Int64())) + } + // Ensure we have an actually valid block and return the signers from its snapshot + if header == nil { + return nil, errUnknownBlock + } + snap, err := api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) + if err != nil { + return nil, err + } + return snap.signers(), nil +} + +// GetSignersAtHash retrieves the list of authorized signers at the specified block. +func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) { + header := api.chain.GetHeaderByHash(hash) + if header == nil { + return nil, errUnknownBlock + } + snap, err := api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) + if err != nil { + return nil, err + } + return snap.signers(), nil +} + +// Proposals returns the current proposals the node tries to uphold and vote on. +func (api *API) Proposals() map[common.Address]bool { + api.aura.lock.RLock() + defer api.aura.lock.RUnlock() + + proposals := make(map[common.Address]bool) + for address, auth := range api.aura.proposals { + proposals[address] = auth + } + return proposals +} + +// Propose injects a new authorization proposal that the signer will attempt to +// push through. +func (api *API) Propose(address common.Address, auth bool) { + api.aura.lock.Lock() + defer api.aura.lock.Unlock() + + api.aura.proposals[address] = auth +} + +// Discard drops a currently running proposal, stopping the signer from casting +// further votes (either for or against). +func (api *API) Discard(address common.Address) { + api.aura.lock.Lock() + defer api.aura.lock.Unlock() + + delete(api.aura.proposals, address) +} + +type status struct { + InturnPercent float64 `json:"inturnPercent"` + SigningStatus map[common.Address]int `json:"sealerActivity"` + NumBlocks uint64 `json:"numBlocks"` +} + +// Status returns the status of the last N blocks, +// - the number of active signers, +// - the number of signers, +// - the percentage of in-turn blocks +func (api *API) Status() (*status, error) { + var ( + numBlocks = uint64(64) + header = api.chain.CurrentHeader() + diff = uint64(0) + optimals = 0 + ) + snap, err := api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) + if err != nil { + return nil, err + } + var ( + signers = snap.signers() + end = header.Number.Uint64() + start = end - numBlocks + ) + if numBlocks > end { + start = 1 + numBlocks = end - start + } + signStatus := make(map[common.Address]int) + for _, s := range signers { + signStatus[s] = 0 + } + for n := start; n < end; n++ { + h := api.chain.GetHeaderByNumber(n) + if h == nil { + return nil, fmt.Errorf("missing block %d", n) + } + if h.Difficulty.Cmp(diffInTurn) == 0 { + optimals++ + } + diff += h.Difficulty.Uint64() + sealer, err := api.aura.Author(h) + if err != nil { + return nil, err + } + signStatus[sealer]++ + } + return &status{ + InturnPercent: float64(100*optimals) / float64(numBlocks), + SigningStatus: signStatus, + NumBlocks: numBlocks, + }, nil +} diff --git a/consensus/aura/aura.go b/consensus/aura/aura.go new file mode 100644 index 000000000000..cfbd1033b2d7 --- /dev/null +++ b/consensus/aura/aura.go @@ -0,0 +1,793 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package Aura implements the proof-of-authority consensus engine. +package aura + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/p2p" + "io" + "io/ioutil" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" +) + +const ( + checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + inmemorySignatures = 4096 // Number of recent block signatures to keep in memory + + wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers +) + +// Aura proof-of-authority protocol constants. +var ( + epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes + + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal + + nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer + nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer. + + uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. + + diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures + diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures +) + +// Various error messages to mark blocks invalid. These should be private to +// prevent engine specific errors from being referenced in the remainder of the +// codebase, inherently breaking if the engine is swapped out. Please put common +// error types into the consensus package. +var ( + // errUnknownBlock is returned when the list of signers is requested for a block + // that is not part of the local blockchain. + errUnknownBlock = errors.New("unknown block") + + // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition + // block has a beneficiary set to non-zeroes. + errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero") + + // errInvalidVote is returned if a nonce value is something else that the two + // allowed constants of 0x00..0 or 0xff..f. + errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f") + + // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block + // has a vote nonce set to non-zeroes. + errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero") + + // errMissingVanity is returned if a block's extra-data section is shorter than + // 32 bytes, which is required to store the signer vanity. + errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") + + // errMissingSignature is returned if a block's extra-data section doesn't seem + // to contain a 65 byte secp256k1 signature. + errMissingSignature = errors.New("extra-data 65 byte signature suffix missing") + + // errExtraSigners is returned if non-checkpoint block contain signer data in + // their extra-data fields. + errExtraSigners = errors.New("non-checkpoint block contains extra signer list") + + // errInvalidValidatorSeal is returned if the extra data field length is not + // equal to the length of a seal + errInvalidExtraData = errors.New("extra data field in block header is invalid") + + // errInvalidCheckpointSigners is returned if a checkpoint block contains an + // invalid list of signers (i.e. non divisible by 20 bytes). + errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block") + + // errMismatchingCheckpointSigners is returned if a checkpoint block contains a + // list of signers different than the one the local node calculated. + errMismatchingCheckpointSigners = errors.New("mismatching signer list on checkpoint block") + + // errInvalidMixDigest is returned if a block's mix digest is non-zero. + errInvalidMixDigest = errors.New("non-zero mix digest") + + // errInvalidUncleHash is returned if a block contains an non-empty uncle list. + errInvalidUncleHash = errors.New("non empty uncle hash") + + // errInvalidDifficulty is returned if the difficulty of a block neither 1 or 2. + errInvalidDifficulty = errors.New("invalid difficulty") + + // errWrongDifficulty is returned if the difficulty of a block doesn't match the + // turn of the signer. + errWrongDifficulty = errors.New("wrong difficulty") + + // errInvalidTimestamp is returned if the timestamp of a block is lower than + // the previous block's timestamp + the minimum block period. + errInvalidTimestamp = errors.New("invalid timestamp") + + // errInvalidVotingChain is returned if an authorization list is attempted to + // be modified via out-of-range or non-contiguous headers. + errInvalidVotingChain = errors.New("invalid voting chain") + + // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity. + errUnauthorizedSigner = errors.New("unauthorized signer") + + // errInvalidSigner is returned if signer will not be able to sign due to validator config + errInvalidSigner = errors.New("unauthorized signer which is not within validators list") + + // errRecentlySigned is returned if a header is signed by an authorized entity + // that already signed a header recently, thus is temporarily not allowed to. + errRecentlySigned = errors.New("recently signed") +) + +// SignerFn hashes and signs the data to be signed by a backing account. +type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { + // If the signature's already cached, return that + hash := header.Hash() + if address, known := sigcache.Get(hash); known { + return address.(common.Address), nil + } + // Retrieve the signature from the header extra-data + if len(header.Seal) > 2 || len(header.Seal[1]) < extraSeal { + return common.Address{}, errMissingSignature + } + + currentSignature := header.Seal[1] + signature := currentSignature[len(currentSignature)-extraSeal:] + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(SealHash(header).Bytes(), signature) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + sigcache.Add(hash, signer) + return signer, nil +} + +// Aura is the proof-of-authority consensus engine proposed to support the +// Ethereum testnet following the Ropsten attacks. +type Aura struct { + config *params.AuraConfig // Consensus engine configuration parameters + db ethdb.Database // Database to store and retrieve snapshot checkpoints + + recents *lru.ARCCache // Snapshots for recent block to speed up reorgs + signatures *lru.ARCCache // Signatures of recent blocks to speed up mining + + proposals map[common.Address]bool // Current list of proposals we are pushing + + signer common.Address // Ethereum address of the signing key + signFn SignerFn // Signer function to authorize hashes with + lock sync.RWMutex // Protects the signer fields + + // The fields below are for testing only + fakeDiff bool // Skip difficulty verifications +} + +// New creates a AuthorityRound proof-of-authority consensus engine with the initial +// signers set to the ones provided by the user. +func New(config *params.AuraConfig, db ethdb.Database) *Aura { + // Set any missing consensus parameters to their defaults + conf := *config + if conf.Epoch == 0 { + conf.Epoch = epochLength + } + // Allocate the snapshot caches and create the engine + recents, _ := lru.NewARC(inmemorySnapshots) + signatures, _ := lru.NewARC(inmemorySignatures) + + return &Aura{ + config: &conf, + db: db, + recents: recents, + signatures: signatures, + proposals: make(map[common.Address]bool), + } +} + +// Author implements consensus.Engine, returning the Ethereum address recovered +// from the signature in the header's extra-data section. +func (a *Aura) Author(header *types.Header) (common.Address, error) { + return ecrecover(header, a.signatures) +} + +// VerifyHeader checks whether a header conforms to the consensus rules. +func (a *Aura) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { + return a.verifyHeader(chain, header, nil) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The +// method returns a quit channel to abort the operations and a results channel to +// retrieve the async verifications (the order is that of the input slice). +func (a *Aura) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + abort := make(chan struct{}) + results := make(chan error, len(headers)) + go func() { + for i, header := range headers { + err := a.verifyHeader(chain, header, headers[:i]) + + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// verifyHeader checks whether a header conforms to the consensus rules.The +// caller may optionally pass in a batch of parents (ascending order) to avoid +// looking those up from the database. This is useful for concurrently verifying +// a batch of new headers. +func (a *Aura) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + if header.Number == nil { + return errUnknownBlock + } + //number := header.Number.Uint64() + + // Don't waste time checking blocks from the future + if header.Time > uint64(time.Now().Unix()) { + return consensus.ErrFutureBlock + } + + // Ensure that the mix digest is zero as we don't have fork protection currently + if header.MixDigest != (common.Hash{}) { + return errInvalidMixDigest + } + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if header.UncleHash != uncleHash { + return errInvalidUncleHash + } + + // If all checks passed, validate any special fields for hard forks + if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { + return err + } + // All basic checks passed, verify cascading fields + return a.verifyCascadingFields(chain, header, parents) +} + +// verifyCascadingFields verifies all the header fields that are not standalone, +// rather depend on a batch of previous headers. The caller may optionally pass +// in a batch of parents (ascending order) to avoid looking those up from the +// database. This is useful for concurrently verifying a batch of new headers. +func (a *Aura) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // The genesis block is the always valid dead-end + number := header.Number.Uint64() + if number == 0 { + return nil + } + // Ensure that the block's timestamp isn't too close to its parent + var parent *types.Header + if len(parents) > 0 { + parent = parents[len(parents)-1] + } else { + parent = chain.GetHeader(header.ParentHash, number-1) + } + if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { + return consensus.ErrUnknownAncestor + } + if parent.Time > header.Time { + return errInvalidTimestamp + } + + // All basic checks passed, verify the seal and return + return a.verifySeal(chain, header, parents) +} + +// snapshot retrieves the authorization snapshot at a given point in time. +func (a *Aura) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { + // Search for a snapshot in memory or on disk for checkpoints + var ( + headers []*types.Header + snap *Snapshot + ) + for snap == nil { + // If an in-memory snapshot was found, use that + if s, ok := a.recents.Get(hash); ok { + snap = s.(*Snapshot) + break + } + // If an on-disk checkpoint snapshot can be found, use that + if number%checkpointInterval == 0 { + if s, err := loadSnapshot(a.config, a.signatures, a.db, hash); err == nil { + log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash) + snap = s + break + } + } + // If we're at the genesis, snapshot the initial state. Alternatively if we're + // at a checkpoint block without a parent (light client CHT), or we have piled + // up more headers than allowed to be reorged (chain reinit from a freezer), + // consider the checkpoint trusted and snapshot it. + if number == 0 || (number%a.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) { + checkpoint := chain.GetHeaderByNumber(number) + if checkpoint != nil { + hash := checkpoint.Hash() + + signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength) + for i := 0; i < len(signers); i++ { + copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:]) + } + snap = newSnapshot(a.config, a.signatures, number, hash, signers) + if err := snap.store(a.db); err != nil { + return nil, err + } + log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash) + break + } + } + // No snapshot for this header, gather the header and move backward + var header *types.Header + if len(parents) > 0 { + // If we have explicit parents, pick from there (enforced) + header = parents[len(parents)-1] + if header.Hash() != hash || header.Number.Uint64() != number { + return nil, consensus.ErrUnknownAncestor + } + parents = parents[:len(parents)-1] + } else { + // No explicit parents (or no more left), reach out to the database + header = chain.GetHeader(hash, number) + if header == nil { + return nil, consensus.ErrUnknownAncestor + } + } + headers = append(headers, header) + number, hash = number-1, header.ParentHash + } + // Previous snapshot found, apply any pending headers on top of it + for i := 0; i < len(headers)/2; i++ { + headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] + } + snap, err := snap.apply(headers) + if err != nil { + return nil, err + } + a.recents.Add(snap.Hash, snap) + + // If we've generated a new checkpoint snapshot, save to disk + if snap.Number%checkpointInterval == 0 && len(headers) > 0 { + if err = snap.store(a.db); err != nil { + return nil, err + } + log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) + } + return snap, err +} + +// VerifyUncles implements consensus.Engine, always returning an error for any +// uncles as this consensus mechanism doesn't permit uncles. +func (a *Aura) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + if len(block.Uncles()) > 0 { + return errors.New("uncles not allowed") + } + return nil +} + +// VerifySeal implements consensus.Engine, checking whether the signature contained +// in the header satisfies the consensus protocol requirements. +func (a *Aura) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { + return a.verifySeal(chain, header, nil) +} + +// verifySeal checks whether the signature contained in the header satisfies the +// consensus protocol requirements. The method accepts an optional list of parent +// headers that aren't yet part of the local blockchain to generate the snapshots +// from. +func (a *Aura) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // Verifying the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + + // Resolve the authorization key and check against signers + signer, err := ecrecover(header, a.signatures) + if err != nil { + return err + } + // Checking authorization + ts := header.Time + + step := ts / a.config.Period + println(header.Number.Uint64()) + + turn := step % uint64(len(a.config.Authorities)) + + if signer != a.config.Authorities[turn] { + // not authorized to sign + return errUnauthorizedSigner + } + + return nil +} + +// Prepare implements consensus.Engine, preparing all the consensus fields of the +// header for running the transactions on top. +func (a *Aura) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + // Nonce is not used in aura engine + header.Nonce = types.BlockNonce{} + number := header.Number.Uint64() + + // Mix digest is not used, set to empty + header.MixDigest = common.Hash{} + + // Fetch the parent + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + + // Set the correct difficulty + calculateExpectedDifficulty := func(parentStep uint64, step uint64, emptyStepsLen uint64) (diff *big.Int) { + maxInt := big.NewInt(0) + maxBig128 := maxInt.Sqrt(math.MaxBig256) + diff = big.NewInt(int64(parentStep - step + emptyStepsLen)) + diff = diff.Add(maxBig128, diff) + + if diff.Cmp(maxBig128) == 1 { + diff = maxBig128 + } + + return + } + + auraHeader := &types.AuraHeader{} + + if len(header.Seal) < 2 { + header.Seal = make([][]byte, 2) + step := uint64(time.Now().Unix()) / a.config.Period + var stepBytes []byte + stepBytes = make([]byte, 8) + binary.LittleEndian.PutUint64(stepBytes, step) + header.Seal[0] = stepBytes + } + + err := auraHeader.FromHeader(header) + + if nil != err { + return err + } + + auraParentHeader := &types.AuraHeader{} + err = auraParentHeader.FromHeader(parent) + header.Difficulty = calculateExpectedDifficulty(auraParentHeader.Step, auraHeader.Step, 0) + + return nil +} + +// Finalize implements consensus.Engine, ensuring no uncles are set, nor block +// rewards given. +func (a *Aura) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { + // No block rewards in PoA, so the state remains as is and uncles are dropped + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.UncleHash = types.CalcUncleHash(nil) +} + +// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, +// nor block rewards given, and returns the final block. +func (a *Aura) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + // No block rewards in PoA, so the state remains as is and uncles are dropped + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.UncleHash = types.CalcUncleHash(nil) + + // Assemble and return the final block for sealing + return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil +} + +// Authorize injects a private key into the consensus engine to mint new blocks +// with. +func (a *Aura) Authorize(signer common.Address, signFn SignerFn) { + a.lock.Lock() + defer a.lock.Unlock() + + a.signer = signer + a.signFn = signFn +} + +// Function should be used if you want to wait until there is current validator turn +// If validator wont be able to seal anytime, function will return error +// Be careful because it can set up very large delay if periods are so long +func (a *Aura) WaitForNextSealerTurn(fromTime int64) (err error) { + closestSealTurnStart, _, err := a.CountClosestTurn(fromTime, 0) + + if nil != err { + return + } + + delay := closestSealTurnStart - fromTime + + if delay < 0 { + return + } + + log.Warn(fmt.Sprintf("waiting: %d seconds for sealing turn, time now: %d", delay, fromTime)) + <-time.After(time.Duration(delay) * time.Second) + log.Warn("this is time now", "timeNow", time.Now().Unix()) + return +} + +// Seal implements consensus.Engine, attempting to create a sealed block using +// the local signing credentials. +// You should use Seal only if current sealer is within its turn, otherwise you will get error +func (a *Aura) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { + log.Trace("Starting sealing in Aura engine", "block", block.Hash()) + header := block.Header() + + // Sealing the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) + if a.config.Period == 0 && len(block.Transactions()) == 0 { + log.Info("Sealing paused, waiting for transactions") + return nil + } + // Don't hold the signer fields for the entire sealing procedure + a.lock.RLock() + signer, signFn := a.signer, a.signFn + a.lock.RUnlock() + + // check if sealer will be ever able to sign + timeNow := time.Now().Unix() + _, _, err := a.CountClosestTurn(timeNow, int64(0)) + + if nil != err { + // not authorized to sign ever + return err + } + + // check if in good turn time frame + allowed, _, _ := a.CheckStep(int64(header.Time), 0) + + if !allowed { + log.Warn( + "Could not seal, because timestamp of header is invalid: Header time: %d, time now: %d", + "headerTime", + header.Time, + "timeNow", + time.Now().Unix(), + "hash", + SealHash(header), + ) + return nil + } + + // Attach time of future execution, not current time + sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeAura, AuraRLP(header)) + if err != nil { + return err + } + + go func() { + select { + case <-stop: + return + default: + header.Seal = make([][]byte, 2) + step := uint64(time.Now().Unix()) / a.config.Period + var stepBytes []byte + stepBytes = make([]byte, 8) + binary.LittleEndian.PutUint64(stepBytes, step) + header.Seal[0] = stepBytes + header.Seal[1] = sighash + } + + select { + case results <- block.WithSeal(header): + default: + log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header)) + } + }() + + return nil +} + +// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty +// that a new block should have based on the previous blocks in the chain and the +// current signer. +func (a *Aura) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { + return chain.Config().Aura.Difficulty +} + +// SealHash returns the hash of a block prior to it being sealed. +func (a *Aura) SealHash(header *types.Header) common.Hash { + return SealHash(header) +} + +// Close implements consensus.Engine. It's a noop for Aura as there are no background threads. +func (a *Aura) Close() error { + return nil +} + +// APIs implements consensus.Engine, returning the user facing RPC API to allow +// controlling the signer voting. +func (a *Aura) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return []rpc.API{{ + Namespace: "aura", + Version: "1.0", + Service: &API{chain: chain, aura: a}, + Public: false, + }} +} + +// SealHash returns the hash of a block prior to it being sealed. +func SealHash(header *types.Header) (hash common.Hash) { + hasher := new(bytes.Buffer) + encodeSigHeader(hasher, header) + signatureHash := crypto.Keccak256(hasher.Bytes()) + var arr [32]byte + copy(arr[:], signatureHash) + return arr +} + +// AuraRLP returns the rlp bytes which needs to be signed for the proof-of-authority +// sealing. The RLP to sign consists of the entire header apart from the 65 byte signature +// contained at the end of the extra data. +func AuraRLP(header *types.Header) []byte { + b := new(bytes.Buffer) + encodeSigHeader(b, header) + return b.Bytes() +} + +// CheckStep should assure you that current time frame allows you to seal block based on validator set +// UnixTimeToCheck allows you to deduce time not based on current time which might be handy +// TimeTolerance allows you to in-flight deduce that propagation is likely or unlikely to fail. Provide 0 if strict. +// For example if sealing the block is about 1 sec and period is 5 secs you would like to know if your +// committed work will ever have a chance to be accepted by others +// Allowed returns if possible to seal +// currentTurnTimestamp returns when time frame of current turn starts in unixTime +// nextTurnTimestamp returns when time frame of next turn starts in unixTime +func (a *Aura) CheckStep(unixTimeToCheck int64, timeTolerance int64) ( + allowed bool, + currentTurnTimestamp int64, + nextTurnTimestamp int64, +) { + guardStepByUnixTime := func(unixTime int64) (allowed bool) { + step := uint64(unixTime) / a.config.Period + turn := step % uint64(len(a.config.Authorities)) + + return a.signer == a.config.Authorities[turn] + } + + countTimeFrameForTurn := func(unixTime int64) (turnStart int64, nextTurn int64) { + timeGap := unixTime % int64(a.config.Period) + turnStart = unixTime + + if timeGap > 0 { + turnStart = unixTime - timeGap + } + + nextTurn = turnStart + int64(a.config.Period) + + return + } + + checkForProvidedUnix := guardStepByUnixTime(unixTimeToCheck) + checkForPromisedInterval := guardStepByUnixTime(unixTimeToCheck + timeTolerance) + currentTurnTimestamp, nextTurnTimestamp = countTimeFrameForTurn(unixTimeToCheck) + allowed = checkForProvidedUnix && checkForPromisedInterval + + return +} + +// CountClosestTurn provides you information should you wait and if so how long for next turn for current validator +// If err is other than nil, it means that you wont be able to seal within this epoch or ever +func (a *Aura) CountClosestTurn(unixTimeToCheck int64, timeTolerance int64) ( + closestSealTurnStart int64, + closestSealTurnStop int64, + err error, +) { + for range a.config.Authorities { + allowed, turnTimestamp, nextTurnTimestamp := a.CheckStep(unixTimeToCheck, timeTolerance) + + if allowed { + closestSealTurnStart = turnTimestamp + closestSealTurnStop = nextTurnTimestamp + return + } + + unixTimeToCheck = nextTurnTimestamp + } + + err = errInvalidSigner + + return +} + +// This allows you to safely decode p2p message into desired headers +// It is created, because multiple clients can have various rlp encoding/decoding mechanisms +// For MixDigest and Nonce will produce error in decoding from parity, +// so it would be great to have one place to decode those +// It leaves no error, just simply empty headers set +func HeadersFromP2PMessage(msg p2p.Msg) (headers []*types.Header) { + var ( + bufferCopy bytes.Buffer + auraHeaders []*types.AuraHeader + tempBytes []byte + ) + tee := io.TeeReader(msg.Payload, &bufferCopy) + readBytes, err := ioutil.ReadAll(tee) + err = rlp.Decode(bytes.NewReader(readBytes), &headers) + + // Now run read of whole message, to do not have any leftovers + _, _ = msg.Payload.Read(tempBytes) + + // Early return, we have read all the headers + if nil == err { + return + } + + log.Warn("Encountered error in aura incoming header", "err", err) + // Remove invalid headers + headers = make([]*types.Header, 0) + + // Fallback as auraHeaders + err = rlp.Decode(bytes.NewReader(readBytes), &auraHeaders) + + for _, header := range auraHeaders { + if nil == err && nil != header { + headers = append(headers, header.TranslateIntoHeader()) + } + } + + return +} + +// Encode to bare hash +func encodeSigHeader(w io.Writer, header *types.Header) { + err := rlp.Encode(w, []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.Extra, + }) + if err != nil { + panic("can't encode: " + err.Error()) + } +} diff --git a/consensus/aura/aura_test.go b/consensus/aura/aura_test.go new file mode 100644 index 000000000000..0a03f9c4fb45 --- /dev/null +++ b/consensus/aura/aura_test.go @@ -0,0 +1,364 @@ +package aura + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + lru "github.com/hashicorp/golang-lru" + "github.com/stretchr/testify/assert" + "math/big" + "strings" + "testing" + "time" +) + +var ( + auraChainConfig *params.AuraConfig + testBankKey, _ = crypto.GenerateKey() + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + auraEngine *Aura +) + +func init() { + authority1, _ := crypto.GenerateKey() + authority2, _ := crypto.GenerateKey() + auraChainConfig = ¶ms.AuraConfig{ + Period: 5, + Epoch: 500, + Authorities: []common.Address{ + testBankAddress, + crypto.PubkeyToAddress(authority1.PublicKey), + crypto.PubkeyToAddress(authority2.PublicKey), + }, + Difficulty: big.NewInt(int64(131072)), + Signatures: nil, + } + + db := rawdb.NewMemoryDatabase() + auraEngine = New(auraChainConfig, db) + + signerFunc := func(account accounts.Account, s string, data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), testBankKey) + } + auraEngine.Authorize(testBankAddress, signerFunc) +} + +func TestAura_CheckStep(t *testing.T) { + currentTime := int64(1602588556) + + t.Run("should return true with no tolerance", func(t *testing.T) { + allowed, currentTurnTimestamp, nextTurnTimestamp := auraEngine.CheckStep(currentTime, 0) + assert.True(t, allowed) + // Period is 5 so next time frame started within -1 from unix time + assert.Equal(t, currentTime-1, currentTurnTimestamp) + // Period is 5 so next time frame starts within 4 secs from unix time + assert.Equal(t, currentTime+4, nextTurnTimestamp) + }) + + t.Run("should return true with small tolerance", func(t *testing.T) { + allowed, currentTurnTimestamp, nextTurnTimestamp := auraEngine.CheckStep( + currentTime, + time.Unix(currentTime, 25).Unix(), + ) + assert.True(t, allowed) + // Period is 5 so next time frame started within -1 from unix time + assert.Equal(t, currentTime-1, currentTurnTimestamp) + // Period is 5 so next time frame starts within 4 secs from unix time + assert.Equal(t, currentTime+4, nextTurnTimestamp) + }) + + t.Run("should return false with no tolerance", func(t *testing.T) { + timeToCheck := currentTime + int64(6) + allowed, currentTurnTimestamp, nextTurnTimestamp := auraEngine.CheckStep(timeToCheck, 0) + assert.False(t, allowed) + assert.Equal(t, timeToCheck-2, currentTurnTimestamp) + assert.Equal(t, timeToCheck+3, nextTurnTimestamp) + }) + + // If base unixTime is invalid fail no matter what tolerance is + // If you start sealing before its your turn or you have missed your time frame you should resubmit work + t.Run("should return false with tolerance", func(t *testing.T) { + timeToCheck := currentTime + int64(5) + allowed, currentTurnTimestamp, nextTurnTimestamp := auraEngine.CheckStep( + timeToCheck, + time.Unix(currentTime+80, 0).Unix(), + ) + assert.False(t, allowed) + assert.Equal(t, timeToCheck-1, currentTurnTimestamp) + assert.Equal(t, timeToCheck+4, nextTurnTimestamp) + }) +} + +func TestAura_CountClosestTurn(t *testing.T) { + currentTime := int64(1602588556) + + t.Run("should return error, because validator wont be able to seal", func(t *testing.T) { + randomValidatorKey, err := crypto.GenerateKey() + assert.Nil(t, err) + auraChainConfig = ¶ms.AuraConfig{ + Period: 5, + Epoch: 500, + Authorities: []common.Address{ + crypto.PubkeyToAddress(randomValidatorKey.PublicKey), + }, + Difficulty: big.NewInt(int64(131072)), + Signatures: nil, + } + + db := rawdb.NewMemoryDatabase() + modifiedAuraEngine := New(auraChainConfig, db) + closestSealTurnStart, closestSealTurnStop, err := modifiedAuraEngine.CountClosestTurn( + time.Now().Unix(), + 0, + ) + assert.Equal(t, errInvalidSigner, err) + assert.Equal(t, int64(0), closestSealTurnStart) + assert.Equal(t, int64(0), closestSealTurnStop) + }) + + t.Run("should return current time frame", func(t *testing.T) { + closestSealTurnStart, closestSealTurnStop, err := auraEngine.CountClosestTurn(currentTime, 0) + assert.Nil(t, err) + assert.Equal(t, currentTime-1, closestSealTurnStart) + assert.Equal(t, currentTime+4, closestSealTurnStop) + }) + + t.Run("should return time frame in future", func(t *testing.T) { + timeModified := currentTime + 5 + closestSealTurnStart, closestSealTurnStop, err := auraEngine.CountClosestTurn(timeModified, 0) + assert.Nil(t, err) + assert.Equal(t, timeModified+9, closestSealTurnStart) + assert.Equal(t, timeModified+14, closestSealTurnStop) + }) +} + +func TestAura_DecodeSeal(t *testing.T) { + // Block 1 rlp data + msg4Node0 := "f90241f9023ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69841314e684b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + input, err := hex.DecodeString(msg4Node0) + assert.Nil(t, err) + + var auraHeaders []*types.AuraHeader + err = rlp.Decode(bytes.NewReader(input), &auraHeaders) + assert.Nil(t, err) + assert.NotEmpty(t, auraHeaders) + + for _, header := range auraHeaders { + // excepted block 1 hash (from parity rpc) + hashExpected := "0x4d286e4f0dbce8d54b27ea70c211bc4b00c8a89ac67f132662c6dc74d9b294e4" + assert.Equal(t, hashExpected, header.Hash().String()) + stdHeader := header.TranslateIntoHeader() + stdHeaderHash := stdHeader.Hash() + assert.Equal(t, hashExpected, stdHeaderHash.String()) + if header.Number.Int64() == int64(1) { + signatureForSeal := new(bytes.Buffer) + encodeSigHeader(signatureForSeal, stdHeader) + messageHashForSeal := SealHash(stdHeader).Bytes() + hexutil.Encode(crypto.Keccak256(signatureForSeal.Bytes())) + pubkey, err := crypto.Ecrecover(messageHashForSeal, stdHeader.Seal[1]) + + assert.Nil(t, err) + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + // 0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf - Block 1 miner + assert.Equal(t, "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", strings.ToLower(signer.Hex())) + } + } +} + +func TestAura_WaitForNextSealerTurn(t *testing.T) { + fixedTime := int64(1602697742) + db := rawdb.NewMemoryDatabase() + + t.Run("Should fail, signer not in validators list", func(t *testing.T) { + specificEngine := New(¶ms.AuraConfig{ + Period: 0, + Epoch: 0, + Authorities: nil, + Difficulty: nil, + Signatures: nil, + }, db) + err := specificEngine.WaitForNextSealerTurn(fixedTime) + assert.NotNil(t, err) + assert.Equal(t, errInvalidSigner, err) + }) + + t.Run("should sleep", func(t *testing.T) { + timeNow := time.Now().Unix() + closestSealTurnStart, _, err := auraEngine.CountClosestTurn(timeNow, 0) + assert.Nil(t, err) + + if closestSealTurnStart == timeNow { + t.Logf("Equal before start") + } + + err = auraEngine.WaitForNextSealerTurn(timeNow) + assert.Nil(t, err) + assert.Equal(t, time.Now().Unix(), closestSealTurnStart) + fmt.Printf("should wait %d secs", closestSealTurnStart-timeNow) + }) +} + +func TestAura_Seal(t *testing.T) { + // block hex comes from worker test and is extracted due to unit-level of testing Seal + blockToSignHex := "0xf902c5f9025ca0f0513bebf98c814b3c28ff61746552f74ed65909a3ca4cc3ea5b56dc6021ee3ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a02c6e36b7f66da996dc550a19d56c9994626304dc77e459963c1b4dde768020cda02457516422f685ff3338d36c41f3eaa26c35b53f4d485d8d93543c1c4b8bdf6ba0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018347e7c4825208845f84393fb86100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0f863f8618080825208943da0ae25cdf7004849e352ba1f8b59ea4b6ebd708203e8801ca00ab99fc4760dfddc35ebd4bf4c4be06e3a2b2d6995fa37b674142c573f7683dda008e2b5c9e9c4597b59d639d7d0aba1b0aa4ddeaf4dceb8b89b914272aa340a1ac0" + blockBytes, err := hexutil.Decode(blockToSignHex) + assert.Nil(t, err) + var block types.Block + err = rlp.DecodeBytes(blockBytes, &block) + assert.Nil(t, err) + + // Header should not contain Signature and Step because for now it is not signed + header := block.Header() + assert.Empty(t, header.Seal) + + // Max timeout for next turn to start sealing + timeout := len(auraEngine.config.Authorities) * int(auraEngine.config.Period) + assert.Nil(t, err) + + // Seal the block + chain := core.BlockChain{} + resultsChan := make(chan *types.Block) + stopChan := make(chan struct{}) + timeNow := time.Now().Unix() + closestSealTurnStart, _, err := auraEngine.CountClosestTurn(timeNow, int64(timeout)) + assert.Nil(t, err) + waitFor := closestSealTurnStart - timeNow + + if waitFor < 1 { + waitFor = 0 + } + + t.Logf("Test is waiting for proper turn to start sealing. Waiting: %v secs", waitFor) + time.Sleep(time.Duration(waitFor) * time.Second) + err = auraEngine.Seal(&chain, &block, resultsChan, stopChan) + + select { + case receivedBlock := <-resultsChan: + assert.Nil(t, err) + assert.IsType(t, &types.Block{}, receivedBlock) + header := receivedBlock.Header() + assert.Len(t, header.Seal, 2) + signatureForSeal := new(bytes.Buffer) + encodeSigHeader(signatureForSeal, header) + messageHashForSeal := SealHash(header).Bytes() + hexutil.Encode(crypto.Keccak256(signatureForSeal.Bytes())) + pubkey, err := crypto.Ecrecover(messageHashForSeal, header.Seal[1]) + + assert.Nil(t, err) + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + // Signer should be equal sealer + assert.Equal(t, strings.ToLower(testBankAddress.String()), strings.ToLower(signer.Hex())) + case <-time.After(time.Duration(timeout) * time.Second): + t.Fatalf("Received timeout") + + case receivedStop := <-stopChan: + t.Fatalf("Received stop, but did not expect this, %v", receivedStop) + } +} + +func TestAura_FromBlock(t *testing.T) { + invalidBlockRlp := "f902acf902a7a004013562d49a87c65aea12a13f12e63381647705f8e68841126a4620ac13927ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008083035092837a120080845f89a639b861d883010916846765746888676f312e31352e32856c696e7578000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f84c88a5871b1300000000b841030916c553834125cab0ea384ab904bac2e7b7fe2a49fda62a98efb5c1b4fc2c26321fc433fe87d33285f1f696330c8cc94801483544eab72e1f289191466c5b01c0c0" + input, err := hex.DecodeString(invalidBlockRlp) + assert.Nil(t, err) + var standardBlock *types.Block + err = rlp.Decode(bytes.NewReader(input), &standardBlock) + assert.Nil(t, err) + assert.NotNil(t, standardBlock) + + auraBlock := &types.AuraBlock{} + err = auraBlock.FromBlock(standardBlock) + assert.Nil(t, err) +} + +func TestHeadersFromP2PMessage(t *testing.T) { + msg4Node0 := "f90241f9023ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69841314e684b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + //headers := make([]*types.Header, 0) + input, err := hex.DecodeString(msg4Node0) + assert.Nil(t, err) + msg := p2p.Msg{ + Code: 0x04, + Size: uint32(len(input)), + Payload: bytes.NewReader(input), + ReceivedAt: time.Time{}, + } + headers := HeadersFromP2PMessage(msg) + assert.Len(t, headers, 1) + + header1 := headers[0] + auraHeader := types.AuraHeader{} + err = auraHeader.FromHeader(header1) + assert.Nil(t, err) + auraHeaders := make([]*types.AuraHeader, 1) + auraHeaders[0] = &auraHeader + encodedBytes, err := rlp.EncodeToBytes(auraHeaders) + assert.Nil(t, err) + msg1 := p2p.Msg{ + Code: 0x04, + Size: uint32(len(encodedBytes)), + Payload: bytes.NewReader(encodedBytes), + ReceivedAt: time.Time{}, + } + headers = make([]*types.Header, 0) + headersFromAura := HeadersFromP2PMessage(msg1) + assert.Len(t, headersFromAura, 1) +} + +func TestAura_VerifySeal(t *testing.T) { + // Block 1 rlp data + msg4Node0 := "f90241f9023ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69841314e684b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + input, err := hex.DecodeString(msg4Node0) + assert.Nil(t, err) + var auraHeaders []*types.AuraHeader + err = rlp.Decode(bytes.NewReader(input), &auraHeaders) + assert.Nil(t, err) + assert.NotEmpty(t, auraHeaders) + var aura Aura + auraConfig := ¶ms.AuraConfig{ + Period: uint64(5), + Authorities: []common.Address{ + common.HexToAddress("0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf"), + common.HexToAddress("0xafe443af9d1504de4c2d486356c421c160fdd7b1"), + }, + } + aura.config = auraConfig + var auraSignatures *lru.ARCCache + auraSignatures, err = lru.NewARC(inmemorySignatures) + assert.Nil(t, err) + auraSignatures.Add(0, "0x6f17a2ade9f6daed3968b73514466e07e3c1fef2d6350946e1a12d2b577af0aa") + aura.signatures = auraSignatures + for _, header := range auraHeaders { + // excepted block 1 hash (from parity rpc) + hashExpected := "0x4d286e4f0dbce8d54b27ea70c211bc4b00c8a89ac67f132662c6dc74d9b294e4" + assert.Equal(t, hashExpected, header.Hash().String()) + stdHeader := header.TranslateIntoHeader() + stdHeaderHash := stdHeader.Hash() + assert.Equal(t, hashExpected, stdHeaderHash.String()) + if header.Number.Int64() == int64(1) { + signatureForSeal := new(bytes.Buffer) + encodeSigHeader(signatureForSeal, stdHeader) + messageHashForSeal := SealHash(stdHeader).Bytes() + hexutil.Encode(crypto.Keccak256(signatureForSeal.Bytes())) + pubkey, err := crypto.Ecrecover(messageHashForSeal, stdHeader.Seal[1]) + assert.Nil(t, err) + err = aura.VerifySeal(nil, stdHeader) + assert.Nil(t, err) + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + // 0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf - Block 1 miner + assert.Equal(t, "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", strings.ToLower(signer.Hex())) + } + } +} diff --git a/consensus/aura/fixtures/block-0-parity.json b/consensus/aura/fixtures/block-0-parity.json new file mode 100644 index 000000000000..7eedcfd92111 --- /dev/null +++ b/consensus/aura/fixtures/block-0-parity.json @@ -0,0 +1,24 @@ +{ + "author": "0x0000000000000000000000000000000000000000", + "difficulty": 131072, + "extraData": "0x", + "gasLimit": 2236962, + "gasUsed": 0, + "hash": "0x2778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788c", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "number": 0, + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields": ["0x80", "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"], + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size": 533, + "stateRoot": "0x40cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133b", + "step": "0", + "timestamp": 0, + "totalDifficulty": 131072, + "transactions": [], + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] +} diff --git a/consensus/aura/fixtures/geth-aura.json b/consensus/aura/fixtures/geth-aura.json new file mode 100644 index 000000000000..41b55294f012 --- /dev/null +++ b/consensus/aura/fixtures/geth-aura.json @@ -0,0 +1,32 @@ +{ + "config": { + "chainId": 8995, + "authorityRound": { + "stepDuration": "5", + "validators": [ + "0x76814b3644f20903b8472434e8c8efb2aa79e546", + "0xcdf269895f63617ea00e1494956f419cf14a2828" + ] + } + }, + "seal": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "difficulty": "0x20000", + "gasLimit": "0x222222", + "alloc": { + "0x0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0x0000000000000000000000000000000000000004": { + "balance": "0x1" + } + } +} diff --git a/consensus/aura/fixtures/parity-aura.json b/consensus/aura/fixtures/parity-aura.json new file mode 100644 index 000000000000..79581b955e43 --- /dev/null +++ b/consensus/aura/fixtures/parity-aura.json @@ -0,0 +1,42 @@ +{ + "name": "AuthorityRound", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators" : { + "list": [ + "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "0xafe443af9d1504de4c2d486356c421c160fdd7b1" + ] + } + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2323" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x222222" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + } +} \ No newline at end of file diff --git a/consensus/aura/snapshot.go b/consensus/aura/snapshot.go new file mode 100644 index 000000000000..2fbf1a9de6f5 --- /dev/null +++ b/consensus/aura/snapshot.go @@ -0,0 +1,326 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package aura + +import ( + "bytes" + "encoding/json" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + lru "github.com/hashicorp/golang-lru" +) + +// Vote represents a single vote that an authorized signer made to modify the +// list of authorizations. +type Vote struct { + Signer common.Address `json:"signer"` // Authorized signer that cast this vote + Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes) + Address common.Address `json:"address"` // Account being voted on to change its authorization + Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account +} + +// Tally is a simple vote tally to keep the current score of votes. Votes that +// go against the proposal aren't counted since it's equivalent to not voting. +type Tally struct { + Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone + Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal +} + +// Snapshot is the state of the authorization voting at a given point in time. +type Snapshot struct { + config *params.AuraConfig // Consensus engine parameters to fine tune behavior + sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover + + Number uint64 `json:"number"` // Block number where the snapshot was created + Hash common.Hash `json:"hash"` // Block hash where the snapshot was created + Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment + Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections + Votes []*Vote `json:"votes"` // List of votes cast in chronological order + Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating +} + +// signersAscending implements the sort interface to allow sorting a list of addresses +type signersAscending []common.Address + +func (s signersAscending) Len() int { return len(s) } +func (s signersAscending) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 } +func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// newSnapshot creates a new snapshot with the specified startup parameters. This +// method does not initialize the set of recent signers, so only ever use if for +// the genesis block. +func newSnapshot(config *params.AuraConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot { + snap := &Snapshot{ + config: config, + sigcache: sigcache, + Number: number, + Hash: hash, + Signers: make(map[common.Address]struct{}), + Recents: make(map[uint64]common.Address), + Tally: make(map[common.Address]Tally), + } + for _, signer := range signers { + snap.Signers[signer] = struct{}{} + } + return snap +} + +// loadSnapshot loads an existing snapshot from the database. +func loadSnapshot(config *params.AuraConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { + blob, err := db.Get(append([]byte("aura-"), hash[:]...)) + if err != nil { + return nil, err + } + snap := new(Snapshot) + if err := json.Unmarshal(blob, snap); err != nil { + return nil, err + } + snap.config = config + snap.sigcache = sigcache + + return snap, nil +} + +// store inserts the snapshot into the database. +func (s *Snapshot) store(db ethdb.Database) error { + blob, err := json.Marshal(s) + if err != nil { + return err + } + return db.Put(append([]byte("aura-"), s.Hash[:]...), blob) +} + +// copy creates a deep copy of the snapshot, though not the individual votes. +func (s *Snapshot) copy() *Snapshot { + cpy := &Snapshot{ + config: s.config, + sigcache: s.sigcache, + Number: s.Number, + Hash: s.Hash, + Signers: make(map[common.Address]struct{}), + Recents: make(map[uint64]common.Address), + Votes: make([]*Vote, len(s.Votes)), + Tally: make(map[common.Address]Tally), + } + for signer := range s.Signers { + cpy.Signers[signer] = struct{}{} + } + for block, signer := range s.Recents { + cpy.Recents[block] = signer + } + for address, tally := range s.Tally { + cpy.Tally[address] = tally + } + copy(cpy.Votes, s.Votes) + + return cpy +} + +// validVote returns whether it makes sense to cast the specified vote in the +// given snapshot context (e.g. don't try to add an already authorized signer). +func (s *Snapshot) validVote(address common.Address, authorize bool) bool { + _, signer := s.Signers[address] + return (signer && !authorize) || (!signer && authorize) +} + +// cast adds a new vote into the tally. +func (s *Snapshot) cast(address common.Address, authorize bool) bool { + // Ensure the vote is meaningful + if !s.validVote(address, authorize) { + return false + } + // Cast the vote into an existing or new tally + if old, ok := s.Tally[address]; ok { + old.Votes++ + s.Tally[address] = old + } else { + s.Tally[address] = Tally{Authorize: authorize, Votes: 1} + } + return true +} + +// uncast removes a previously cast vote from the tally. +func (s *Snapshot) uncast(address common.Address, authorize bool) bool { + // If there's no tally, it's a dangling vote, just drop + tally, ok := s.Tally[address] + if !ok { + return false + } + // Ensure we only revert counted votes + if tally.Authorize != authorize { + return false + } + // Otherwise revert the vote + if tally.Votes > 1 { + tally.Votes-- + s.Tally[address] = tally + } else { + delete(s.Tally, address) + } + return true +} + +// apply creates a new authorization snapshot by applying the given headers to +// the original one. +func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { + // Allow passing in no headers for cleaner code + if len(headers) == 0 { + return s, nil + } + // Sanity check that the headers can be applied + for i := 0; i < len(headers)-1; i++ { + if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { + return nil, errInvalidVotingChain + } + } + if headers[0].Number.Uint64() != s.Number+1 { + return nil, errInvalidVotingChain + } + // Iterate through the headers and create a new snapshot + snap := s.copy() + + var ( + start = time.Now() + logged = time.Now() + ) + for i, header := range headers { + // Remove any votes on checkpoint blocks + number := header.Number.Uint64() + if number%s.config.Epoch == 0 { + snap.Votes = nil + snap.Tally = make(map[common.Address]Tally) + } + // Delete the oldest signer from the recent list to allow it signing again + if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { + delete(snap.Recents, number-limit) + } + // Resolve the authorization key and check against signers + signer, err := ecrecover(header, s.sigcache) + if err != nil { + return nil, err + } + if _, ok := snap.Signers[signer]; !ok { + return nil, errUnauthorizedSigner + } + for _, recent := range snap.Recents { + if recent == signer { + return nil, errRecentlySigned + } + } + snap.Recents[number] = signer + + // Header authorized, discard any previous votes from the signer + for i, vote := range snap.Votes { + if vote.Signer == signer && vote.Address == header.Coinbase { + // Uncast the vote from the cached tally + snap.uncast(vote.Address, vote.Authorize) + + // Uncast the vote from the chronological list + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + break // only one vote allowed + } + } + // Tally up the new vote from the signer + //var authorize bool + //switch { + //case bytes.Equal(header.Nonce[:], nonceAuthVote): + // authorize = true + //case bytes.Equal(header.Nonce[:], nonceDropVote): + // authorize = false + //default: + // return nil, errInvalidVote + //} + //if snap.cast(header.Coinbase, authorize) { + // snap.Votes = append(snap.Votes, &Vote{ + // Signer: signer, + // Block: number, + // Address: header.Coinbase, + // Authorize: authorize, + // }) + //} + // If the vote passed, update the list of signers + if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 { + if tally.Authorize { + snap.Signers[header.Coinbase] = struct{}{} + } else { + delete(snap.Signers, header.Coinbase) + + // Signer list shrunk, delete any leftover recent caches + if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { + delete(snap.Recents, number-limit) + } + // Discard any previous votes the deauthorized signer cast + for i := 0; i < len(snap.Votes); i++ { + if snap.Votes[i].Signer == header.Coinbase { + // Uncast the vote from the cached tally + snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) + + // Uncast the vote from the chronological list + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + + i-- + } + } + } + // Discard any previous votes around the just changed account + for i := 0; i < len(snap.Votes); i++ { + if snap.Votes[i].Address == header.Coinbase { + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + i-- + } + } + delete(snap.Tally, header.Coinbase) + } + // If we're taking too much time (ecrecover), notify the user once a while + if time.Since(logged) > 8*time.Second { + log.Info("Reconstructing voting history", "processed", i, "total", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) + logged = time.Now() + } + } + if time.Since(start) > 8*time.Second { + log.Info("Reconstructed voting history", "processed", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) + } + snap.Number += uint64(len(headers)) + snap.Hash = headers[len(headers)-1].Hash() + + return snap, nil +} + +// signers retrieves the list of authorized signers in ascending order. +func (s *Snapshot) signers() []common.Address { + sigs := make([]common.Address, 0, len(s.Signers)) + for sig := range s.Signers { + sigs = append(sigs, sig) + } + sort.Sort(signersAscending(sigs)) + return sigs +} + +// inturn returns if a signer at a given block height is in-turn or not. +func (s *Snapshot) inturn(number uint64, signer common.Address) bool { + signers, offset := s.signers(), 0 + for offset < len(signers) && signers[offset] != signer { + offset++ + } + return (number % uint64(len(signers))) == uint64(offset) +} diff --git a/core/chain_indexer.go b/core/chain_indexer.go index 066bca10009b..3631a04e03a9 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -401,7 +401,7 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com } header := rawdb.ReadHeader(c.chainDb, hash, number) if header == nil { - return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4]) + return common.Hash{}, fmt.Errorf("block #%d [%x] not found", number, hash) } else if header.ParentHash != lastHead { return common.Hash{}, fmt.Errorf("chain reorged during section processing") } diff --git a/core/gen_genesis.go b/core/gen_genesis.go index bb8ea1d6a239..032d4f0a7b1e 100644 --- a/core/gen_genesis.go +++ b/core/gen_genesis.go @@ -15,6 +15,7 @@ import ( var _ = (*genesisSpecMarshaling)(nil) +// MarshalJSON marshals as JSON. func (g Genesis) MarshalJSON() ([]byte, error) { type Genesis struct { Config *params.ChainConfig `json:"config"` @@ -29,6 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) { Number math.HexOrDecimal64 `json:"number"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` + Seal Seal `json:"seal"` } var enc Genesis enc.Config = g.Config @@ -48,9 +50,11 @@ func (g Genesis) MarshalJSON() ([]byte, error) { enc.Number = math.HexOrDecimal64(g.Number) enc.GasUsed = math.HexOrDecimal64(g.GasUsed) enc.ParentHash = g.ParentHash + enc.Seal = g.Seal return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (g *Genesis) UnmarshalJSON(input []byte) error { type Genesis struct { Config *params.ChainConfig `json:"config"` @@ -65,6 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { Number *math.HexOrDecimal64 `json:"number"` GasUsed *math.HexOrDecimal64 `json:"gasUsed"` ParentHash *common.Hash `json:"parentHash"` + Seal *Seal `json:"seal"` } var dec Genesis if err := json.Unmarshal(input, &dec); err != nil { @@ -112,5 +117,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error { if dec.ParentHash != nil { g.ParentHash = *dec.ParentHash } + if dec.Seal != nil { + g.Seal = *dec.Seal + } return nil } diff --git a/core/gen_genesis_account.go b/core/gen_genesis_account.go index 64fb9b9248f9..a9d47e6ba355 100644 --- a/core/gen_genesis_account.go +++ b/core/gen_genesis_account.go @@ -14,6 +14,7 @@ import ( var _ = (*genesisAccountMarshaling)(nil) +// MarshalJSON marshals as JSON. func (g GenesisAccount) MarshalJSON() ([]byte, error) { type GenesisAccount struct { Code hexutil.Bytes `json:"code,omitempty"` @@ -36,6 +37,7 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) { return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (g *GenesisAccount) UnmarshalJSON(input []byte) error { type GenesisAccount struct { Code *hexutil.Bytes `json:"code,omitempty"` diff --git a/core/gen_genesis_seal.go b/core/gen_genesis_seal.go new file mode 100644 index 000000000000..2c23206e06b1 --- /dev/null +++ b/core/gen_genesis_seal.go @@ -0,0 +1,42 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package core + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*genesisSealMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s Seal) MarshalJSON() ([]byte, error) { + type Seal struct { + Step hexutil.Bytes `json:"step,omitempty"` + Signature hexutil.Bytes `json:"signature,omitempty"` + } + var enc Seal + enc.Step = s.Step + enc.Signature = s.Signature + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *Seal) UnmarshalJSON(input []byte) error { + type Seal struct { + Step *hexutil.Bytes `json:"step,omitempty"` + Signature *hexutil.Bytes `json:"signature,omitempty"` + } + var dec Seal + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Step != nil { + s.Step = *dec.Step + } + if dec.Signature != nil { + s.Signature = *dec.Signature + } + return nil +} diff --git a/core/genesis.go b/core/genesis.go index 4525b9c17440..a5a753d1004c 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -41,6 +41,7 @@ import ( //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go +//go:generate gencodec -type Seal -field-override genesisSealMarshaling -out gen_genesis_seal.go var errGenesisNoConfig = errors.New("genesis has no chain configuration") @@ -62,6 +63,9 @@ type Genesis struct { Number uint64 `json:"number"` GasUsed uint64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` + + // Seal field is used for aura consensus engine + Seal Seal `json:"seal"` } // GenesisAlloc specifies the initial state that is part of the genesis block. @@ -79,6 +83,12 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { return nil } +// Seal is a struct for aura consensus engine +type Seal struct { + Step []byte `json:"step,omitempty"` + Signature []byte `json:"signature,omitempty"` +} + // GenesisAccount is an account in the state of the genesis block. type GenesisAccount struct { Code []byte `json:"code,omitempty"` @@ -108,6 +118,12 @@ type genesisAccountMarshaling struct { PrivateKey hexutil.Bytes } +// Seal marshaling struct used for gencodec +type genesisSealMarshaling struct { + Step hexutil.Bytes + Signature hexutil.Bytes +} + // storageJSON represents a 256 bit byte array, but allows less than 256 bits when // unmarshaling from hex. type storageJSON common.Hash @@ -267,18 +283,25 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { } root := statedb.IntermediateRoot(false) head := &types.Header{ - Number: new(big.Int).SetUint64(g.Number), - Nonce: types.EncodeNonce(g.Nonce), - Time: g.Timestamp, - ParentHash: g.ParentHash, - Extra: g.ExtraData, - GasLimit: g.GasLimit, - GasUsed: g.GasUsed, - Difficulty: g.Difficulty, - MixDigest: g.Mixhash, - Coinbase: g.Coinbase, - Root: root, + ParentHash: g.ParentHash, + Time: g.Timestamp, + Number: new(big.Int).SetUint64(g.Number), + Coinbase: g.Coinbase, + TxHash: types.EmptyRootHash, + UncleHash: types.EmptyUncleHash, + Extra: g.ExtraData, + Root: root, + ReceiptHash: types.EmptyRootHash, + GasUsed: g.GasUsed, + GasLimit: g.GasLimit, + Difficulty: g.Difficulty, + Seal: make([][]byte, 2), } + + // assign step and signature in header + head.Seal[0] = g.Seal.Step + head.Seal[1] = g.Seal.Signature + if g.GasLimit == 0 { head.GasLimit = params.GenesisGasLimit } @@ -380,6 +403,18 @@ func DefaultGoerliGenesisBlock() *Genesis { } } +// DefaultLuksoGenesisBlock returns the Lukso-aura network genesis block. +func DefaultLuksoGenesisBlock() *Genesis { + return &Genesis{ + Config: params.LuksoChainConfig, + Timestamp: 0, + ExtraData: hexutil.MustDecode("0x"), + GasLimit: 6000000, + Difficulty: big.NewInt(131072), + Alloc: decodePrealloc(luksoAllocData), + } +} + func DefaultYoloV1GenesisBlock() *Genesis { return &Genesis{ Config: params.YoloV1ChainConfig, diff --git a/core/genesis_alloc.go b/core/genesis_alloc.go index 3e03d16407b5..5fe04c079dc2 100644 --- a/core/genesis_alloc.go +++ b/core/genesis_alloc.go @@ -26,3 +26,4 @@ const ropstenAllocData = "\xf9\x03\xa4\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03 const rinkebyAllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x941\xb9\x8d\x14\x00{\xde\xe67)\x80\x86\x98\x8a\v\xbd1\x18E#\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" const goerliAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" const yoloV1AllocData = "\xf9\x03\xb7\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf6\x94\x8a7\x86o\xd3b|\x92\x05\xa3|\x86\x85fo2\xec\a\xbb\x1b\xa0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +const luksoAllocData = "\xf9\x04\x06\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\u007f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xe0\x94L*\xe4\x82Y5\x05\xf0\x16<\xde\xfc\a>\x81\xc6<\xdaA\a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa8\xe8\xf1G2e\x8eKQ\xe8q\x191\x05:\x8ai\xba\xf2\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe1\x94\u0665\x17\x9f\t\x1d\x85\x05\x1d<\x98'\x85\xef\xd1E\\\uc199\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbJG\xe3\xc1$H\xf4\xad\x00\x00\x00" \ No newline at end of file diff --git a/core/headerchain.go b/core/headerchain.go index f5a8e21cfc6c..d9141e73c5ad 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -427,15 +427,41 @@ func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int { func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { // Short circuit if the header's already in the cache, retrieve otherwise if header, ok := hc.headerCache.Get(hash); ok { - return header.(*types.Header) + typesHeader := header.(*types.Header) + + // Lets fix the hash? It MUST be proper, no way to hashes not matching. + // If it is found, it should be valid. + if hash.String() == typesHeader.Hash().String() { + return typesHeader + } + + log.Warn( + "Invalid header hash from cache, trying from db", + "expected", + hash.String(), + "got", + typesHeader.Hash().String(), + ) } header := rawdb.ReadHeader(hc.chainDb, hash, number) if header == nil { return nil } + // Cache the found header for next time and return - hc.headerCache.Add(hash, header) - return header + if hash.String() == header.Hash().String() { + hc.headerCache.Add(hash, header) + return header + } + + log.Warn( + "Invalid header hash from database, returning nil", + "expected", + hash.String(), + "got", + header.Hash().String(), + ) + return nil } // GetHeaderByHash retrieves a block header from the database by hash, caching it if diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index c948cdc7c60e..bd3720375c5a 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -290,6 +290,32 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu if len(data) > 0 && crypto.Keccak256Hash(data) == hash { return data } + + if len(data) < 1 { + return nil + } + + // Try to decode it into aura header and then hash it + var ( + header types.Header + auraHeader types.AuraHeader + ) + err := rlp.DecodeBytes(data, &header) + + if nil != err { + return nil + } + + err = auraHeader.FromHeader(&header) + + if nil != err { + return nil + } + + if hash == auraHeader.Hash() { + return data + } + return nil // Can't find the data anywhere. } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 074c24d8fec7..06f4fd68ce58 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/hex" "fmt" + "github.com/stretchr/testify/assert" "io/ioutil" "math/big" "os" @@ -47,7 +48,7 @@ func TestHeaderStorage(t *testing.T) { if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil { t.Fatalf("Stored header not found") } else if entry.Hash() != header.Hash() { - t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header) + t.Fatalf("Retrieved header mismatch: have %x, want %x", entry.Hash(), header.Hash()) } if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { t.Fatalf("Stored header RLP not found") @@ -66,6 +67,43 @@ func TestHeaderStorage(t *testing.T) { } } +func TestEncodeAndDecodeAuraToDatabase(t *testing.T) { + t.Run("Block 1 from parity", func(t *testing.T) { + number := uint64(1) + expectedDataHash := common.HexToHash("0x4d286e4f0dbce8d54b27ea70c211bc4b00c8a89ac67f132662c6dc74d9b294e4") + + t.Run("should not find any value", func(t *testing.T) { + db := NewMemoryDatabase() + defer func() { + _ = db.Close() + }() + rawValue := ReadHeaderRLP(db, expectedDataHash, number) + assert.Nil(t, rawValue) + }) + + t.Run("should find hash in leveldb", func(t *testing.T) { + db := NewMemoryDatabase() + defer func() { + _ = db.Close() + }() + block1Data := "0xf9026ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f84c8884e6141300000000b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + block1Bytes := common.FromHex(block1Data) + levelDbHeaderKey := headerKey(number, expectedDataHash) + err := db.Put(levelDbHeaderKey, block1Bytes) + assert.Nil(t, err) + + // mock behaviour of get block 1 from leveldb + blockBytes, err := db.Get(levelDbHeaderKey) + assert.Nil(t, err) + assert.NotEmpty(t, blockBytes) + assert.Equal(t, block1Bytes, blockBytes) + + rawValue := ReadHeaderRLP(db, expectedDataHash, number) + assert.NotNil(t, rawValue) + }) + }) +} + // Tests block body storage and retrieval operations. func TestBodyStorage(t *testing.T) { db := NewMemoryDatabase() diff --git a/core/types/block.go b/core/types/block.go index 8096ebb75516..51087e0c6175 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -23,6 +23,7 @@ import ( "io" "math/big" "reflect" + "sort" "sync" "sync/atomic" "time" @@ -83,8 +84,31 @@ type Header struct { GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` + MixDigest common.Hash `json:"mixHash,omitempty"` + Nonce BlockNonce `json:"nonce,omitempty"` + + // seal field for aura engine + Seal [][]uint8 `json:"seal"` +} + +//go:generate gencodec -type AuraHeader -field-override headerMarshaling -out gen_aura_header_json.go +type AuraHeader struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + Step uint64 `json:"step" gencodec:"required"` + SealFields []interface{} `json:"sealFields" gencodec:"required" rlp:"-"` + Signature []byte `json:"-"` } // field type overrides for gencodec @@ -98,10 +122,65 @@ type headerMarshaling struct { Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON } +func (auraHeader *AuraHeader) Hash() common.Hash { + currentSeal := make([][]byte, 2) + + for index, seal := range auraHeader.SealFields { + sealBytes, ok := seal.([]byte) + + if !ok { + continue + } + + currentSeal[index] = sealBytes + } + + return rlpHash(auraHeader) +} + // Hash returns the block hash of the header, which is simply the keccak256 hash of its // RLP encoding. func (h *Header) Hash() common.Hash { - return rlpHash(h) + // TODO : Keccak256 of RLP encoded Aura header. Needs to check when header sync and sealing work + if h.Seal == nil { + return rlpHash(h) + } + + auraHeader := AuraHeader{ + ParentHash: h.ParentHash, + UncleHash: h.UncleHash, + Coinbase: h.Coinbase, + Root: h.Root, + TxHash: h.TxHash, + ReceiptHash: h.ReceiptHash, + Bloom: h.Bloom, + Difficulty: h.Difficulty, + Number: h.Number, + GasLimit: h.GasLimit, + GasUsed: h.GasUsed, + Time: h.Time, + Extra: h.Extra, + } + + // fields are not propagated, we should return normal header + // TODO: deduce how to remove this recursion + if len(h.Seal) < 1 { + return rlpHash(h) + } + + step := h.Seal[0] + signature := h.Seal[1] + + // If step is like 0x or is invalid cast it to 0 in []uint8 format + if len(step) != 8 { + step = make([]byte, 8) + binary.LittleEndian.PutUint64(step, 0) + } + + auraHeader.Step = binary.LittleEndian.Uint64(step) + auraHeader.Signature = signature + + return rlpHash(auraHeader) } var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size()) @@ -121,7 +200,7 @@ func (h *Header) SanityCheck() error { return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen()) } if h.Difficulty != nil { - if diffLen := h.Difficulty.BitLen(); diffLen > 80 { + if diffLen := h.Difficulty.BitLen(); diffLen > 168 { return fmt.Errorf("too large block difficulty: bitlen %d", diffLen) } } @@ -165,6 +244,42 @@ type Body struct { Uncles []*Header } +// TODO: I know that it can be done via inheritance but too much work for now. +type AuraBlock struct { + Header *AuraHeader + Uncles []*Header + Transactions Transactions + Rest []interface{} `rlp:"tail"` +} + +func (auraBlock *AuraBlock) FromBlock(block *Block) (err error) { + auraBlock.Transactions = block.transactions + auraBlock.Uncles = block.uncles + header := block.header + auraBlock.Header = &AuraHeader{} + err = auraBlock.Header.FromHeader(header) + + return +} + +func (auraBlock *AuraBlock) TranslateIntoBlock() (err error, block *Block) { + header := auraBlock.Header + + if nil == header { + return fmt.Errorf("header in aura block is nil"), nil + } + + standardHeader := header.TranslateIntoHeader() + + block = &Block{ + header: standardHeader, + uncles: auraBlock.Uncles, + transactions: auraBlock.Transactions, + } + + return +} + // Block represents an entire block in the Ethereum blockchain. type Block struct { header *Header @@ -214,6 +329,94 @@ type storageblock struct { TD *big.Int } +func (auraHeader *AuraHeader) FromHeader(header *Header) (err error) { + sealLen := len(header.Seal) + + if sealLen < 2 { + err = fmt.Errorf("expected 2 or more Seal fields, got: %d", sealLen) + return + } + + sealStepCheck := func()bool { return len(header.Seal[0]) == 8 } + genesisBlockCheck := header.Number.Uint64() == 0 + + if genesisBlockCheck && !sealStepCheck() { + stepBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(stepBytes, 0) + header.Seal[0] = stepBytes + } + + if !sealStepCheck() { + err = fmt.Errorf("expected 8 bytes in step") + return + } + + + auraHeader.ParentHash = header.ParentHash + auraHeader.UncleHash = header.UncleHash + auraHeader.Coinbase = header.Coinbase + auraHeader.Root = header.Root + auraHeader.TxHash = header.TxHash + auraHeader.ReceiptHash = header.ReceiptHash + auraHeader.Bloom = header.Bloom + auraHeader.Difficulty = header.Difficulty + auraHeader.Number = header.Number + auraHeader.GasLimit = header.GasLimit + auraHeader.GasUsed = header.GasUsed + auraHeader.Time = header.Time + auraHeader.Extra = header.Extra + auraHeader.Step = binary.LittleEndian.Uint64(header.Seal[0]) + auraHeader.Signature = header.Seal[1] + auraHeader.SealFields = make([]interface{}, 2) + auraHeader.SealFields[0] = auraHeader.Step + auraHeader.SealFields[1] = auraHeader.Signature + + return +} + +func (auraHeader *AuraHeader) TranslateIntoHeader() (header *Header) { + currentSeal := make([][]byte, 2) + + // From Json there is no Header Seal + for index, seal := range auraHeader.SealFields { + sealBytes, ok := seal.([]byte) + + if !ok { + continue + } + + currentSeal[index] = sealBytes + } + + // From RLP there is no SealFields + if len(auraHeader.SealFields) < 1 { + stepBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(stepBytes, auraHeader.Step) + + currentSeal[0] = stepBytes + currentSeal[1] = auraHeader.Signature + } + + header = &Header{ + ParentHash: auraHeader.ParentHash, + UncleHash: auraHeader.UncleHash, + Coinbase: auraHeader.Coinbase, + Root: auraHeader.Root, + TxHash: auraHeader.TxHash, + ReceiptHash: auraHeader.ReceiptHash, + Bloom: auraHeader.Bloom, + Difficulty: auraHeader.Difficulty, + Number: auraHeader.Number, + GasLimit: auraHeader.GasLimit, + GasUsed: auraHeader.GasUsed, + Time: auraHeader.Time, + Extra: auraHeader.Extra, + Seal: currentSeal, + } + + return +} + // NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. @@ -282,6 +485,9 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { var eb extblock _, size, _ := s.Kind() if err := s.Decode(&eb); err != nil { + // [19 21 174 194] + //panic(fmt.Sprintf("%v", *b)) + return err } b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs @@ -415,3 +621,26 @@ func (b *Block) Hash() common.Hash { } type Blocks []*Block + +type BlockBy func(b1, b2 *Block) bool + +func (self BlockBy) Sort(blocks Blocks) { + bs := blockSorter{ + blocks: blocks, + by: self, + } + sort.Sort(bs) +} + +type blockSorter struct { + blocks Blocks + by func(b1, b2 *Block) bool +} + +func (self blockSorter) Len() int { return len(self.blocks) } +func (self blockSorter) Swap(i, j int) { + self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +} +func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } diff --git a/core/types/block_test.go b/core/types/block_test.go index 4dfdcf95457f..814eb47dac10 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -18,9 +18,14 @@ package types import ( "bytes" + "encoding/hex" + "encoding/json" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" "hash" "math/big" "reflect" + "strings" "testing" "github.com/ethereum/go-ethereum/common" @@ -69,6 +74,159 @@ func TestBlockEncoding(t *testing.T) { } } +func TestBlockEncodingAuraHeader(t *testing.T) { + msg7FromNode0 := "f9023ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69841314e684b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + blockEncAuraHeader := common.FromHex(msg7FromNode0) + var auraHeader AuraHeader + err := rlp.DecodeBytes(blockEncAuraHeader, &auraHeader) + assert.Nil(t, err) +} + +func TestBlockEncodingAura(t *testing.T) { + t.Run("Should encode block from json", func(t *testing.T) { + getLatestBlockResponse := ` + { + "author": "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "difficulty":"0x400", + "extraData": "0xdb830300018c4f70656e457468657265756d86312e34332e31826c69", + "gasLimit": "0x8000000", + "gasUsed": "0x0", + "hash": "0x3b9384a861bba3bea8f28161f6fabbca4a6215cead9ce7c363536ffd70ffb63f", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", + "number": "0xCbCd", + "parentHash": "0xe076375bfb9bb5eceacbace9562a5b07e299dfc9455b24f9f5a8e08fa703e944", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields": ["0x8413167e1c", "0xb8414443597ee9435882330f0edfa604a7dfe896a6ab47becb5e8289e995285a170f035a3dae13a510c73be3430ce1ec116138f7fe65e8017e635929dbc32a58853901"], + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature": "4443597ee9435882330f0edfa604a7dfe896a6ab47becb5e8289e995285a170f035a3dae13a510c73be3430ce1ec116138f7fe65e8017e635929dbc32a58853901", + "size": 584, + "stateRoot": "0x40cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133b", + "step": 320241180, + "timestamp": "0x5F70768C", + "totalDifficulty": 1.7753551929366122454274643393537642576131607e+43, + "transactions": [], + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + }` + + var auraHeader AuraHeader + jsonBytes := []byte(getLatestBlockResponse) + err := json.Unmarshal(jsonBytes, &auraHeader) + assert.Nil(t, err) + + stdHeader := auraHeader.TranslateIntoHeader() + assert.Nil(t, err) + assert.IsType(t, &Header{}, stdHeader) + + var buf bytes.Buffer + block := NewBlock(stdHeader, nil, nil, nil, nil) + err = block.EncodeRLP(&buf) + assert.Nil(t, err) + + var auraHeaderRlpBytes bytes.Buffer + err = rlp.Encode(&auraHeaderRlpBytes, &auraHeader) + assert.Nil(t, err) + + t.Run("Rlp decode into standard block", func(t *testing.T) { + var stdBlock Block + err = rlp.Decode(&buf, &stdBlock) + assert.Nil(t, err) + header := stdBlock.Header() + assert.NotNil(t, header) + assert.NotEmpty(t, header.Seal) + }) + + t.Run("Rlp decode into aura header", func(t *testing.T) { + var decodedAuraHeader AuraHeader + err = rlp.Decode(&auraHeaderRlpBytes, &decodedAuraHeader) + assert.Nil(t, err) + }) + }) + + t.Run("Message 0x07 - incoming block", func(t *testing.T) { + msg7FromNode0 := "f9025bf90245f90240a09041480ab2f8b1217f2278e000fd5198d58e8c31b6180946da6bbcc20b516055a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffd828d5b837a120080845f6e06189cdb830300018c4f70656e457468657265756d86312e34332e31826c698413160138b841e9669a4e282d5e6fd2e09ae6f4c7253cead13da53456653eec3212e70c61d2be54f370334c684b102d39efeb87543fb84f58f86bdad5f10d0f6e357ee9d093ad01c0c0928d5affffffffffffffffffffffffeceb716d" + + type mappedAura struct { + Block *AuraBlock + TD *big.Int + } + + var mappedAuraResp mappedAura + input, err := hex.DecodeString(msg7FromNode0) + assert.Nil(t, err) + err = rlp.Decode(bytes.NewReader(input), &mappedAuraResp) + assert.Nil(t, err) + + auraBlock := mappedAuraResp.Block + assert.NotNil(t, auraBlock) + err, stdBlock := auraBlock.TranslateIntoBlock() + assert.Nil(t, err) + assert.IsType(t, &Block{}, stdBlock) + + t.Run("Block should be valid", func(t *testing.T) { + stdBlockHash := stdBlock.Hash() + assert.NotNil(t, auraBlock.Header) + stdHeader := auraBlock.Header.TranslateIntoHeader() + stdHeaderHash := stdHeader.Hash() + assert.Equal(t, stdBlock.header, stdHeader) + assert.Equal(t, stdHeaderHash, stdBlockHash) + }) + }) + + t.Run("Message 0x04 - incoming batch of headers", func(t *testing.T) { + msg4Node0 := "f90243f90240a00ca8498075429689026161c395e4238fd4ba4bc61f10b8985f8bad53207472cca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffd82d29c837a120080845f70baa29cdb830300018c4f70656e457468657265756d86312e34332e31826c698413168bbab84196d75288c30ee8e025d3e7058511fb887d4830790169a14d1fdfbf53d266473a6a5ef632cd7e7d725cb3db2c2ce8ce253d599bf44a09faabcc9a32905f21502300" + input, err := hex.DecodeString(msg4Node0) + assert.Nil(t, err) + + var auraHeaders []*AuraHeader + err = rlp.Decode(bytes.NewReader(input), &auraHeaders) + assert.Nil(t, err) + assert.NotEmpty(t, auraHeaders) + }) + + t.Run("Message 0x04 - incoming batch of headers with block 1 included", func(t *testing.T) { + msg4Node0 := "f90241f9023ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69841314e684b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + input, err := hex.DecodeString(msg4Node0) + assert.Nil(t, err) + + var auraHeaders []*AuraHeader + err = rlp.Decode(bytes.NewReader(input), &auraHeaders) + assert.Nil(t, err) + assert.NotEmpty(t, auraHeaders) + + for _, header := range auraHeaders { + if header.Number.Int64() == int64(1) { + hashExpected := "0x4d286e4f0dbce8d54b27ea70c211bc4b00c8a89ac67f132662c6dc74d9b294e4" + assert.Equal(t, hashExpected, header.Hash().String()) + stdHeader := header.TranslateIntoHeader() + stdHeaderHash := stdHeader.Hash() + assert.Equal(t, hashExpected, stdHeaderHash.String()) + messageHash, err := hexutil.Decode("0x1e1eb0a19950239566988fc61cc981b880df57c25c50d879c1f1f4b8d0ce6a71") + pubkey, err := crypto.Ecrecover(messageHash, stdHeader.Seal[1]) + assert.Nil(t, err) + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + println(signer.Hex()) + assert.Equal(t, "0x70ad1a5fba52e27173d23ad87ad97c9bbe249abf", strings.ToLower(signer.Hex())) + } + } + }) + + t.Run("Factory from Header to AuraHeader", func(t *testing.T) { + headerData := "0xf9026ea02778716827366f0a5479d7a907800d183c57382fa7142b84fbb71db143cf788ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470ad1a5fba52e27173d23ad87ad97c9bbe249abfa040cf4430ecaa733787d1a65154a3b9efb560c95d9e324a23b97f0609b539133ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090ffffffffffffffffffffffffeceb197b0183222aa980845f6880949cdb830300018c4f70656e457468657265756d86312e34332e31826c69a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f84c8884e6141300000000b84179d277eb6b97d25776793c1a98639d8d41da413fba24c338ee83bff533eac3695a0afaec6df1b77a48681a6a995798964adec1bb406c91b6bbe35f115a828a4101" + headerBytes := common.FromHex(headerData) + var header Header + err := rlp.DecodeBytes(headerBytes, &header) + assert.Nil(t, err) + var auraHeader AuraHeader + err = auraHeader.FromHeader(&header) + assert.Nil(t, err) + hashExpected := "0x4d286e4f0dbce8d54b27ea70c211bc4b00c8a89ac67f132662c6dc74d9b294e4" + assert.Equal(t, hashExpected, auraHeader.Hash().String()) + }) +} + func TestUncleHash(t *testing.T) { uncles := make([]*Header, 0) h := CalcUncleHash(uncles) diff --git a/core/types/gen_aura_header_json.go b/core/types/gen_aura_header_json.go new file mode 100644 index 000000000000..6ee6fc87bb27 --- /dev/null +++ b/core/types/gen_aura_header_json.go @@ -0,0 +1,146 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*headerMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (a AuraHeader) MarshalJSON() ([]byte, error) { + type AuraHeader struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + Step uint64 `json:"step" gencodec:"required"` + SealFields []interface{} `json:"sealFields" gencodec:"required" rlp:"-"` + Signature []byte `json:"-"` + Hash common.Hash `json:"hash"` + } + var enc AuraHeader + enc.ParentHash = a.ParentHash + enc.UncleHash = a.UncleHash + enc.Coinbase = a.Coinbase + enc.Root = a.Root + enc.TxHash = a.TxHash + enc.ReceiptHash = a.ReceiptHash + enc.Bloom = a.Bloom + enc.Difficulty = (*hexutil.Big)(a.Difficulty) + enc.Number = (*hexutil.Big)(a.Number) + enc.GasLimit = hexutil.Uint64(a.GasLimit) + enc.GasUsed = hexutil.Uint64(a.GasUsed) + enc.Time = hexutil.Uint64(a.Time) + enc.Extra = a.Extra + enc.Step = a.Step + enc.SealFields = a.SealFields + enc.Signature = a.Signature + enc.Hash = a.Hash() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *AuraHeader) UnmarshalJSON(input []byte) error { + type AuraHeader struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + Step *uint64 `json:"step" gencodec:"required"` + SealFields []interface{} `json:"sealFields" gencodec:"required" rlp:"-"` + Signature []byte `json:"-"` + } + var dec AuraHeader + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for AuraHeader") + } + a.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for AuraHeader") + } + a.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for AuraHeader") + } + a.Coinbase = *dec.Coinbase + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for AuraHeader") + } + a.Root = *dec.Root + if dec.TxHash == nil { + return errors.New("missing required field 'transactionsRoot' for AuraHeader") + } + a.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for AuraHeader") + } + a.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for AuraHeader") + } + a.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for AuraHeader") + } + a.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Number == nil { + return errors.New("missing required field 'number' for AuraHeader") + } + a.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for AuraHeader") + } + a.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for AuraHeader") + } + a.GasUsed = uint64(*dec.GasUsed) + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for AuraHeader") + } + a.Time = uint64(*dec.Time) + if dec.Extra == nil { + return errors.New("missing required field 'extraData' for AuraHeader") + } + a.Extra = *dec.Extra + if dec.Step == nil { + return errors.New("missing required field 'step' for AuraHeader") + } + a.Step = *dec.Step + if dec.SealFields == nil { + return errors.New("missing required field 'sealFields' for AuraHeader") + } + a.SealFields = dec.SealFields + if dec.Signature != nil { + a.Signature = dec.Signature + } + return nil +} diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 4212b8d94d25..b4e1c18c1cce 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -29,8 +29,9 @@ func (h Header) MarshalJSON() ([]byte, error) { GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` + MixDigest common.Hash `json:"mixHash, omitempty"` + Nonce BlockNonce `json:"nonce,omitempty"` + Seal [][]uint8 `json:"seal"` Hash common.Hash `json:"hash"` } var enc Header @@ -49,6 +50,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.Extra = h.Extra enc.MixDigest = h.MixDigest enc.Nonce = h.Nonce + enc.Seal = h.Seal enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -69,8 +71,9 @@ func (h *Header) UnmarshalJSON(input []byte) error { GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` + MixDigest *common.Hash `json:"mixHash, omitempty"` + Nonce *BlockNonce `json:"nonce,omitempty"` + Seal [][]uint8 `json:"seal"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -134,5 +137,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.Nonce != nil { h.Nonce = *dec.Nonce } + if dec.Seal != nil { + h.Seal = dec.Seal + } return nil } diff --git a/eth/backend.go b/eth/backend.go index 3fd027137c7f..170526607e8c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/aura" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -197,6 +198,13 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil { return nil, err } + + _, err = eth.authorizeEngine() + + if nil != err { + log.Error(fmt.Sprintf("could not authorize engine. Err: %s", err.Error())) + } + eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) @@ -245,6 +253,9 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co if chainConfig.Clique != nil { return clique.New(chainConfig.Clique, db) } + if chainConfig.Aura != nil { + return aura.New(chainConfig.Aura, db) + } // Otherwise assume proof-of-work switch config.PowMode { case ethash.ModeFake: @@ -441,30 +452,60 @@ func (s *Ethereum) StartMining(threads int) error { price := s.gasPrice s.lock.RUnlock() s.txPool.SetGasPrice(price) + eb, err := s.authorizeEngine() - // Configure the local mining address - eb, err := s.Etherbase() - if err != nil { - log.Error("Cannot start mining without etherbase", "err", err) - return fmt.Errorf("etherbase missing: %v", err) - } - if clique, ok := s.engine.(*clique.Clique); ok { - wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) - if wallet == nil || err != nil { - log.Error("Etherbase account unavailable locally", "err", err) - return fmt.Errorf("signer missing: %v", err) - } - clique.Authorize(eb, wallet.SignData) + if nil != err { + return err } + // If mining is started, we can disable the transaction rejection mechanism // introduced to speed sync times. atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) - go s.miner.Start(eb) } return nil } +// Authorize engine varies on type of consensus +func (s *Ethereum) authorizeEngine() (eb common.Address, err error) { + eb, err = s.Etherbase() + + if err != nil { + log.Error("Cannot autorize engine without etherbase", "err", err) + err = fmt.Errorf("etherbase missing: %v", err) + return + } + + wallet, e := s.accountManager.Find(accounts.Account{Address: eb}) + + if wallet == nil || e != nil { + log.Error("Etherbase account unavailable locally", "err", err) + err = fmt.Errorf("signer missing: %v", err) + return + } + + var emptyAddress common.Address + + // Assign config etherbase to miner if was not previously set + if emptyAddress == s.config.Miner.Etherbase { + s.config.Miner.Etherbase = eb + } + + // Specific engines will be signed by its non interface authorization + switch engine := s.engine.(type) { + case *clique.Clique: + { + engine.Authorize(eb, wallet.SignData) + } + case *aura.Aura: + { + engine.Authorize(eb, wallet.SignData) + } + } + + return +} + // StopMining terminates the miner, both at the consensus engine level as well as // at the block creation level. func (s *Ethereum) StopMining() { diff --git a/eth/handler.go b/eth/handler.go index 7482a2f96ef2..07325310dd93 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/consensus/aura" "math" "math/big" "sync" @@ -388,6 +389,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } defer msg.Discard() + // Handle aura engine separately + engine := pm.blockchain.Engine() + _, isAura := engine.(*aura.Aura) + // Handle the message depending on its contents switch { case msg.Code == StatusMsg: @@ -485,8 +490,18 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { case msg.Code == BlockHeadersMsg: // A batch of headers arrived to one of our previous requests var headers []*types.Header - if err := msg.Decode(&headers); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) + + err := basicDecodeIfNotAura(msg, &headers, isAura) + + if nil != err { + return err + } + + // This fallback is done, because of how rlp is implemented in go-ethereum + // AuraEngine on other clients does not have MixDigest and Nonce, so encoding leads to error + // ideally there could be a switch with fallbacks for each consensus engine that will be implemented + if isAura { + headers = aura.HeadersFromP2PMessage(msg) } // If no headers were received, but we're expencting a checkpoint header, consider it that if len(headers) == 0 && p.syncDrop != nil { @@ -707,9 +722,32 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { case msg.Code == NewBlockMsg: // Retrieve and decode the propagated block var request newBlockData - if err := msg.Decode(&request); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) + + if isAura { + var mappedRequest auraNewBlockData + err = msg.Decode(&mappedRequest) + + if nil != err { + return errResp(ErrDecode, "%v: %v", msg, err) + } + + auraBlock := mappedRequest.Block + err, block := auraBlock.TranslateIntoBlock() + + if nil != err { + return errResp(ErrDecode, "%v: %v", msg, err) + } + + request.Block = block + request.TD = mappedRequest.TD } + + err := basicDecodeIfNotAura(msg, &request, isAura) + + if nil != err { + return err + } + if hash := types.CalcUncleHash(request.Block.Uncles()); hash != request.Block.UncleHash() { log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash()) break // TODO(karalabe): return error eventually, but wait a few releases @@ -944,3 +982,17 @@ func (pm *ProtocolManager) NodeInfo() *NodeInfo { Head: currentBlock.Hash(), } } + +func basicDecodeIfNotAura(msg p2p.Msg, value interface{}, isAura bool) (err error) { + if isAura { + return + } + + err = msg.Decode(&value) + + if nil != err { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + + return +} diff --git a/eth/peer.go b/eth/peer.go index 21b82a19c547..8d774aa08861 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -469,7 +469,22 @@ func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { p.knownBlocks.Pop() } p.knownBlocks.Add(block.Hash()) - return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) + + blockHeader := block.Header() + isAuraBlockType := len(blockHeader.Seal) == 2 + + if !isAuraBlockType { + return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) + } + + auraBlock := &types.AuraBlock{} + err := auraBlock.FromBlock(block) + + if nil != err { + return err + } + + return p2p.Send(p.rw, NewBlockMsg, []interface{}{auraBlock, td}) } // AsyncSendNewBlock queues an entire block for propagation to a remote peer. If @@ -487,9 +502,27 @@ func (p *peer) AsyncSendNewBlock(block *types.Block, td *big.Int) { } } +// SendBlockHeaders sends a batch of block headers to the remote peer. // SendBlockHeaders sends a batch of block headers to the remote peer. func (p *peer) SendBlockHeaders(headers []*types.Header) error { - return p2p.Send(p.rw, BlockHeadersMsg, headers) + if len(headers) < 1 || len(headers[0].Seal) < 2 { + return p2p.Send(p.rw, BlockHeadersMsg, headers) + } + + auraHeaders := make([]*types.AuraHeader, 0) + + for _, header := range headers { + auraHeader := types.AuraHeader{} + err := auraHeader.FromHeader(header) + + if nil != err { + panic(err) + } + + auraHeaders = append(auraHeaders, &auraHeader) + } + + return p2p.Send(p.rw, BlockHeadersMsg, auraHeaders) } // SendBlockBodies sends a batch of block contents to the remote peer. diff --git a/eth/protocol.go b/eth/protocol.go index dc75d6b31a76..01981be87bd3 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -198,6 +198,11 @@ type newBlockData struct { TD *big.Int } +type auraNewBlockData struct { + Block *types.AuraBlock + TD *big.Int +} + // sanityCheck verifies that the values are reasonable, as a DoS protection func (request *newBlockData) sanityCheck() error { if err := request.Block.SanityCheck(); err != nil { @@ -205,7 +210,7 @@ func (request *newBlockData) sanityCheck() error { } //TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times // larger, it will still fit within 100 bits - if tdlen := request.TD.BitLen(); tdlen > 100 { + if tdlen := request.TD.BitLen(); tdlen > 168 { return fmt.Errorf("too large block TD: bitlen %d", tdlen) } return nil diff --git a/eth/sync.go b/eth/sync.go index 26badd1e21c2..12e14d015451 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -259,7 +259,12 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { } mode, ourTD := cs.modeAndLocalHead() op := peerToSyncOp(mode, peer) - if op.td.Cmp(ourTD) <= 0 { + + if nil == ourTD { + ourTD = big.NewInt(0) + } + + if nil != op && op.td.Cmp(ourTD) <= 0 { return nil // We're in sync. } return op diff --git a/go.mod b/go.mod old mode 100755 new mode 100644 index 3da9a262cf8d..e6c2460ec57e --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c github.com/fatih/color v1.3.0 github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc + github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect @@ -58,10 +59,12 @@ require ( github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/mobile v0.0.0-20200801112145-973feb4309de // indirect golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 golang.org/x/text v0.3.3 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 + golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 diff --git a/go.sum b/go.sum old mode 100755 new mode 100644 index 31c2c48221a3..b9a8b4ff6e66 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -45,6 +46,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9 h1:J82+/8rub3qSy0HxEnoYD8cs+HDlHWYrqYXe2Vqxluk= github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -63,12 +65,18 @@ github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcr github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/fatih/color v1.3.0 h1:YehCCcyeQ6Km0D6+IapqPinWBK6y+0eB5umvZXK9WPs= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f h1:Y/gg/utVetS+WS6htAKCTDralkm/8hLIIUAtLFdbdQ8= +github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f/go.mod h1:q+7Z5oyy8cvKF3TakcuihvQvBHFTnXjB+7UP1e2Q+1o= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= +github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c h1:uYNKzPntb8c6DKvP9EfrBjkLkU7pM4lM+uuHSIa8UtU= +github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= @@ -82,6 +90,7 @@ github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -129,6 +138,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= @@ -153,6 +163,7 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c h1:1RHs3tNxjXGHeul8z2t6H2N2TlAqpKe5yryJztRx4Jk= github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -205,22 +216,42 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -231,6 +262,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 h1:AvbQYmiaaaza3cW3QXRyPo5kYgpFIzOAfeAAN7m3qQ4= golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -239,6 +271,15 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9 h1:m9xhlkk2j+sO9WjAgNfTtl505MN7ZkuW69nOcBlp9qY= +golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346 h1:hzJjkvxUIF3bSt+v8N5tBQNx/605vszZJ+3XsIamzZo= +golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= @@ -251,6 +292,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/miner/stress_aura.go b/miner/stress_aura.go new file mode 100644 index 000000000000..ba578d8b030c --- /dev/null +++ b/miner/stress_aura.go @@ -0,0 +1,245 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build none + +// This file contains a miner stress test based on the Clique consensus engine. +package main + +import ( + "bytes" + "crypto/ecdsa" + "io/ioutil" + "math/big" + "math/rand" + "os" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +var ( + chainId = rand.Intn(1000) + 100 +) + +func main() { + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + fdlimit.Raise(2048) + + // Generate a batch of accounts to seal and fund with + faucets := make([]*ecdsa.PrivateKey, 128) + for i := 0; i < len(faucets); i++ { + faucets[i], _ = crypto.GenerateKey() + } + sealers := make([]*ecdsa.PrivateKey, 4) + for i := 0; i < len(sealers); i++ { + sealers[i], _ = crypto.GenerateKey() + } + // Create an Aura network genesis + genesis := makeGenesis(faucets, sealers) + + var ( + nodes []*eth.Ethereum + enodes []*enode.Node + ) + + for _, sealer := range sealers { + // Start the node and wait until it's up + stack, ethBackend, err := makeSealer(genesis) + if err != nil { + panic(err) + } + defer stack.Close() + + for stack.Server().NodeInfo().Ports.Listener == 0 { + time.Sleep(250 * time.Millisecond) + } + // Connect the node to all the previous ones + for _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + nodes = append(nodes, ethBackend) + enodes = append(enodes, stack.Server().Self()) + + // Inject the signer key and start sealing with it + store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + signer, err := store.ImportECDSA(sealer, "") + if err != nil { + panic(err) + } + if err := store.Unlock(signer, ""); err != nil { + panic(err) + } + } + + // Iterate over all the nodes and start signing on them + time.Sleep(3 * time.Second) + for _, node := range nodes { + if err := node.StartMining(1); err != nil { + panic(err) + } + } + time.Sleep(3 * time.Second) + + // Start injecting transactions from the faucet like crazy + nonces := make([]uint64, len(faucets)) + for { + // Pick a random signer node + index := rand.Intn(len(faucets)) + backend := nodes[index%len(nodes)] + + // Create a self transaction and inject into the pool + tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000), nil), types.HomesteadSigner{}, faucets[index]) + if err != nil { + panic(err) + } + if err := backend.TxPool().AddLocal(tx); err != nil { + panic(err) + } + nonces[index]++ + + // Wait if we're too saturated + if pend, _ := backend.TxPool().Stats(); pend > 2048 { + time.Sleep(100 * time.Millisecond) + } + } +} + +// makeGenesis creates a custom Clique genesis block based on some pre-defined +// signer and faucet accounts. +func makeGenesis(faucets []*ecdsa.PrivateKey, sealers []*ecdsa.PrivateKey) *core.Genesis { + // Create a Genesis for aura port + genesis := &core.Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(int64(chainId)), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + YoloV1Block: nil, + EWASMBlock: nil, + Ethash: nil, + Clique: nil, + Aura: ¶ms.AuraConfig{ + Period: uint64(rand.Intn(5)) + 1, + Epoch: 600, + Authorities: make([]common.Address, len(sealers)), + Difficulty: nil, + Signatures: nil, + }, + }, + Timestamp: 0, + ExtraData: hexutil.MustDecode("0x"), + GasLimit: 6000000, + Difficulty: big.NewInt(131072), + } + genesis.GasLimit = 25000000 + + genesis.Config.EIP150Hash = common.Hash{} + + genesis.Alloc = core.GenesisAlloc{} + for _, faucet := range faucets { + genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ + Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), + } + } + // Sort the signers and embed into the extra-data section + signers := make([]common.Address, len(sealers)) + for i, sealer := range sealers { + signers[i] = crypto.PubkeyToAddress(sealer.PublicKey) + } + for i := 0; i < len(signers); i++ { + for j := i + 1; j < len(signers); j++ { + if bytes.Compare(signers[i][:], signers[j][:]) > 0 { + signers[i], signers[j] = signers[j], signers[i] + } + } + } + //genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) + for i, signer := range signers { + genesis.Config.Aura.Authorities[i] = signer + } + + genesis.Coinbase = signers[0] + // Return the genesis block for initialization + return genesis +} + +func makeSealer(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { + // Define the basic configurations for the Ethereum node + datadir, _ := ioutil.TempDir("", "") + + config := &node.Config{ + Name: "geth", + Version: params.Version, + DataDir: datadir, + P2P: p2p.Config{ + ListenAddr: "0.0.0.0:0", + NoDiscovery: true, + MaxPeers: 25, + }, + NoUSB: true, + } + // Start the node and configure a full Ethereum node on it + stack, err := node.New(config) + if err != nil { + return nil, nil, err + } + // Create and register the backend + ethBackend, err := eth.New(stack, ð.Config{ + Genesis: genesis, + NetworkId: genesis.Config.ChainID.Uint64(), + SyncMode: downloader.FullSync, + DatabaseCache: 256, + DatabaseHandles: 256, + TxPool: core.DefaultTxPoolConfig, + GPO: eth.DefaultConfig.GPO, + Miner: miner.Config{ + GasFloor: genesis.GasLimit * 9 / 10, + GasCeil: genesis.GasLimit * 11 / 10, + GasPrice: big.NewInt(1), + }, + }) + if err != nil { + return nil, nil, err + } + + err = stack.Start() + return stack, ethBackend, err +} diff --git a/miner/worker.go b/miner/worker.go index f042fd8e33c8..6938530110f1 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,6 +19,7 @@ package miner import ( "bytes" "errors" + "github.com/ethereum/go-ethereum/consensus/aura" "math/big" "sync" "sync/atomic" @@ -217,8 +218,49 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) - // Sanitize recommit interval if the user-specified one is too short. + // Recommit should match specific engine demands recommit := worker.config.Recommit + + if nil != chainConfig.Aura { + recommit = time.Duration(int(chainConfig.Aura.Period)) * time.Second + worker.disablePreseal() + // skipSealHook will prevent sealing block that is too quick to its parent + worker.skipSealHook = func(t *task) (shouldSkip bool) { + pendingBlock := t.block + blockchain := eth.BlockChain() + currentHeader := blockchain.CurrentHeader() + interval := time.Duration(pendingBlock.Time()-currentHeader.Time) * time.Second + expectedInterval := time.Duration(chainConfig.Aura.Period) * time.Second + shouldSkip = expectedInterval > interval + + if shouldSkip { + log.Info( + "skipping sealing, interval lower than expected", + "expected", + expectedInterval, + "current", + interval, + ) + + return + } + + // It will panic if engine is other than aura + auraEngine := engine.(*aura.Aura) + allowed, _, _ := auraEngine.CheckStep(int64(t.block.Time()), 0) + shouldSkip = !allowed + + if shouldSkip { + log.Info("skipping sealing, wrong step for sealer") + } + + currentHeader.Time = uint64(time.Now().Unix()) + + return + } + } + + // Sanitize recommit interval if the user-specified one is too short. if recommit < minRecommitInterval { log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) recommit = minRecommitInterval @@ -340,12 +382,15 @@ func (w *worker) newWorkLoop(recommit time.Duration) { defer timer.Stop() <-timer.C // discard the initial tick + _, isAuraEngine := w.engine.(*aura.Aura) + // commit aborts in-flight transaction execution with given signal and resubmits a new one. commit := func(noempty bool, s int32) { if interrupt != nil { atomic.StoreInt32(interrupt, s) } interrupt = new(int32) + w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp} timer.Reset(recommit) atomic.StoreInt32(&w.newTxs, 0) @@ -365,7 +410,6 @@ func (w *worker) newWorkLoop(recommit time.Duration) { select { case <-w.startCh: clearPending(w.chain.CurrentBlock().NumberU64()) - timestamp = time.Now().Unix() commit(false, commitInterruptNewHead) case head := <-w.chainHeadCh: @@ -374,6 +418,12 @@ func (w *worker) newWorkLoop(recommit time.Duration) { commit(false, commitInterruptNewHead) case <-timer.C: + if w.isRunning() && isAuraEngine { + timestamp = time.Now().Unix() + commit(true, commitInterruptResubmit) + continue + } + // If mining is running resubmit a new work cycle periodically to pull in // higher priced transactions. Disable this overhead for pending blocks. if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) { @@ -872,6 +922,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) Time: uint64(timestamp), } // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) + // Other check is if aura is running if w.isRunning() { if w.coinbase == (common.Address{}) { log.Error("Refusing to mine without etherbase") diff --git a/miner/worker_test.go b/miner/worker_test.go index a5c558ba5f4b..68165b223d75 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -17,6 +17,9 @@ package miner import ( + "encoding/binary" + "github.com/ethereum/go-ethereum/consensus/aura" + "github.com/stretchr/testify/assert" "math/big" "math/rand" "sync/atomic" @@ -52,6 +55,7 @@ var ( testTxPoolConfig core.TxPoolConfig ethashChainConfig *params.ChainConfig cliqueChainConfig *params.ChainConfig + auraChainConfig *params.ChainConfig // Test accounts testBankKey, _ = crypto.GenerateKey() @@ -81,6 +85,22 @@ func init() { Period: 10, Epoch: 30000, } + authority1, _ := crypto.GenerateKey() + authority2, _ := crypto.GenerateKey() + auraChainConfig = params.TestChainConfig + auraChainConfig.Aura = ¶ms.AuraConfig{ + Period: 5, + Epoch: 500, + Authorities: []common.Address{ + testBankAddress, + crypto.PubkeyToAddress(authority1.PublicKey), + crypto.PubkeyToAddress(authority2.PublicKey), + }, + Difficulty: big.NewInt(int64(131072)), + Signatures: nil, + } + auraChainConfig.Clique = nil + auraChainConfig.Ethash = nil tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) pendingTxs = append(pendingTxs, tx1) tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) @@ -99,19 +119,31 @@ type testWorkerBackend struct { } func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { - var gspec = core.Genesis{ - Config: chainConfig, - Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - } + var ( + gspec = core.Genesis{ + Config: chainConfig, + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + } + signerFunc = func(account accounts.Account, s string, data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), testBankKey) + } + ) switch e := engine.(type) { case *clique.Clique: gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) - e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { - return crypto.Sign(crypto.Keccak256(data), testBankKey) - }) + e.Authorize(testBankAddress, signerFunc) case *ethash.Ethash: + case *aura.Aura: + stepBytes := make([]byte, 8) + signature := make([]byte, crypto.SignatureLength) + binary.LittleEndian.PutUint64(stepBytes, 0) + gspec.Seal = core.Seal{ + Step: stepBytes, + Signature: signature, + } + e.Authorize(testBankAddress, signerFunc) default: t.Fatalf("unexpected consensus engine type: %T", engine) } @@ -184,28 +216,48 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens } func TestGenerateBlockAndImportEthash(t *testing.T) { - testGenerateBlockAndImport(t, false) + db := rawdb.NewMemoryDatabase() + chainConfig := params.AllEthashProtocolChanges + engine := ethash.NewFaker() + testGenerateBlockAndImport(t, engine, chainConfig, db) } func TestGenerateBlockAndImportClique(t *testing.T) { - testGenerateBlockAndImport(t, true) + db := rawdb.NewMemoryDatabase() + chainConfig := params.AllCliqueProtocolChanges + chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} + engine := clique.New(chainConfig.Clique, db) + testGenerateBlockAndImport(t, engine, chainConfig, db) } -func testGenerateBlockAndImport(t *testing.T, isClique bool) { - var ( - engine consensus.Engine - chainConfig *params.ChainConfig - db = rawdb.NewMemoryDatabase() - ) - if isClique { - chainConfig = params.AllCliqueProtocolChanges - chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} - engine = clique.New(chainConfig.Clique, db) - } else { - chainConfig = params.AllEthashProtocolChanges - engine = ethash.NewFaker() - } +func TestGenerateBlockAndImportAura(t *testing.T) { + db := rawdb.NewMemoryDatabase() + // Use config that has 1s period + authority1, _ := crypto.GenerateKey() + period1Config2nodes := ¶ms.AuraConfig{ + // For some reason period 1 fails. Maybe it is due to user-defined resubmit? Or maybe execution is too slow? + Period: 2, + Epoch: 500, + Authorities: []common.Address{ + testBankAddress, + crypto.PubkeyToAddress(authority1.PublicKey), + }, + Difficulty: big.NewInt(int64(1)), + Signatures: nil, + } + currentAuraChainConfig := params.TestChainConfig + currentAuraChainConfig.Aura = period1Config2nodes + engine := aura.New(period1Config2nodes, db) + testGenerateBlockAndImport(t, engine, currentAuraChainConfig, db) +} +// Here pass engine and deduce logic by engine type +func testGenerateBlockAndImport( + t *testing.T, + engine consensus.Engine, + chainConfig *params.ChainConfig, + db ethdb.Database, +) { w, b := newTestWorker(t, chainConfig, engine, db, 0) defer w.close() @@ -227,6 +279,15 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) { // Start mining! w.start() + timeout := time.Duration(3) + _, isAuraEngine := engine.(*aura.Aura) + insertedBlocks := make([]*types.Block, 0) + + if isAuraEngine { + // Wait twice the size of duration + timeout = time.Duration(int(chainConfig.Aura.Period) * len(chainConfig.Aura.Authorities) * 2) + } + for i := 0; i < 5; i++ { b.txPool.AddLocal(b.newRandomTx(true)) b.txPool.AddLocal(b.newRandomTx(false)) @@ -239,10 +300,14 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) { if _, err := chain.InsertChain([]*types.Block{block}); err != nil { t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err) } - case <-time.After(3 * time.Second): // Worker needs 1s to include new changes. + + insertedBlocks = append(insertedBlocks, block) + case <-time.After(timeout * time.Second): // Worker needs 1s to include new changes. In aura logic is different t.Fatalf("timeout") } } + + assert.Equal(t, 5, len(insertedBlocks)) } func TestEmptyWorkEthash(t *testing.T) { diff --git a/mobile/params.go b/mobile/params.go index 43ac00474081..9b11b3a95291 100644 --- a/mobile/params.go +++ b/mobile/params.go @@ -59,6 +59,15 @@ func GoerliGenesis() string { return string(enc) } +// LuksoGenesis returns the JSON spec to use for the Lukso test network +func LuksoGenesis() string { + enc, err := json.Marshal(core.DefaultLuksoGenesisBlock()) + if err != nil { + panic(err) + } + return string(enc) +} + // FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated // by the foundation running the V5 discovery protocol. func FoundationBootnodes() *Enodes { diff --git a/params/bootnodes.go b/params/bootnodes.go index c8736b8ae87e..b8810efc9584 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -63,6 +63,13 @@ var GoerliBootnodes = []string{ "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", } +// LuksoBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Lukso aura test network +var LuksoBootnodes = []string{ + // TODO - Need to change + "enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303", +} + // YoloV1Bootnodes are the enode URLs of the P2P bootstrap nodes running on the // YOLOv1 ephemeral test network. var YoloV1Bootnodes = []string{ diff --git a/params/config.go b/params/config.go index e5ec64b2bf76..50ba6bdd319c 100644 --- a/params/config.go +++ b/params/config.go @@ -31,6 +31,7 @@ var ( RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177") GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") + LuksoGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") YoloV1GenesisHash = common.HexToHash("0xc3fd235071f24f93865b0850bd2a2119b30f7224d18a0e34c7bbf549ad7e3d36") ) @@ -186,7 +187,7 @@ var ( PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(1561651), MuirGlacierBlock: nil, - Clique: &CliqueConfig{ + Aura: &AuraConfig{ Period: 15, Epoch: 30000, }, @@ -213,6 +214,31 @@ var ( Threshold: 2, } + // LuksoChainConfig contains the chain parameters to run a node on the lukso-aura test network. + LuksoChainConfig = &ChainConfig{ + ChainID: big.NewInt(5), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(1561651), + MuirGlacierBlock: nil, + Aura: &AuraConfig{ + Period: 15, + Epoch: 30000, + Authorities: []common.Address{ + common.HexToAddress("0x0082a7bf6aaadab094061747872243059c3c6a07"), + common.HexToAddress("0x00faa37564140c1a5e96095f05466b9f73441e44"), + }, + Difficulty: big.NewInt(131072), + }, + } + // YoloV1ChainConfig contains the chain parameters to run a node on the YOLOv1 test network. YoloV1ChainConfig = &ChainConfig{ ChainID: big.NewInt(133519467574833), @@ -239,16 +265,18 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} + + AuraProtocolChanges = &ChainConfig{big.NewInt(5), big.NewInt(2), nil, false, big.NewInt(2), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, nil, &AuraConfig{Period: 15, Epoch: 30000, Authorities: []common.Address{common.HexToAddress("0x540a9fe3d2381016dec8ffba7235c6fb00b0f942")}, Difficulty: big.NewInt(131072)}} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -326,6 +354,7 @@ type ChainConfig struct { // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` + Aura *AuraConfig `json:"aura,omitempty"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing. @@ -342,11 +371,35 @@ type CliqueConfig struct { Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint } +type Signature []byte +type Signatures []Signature + +// AuraConfig is the consensus engine configs for proof-of-authority based sealing. + +//TODO: THIS IMHO SHOULD BE SAME AS IN PARITY (do not break naming convention) +type AuraConfig struct { + Period uint64 `json:"period"` // Number of seconds between blocks to enforce + Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint + Authorities []common.Address `json:"authorities"` // list of addresses of authorities + Difficulty *big.Int `json:"difficulty"` // Constant block difficulty + Signatures Signatures `json:"signatures"` +} + // String implements the stringer interface, returning the consensus engine details. func (c *CliqueConfig) String() string { return "clique" } +// String implements the stringer interface, returning the consensus engine details. +func (a *AuraConfig) String() string { + return "aura" +} + +// Return diffulty rate for Aura concensus +func (a *AuraConfig) GetDifficulty() (num *big.Int) { + return a.Difficulty +} + // String implements the fmt.Stringer interface. func (c *ChainConfig) String() string { var engine interface{} @@ -355,6 +408,8 @@ func (c *ChainConfig) String() string { engine = c.Ethash case c.Clique != nil: engine = c.Clique + case c.Aura != nil: + engine = c.Aura default: engine = "unknown" } @@ -618,16 +673,5 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { if chainID == nil { chainID = new(big.Int) } - return Rules{ - ChainID: new(big.Int).Set(chainID), - IsHomestead: c.IsHomestead(num), - IsEIP150: c.IsEIP150(num), - IsEIP155: c.IsEIP155(num), - IsEIP158: c.IsEIP158(num), - IsByzantium: c.IsByzantium(num), - IsConstantinople: c.IsConstantinople(num), - IsPetersburg: c.IsPetersburg(num), - IsIstanbul: c.IsIstanbul(num), - IsYoloV1: c.IsYoloV1(num), - } + return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num)} } diff --git a/rlp/decode.go b/rlp/decode.go index 5f3f5eedfd1b..705056da43f1 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -131,7 +131,7 @@ func wrapStreamError(err error, typ reflect.Type) error { case errUintOverflow: return &decodeError{msg: "input string too long", typ: typ} case errNotAtEOL: - return &decodeError{msg: "input list has too many elements", typ: typ} + return &decodeError{msg: fmt.Sprintf("input list has too many elements got: %v", typ), typ: typ} } return err } @@ -150,6 +150,7 @@ var ( func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { kind := typ.Kind() + switch { case typ == rawValueType: return decodeRawValue, nil @@ -748,6 +749,7 @@ func (s *Stream) Decode(val interface{}) error { if val == nil { return errDecodeIntoNil } + rval := reflect.ValueOf(val) rtyp := rval.Type() if rtyp.Kind() != reflect.Ptr { @@ -762,6 +764,7 @@ func (s *Stream) Decode(val interface{}) error { } err = decoder(s, rval.Elem()) + if decErr, ok := err.(*decodeError); ok && len(decErr.ctx) > 0 { // add decode target type to error so context has more meaning decErr.ctx = append(decErr.ctx, fmt.Sprint("(", rtyp.Elem(), ")")) diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 167e9974b96d..5c913a75e1d6 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "io" "math/big" "reflect" @@ -457,6 +458,11 @@ var decodeTests = []decodeTest{ {input: "820001", ptr: new(big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"}, {input: "8105", ptr: new(big.Int), error: "rlp: non-canonical size information for *big.Int"}, + // common hash + // Motivation is that in aura mixDigest is not present + {input: "", ptr: new(common.Hash), value: common.Hash{}}, + {input: "C601C402C203C0", ptr: new(common.Hash), error: "rlp: expected input string or byte for common.Hash"}, + // structs { input: "C50583343434",