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",