diff --git a/cmd/genesis/gen_genesis.go b/cmd/genesis/gen_genesis.go new file mode 100644 index 0000000000..ee03368d1d --- /dev/null +++ b/cmd/genesis/gen_genesis.go @@ -0,0 +1,56 @@ +package main + +import ( + "compress/gzip" + "io" + "log" + "os" + "strings" + + "github.com/spruce-solutions/go-quai/consensus/blake3" + "github.com/spruce-solutions/go-quai/core" + "github.com/spruce-solutions/go-quai/core/rawdb" + "github.com/spruce-solutions/go-quai/params" +) + +func main() { + var ( + testdb = rawdb.NewMemoryDatabase() + genesis = core.RopstenPrimeGenesisBlock().MustCommit(testdb) + fn = "test_knot.rlp" + ) + + blake3Config := blake3.Config{ + MiningThreads: 0, + NotifyFull: true, + } + + blake3Engine, err := blake3.New(blake3Config, nil, false) + if nil != err { + log.Fatal("Failed to create Blake3 engine: ", err) + } + + blocks, _ := core.GenerateKnot(params.TestChainConfig, genesis, blake3Engine, testdb, 9, nil) + + // Open the file handle and potentially wrap with a gzip stream + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + log.Panic("error opening file") + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + + for _, block := range blocks { + + if err := block.EncodeRLP(writer); err != nil { + log.Panic("error writing") + } + } + + log.Println("Exported blockchain", "file", fn) +} diff --git a/consensus/misc/gaslimit.go b/consensus/misc/gaslimit.go index 94b39031ef..0139886d97 100644 --- a/consensus/misc/gaslimit.go +++ b/consensus/misc/gaslimit.go @@ -27,6 +27,7 @@ import ( // in relation to the parent gas limit. func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error { // Verify that the gas limit remains within allowed bounds + fmt.Println("Parent Gas", parentGasLimit, headerGasLimit) diff := int64(parentGasLimit) - int64(headerGasLimit) if diff < 0 { diff *= -1 diff --git a/core/chain_makers.go b/core/chain_makers.go index a0f9950612..58d61a3c24 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -19,7 +19,9 @@ package core import ( "errors" "fmt" + "log" "math/big" + "sync" "github.com/spruce-solutions/go-quai/common" "github.com/spruce-solutions/go-quai/consensus" @@ -204,7 +206,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse chainreader := &fakeChainReader{config: config} genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} - b.header = makeHeader(chainreader, parent, statedb, b.engine) + location := []byte{1, 1} + b.header = makeHeader(chainreader, parent, statedb, b.engine, location) // Execute any user modifications to the block if gen != nil { @@ -239,7 +242,92 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse return blocks, receipts } -func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { +type knot struct { + block *types.Block +} + +func GenerateKnot(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { + if config == nil { + config = params.TestChainConfig + } + blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) + chainreader := &fakeChainReader{config: config} + + resultLoop := func(i int, k *knot, statedb *state.StateDB, results chan *types.Block, stop chan struct{}, wg *sync.WaitGroup) error { + for { + select { + case block := <-results: + context, err := engine.GetDifficultyOrder(block.Header()) + if err != nil { + log.Println("Block mined has an invalid order") + } + + if context == 0 { + log.Println("PRIME:", block.Header().Location, block.Header().Number, block.Header().Hash()) + } + + // Write state changes to db + root, err := statedb.Commit(config.IsEIP158(block.Number())) + if err != nil { + panic(fmt.Sprintf("state write error: %v", err)) + } + if err := statedb.Database().TrieDB().Commit(root, false, nil); err != nil { + panic(fmt.Sprintf("trie write error: %v", err)) + } + + k.block = block + + defer wg.Done() + + return nil + } + } + } + + genblock := func(i int, parent *types.Block, statedb *state.StateDB, location []byte, resultCh chan *types.Block, stopCh chan struct{}) { + b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} + b.header = makeHeader(chainreader, parent, statedb, b.engine, location) + + // Execute any user modifications to the block + if gen != nil { + gen(i, b) + } + if b.engine != nil { + // Finalize and seal the block + block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts) + + // Mine the block + if err := engine.Seal(chainreader, block, resultCh, stopCh); err != nil { + log.Println("Block sealing failed", "err", err) + } + } + } + + knot := &knot{ + block: parent, + } + + locations := [][]byte{[]byte{1, 1}, []byte{1, 2}, []byte{1, 3}, []byte{2, 1}, []byte{2, 2}, []byte{2, 3}, []byte{3, 1}, []byte{3, 2}, []byte{3, 3}} + for i := 0; i < len(locations); i++ { + statedb, err := state.New(parent.Root(), state.NewDatabase(db), nil) + if err != nil { + panic(err) + } + resultCh := make(chan *types.Block) + exitCh := make(chan struct{}) + + var wg sync.WaitGroup + wg.Add(1) + go resultLoop(i, knot, statedb, resultCh, exitCh, &wg) + genblock(i, parent, statedb, locations[i], resultCh, exitCh) + wg.Wait() + blocks[i] = knot.block + parent = knot.block + } + return blocks, receipts +} + +func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine, location []byte) *types.Header { var time uint64 if parent.Time() == 0 { time = 10 @@ -264,32 +352,24 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S GasLimit: []uint64{0, 0, 0}, GasUsed: []uint64{0, 0, 0}, Extra: [][]byte{[]byte(nil), []byte(nil), []byte(nil)}, - } - header.GasLimit[types.QuaiNetworkContext] = parent.GasLimit() - - parentHeader := &types.Header{ - Number: []*big.Int{big.NewInt(int64(1)), big.NewInt(int64(1)), big.NewInt(int64(1))}, - Difficulty: []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, - UncleHash: types.EmptyUncleHash, - Time: time - 10, + Location: location, } - if chain.Config().IsLondon(header.Number[types.QuaiNetworkContext]) { - if !chain.Config().IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit[types.QuaiNetworkContext] = CalcGasLimit(parentGasLimit, parent.GasUsed(), 0) - } - } + // Setting this to params.MinGasLimit for now since + header.GasLimit[types.QuaiNetworkContext] = params.MinGasLimit - parentHeader.Number[types.QuaiNetworkContext] = new(big.Int).Add(parent.Number(), common.Big1) - parentHeader.Difficulty[types.QuaiNetworkContext] = parent.Difficulty() - parentHeader.UncleHash[types.QuaiNetworkContext] = parent.UncleHash() + parentHeader := parent.Header() header.Root[types.QuaiNetworkContext] = state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())) header.ParentHash[types.QuaiNetworkContext] = parent.Hash() header.Coinbase[types.QuaiNetworkContext] = parent.Coinbase() header.Difficulty[types.QuaiNetworkContext] = engine.CalcDifficulty(chain, time, parentHeader, types.QuaiNetworkContext) + header.Number[types.QuaiNetworkContext] = new(big.Int).Add(parent.Number(), common.Big1) + if len(parent.Header().Location) > 0 && header.Location[0] == parent.Header().Location[0] { + header.Number[types.QuaiNetworkContext+1] = new(big.Int).Add(parent.Header().Number[types.QuaiNetworkContext+1], common.Big1) + + } return header } diff --git a/core/genesis.go b/core/genesis.go index a7d4679b33..2c5a596b48 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -47,14 +47,16 @@ var errGenesisNoConfig = errors.New("genesis has no chain configuration") // Genesis specifies the header fields, state of a genesis block. It also defines hard // fork switch-over blocks through the chain configuration. type Genesis struct { - Config *params.ChainConfig `json:"config"` - Nonce uint64 `json:"nonce"` - Timestamp uint64 `json:"timestamp"` - ExtraData [][]byte `json:"extraData"` - GasLimit []uint64 `json:"gasLimit" gencodec:"required"` - Difficulty []*big.Int `json:"difficulty" gencodec:"required"` - Coinbase []common.Address `json:"coinbase"` - Alloc GenesisAlloc `json:"alloc" gencodec:"required"` + Config *params.ChainConfig `json:"config"` + Knot []*types.Block `json:"knot"` + + Nonce uint64 `json:"nonce"` + Timestamp uint64 `json:"timestamp"` + ExtraData [][]byte `json:"extraData"` + GasLimit []uint64 `json:"gasLimit" gencodec:"required"` + Difficulty []*big.Int `json:"difficulty" gencodec:"required"` + Coinbase []common.Address `json:"coinbase"` + Alloc GenesisAlloc `json:"alloc" gencodec:"required"` // These fields are used for consensus tests. Please don't use them // in actual genesis blocks. @@ -288,7 +290,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { Root: []common.Hash{root, root, root}, } - if g.GasLimit[0] == 0 { + if len(g.GasLimit) == 0 { head.GasLimit[types.QuaiNetworkContext] = params.GenesisGasLimit } @@ -350,8 +352,9 @@ func MainnetPrimeGenesisBlock() *Genesis { return &Genesis{ Config: params.MainnetPrimeChainConfig, ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, + Coinbase: []common.Address{common.Address{}, common.Address{}, common.Address{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, - Nonce: 60069, + Nonce: 60066, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, GasUsed: []uint64{0, 0, 0}, @@ -366,7 +369,7 @@ func MainnetRegionGenesisBlock(regionParams *params.ChainConfig) *Genesis { Config: regionParams, ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, - Nonce: 60069, + Nonce: 60066, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, GasUsed: []uint64{0, 0, 0}, @@ -381,7 +384,7 @@ func MainnetZoneGenesisBlock(zoneParams *params.ChainConfig) *Genesis { Config: zoneParams, ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, - Nonce: 60069, + Nonce: 60066, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, GasUsed: []uint64{0, 0, 0}, @@ -394,13 +397,15 @@ func MainnetZoneGenesisBlock(zoneParams *params.ChainConfig) *Genesis { func RopstenPrimeGenesisBlock() *Genesis { return &Genesis{ Config: params.RopstenPrimeChainConfig, + Knot: ReadKnot("test_knot.rlp"), ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, + Coinbase: []common.Address{common.Address{}, common.Address{}, common.Address{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, Nonce: 11, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, GasUsed: []uint64{0, 0, 0}, - Difficulty: []*big.Int{big.NewInt(3448576), big.NewInt(1248576), big.NewInt(168576)}, + Difficulty: []*big.Int{big.NewInt(30), big.NewInt(20), big.NewInt(10)}, Alloc: decodePrealloc(ropstenAllocData), } } @@ -409,8 +414,9 @@ func RopstenPrimeGenesisBlock() *Genesis { func RopstenRegionGenesisBlock(regionParams *params.ChainConfig) *Genesis { return &Genesis{ Config: regionParams, + Knot: ReadKnot("test_knot.rlp"), ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, - Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, + Coinbase: []common.Address{common.Address{}, common.Address{}, common.Address{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, Nonce: 11, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, @@ -424,8 +430,9 @@ func RopstenRegionGenesisBlock(regionParams *params.ChainConfig) *Genesis { func RopstenZoneGenesisBlock(zoneParams *params.ChainConfig) *Genesis { return &Genesis{ Config: zoneParams, + Knot: ReadKnot("test_knot.rlp"), ParentHash: []common.Hash{common.Hash{}, common.Hash{}, common.Hash{}}, - Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, + Coinbase: []common.Address{common.Address{}, common.Address{}, common.Address{}}, Number: []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}, Nonce: 11, ExtraData: [][]byte{hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")}, GasLimit: []uint64{500000, 500000, 500000}, diff --git a/core/knot.go b/core/knot.go new file mode 100644 index 0000000000..680e48847e --- /dev/null +++ b/core/knot.go @@ -0,0 +1,50 @@ +package core + +import ( + "compress/gzip" + "fmt" + "io" + "os" + "strings" + + "github.com/spruce-solutions/go-quai/core/types" + "github.com/spruce-solutions/go-quai/rlp" +) + +func ReadKnot(chainfile string) []*types.Block { + // Load chain.rlp. + fmt.Println("Loading ReadKnot") + fh, err := os.Open(chainfile) + if err != nil { + fmt.Println("OPEN ERR 1", err) + return nil + } + defer fh.Close() + var reader io.Reader = fh + if strings.HasSuffix(chainfile, ".gz") { + if reader, err = gzip.NewReader(reader); err != nil { + fmt.Println("READER ERR 1", err) + return nil + } + } + stream := rlp.NewStream(reader, 0) + var blocks = make([]*types.Block, 1) + for i := 0; ; i++ { + var b types.Block + if err := stream.Decode(&b); err == io.EOF { + break + } else if err != nil { + fmt.Println("DECODE ERR 1", err) + return nil + } + if b.NumberU64() != uint64(i+1) { + fmt.Println("DECODE ERR 2", err) + return nil + } + blocks = append(blocks, &b) + } + for _, block := range blocks { + fmt.Println(block) + } + return blocks +} diff --git a/eth/backend.go b/eth/backend.go index 7fb5c80d1c..0e41a77e68 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -18,6 +18,7 @@ package eth import ( + "bytes" "errors" "fmt" "math/big" @@ -135,6 +136,41 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } + + knotSet := make([]*types.Block, 0) + switch types.QuaiNetworkContext { + case params.PRIME: + knotSet = config.Genesis.Knot + case params.REGION: + for i, block := range config.Genesis.Knot { + if i != 0 { + if block.Header().Location[0] == chainConfig.Location[0] { + knotSet = append(knotSet, block) + } + } + } + case params.ZONE: + for i, block := range config.Genesis.Knot { + if i != 0 { + if bytes.Equal(block.Header().Location, chainConfig.Location) { + knotSet = append(knotSet, block) + } + } + } + } + + for i, block := range knotSet { + if i != 0 { + rawdb.WriteTd(chainDb, block.Hash(), block.NumberU64(), config.Genesis.Difficulty) + rawdb.WriteBlock(chainDb, block) + rawdb.WriteReceipts(chainDb, block.Hash(), block.NumberU64(), nil) + rawdb.WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + rawdb.WriteHeadBlockHash(chainDb, block.Hash()) + rawdb.WriteHeadFastBlockHash(chainDb, block.Hash()) + rawdb.WriteHeadHeaderHash(chainDb, block.Hash()) + } + } + log.Info("Initialised chain configuration", "config", chainConfig) if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { @@ -195,6 +231,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) diff --git a/test_knot.rlp b/test_knot.rlp new file mode 100755 index 0000000000..d640a176ff Binary files /dev/null and b/test_knot.rlp differ