// Copyright 2015 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 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" "github.com/dexon-foundation/dexon/consensus/misc" "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 { i int parent *types.Block chain []*types.Block header *types.Header statedb *state.StateDB gasPool *GasPool txs []*types.Transaction receipts []*types.Receipt uncles []*types.Header config *params.ChainConfig engine consensus.Engine } // SetCoinbase sets the coinbase of the generated block. // It can be called at most once. func (b *BlockGen) SetCoinbase(addr common.Address) { if b.gasPool != nil { if len(b.txs) > 0 { panic("coinbase must be set before adding transactions") } panic("coinbase can only be set once") } b.header.Coinbase = addr b.gasPool = new(GasPool).AddGas(b.header.GasLimit) } // SetExtra sets the extra data field of the generated block. func (b *BlockGen) SetExtra(data []byte) { b.header.Extra = data } // SetNonce sets the nonce field of the generated block. func (b *BlockGen) SetNonce(nonce types.BlockNonce) { b.header.Nonce = nonce } // AddTx adds a transaction to the generated block. If no coinbase has // been set, the block's coinbase is set to the zero address. // // AddTx panics if the transaction cannot be executed. In addition to // the protocol-imposed limitations (gas limit, etc.), there are some // further limitations on the content of transactions that can be // added. Notably, contract code relying on the BLOCKHASH instruction // will panic during execution. func (b *BlockGen) AddTx(tx *types.Transaction) { b.AddTxWithChain(nil, tx) } // AddTxWithChain adds a transaction to the generated block. If no coinbase has // been set, the block's coinbase is set to the zero address. // // AddTxWithChain panics if the transaction cannot be executed. In addition to // the protocol-imposed limitations (gas limit, etc.), there are some // further limitations on the content of transactions that can be // added. If contract code relies on the BLOCKHASH instruction, // the block in chain will be returned. func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { if b.gasPool == nil { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) } // Number returns the block number of the block being generated. func (b *BlockGen) Number() *big.Int { return new(big.Int).Set(b.header.Number) } // AddUncheckedReceipt forcefully adds a receipts to the block without a // backing transaction. // // AddUncheckedReceipt will cause consensus failures when used during real // chain processing. This is best used in conjunction with raw block insertion. func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) { b.receipts = append(b.receipts, receipt) } // TxNonce returns the next valid transaction nonce for the // account at addr. It panics if the account does not exist. func (b *BlockGen) TxNonce(addr common.Address) uint64 { if !b.statedb.Exist(addr) { panic("account does not exist") } return b.statedb.GetNonce(addr) } // AddUncle adds an uncle header to the generated block. func (b *BlockGen) AddUncle(h *types.Header) { b.uncles = append(b.uncles, h) } // PrevBlock returns a previously generated block by number. It panics if // num is greater or equal to the number of the block being generated. // For index -1, PrevBlock returns the parent block given to GenerateChain. func (b *BlockGen) PrevBlock(index int) *types.Block { if index >= b.i { panic(fmt.Errorf("block index %d out of range (%d,%d)", index, -1, b.i)) } if index == -1 { return b.parent } return b.chain[index] } // OffsetTime modifies the time instance of a block, implicitly changing its // associated difficulty. It's useful to test scenarios where forking is not // tied to chain length directly. func (b *BlockGen) OffsetTime(seconds int64) { b.header.Time.Add(b.header.Time, big.NewInt(seconds)) if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } chainreader := &fakeChainReader{config: b.config} b.header.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time.Uint64(), b.parent.Header()) } // GenerateChain creates a chain of n blocks. The first block's // parent will be the provided parent. db is used to store // intermediate states and should contain the parent's state trie. // // The generator function is called with a new block generator for // every block. Any transactions and uncles added to the generator // become part of the block. If gen is nil, the blocks will be empty // and their coinbase will be the zero address. // // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. func GenerateChain(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} 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) // Mutate the state and block according to any hard-fork specs if daoBlock := config.DAOForkBlock; daoBlock != nil { limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 { if config.DAOForkSupport { b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra) } } } if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 { misc.ApplyDAOHardFork(statedb) } // 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.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 } func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) } else { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } return &types.Header{ Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{ Number: parent.Number(), Time: new(big.Int).Sub(time, big.NewInt(10)), Difficulty: parent.Difficulty(), UncleHash: parent.UncleHash(), }), GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()), Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } } 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) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() } return headers } // makeBlockChain creates a deterministic chain of blocks rooted at parent. func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Block { blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) return blocks } type fakeChainReader struct { config *params.ChainConfig genesis *types.Block } // Config returns the chain configuration. func (cr *fakeChainReader) Config() *params.ChainConfig { return cr.config } func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil } func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil } 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 }