diff options
author | Sonic <sonic@dexon.org> | 2019-01-24 10:38:28 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-03-12 12:19:09 +0800 |
commit | d711fef2d53b6dd8f55060c8463f7b38716c44c7 (patch) | |
tree | 63188050bf046618df515f71f3c5d5c6b59b0f8f /core | |
parent | e8f768ccdbe2c7f5039153f6c6e7c2b668831fb2 (diff) | |
download | dexon-d711fef2d53b6dd8f55060c8463f7b38716c44c7.tar.gz dexon-d711fef2d53b6dd8f55060c8463f7b38716c44c7.tar.zst dexon-d711fef2d53b6dd8f55060c8463f7b38716c44c7.zip |
core, dex/downloader: polish headers verification and blocks insertion logic (#168)
Refactor GenerateDexonChain function, move governance tx logic to
the user of GenerateDexonChain (testchain_test.go) and move fake node set
code to FakeDexcon.
Diffstat (limited to 'core')
-rw-r--r-- | core/block_validator.go | 9 | ||||
-rw-r--r-- | core/blockchain.go | 44 | ||||
-rw-r--r-- | core/blockchain_test.go | 4 | ||||
-rw-r--r-- | core/chain_makers.go | 476 | ||||
-rw-r--r-- | core/dexon_chain_makers.go | 312 | ||||
-rw-r--r-- | core/headerchain.go | 144 | ||||
-rw-r--r-- | core/types/block.go | 11 | ||||
-rw-r--r-- | core/vm/governance.go | 123 |
8 files changed, 614 insertions, 509 deletions
diff --git a/core/block_validator.go b/core/block_validator.go index f42ea6b11..856eec27b 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -23,6 +23,7 @@ import ( "github.com/dexon-foundation/dexon/consensus" "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/log" "github.com/dexon-foundation/dexon/params" ) @@ -103,14 +104,16 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat } func (v *BlockValidator) ValidateWitnessData(height uint64, blockHash common.Hash) error { - b := v.bc.GetBlockByNumber(height) + b := v.bc.GetHeaderByNumber(height) if b == nil { - return fmt.Errorf("can not find block %v either pending or confirmed block", height) + log.Error("can not find block %v either pending or confirmed block", height) + return consensus.ErrWitnessMismatch } if b.Hash() != blockHash { - return fmt.Errorf("invalid witness block %s vs %s", + log.Error("invalid witness block %s vs %s", b.Hash().String(), blockHash.String()) + return consensus.ErrWitnessMismatch } return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 10e44fa4f..299c6f162 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -38,6 +38,7 @@ import ( "github.com/dexon-foundation/dexon/common/mclock" "github.com/dexon-foundation/dexon/common/prque" "github.com/dexon-foundation/dexon/consensus" + "github.com/dexon-foundation/dexon/consensus/dexcon" "github.com/dexon-foundation/dexon/core/rawdb" "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" @@ -145,6 +146,7 @@ type BlockChain struct { roundHeightMap sync.Map + gov *Governance verifierCache *dexCore.TSigVerifierCache confirmedBlockInitMu sync.Mutex @@ -222,8 +224,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par } } - gov := NewGovernance(NewGovernanceStateDB(bc)) - bc.verifierCache = dexCore.NewTSigVerifierCache(gov, 5) + bc.gov = NewGovernance(NewGovernanceStateDB(bc)) + bc.verifierCache = dexCore.NewTSigVerifierCache(bc.gov, 5) // Init round height map curblock := bc.CurrentBlock() @@ -236,7 +238,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par log.Debug("Init round height", "height", curblock.NumberU64(), "round", curblock.Round()) bc.storeRoundHeight(uint64(0), uint64(0)) } else { - prevh := gov.GetRoundHeight(r - 1) + prevh := bc.gov.GetRoundHeight(r - 1) if prevh == uint64(0) && (r-1) != uint64(0) { // Previous round height should be already snapshoted // in governance state at this moment. @@ -245,7 +247,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par log.Debug("Init previous round height", "height", prevh, "round", r-1) bc.storeRoundHeight(r-1, prevh) - curh := gov.GetRoundHeight(r) + curh := bc.gov.GetRoundHeight(r) // Current round height is not snapshoted in governance state yet, if curh == uint64(0) { @@ -1658,7 +1660,8 @@ func (bc *BlockChain) insertDexonChain(chain types.Blocks) (int, []interface{}, // Wait for the block's verification to complete bstart := time.Now() - err := bc.hc.verifyTSig(block.Header(), bc.verifierCache) + // VerifyDexonHeader will verify tsig, witness and ensure dexon header is correct. + err := bc.hc.VerifyDexonHeader(block.Header(), bc.gov, bc.verifierCache, bc.Validator()) if err == nil { err = bc.Validator().ValidateBody(block) } @@ -1753,8 +1756,10 @@ func (bc *BlockChain) insertDexonChain(chain types.Blocks) (int, []interface{}, chainBlock := bc.GetBlockByNumber(block.NumberU64()) if chainBlock != nil { if chainBlock.Hash() != block.Hash() { - return i, nil, nil, fmt.Errorf("block %v exist but hash is not equal: exist %v expect %v", block.NumberU64(), - chainBlock.Hash(), block.Hash()) + err := fmt.Errorf("block at %d exists but hash is not equal: exist %v expect %v", + block.NumberU64(), chainBlock.NumberU64(), block.Hash()) + bc.reportBlock(block, nil, fmt.Errorf("%v (new block)", err)) + bc.reportBlock(chainBlock, nil, fmt.Errorf("%v (old block)", err)) } continue @@ -1807,7 +1812,7 @@ func (bc *BlockChain) insertDexonChain(chain types.Blocks) (int, []interface{}, } func (bc *BlockChain) VerifyDexonHeader(header *types.Header) error { - return bc.hc.verifyTSig(header, bc.verifierCache) + return bc.hc.VerifyDexonHeader(header, bc.gov, bc.verifierCache, bc.Validator()) } func (bc *BlockChain) ProcessBlock(block *types.Block, witness *coreTypes.Witness) (*common.Hash, error) { @@ -1834,6 +1839,7 @@ func (bc *BlockChain) processBlock( ) bstart := time.Now() + var witnessBlockHash common.Hash if err := rlp.Decode(bytes.NewReader(witness.Data), &witnessBlockHash); err != nil { log.Error("Witness rlp decode failed", "error", err) @@ -1841,7 +1847,7 @@ func (bc *BlockChain) processBlock( } if err := bc.Validator().ValidateWitnessData(witness.Height, witnessBlockHash); err != nil { - return nil, nil, nil, fmt.Errorf("validate witness data error: %v", err) + return nil, nil, nil, err } var ( @@ -1891,10 +1897,12 @@ func (bc *BlockChain) processBlock( chainBlock := bc.GetBlockByNumber(newBlock.NumberU64()) if chainBlock != nil { if chainBlock.Hash() != newBlock.Hash() { - return nil, nil, nil, fmt.Errorf("block %v exist but hash is not equal: exist %v but get %v", newBlock.NumberU64(), - chainBlock.Hash(), newBlock.Hash()) + err := fmt.Errorf("block at %d exists but hash is not equal: exist %v expect %v", + newBlock.NumberU64(), chainBlock.NumberU64(), newBlock.Hash()) + bc.reportBlock(chainBlock, nil, fmt.Errorf("%v (remote inserted block)", err)) + bc.reportBlock(newBlock, receipts, fmt.Errorf("%v (local delivered block)", err)) + return nil, nil, nil, err } - return &root, nil, nil, nil } @@ -1983,8 +1991,11 @@ func (bc *BlockChain) ProcessEmptyBlock(block *types.Block) (*common.Hash, error chainBlock := bc.GetBlockByNumber(newBlock.NumberU64()) if chainBlock != nil { if chainBlock.Hash() != newBlock.Hash() { - return nil, fmt.Errorf("block %v exist but hash is not equal: exist %v expect %v", newBlock.NumberU64(), - chainBlock.Hash(), newBlock.Hash()) + err := fmt.Errorf("block at %d exists but hash is not equal: exist %v expect %v", + newBlock.NumberU64(), chainBlock.NumberU64(), newBlock.Hash()) + bc.reportBlock(chainBlock, nil, fmt.Errorf("%v (remote inserted block)", err)) + bc.reportBlock(newBlock, nil, fmt.Errorf("%v (local delivered block)", err)) + return nil, err } return &root, nil @@ -2275,9 +2286,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i return bc.hc.InsertHeaderChain(chain, whFunc, start) } -func (bc *BlockChain) InsertDexonHeaderChain(chain []*types.HeaderWithGovState, verifierCache *dexCore.TSigVerifierCache) (int, error) { +func (bc *BlockChain) InsertDexonHeaderChain(chain []*types.HeaderWithGovState, + gov dexcon.GovernanceStateFetcher, verifierCache *dexCore.TSigVerifierCache) (int, error) { start := time.Now() - if i, err := bc.hc.ValidateDexonHeaderChain(chain, verifierCache); err != nil { + if i, err := bc.hc.ValidateDexonHeaderChain(chain, gov, verifierCache, bc.Validator()); err != nil { return i, err } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index d18cfd604..7fbd51290 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1523,7 +1523,7 @@ func TestProcessBlock(t *testing.T) { Height: processNum + 1, Data: witnessDataBytes, }) - if err == nil || !strings.Contains(err.Error(), "can not find block") { + if err != consensus.ErrWitnessMismatch { t.Fatalf("not expected fail: %v", err) } @@ -1542,7 +1542,7 @@ func TestProcessBlock(t *testing.T) { Height: processNum - 1, Data: witnessDataBytes, }) - if err == nil || !strings.Contains(err.Error(), "invalid witness block") { + if err != consensus.ErrWitnessMismatch { t.Fatalf("not expected fail: %v", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 3d68a3e86..2566e382c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -17,30 +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" "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() { @@ -265,137 +254,6 @@ 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) @@ -429,337 +287,3 @@ 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/dexon_chain_makers.go b/core/dexon_chain_makers.go new file mode 100644 index 000000000..3031f3ae8 --- /dev/null +++ b/core/dexon_chain_makers.go @@ -0,0 +1,312 @@ +// 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 <http://www.gnu.org/licenses/>. + +package core + +import ( + "fmt" + "math/big" + "math/rand" + "time" + + coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/consensus" + "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/ethdb" + "github.com/dexon-foundation/dexon/params" + "github.com/dexon-foundation/dexon/rlp" +) + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +// DexonBlockGen creates blocks for testing. +// See GenerateChain for a detailed explanation. +type DexonBlockGen struct { + i int + parent *types.Block + chain []*types.Block + header *types.Header + statedb *state.StateDB + + dirtyNonce map[common.Address]uint64 + + position coreTypes.Position + + gasPool *GasPool + txs []*types.Transaction + receipts []*types.Receipt + + config *params.ChainConfig + engine consensus.Engine +} + +// SetCoinbase sets the coinbase of the generated block. +// It can be called at most once. +func (b *DexonBlockGen) 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 *DexonBlockGen) SetExtra(data []byte) { + b.header.Extra = data +} + +// SetNonce sets the nonce field of the generated block. +func (b *DexonBlockGen) SetNonce(nonce types.BlockNonce) { + b.header.Nonce = nonce +} + +func (b *DexonBlockGen) SetPosition(position coreTypes.Position) { + b.position = position +} + +// 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. If contract code relies on the BLOCKHASH instruction, +// the block in chain will be returned. +func (b *DexonBlockGen) AddTx(tx *types.Transaction) { + // TODO: check nonce and maintain nonce. + b.txs = append(b.txs, tx) +} + +func (b *DexonBlockGen) PreparePayload() []byte { + return nil +} + +func (b *DexonBlockGen) ProcessTransactions(c ChainContext) { + if b.gasPool == nil { + b.SetCoinbase(common.Address{}) + } + for _, tx := range b.txs { + b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) + // TODO: fix the chain context parameter + receipt, _, err := ApplyTransaction(b.config, c, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + if err != nil { + panic(err) + } + b.receipts = append(b.receipts, receipt) + } +} + +// Number returns the block number of the block being generated. +func (b *DexonBlockGen) 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 *DexonBlockGen) 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 *DexonBlockGen) TxNonce(addr common.Address) uint64 { + if !b.statedb.Exist(addr) { + panic("account does not exist") + } + if nonce, exists := b.dirtyNonce[addr]; exists { + return nonce + } + return b.statedb.GetNonce(addr) +} + +// 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 *DexonBlockGen) 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] +} + +func GenerateDexonChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *DexonBlockGen)) ([]*types.Block, []types.Receipts) { + if config == nil { + config = params.TestChainConfig + } + if engine == nil { + panic("engine is nil") + } + blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) + chain := &fakeDexonChain{ + config: config, + engine: engine, + genesis: parent, + db: db, + headersByNumber: make(map[uint64]*types.Header), + roundHeight: make(map[uint64]uint64), + } + genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { + b := &DexonBlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} + b.header = makeDexonHeader(chain, parent, statedb, b.engine) + + // Execute any user modifications to the block + if gen != nil { + gen(i, b) + } + + b.header.DexconMeta = makeDexconMeta(b, parent) + + // sign tsig to create dexcon, prepare witness + b.engine.Prepare(chain, b.header) + + // Run EVM and finalize the block. + b.ProcessTransactions(chain) + + // Finalize and seal the block + block, _ := b.engine.Finalize(chain, b.header, statedb, b.txs, nil, 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 + } + 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 + chain.InsertBlock(block) + } + return blocks, receipts +} + +func makeDexonHeader(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: new(big.Int).Add(parent.Difficulty(), common.Big1), + GasLimit: chain.Config().Dexcon.BlockGasLimit, + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: time, + } +} + +// Setup DexconMeata skeleton +func makeDexconMeta(b *DexonBlockGen, parent *types.Block) []byte { + // Setup witness + h := parent.Number().Int64() - rand.Int63n(6) + if h < 0 { + h = 0 + } + witnessedBlock := b.chain[h] + if witnessedBlock == nil { + witnessedBlock = parent + } + witnessedBlockHash := witnessedBlock.Hash() + data, err := rlp.EncodeToBytes(&witnessedBlockHash) + if err != nil { + panic(err) + } + + block := coreTypes.Block{ + Position: b.position, + Witness: coreTypes.Witness{ + Height: witnessedBlock.NumberU64(), + Data: data, + }, + } + + dexconMeta, err := rlp.EncodeToBytes(&block) + if err != nil { + panic(err) + } + return dexconMeta +} + +type fakeDexonChain struct { + config *params.ChainConfig + engine consensus.Engine + db ethdb.Database + genesis *types.Block + headersByNumber map[uint64]*types.Header + roundHeight map[uint64]uint64 +} + +func (f *fakeDexonChain) InsertBlock(block *types.Block) { + f.headersByNumber[block.NumberU64()] = block.Header() + if _, exists := f.roundHeight[block.Round()]; !exists { + f.roundHeight[block.Round()] = block.NumberU64() + } +} + +// Config returns the chain configuration. +func (f *fakeDexonChain) Config() *params.ChainConfig { return f.config } +func (f *fakeDexonChain) Engine() consensus.Engine { return f.engine } +func (f *fakeDexonChain) CurrentHeader() *types.Header { return nil } +func (f *fakeDexonChain) GetHeaderByHash(hash common.Hash) *types.Header { return nil } +func (f *fakeDexonChain) GetHeader(hash common.Hash, number uint64) *types.Header { return nil } +func (f *fakeDexonChain) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } + +func (f *fakeDexonChain) GetHeaderByNumber(number uint64) *types.Header { + if number == 0 { + return f.genesis.Header() + } + return f.headersByNumber[number] +} + +func (f *fakeDexonChain) StateAt(hash common.Hash) (*state.StateDB, error) { + return state.New(hash, state.NewDatabase(f.db)) +} + +func (f *fakeDexonChain) GetRoundHeight(round uint64) (uint64, bool) { + if round == 0 { + return 0, true + } + height, ok := f.roundHeight[round] + return height, ok +} diff --git a/core/headerchain.go b/core/headerchain.go index 04f8bcb3c..0ebf1935c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -23,6 +23,7 @@ import ( "math" "math/big" mrand "math/rand" + "reflect" "sync/atomic" "time" @@ -32,6 +33,7 @@ import ( "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/consensus" + "github.com/dexon-foundation/dexon/consensus/dexcon" "github.com/dexon-foundation/dexon/core/rawdb" "github.com/dexon-foundation/dexon/core/types" "github.com/dexon-foundation/dexon/crypto" @@ -394,7 +396,9 @@ func (hc *HeaderChain) WriteDexonHeader(header *types.HeaderWithGovState) (statu type Wh2Callback func(*types.HeaderWithGovState) error -func (hc *HeaderChain) ValidateDexonHeaderChain(chain []*types.HeaderWithGovState, verifierCache *dexCore.TSigVerifierCache) (int, error) { +func (hc *HeaderChain) ValidateDexonHeaderChain(chain []*types.HeaderWithGovState, + gov dexcon.GovernanceStateFetcher, + verifierCache *dexCore.TSigVerifierCache, validator Validator) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { @@ -415,40 +419,156 @@ func (hc *HeaderChain) ValidateDexonHeaderChain(chain []*types.HeaderWithGovStat return 0, errors.New("aborted") } - if err := hc.verifyTSig(header.Header, verifierCache); err != nil { + if i == 0 { + log.Debug("validate header chain", "parent", header.ParentHash.String(), "number", header.Number.Uint64()-1) + if parent := hc.GetHeader(header.ParentHash, header.Number.Uint64()-1); parent == nil { + return 0, consensus.ErrUnknownAncestor + } + } + + if err := hc.verifyDexonHeader(header.Header, gov, verifierCache); err != nil { return i, err } + + // Verify witness + var coreBlock coreTypes.Block + if err := rlp.DecodeBytes(header.DexconMeta, &coreBlock); err != nil { + return i, err + } + + if !coreBlock.IsEmpty() { + var witnessBlockHash common.Hash + if err := rlp.DecodeBytes(coreBlock.Witness.Data, &witnessBlockHash); err != nil { + log.Error("decode witness data fail", "err", err) + return i, err + } + + index := int64(coreBlock.Witness.Height) - int64(chain[0].Number.Uint64()) + if index < 0 { + if err := validator.ValidateWitnessData( + coreBlock.Witness.Height, witnessBlockHash); err != nil { + return i, err + } + } else { + if witnessBlockHash != chain[index].Hash() { + return i, consensus.ErrWitnessMismatch + } + } + } } return 0, nil } -func (hc *HeaderChain) verifyTSig(header *types.Header, verifierCache *dexCore.TSigVerifierCache) error { +func (hc *HeaderChain) VerifyDexonHeader(header *types.Header, + gov dexcon.GovernanceStateFetcher, + verifierCache *dexCore.TSigVerifierCache, validator Validator) error { + + if parent := hc.GetHeader(header.ParentHash, header.Number.Uint64()-1); parent == nil { + return consensus.ErrUnknownAncestor + } + + if err := hc.verifyDexonHeader(header, gov, verifierCache); err != nil { + return err + } + + // Verify witness + var coreBlock coreTypes.Block + if err := rlp.DecodeBytes(header.DexconMeta, &coreBlock); err != nil { + return err + } + + if !coreBlock.IsEmpty() { + var witnessBlockHash common.Hash + if err := rlp.DecodeBytes(coreBlock.Witness.Data, &witnessBlockHash); err != nil { + log.Error("decode witness data fail", "err", err) + return err + } + + if err := validator.ValidateWitnessData( + coreBlock.Witness.Height, witnessBlockHash); err != nil { + return err + } + } + return nil +} + +func (hc *HeaderChain) verifyDexonHeader(header *types.Header, + gov dexcon.GovernanceStateFetcher, + verifierCache *dexCore.TSigVerifierCache) error { + // If the header is a banned one, straight out abort if BadHashes[header.Hash()] { return ErrBlacklistedHash } - if header.Round == 0 { - return nil + // Difficulty should always be 1. + if header.Difficulty.Cmp(big.NewInt(1)) != 0 { + return fmt.Errorf("difficulty should be 1") } - var dexconMeta coreTypes.Block - if err := rlp.DecodeBytes(header.DexconMeta, &dexconMeta); err != nil { - return err + // Verify fields that should be same as dexcon meta. + var coreBlock coreTypes.Block + if err := rlp.DecodeBytes(header.DexconMeta, &coreBlock); err != nil { + return fmt.Errorf("decode dexcon meta fail, number=%d, err=%v", + header.Number.Uint64(), err) + } + + if err := hc.verifyTSig(&coreBlock, verifierCache); err != nil { + log.Debug("verify header sig fail, number=%d, err=%v", + header.Number.Uint64(), err) + } + + if header.Coinbase != common.BytesToAddress(coreBlock.ProposerID.Bytes()) { + return fmt.Errorf("coinbase mismatch") + } + + if header.Time.Cmp(big.NewInt(coreBlock.Finalization.Timestamp.UnixNano()/1000000)) != 0 { + return fmt.Errorf("timestamp mismatch") + } + + if header.Number.Uint64() != coreBlock.Finalization.Height { + return fmt.Errorf("height mismatch") + } + + if !reflect.DeepEqual(header.Randomness, coreBlock.Finalization.Randomness) { + return fmt.Errorf("randomness mismatch") + } + + if header.Round != coreBlock.Position.Round { + return fmt.Errorf("round mismatch") + } + + gs := gov.GetGovStateHelperAtRound(header.Round) + config := gs.Configuration() + if header.GasLimit != config.BlockGasLimit { + return fmt.Errorf("block gas limit mismatch") + } + return nil +} + +func (hc *HeaderChain) verifyTSig(coreBlock *coreTypes.Block, + verifierCache *dexCore.TSigVerifierCache) error { + + round := coreBlock.Position.Round + randomness := coreBlock.Finalization.Randomness + + if round == 0 { + return nil } - v, ok, err := verifierCache.UpdateAndGet(header.Round) + // Verify threshold signature + v, ok, err := verifierCache.UpdateAndGet(round) if err != nil { panic(err) } if !ok { - panic(fmt.Errorf("DKG of round %d is not finished", header.Round)) + panic(fmt.Errorf("DKG of round %d is not finished", round)) } - if !v.VerifySignature(dexconMeta.Hash, coreCrypto.Signature{ + if !v.VerifySignature(coreBlock.Hash, coreCrypto.Signature{ Type: "bls", - Signature: header.Randomness}) { + Signature: randomness}) { return fmt.Errorf("signature invalid") } return nil diff --git a/core/types/block.go b/core/types/block.go index 137585b6a..cf854779d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -267,6 +267,17 @@ func CopyHeader(h *Header) *Header { cpy.Extra = make([]byte, len(h.Extra)) copy(cpy.Extra, h.Extra) } + if cpy.Reward = new(big.Int); h.Reward != nil { + cpy.Reward.Set(h.Reward) + } + if len(h.Randomness) > 0 { + cpy.Randomness = make([]byte, len(h.Randomness)) + copy(cpy.Randomness, h.Randomness) + } + if len(h.DexconMeta) > 0 { + cpy.DexconMeta = make([]byte, len(h.DexconMeta)) + copy(cpy.DexconMeta, h.DexconMeta) + } return &cpy } diff --git a/core/vm/governance.go b/core/vm/governance.go index e77519c58..a0014005d 100644 --- a/core/vm/governance.go +++ b/core/vm/governance.go @@ -2193,3 +2193,126 @@ func (g *GovernanceContract) snapshotRound(round, height *big.Int) ([]byte, erro g.state.PushRoundHeight(height) return nil, nil } + +func PackProposeCRS(round uint64, signedCRS []byte) ([]byte, error) { + method := GovernanceContractName2Method["proposeCRS"] + res, err := method.Inputs.Pack(big.NewInt(int64(round)), signedCRS) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackNotifyRoundHeight(targetRound, consensusHeight uint64) ([]byte, error) { + method := GovernanceContractName2Method["snapshotRound"] + res, err := method.Inputs.Pack( + big.NewInt(int64(targetRound)), big.NewInt(int64(consensusHeight))) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackAddDKGMasterPublicKey(round uint64, mpk *dkgTypes.MasterPublicKey) ([]byte, error) { + method := GovernanceContractName2Method["addDKGMasterPublicKey"] + encoded, err := rlp.EncodeToBytes(mpk) + if err != nil { + return nil, err + } + res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackAddDKGMPKReady(round uint64, ready *dkgTypes.MPKReady) ([]byte, error) { + method := GovernanceContractName2Method["addDKGMPKReady"] + encoded, err := rlp.EncodeToBytes(ready) + if err != nil { + return nil, err + } + res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackAddDKGComplaint(round uint64, complaint *dkgTypes.Complaint) ([]byte, error) { + method := GovernanceContractName2Method["addDKGComplaint"] + encoded, err := rlp.EncodeToBytes(complaint) + if err != nil { + return nil, err + } + + res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackAddDKGFinalize(round uint64, final *dkgTypes.Finalize) ([]byte, error) { + method := GovernanceContractName2Method["addDKGFinalize"] + encoded, err := rlp.EncodeToBytes(final) + if err != nil { + return nil, err + } + + res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + if err != nil { + return nil, err + } + data := append(method.Id(), res...) + return data, nil +} + +func PackReportForkVote(vote1, vote2 *coreTypes.Vote) ([]byte, error) { + method := GovernanceContractName2Method["report"] + + vote1Bytes, err := rlp.EncodeToBytes(vote1) + if err != nil { + return nil, err + } + + vote2Bytes, err := rlp.EncodeToBytes(vote2) + if err != nil { + return nil, err + } + + res, err := method.Inputs.Pack(big.NewInt(ReportTypeForkVote), vote1Bytes, vote2Bytes) + if err != nil { + return nil, err + } + + data := append(method.Id(), res...) + return data, nil +} + +func PackReportForkBlock(block1, block2 *coreTypes.Block) ([]byte, error) { + method := GovernanceContractName2Method["report"] + + block1Bytes, err := rlp.EncodeToBytes(block1) + if err != nil { + return nil, err + } + + block2Bytes, err := rlp.EncodeToBytes(block2) + if err != nil { + return nil, err + } + + res, err := method.Inputs.Pack(big.NewInt(ReportTypeForkBlock), block1Bytes, block2Bytes) + if err != nil { + return nil, err + } + + data := append(method.Id(), res...) + return data, nil +} |