aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSonic <sonic@dexon.org>2018-11-05 11:26:27 +0800
committerWei-Ning Huang <w@dexon.org>2019-04-09 21:32:52 +0800
commitd9957d3bd4ec2b8b2f936d12a722942313a46076 (patch)
tree6530a5274edca63eece264d57228eb9274968398
parent993bced1877026e6a96c7768bbc482199e270527 (diff)
downloaddexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar.gz
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar.bz2
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar.lz
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar.xz
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.tar.zst
dexon-d9957d3bd4ec2b8b2f936d12a722942313a46076.zip
core: GenerateChainWithRoundChange for testing
-rw-r--r--core/chain_makers.go482
-rw-r--r--core/chain_makers_test.go110
-rw-r--r--core/genesis.go6
3 files changed, 598 insertions, 0 deletions
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 40d34fd93..b464555d3 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -17,8 +17,19 @@
package core
import (
+ "crypto/ecdsa"
+ "encoding/binary"
"fmt"
"math/big"
+ "math/rand"
+ "time"
+
+ coreCommon "github.com/dexon-foundation/dexon-consensus/common"
+ coreCrypto "github.com/dexon-foundation/dexon-consensus/core/crypto"
+ coreDKG "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg"
+ coreEcdsa "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa"
+ coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"
+ coreTypesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg"
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/consensus"
@@ -26,10 +37,16 @@ import (
"github.com/dexon-foundation/dexon/core/state"
"github.com/dexon-foundation/dexon/core/types"
"github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/crypto"
"github.com/dexon-foundation/dexon/ethdb"
"github.com/dexon-foundation/dexon/params"
+ "github.com/dexon-foundation/dexon/rlp"
)
+func init() {
+ rand.Seed(time.Now().UTC().UnixNano())
+}
+
// BlockGen creates blocks for testing.
// See GenerateChain for a detailed explanation.
type BlockGen struct {
@@ -248,6 +265,137 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
}
}
+func GenerateChainWithRoundChange(config *params.ChainConfig, parent *types.Block,
+ engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen),
+ nodeSet *NodeSet, roundInterval int) ([]*types.Block, []types.Receipts) {
+ if config == nil {
+ config = params.TestChainConfig
+ }
+
+ round := parent.Header().Round
+
+ blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
+ chainreader := &fakeChainReader{config: config}
+ genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
+ b := &BlockGen{i: i, parent: parent, chain: blocks, statedb: statedb, config: config, engine: engine}
+ b.header = makeHeader(chainreader, parent, statedb, b.engine)
+ b.header.DexconMeta = makeDexconMeta(round, parent, nodeSet)
+
+ switch i % roundInterval {
+ case 0:
+ // First block of this round, notify round height
+ tx := nodeSet.NotifyRoundHeightTx(round, b.header.Number.Uint64(), b)
+ b.AddTx(tx)
+ case roundInterval / 2:
+ // Run DKG for next round part 1, AddMasterPublicKey
+ nodeSet.RunDKG(round, 2)
+ for _, node := range nodeSet.nodes[round] {
+ tx := node.MasterPublicKeyTx(round, b.TxNonce(node.address))
+ b.AddTx(tx)
+ }
+ case (roundInterval / 2) + 1:
+ // Run DKG for next round part 2, DKG finalize
+ for _, node := range nodeSet.nodes[round] {
+ tx := node.DKGFinalizeTx(round, b.TxNonce(node.address))
+ b.AddTx(tx)
+ }
+ case (roundInterval / 2) + 2:
+ // Current DKG set create signed CRS for next round and propose it
+ nodeSet.SignedCRS(round)
+ tx := nodeSet.CRSTx(round+1, b)
+ b.AddTx(tx)
+ case roundInterval - 1:
+ // Round change
+ round++
+ }
+
+ // Execute any user modifications to the block and finalize it
+ if gen != nil {
+ gen(i, b)
+ }
+
+ if b.engine != nil {
+ block, _ := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
+ // Write state changes to db
+ root, err := statedb.Commit(config.IsEIP158(b.header.Number))
+ if err != nil {
+ panic(fmt.Sprintf("state write error: %v", err))
+ }
+ if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
+ panic(fmt.Sprintf("trie write error: %v", err))
+ }
+ return block, b.receipts
+ }
+ return nil, nil
+ }
+ for i := 0; i < n; i++ {
+ statedb, err := state.New(parent.Root(), state.NewDatabase(db))
+ if err != nil {
+ panic(err)
+ }
+ block, receipt := genblock(i, parent, statedb)
+ blocks[i] = block
+ receipts[i] = receipt
+ parent = block
+ }
+ return blocks, receipts
+}
+
+type witnessData struct {
+ Root common.Hash
+ TxHash common.Hash
+ ReceiptHash common.Hash
+}
+
+func makeDexconMeta(round uint64, parent *types.Block, nodeSet *NodeSet) []byte {
+ data, err := rlp.EncodeToBytes(&witnessData{
+ Root: parent.Root(),
+ TxHash: parent.TxHash(),
+ ReceiptHash: parent.ReceiptHash(),
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // only put required data, ignore information for BA, ex: acks, votes
+ coreBlock := coreTypes.Block{
+ Witness: coreTypes.Witness{
+ Height: parent.Number().Uint64(),
+ Data: data,
+ },
+ }
+
+ blockHash, err := hashBlock(&coreBlock)
+ if err != nil {
+ panic(err)
+ }
+
+ var parentCoreBlock coreTypes.Block
+
+ if parent.Number().Uint64() != 0 {
+ if err := rlp.DecodeBytes(
+ parent.Header().DexconMeta, &parentCoreBlock); err != nil {
+ panic(err)
+ }
+ }
+
+ parentCoreBlockHash, err := hashBlock(&parentCoreBlock)
+ if err != nil {
+ panic(err)
+ }
+ randomness := nodeSet.Randomness(round, blockHash)
+ coreBlock.Finalization.ParentHash = coreCommon.Hash(parentCoreBlockHash)
+ coreBlock.Finalization.Randomness = randomness
+ coreBlock.Finalization.Timestamp = time.Now().UTC()
+ coreBlock.Finalization.Height = parent.Number().Uint64()
+
+ dexconMeta, err := rlp.EncodeToBytes(&coreBlock)
+ if err != nil {
+ panic(err)
+ }
+ return dexconMeta
+}
+
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Header {
blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, engine, db, seed)
@@ -281,3 +429,337 @@ func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
+
+type node struct {
+ cryptoKey coreCrypto.PrivateKey
+ ecdsaKey *ecdsa.PrivateKey
+
+ id coreTypes.NodeID
+ dkgid coreDKG.ID
+ address common.Address
+ prvShares *coreDKG.PrivateKeyShares
+ pubShares *coreDKG.PublicKeyShares
+ receivedPrvShares *coreDKG.PrivateKeyShares
+ recoveredPrivateKey *coreDKG.PrivateKey
+ signer types.Signer
+
+ mpk *coreTypesDKG.MasterPublicKey
+}
+
+func newNode(privkey *ecdsa.PrivateKey, signer types.Signer) *node {
+ k := coreEcdsa.NewPrivateKeyFromECDSA(privkey)
+ id := coreTypes.NewNodeID(k.PublicKey())
+ return &node{
+ cryptoKey: k,
+ ecdsaKey: privkey,
+ id: id,
+ dkgid: coreDKG.NewID(id.Bytes()),
+ address: crypto.PubkeyToAddress(privkey.PublicKey),
+ signer: signer,
+ }
+}
+
+func (n *node) ID() coreTypes.NodeID {
+ return n.id
+}
+
+func (n *node) DKGID() coreDKG.ID {
+ return n.dkgid
+}
+
+// return signed dkg master public key
+func (n *node) MasterPublicKeyTx(round uint64, nonce uint64) *types.Transaction {
+ mpk := &coreTypesDKG.MasterPublicKey{
+ ProposerID: n.ID(),
+ Round: round,
+ DKGID: n.DKGID(),
+ PublicKeyShares: *n.pubShares,
+ }
+
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, mpk.Round)
+
+ hash := crypto.Keccak256Hash(
+ mpk.ProposerID.Hash[:],
+ mpk.DKGID.GetLittleEndian(),
+ mpk.PublicKeyShares.MasterKeyBytes(),
+ binaryRound,
+ )
+
+ var err error
+ mpk.Signature, err = n.cryptoKey.Sign(coreCommon.Hash(hash))
+ if err != nil {
+ panic(err)
+ }
+
+ method := vm.GovernanceContractName2Method["addDKGMasterPublicKey"]
+ encoded, err := rlp.EncodeToBytes(mpk)
+ if err != nil {
+ panic(err)
+ }
+
+ res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded)
+ if err != nil {
+ panic(err)
+ }
+ data := append(method.Id(), res...)
+ return n.CreateGovTx(nonce, data)
+}
+
+func (n *node) DKGFinalizeTx(round uint64, nonce uint64) *types.Transaction {
+ final := coreTypesDKG.Finalize{
+ ProposerID: n.ID(),
+ Round: round,
+ }
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, final.Round)
+ hash := crypto.Keccak256Hash(
+ final.ProposerID.Hash[:],
+ binaryRound,
+ )
+
+ var err error
+ final.Signature, err = n.cryptoKey.Sign(coreCommon.Hash(hash))
+ if err != nil {
+ panic(err)
+ }
+
+ method := vm.GovernanceContractName2Method["addDKGFinalize"]
+
+ encoded, err := rlp.EncodeToBytes(final)
+ if err != nil {
+ panic(err)
+ }
+
+ res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded)
+ if err != nil {
+ panic(err)
+ }
+
+ data := append(method.Id(), res...)
+ return n.CreateGovTx(nonce, data)
+}
+
+func (n *node) CreateGovTx(nonce uint64, data []byte) *types.Transaction {
+ tx, err := types.SignTx(types.NewTransaction(
+ nonce,
+ vm.GovernanceContractAddress,
+ big.NewInt(0),
+ uint64(2000000),
+ big.NewInt(1e10),
+ data), n.signer, n.ecdsaKey)
+ if err != nil {
+ panic(err)
+ }
+ return tx
+}
+
+type NodeSet struct {
+ signer types.Signer
+ privkeys []*ecdsa.PrivateKey
+ nodes map[uint64][]*node
+ crs map[uint64]common.Hash
+ signedCRS map[uint64][]byte
+}
+
+func NewNodeSet(round uint64, crs common.Hash, signer types.Signer,
+ privkeys []*ecdsa.PrivateKey) *NodeSet {
+ n := &NodeSet{
+ signer: signer,
+ privkeys: privkeys,
+ nodes: make(map[uint64][]*node),
+ crs: make(map[uint64]common.Hash),
+ signedCRS: make(map[uint64][]byte),
+ }
+ n.crs[round] = crs
+ n.RunDKG(round, 2)
+ return n
+}
+
+func (n *NodeSet) CRS(round uint64) common.Hash {
+ if c, ok := n.crs[round]; ok {
+ return c
+ }
+ panic("crs not exist")
+}
+
+// Assume All nodes in NodeSet are in DKG Set too.
+func (n *NodeSet) RunDKG(round uint64, threshold int) {
+ var ids coreDKG.IDs
+ var nodes []*node
+ for _, key := range n.privkeys {
+ node := newNode(key, n.signer)
+ nodes = append(nodes, node)
+ ids = append(ids, node.DKGID())
+ }
+
+ for _, node := range nodes {
+ node.prvShares, node.pubShares = coreDKG.NewPrivateKeyShares(threshold)
+ node.prvShares.SetParticipants(ids)
+ node.receivedPrvShares = coreDKG.NewEmptyPrivateKeyShares()
+ }
+
+ // exchange keys
+ for _, sender := range nodes {
+ for _, receiver := range nodes {
+ // no need to verify
+ prvShare, ok := sender.prvShares.Share(receiver.DKGID())
+ if !ok {
+ panic("not ok")
+ }
+ receiver.receivedPrvShares.AddShare(sender.DKGID(), prvShare)
+ }
+ }
+
+ // recover private key
+ for _, node := range nodes {
+ privKey, err := node.receivedPrvShares.RecoverPrivateKey(ids)
+ if err != nil {
+ panic(err)
+ }
+ node.recoveredPrivateKey = privKey
+ }
+
+ // store these nodes
+ n.nodes[round] = nodes
+}
+
+func (n *NodeSet) Randomness(round uint64, hash common.Hash) []byte {
+ if round == 0 {
+ return []byte{}
+ }
+ return n.TSig(round-1, hash)
+}
+
+func (n *NodeSet) SignedCRS(round uint64) {
+ signedCRS := n.TSig(round, n.crs[round])
+ n.signedCRS[round+1] = signedCRS
+ n.crs[round+1] = crypto.Keccak256Hash(signedCRS)
+}
+
+func (n *NodeSet) TSig(round uint64, hash common.Hash) []byte {
+ var ids coreDKG.IDs
+ var psigs []coreDKG.PartialSignature
+ for _, node := range n.nodes[round] {
+ ids = append(ids, node.DKGID())
+ }
+ for _, node := range n.nodes[round] {
+ sig, err := node.recoveredPrivateKey.Sign(coreCommon.Hash(hash))
+ if err != nil {
+ panic(err)
+ }
+ psigs = append(psigs, coreDKG.PartialSignature(sig))
+ // ids = append(ids, node.DKGID())
+
+ // FIXME: Debug verify signature
+ pk := coreDKG.NewEmptyPublicKeyShares()
+ for _, nnode := range n.nodes[round] {
+ p, err := nnode.pubShares.Share(node.DKGID())
+ if err != nil {
+ panic(err)
+ }
+ err = pk.AddShare(nnode.DKGID(), p)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ recovered, err := pk.RecoverPublicKey(ids)
+ if err != nil {
+ panic(err)
+ }
+
+ if !recovered.VerifySignature(coreCommon.Hash(hash), sig) {
+ panic("##########can not verify signature")
+ }
+ }
+
+ sig, err := coreDKG.RecoverSignature(psigs, ids)
+ if err != nil {
+ panic(err)
+ }
+ return sig.Signature
+}
+
+func (n *NodeSet) CRSTx(round uint64, b *BlockGen) *types.Transaction {
+ method := vm.GovernanceContractName2Method["proposeCRS"]
+ res, err := method.Inputs.Pack(big.NewInt(int64(round)), n.signedCRS[round])
+ if err != nil {
+ panic(err)
+ }
+ data := append(method.Id(), res...)
+
+ node := n.nodes[round-1][0]
+ return node.CreateGovTx(b.TxNonce(node.address), data)
+}
+
+func (n *NodeSet) NotifyRoundHeightTx(round, height uint64,
+ b *BlockGen) *types.Transaction {
+ method := vm.GovernanceContractName2Method["snapshotRound"]
+ res, err := method.Inputs.Pack(
+ big.NewInt(int64(round)), big.NewInt(int64(height)))
+ if err != nil {
+ panic(err)
+ }
+ data := append(method.Id(), res...)
+
+ var r uint64
+ if round < 1 {
+ r = 0
+ } else {
+ r = round - 1
+ }
+ node := n.nodes[r][0]
+ return node.CreateGovTx(b.TxNonce(node.address), data)
+}
+
+// Copy from dexon consensus core
+// TODO(sonic): polish this
+func hashBlock(block *coreTypes.Block) (common.Hash, error) {
+ hashPosition := hashPosition(block.Position)
+ // Handling Block.Acks.
+ binaryAcks := make([][]byte, len(block.Acks))
+ for idx, ack := range block.Acks {
+ binaryAcks[idx] = ack[:]
+ }
+ hashAcks := crypto.Keccak256Hash(binaryAcks...)
+ binaryTimestamp, err := block.Timestamp.UTC().MarshalBinary()
+ if err != nil {
+ return common.Hash{}, err
+ }
+ binaryWitness, err := hashWitness(&block.Witness)
+ if err != nil {
+ return common.Hash{}, err
+ }
+
+ hash := crypto.Keccak256Hash(
+ block.ProposerID.Hash[:],
+ block.ParentHash[:],
+ hashPosition[:],
+ hashAcks[:],
+ binaryTimestamp[:],
+ block.PayloadHash[:],
+ binaryWitness[:])
+ return hash, nil
+}
+
+func hashPosition(position coreTypes.Position) common.Hash {
+ binaryChainID := make([]byte, 4)
+ binary.LittleEndian.PutUint32(binaryChainID, position.ChainID)
+
+ binaryHeight := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryHeight, position.Height)
+
+ return crypto.Keccak256Hash(
+ binaryChainID,
+ binaryHeight,
+ )
+}
+
+func hashWitness(witness *coreTypes.Witness) (common.Hash, error) {
+ binaryHeight := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryHeight, witness.Height)
+ return crypto.Keccak256Hash(
+ binaryHeight,
+ witness.Data), nil
+}
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 0be6625d9..c240ed824 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "crypto/ecdsa"
"fmt"
"math/big"
@@ -98,3 +99,112 @@ func ExampleGenerateChain() {
// balance of addr2: 10000
// balance of addr3: 19687500000000001000
}
+
+func ExampleGenerateChainWithRoundChange() {
+ var (
+ // genesis node set
+ nodekey1, _ = crypto.HexToECDSA("3cf5bdee098cc34536a7b0e80d85e07a380efca76fc12136299b9e5ba24193c8")
+ nodekey2, _ = crypto.HexToECDSA("96c9f1435d53577db18d45411326311529a0e8affb19218e27f65769a482c0fb")
+ nodekey3, _ = crypto.HexToECDSA("b25e955e30dd87cbaec83287beea6ec9c4c72498bc66905590756bf48da5d1fc")
+ nodekey4, _ = crypto.HexToECDSA("35577f65312f4a5e0b5391f5385043a6bc7b51fa4851a579e845b5fea33efded")
+ nodeaddr1 = crypto.PubkeyToAddress(nodekey1.PublicKey)
+ nodeaddr2 = crypto.PubkeyToAddress(nodekey2.PublicKey)
+ nodeaddr3 = crypto.PubkeyToAddress(nodekey3.PublicKey)
+ nodeaddr4 = crypto.PubkeyToAddress(nodekey4.PublicKey)
+
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
+ addr2 = crypto.PubkeyToAddress(key2.PublicKey)
+ addr3 = crypto.PubkeyToAddress(key3.PublicKey)
+
+ db = ethdb.NewMemDatabase()
+ )
+
+ ether := big.NewInt(1e18)
+ gspec := Genesis{
+ Config: params.TestnetChainConfig,
+ Alloc: GenesisAlloc{
+ nodeaddr1: {
+ Balance: new(big.Int).Mul(big.NewInt(1000), ether),
+ Staked: new(big.Int).Mul(big.NewInt(500), ether),
+ PublicKey: crypto.FromECDSAPub(&nodekey1.PublicKey),
+ },
+ nodeaddr2: {
+ Balance: new(big.Int).Mul(big.NewInt(1000), ether),
+ Staked: new(big.Int).Mul(big.NewInt(500), ether),
+ PublicKey: crypto.FromECDSAPub(&nodekey2.PublicKey),
+ },
+ nodeaddr3: {
+ Balance: new(big.Int).Mul(big.NewInt(1000), ether),
+ Staked: new(big.Int).Mul(big.NewInt(500), ether),
+ PublicKey: crypto.FromECDSAPub(&nodekey3.PublicKey),
+ },
+ nodeaddr4: {
+ Balance: new(big.Int).Mul(big.NewInt(1000), ether),
+ Staked: new(big.Int).Mul(big.NewInt(500), ether),
+ PublicKey: crypto.FromECDSAPub(&nodekey4.PublicKey),
+ },
+ addr1: {
+ Balance: big.NewInt(1000000),
+ },
+ },
+ }
+ genesis := gspec.MustCommit(db)
+ crs := crypto.Keccak256Hash([]byte(gspec.Config.Dexcon.GenesisCRSText))
+ signer := types.NewEIP155Signer(gspec.Config.ChainID)
+ nodeSet := NewNodeSet(uint64(0), crs, signer,
+ []*ecdsa.PrivateKey{nodekey1, nodekey2, nodekey3, nodekey4})
+
+ // This call generates a chain of 1000 blocks. The function runs for
+ // each block and adds different features to gen based on the
+ // block index.
+ chain, _ := GenerateChainWithRoundChange(gspec.Config, genesis, ethash.NewFaker(), db, 1000, func(i int, gen *BlockGen) {
+ switch i {
+ case 0:
+ // In block 1, addr1 sends addr2 some ether.
+ tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ gen.AddTx(tx)
+ case 1:
+ // In block 2, addr1 sends some more ether to addr2.
+ // addr2 passes it on to addr3.
+ tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ gen.AddTx(tx1)
+ gen.AddTx(tx2)
+ case 2:
+ // Block 3 is empty but was mined by addr3.
+ gen.SetCoinbase(addr3)
+ gen.SetExtra([]byte("yeehaw"))
+ case 3:
+ // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
+ b2 := gen.PrevBlock(1).Header()
+ b2.Extra = []byte("foo")
+ gen.AddUncle(b2)
+ b3 := gen.PrevBlock(2).Header()
+ b3.Extra = []byte("foo")
+ gen.AddUncle(b3)
+ }
+ }, nodeSet, 30)
+
+ // Import the chain. This runs all block validation rules.
+ blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
+ defer blockchain.Stop()
+
+ if i, err := blockchain.InsertChain(chain); err != nil {
+ fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
+ return
+ }
+
+ state, _ := blockchain.State()
+ fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
+ fmt.Println("balance of addr1:", state.GetBalance(addr1))
+ fmt.Println("balance of addr2:", state.GetBalance(addr2))
+ fmt.Println("balance of addr3:", state.GetBalance(addr3))
+ // Output:
+ // last block: #5
+ // balance of addr1: 989000
+ // balance of addr2: 10000
+ // balance of addr3: 19687500000000001000
+}
diff --git a/core/genesis.go b/core/genesis.go
index 2412ede30..8b957edd2 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -257,6 +257,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
totalStaked := big.NewInt(0)
for addr, account := range g.Alloc {
+ if account.Staked == nil {
+ account.Staked = big.NewInt(0)
+ }
statedb.AddBalance(addr, new(big.Int).Sub(account.Balance, account.Staked))
statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce)
@@ -278,6 +281,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
for _, addr := range keys {
account := g.Alloc[addr]
+ if account.Staked == nil {
+ account.Staked = big.NewInt(0)
+ }
if account.Staked.Cmp(big.NewInt(0)) > 0 {
govStateHelper.Stake(addr, account.PublicKey, account.Staked)
}