diff options
author | Sonic <sonic@dexon.org> | 2019-01-24 10:38:28 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-04-09 21:32:56 +0800 |
commit | bbb1ebede10cd691de4ef2fe4bf276d2fa357a31 (patch) | |
tree | c046b5f369c85215761d2db1e4ff890a478ceab4 | |
parent | d79158954a8cea9e14311e9783de82fdbd7a8888 (diff) | |
download | dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar.gz dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar.bz2 dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar.lz dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar.xz dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.tar.zst dexon-bbb1ebede10cd691de4ef2fe4bf276d2fa357a31.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.
-rw-r--r-- | consensus/dexcon/fake_dexcon.go | 300 | ||||
-rw-r--r-- | consensus/errors.go | 2 | ||||
-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 | ||||
-rw-r--r-- | dex/downloader/downloader.go | 6 | ||||
-rw-r--r-- | dex/downloader/downloader_test.go | 68 | ||||
-rw-r--r-- | dex/downloader/testchain_test.go | 175 | ||||
-rw-r--r-- | dex/governance.go | 94 |
14 files changed, 1143 insertions, 625 deletions
diff --git a/consensus/dexcon/fake_dexcon.go b/consensus/dexcon/fake_dexcon.go new file mode 100644 index 000000000..ca48b5275 --- /dev/null +++ b/consensus/dexcon/fake_dexcon.go @@ -0,0 +1,300 @@ +package dexcon + +import ( + "crypto/ecdsa" + "math/big" + "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" + coreUtils "github.com/dexon-foundation/dexon-consensus/core/utils" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/consensus" + "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/rlp" +) + +type FakeDexcon struct { + *Dexcon + nodes *NodeSet +} + +func NewFaker(nodes *NodeSet) *FakeDexcon { + return &FakeDexcon{ + Dexcon: New(), + nodes: nodes, + } +} + +func (f *FakeDexcon) Prepare(chain consensus.ChainReader, header *types.Header) error { + var coreBlock coreTypes.Block + if err := rlp.DecodeBytes(header.DexconMeta, &coreBlock); err != nil { + return err + } + + blockHash, err := coreUtils.HashBlock(&coreBlock) + if err != nil { + return err + } + + parentHeader := chain.GetHeaderByNumber(header.Number.Uint64() - 1) + var parentCoreBlock coreTypes.Block + if parentHeader.Number.Uint64() != 0 { + if err := rlp.DecodeBytes( + parentHeader.DexconMeta, &parentCoreBlock); err != nil { + return err + } + } + + parentCoreBlockHash, err := coreUtils.HashBlock(&parentCoreBlock) + if err != nil { + return err + } + randomness := f.nodes.Randomness(header.Round, common.Hash(blockHash)) + coreBlock.Finalization.ParentHash = parentCoreBlockHash + coreBlock.Finalization.Randomness = randomness + coreBlock.Finalization.Timestamp = time.Now().UTC() + coreBlock.Finalization.Height = parentHeader.Number.Uint64() + + dexconMeta, err := rlp.EncodeToBytes(&coreBlock) + if err != nil { + return err + } + header.DexconMeta = dexconMeta + 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 *coreUtils.Signer + txSigner types.Signer + + mpk *coreTypesDKG.MasterPublicKey +} + +func newNode(privkey *ecdsa.PrivateKey, txSigner 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: coreUtils.NewSigner(k), + txSigner: txSigner, + } +} + +func (n *Node) ID() coreTypes.NodeID { return n.id } +func (n *Node) DKGID() coreDKG.ID { return n.dkgid } +func (n *Node) Address() common.Address { return n.address } + +func (n *Node) MasterPublicKey(round uint64) *coreTypesDKG.MasterPublicKey { + mpk := &coreTypesDKG.MasterPublicKey{ + ProposerID: n.ID(), + Round: round, + DKGID: n.DKGID(), + PublicKeyShares: *n.pubShares, + } + + if err := n.signer.SignDKGMasterPublicKey(mpk); err != nil { + panic(err) + } + return mpk +} + +func (n *Node) DKGMPKReady(round uint64) *coreTypesDKG.MPKReady { + ready := &coreTypesDKG.MPKReady{ + ProposerID: n.ID(), + Round: round, + } + + if err := n.signer.SignDKGMPKReady(ready); err != nil { + panic(err) + } + return ready +} + +func (n *Node) DKGFinalize(round uint64) *coreTypesDKG.Finalize { + final := &coreTypesDKG.Finalize{ + ProposerID: n.ID(), + Round: round, + } + + if err := n.signer.SignDKGFinalize(final); err != nil { + panic(err) + } + return final +} + +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.txSigner, 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, signedCRS []byte, 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.signedCRS[round] = signedCRS + n.crs[round] = crypto.Keccak256Hash(signedCRS) + return n +} + +func (n *NodeSet) Nodes(round uint64) []*Node { + if nodes, ok := n.nodes[round]; ok { + return nodes + } + panic("dkg not ready") +} + +func (n *NodeSet) CRS(round uint64) common.Hash { + if c, ok := n.crs[round]; ok { + return c + } + panic("crs not exist") +} + +func (n *NodeSet) SignedCRS(round uint64) []byte { + if c, ok := n.signedCRS[round]; ok { + return c + } + panic("signedCRS 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, hash) +} + +func (n *NodeSet) SignCRS(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 +} diff --git a/consensus/errors.go b/consensus/errors.go index a005c5f63..204f09f21 100644 --- a/consensus/errors.go +++ b/consensus/errors.go @@ -34,4 +34,6 @@ var ( // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's // plus one. ErrInvalidNumber = errors.New("invalid block number") + + ErrWitnessMismatch = errors.New("witness mismatch") ) 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 8bbf6e809..5e3b4b30a 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 @@ -2297,9 +2308,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 967d0d3c5..1f5197002 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1729,7 +1729,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) } @@ -1748,7 +1748,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 b464555d3..27aff215c 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..128cc454b --- /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 uint64 + if parent.Time() == 0 { + time = 10 + } else { + time = parent.Time() + 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 e988a53a2..17cedffdd 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 != uint64(coreBlock.Finalization.Timestamp.UnixNano()/1000000) { + 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 4ff34c229..88e537e3a 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -264,6 +264,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 +} diff --git a/dex/downloader/downloader.go b/dex/downloader/downloader.go index 6ff8c122e..e15844668 100644 --- a/dex/downloader/downloader.go +++ b/dex/downloader/downloader.go @@ -27,6 +27,7 @@ import ( ethereum "github.com/dexon-foundation/dexon" "github.com/dexon-foundation/dexon/common" + "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" @@ -177,7 +178,8 @@ type LightChain interface { GetGovStateByNumber(number uint64) (*types.GovState, error) // InsertDexonHeaderChain inserts a batch of headers into the local chain. - InsertDexonHeaderChain([]*types.HeaderWithGovState, *dexCore.TSigVerifierCache) (int, error) + InsertDexonHeaderChain([]*types.HeaderWithGovState, + dexcon.GovernanceStateFetcher, *dexCore.TSigVerifierCache) (int, error) // Rollback removes a few recently added elements from the local chain. Rollback([]common.Hash) @@ -1428,7 +1430,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, number uint64) } } - if n, err := d.lightchain.InsertDexonHeaderChain(chunk, d.verifierCache); err != nil { + if n, err := d.lightchain.InsertDexonHeaderChain(chunk, d.gov, d.verifierCache); err != nil { // If some headers were inserted, add them too to the rollback list if n > 0 { for _, h := range chunk[:n] { diff --git a/dex/downloader/downloader_test.go b/dex/downloader/downloader_test.go index 0d92ad97f..80993bd75 100644 --- a/dex/downloader/downloader_test.go +++ b/dex/downloader/downloader_test.go @@ -26,12 +26,16 @@ import ( ethereum "github.com/dexon-foundation/dexon" dexCore "github.com/dexon-foundation/dexon-consensus/core" + coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/consensus/dexcon" "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/event" + "github.com/dexon-foundation/dexon/rlp" "github.com/dexon-foundation/dexon/trie" ) @@ -200,7 +204,8 @@ func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error { } // InsertDexonHeaderChain injects a new batch of headers into the simulated chain. -func (dl *downloadTester) InsertDexonHeaderChain(headers []*types.HeaderWithGovState, verifierCache *dexCore.TSigVerifierCache) (i int, err error) { +func (dl *downloadTester) InsertDexonHeaderChain(headers []*types.HeaderWithGovState, + gov dexcon.GovernanceStateFetcher, verifierCache *dexCore.TSigVerifierCache) (i int, err error) { dl.lock.Lock() defer dl.lock.Unlock() @@ -221,6 +226,29 @@ func (dl *downloadTester) InsertDexonHeaderChain(headers []*types.HeaderWithGovS if _, ok := dl.ownHeaders[header.ParentHash]; !ok { return i, errors.New("unknown parent") } + + // Verify witness + var coreBlock coreTypes.Block + if err := rlp.DecodeBytes(header.DexconMeta, &coreBlock); err != nil { + return i, err + } + + var witnessBlockHash common.Hash + if err := rlp.DecodeBytes(coreBlock.Witness.Data, &witnessBlockHash); err != nil { + return i, err + } + + if uint64(len(dl.ownHashes)) < coreBlock.Witness.Height+1 { + return i, errors.New("unknown witness") + } + h := dl.ownHeaders[dl.ownHashes[coreBlock.Witness.Height]] + if h == nil { + return i, errors.New("unknown witness") + } + + if h.Hash() != witnessBlockHash { + return i, errors.New("witness root mismatch") + } dl.ownHashes = append(dl.ownHashes, header.Hash()) dl.ownHeaders[header.Hash()] = header.Header } @@ -807,6 +835,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { missing := fsHeaderSafetyNet + MaxHeaderFetch + 1 fastAttackChain := chain.shorten(chain.len()) delete(fastAttackChain.headerm, fastAttackChain.chain[missing]) + tester.newPeer("fast-attack", protocol, fastAttackChain) if err := tester.sync("fast-attack", 0, mode); err == nil { @@ -860,6 +889,43 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { } } + // witness mismatch + realChain := chain.shorten(chain.len()) + fakeChain := chain.shorten(chain.len()) + + for i := chain.len() - 100; i < chain.len(); i++ { + realHash := realChain.chain[i] + realHeader := realChain.headerm[realHash] + realBlock := realChain.blockm[realHash] + realReceipt := realChain.receiptm[realHash] + fakeHeader := types.CopyHeader(realHeader) + if i == chain.len()-100 { + fakeHeader.Root = common.Hash{} + } else { + fakeHeader.ParentHash = fakeChain.chain[i-1] + } + + fakeBlock := types.NewBlock(fakeHeader, realBlock.Transactions(), realBlock.Uncles(), realReceipt) + + fakeChain.chain[i] = fakeBlock.Hash() + fakeChain.blockm[fakeBlock.Hash()] = fakeBlock + fakeChain.headerm[fakeBlock.Hash()] = fakeBlock.Header() + fakeChain.receiptm[fakeBlock.Hash()] = realReceipt + } + + tester.newPeer("mismatch-attack", protocol, fakeChain) + if err := tester.sync("mismatch-attack", 0, mode); err == nil { + t.Fatalf("succeeded block attacker synchronisation") + } + if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) + } + if mode == FastSync { + if head := tester.CurrentBlock().NumberU64(); head != 0 { + t.Errorf("fast sync pivot block #%d not rolled back", head) + } + } + // synchronise with the valid peer and make sure sync succeeds. Since the last rollback // should also disable fast syncing for this process, verify that we did a fresh full // sync. Note, we can't assert anything about the receipts since we won't purge the diff --git a/dex/downloader/testchain_test.go b/dex/downloader/testchain_test.go index 810fb41b3..d96ebcfbf 100644 --- a/dex/downloader/testchain_test.go +++ b/dex/downloader/testchain_test.go @@ -17,11 +17,12 @@ package downloader import ( + "crypto/ecdsa" "fmt" "math/big" "github.com/dexon-foundation/dexon/common" - "github.com/dexon-foundation/dexon/consensus/ethash" + "github.com/dexon-foundation/dexon/consensus/dexcon" "github.com/dexon-foundation/dexon/core" "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" @@ -33,17 +34,17 @@ import ( // Test chain parameters. var ( - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddress = crypto.PubkeyToAddress(testKey.PublicKey) - testDB = ethdb.NewMemDatabase() - testGenesis = genesisBlockForTesting(testDB, testAddress, big.NewInt(1000000000)) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddress = crypto.PubkeyToAddress(testKey.PublicKey) + testDB = ethdb.NewMemDatabase() + testGenesis, testNodes = genesisBlockForTesting(testDB, testAddress, big.NewInt(1000000000)) ) // The common prefix of all test chains: -var testChainBase = newTestChain(blockCacheItems+200, testGenesis) +var testChainBase = newTestChain(blockCacheItems+200, testGenesis, testNodes) func genesisBlockForTesting(db ethdb.Database, - addr common.Address, balance *big.Int) *types.Block { + addr common.Address, balance *big.Int) (*types.Block, *dexcon.NodeSet) { var ( // genesis node set nodekey1, _ = crypto.HexToECDSA("3cf5bdee098cc34536a7b0e80d85e07a380efca76fc12136299b9e5ba24193c8") @@ -87,11 +88,17 @@ func genesisBlockForTesting(db ethdb.Database, }, } genesis := gspec.MustCommit(db) - return genesis + + signedCRS := []byte(gspec.Config.Dexcon.GenesisCRSText) + signer := types.NewEIP155Signer(gspec.Config.ChainID) + nodeSet := dexcon.NewNodeSet(uint64(0), signedCRS, signer, + []*ecdsa.PrivateKey{nodekey1, nodekey2, nodekey3, nodekey4}) + return genesis, nodeSet } type testChain struct { genesis *types.Block + nodes *dexcon.NodeSet chain []common.Hash headerm map[common.Hash]*types.Header blockm map[common.Hash]*types.Block @@ -99,23 +106,16 @@ type testChain struct { } // newTestChain creates a blockchain of the given length. -func newTestChain(length int, genesis *types.Block) *testChain { +func newTestChain(length int, genesis *types.Block, nodes *dexcon.NodeSet) *testChain { tc := new(testChain).copy(length) tc.genesis = genesis tc.chain = append(tc.chain, genesis.Hash()) tc.headerm[tc.genesis.Hash()] = tc.genesis.Header() tc.blockm[tc.genesis.Hash()] = tc.genesis - tc.generate(length-1, 0, genesis, false) + tc.generate(length-1, 0, genesis, nodes, false) return tc } -// makeFork creates a fork on top of the test chain. -func (tc *testChain) makeFork(length int, heavy bool, seed byte) *testChain { - fork := tc.copy(tc.len() + length) - fork.generate(length, seed, tc.headBlock(), heavy) - return fork -} - // shorten creates a copy of the chain with the given length. It panics if the // length is longer than the number of available blocks. func (tc *testChain) shorten(length int) *testChain { @@ -146,16 +146,113 @@ func (tc *testChain) copy(newlen int) *testChain { // the returned hash chain is ordered head->parent. In addition, every 22th block // contains a transaction and every 5th an uncle to allow testing correct block // reassembly. -func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { +func (tc *testChain) generate(n int, seed byte, parent *types.Block, nodes *dexcon.NodeSet, heavy bool) { // start := time.Now() // defer func() { fmt.Printf("test chain generated in %v\n", time.Since(start)) }() - blocks, receipts := core.GenerateChain(params.TestnetChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { + engine := dexcon.NewFaker(testNodes) + govFetcher := newGovStateFetcher(state.NewDatabase(testDB)) + govFetcher.SnapshotRound(0, tc.genesis.Root()) + engine.SetGovStateFetcher(govFetcher) + + round := uint64(0) + roundInterval := int(100) + + addTx := func(block *core.DexonBlockGen, node *dexcon.Node, data []byte) { + nonce := block.TxNonce(node.Address()) + tx := node.CreateGovTx(nonce, data) + block.AddTx(tx) + } + + blocks, receipts := core.GenerateDexonChain(params.TestnetChainConfig, parent, engine, testDB, n, func(i int, block *core.DexonBlockGen) { block.SetCoinbase(common.Address{seed}) - // If a heavy chain is requested, delay blocks to raise difficulty - if heavy { - block.OffsetTime(-1) + if round == 0 { + switch i { + case 1: + testNodes.RunDKG(round, 2) + // Add DKG MasterPublicKeys + for _, node := range testNodes.Nodes(round) { + data, err := vm.PackAddDKGMasterPublicKey(round, node.MasterPublicKey(round)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case 2: + // Add DKG MPKReady + for _, node := range testNodes.Nodes(round) { + data, err := vm.PackAddDKGMPKReady(round, node.DKGMPKReady(round)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case 3: + // Add DKG Finalize + for _, node := range testNodes.Nodes(round) { + data, err := vm.PackAddDKGFinalize(round, node.DKGFinalize(round)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + } } + + half := roundInterval / 2 + switch i % roundInterval { + case 0: + if round > 0 { + node := testNodes.Nodes(round)[0] + data, err := vm.PackNotifyRoundHeight(round, uint64(i)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case half: + // Sign current CRS to geneate the next round CRS and propose it. + testNodes.SignCRS(round) + node := testNodes.Nodes(round)[0] + data, err := vm.PackProposeCRS(round, testNodes.SignedCRS(round+1)) + if err != nil { + panic(err) + } + addTx(block, node, data) + case half + 1: + // Run the DKG for next round. + testNodes.RunDKG(round+1, 2) + + // Add DKG MasterPublicKeys + for _, node := range testNodes.Nodes(round + 1) { + data, err := vm.PackAddDKGMasterPublicKey(round+1, node.MasterPublicKey(round+1)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case half + 2: + // Add DKG MPKReady + for _, node := range testNodes.Nodes(round + 1) { + data, err := vm.PackAddDKGMPKReady(round+1, node.DKGMPKReady(round+1)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case half + 3: + // Add DKG Finalize + for _, node := range testNodes.Nodes(round + 1) { + data, err := vm.PackAddDKGFinalize(round+1, node.DKGFinalize(round+1)) + if err != nil { + panic(err) + } + addTx(block, node, data) + } + case roundInterval - 1: + round++ + } + // Include transactions to the miner to make blocks more interesting. if parent == tc.genesis && i%22 == 0 { signer := types.MakeSigner(params.TestnetChainConfig, block.Number()) @@ -165,13 +262,6 @@ func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) } block.AddTx(tx) } - // if the block number is a multiple of 5, add a bonus uncle to the block - if i > 0 && i%5 == 0 { - block.AddUncle(&types.Header{ - ParentHash: block.PrevBlock(i - 1).Hash(), - Number: big.NewInt(block.Number().Int64() - 1), - }) - } }) // Convert the block-chain into a hash-chain and header/block maps @@ -258,3 +348,30 @@ func (tc *testChain) hashToNumber(target common.Hash) (uint64, bool) { } return 0, false } + +type govStateFetcher struct { + db state.Database + rootByRound map[uint64]common.Hash +} + +func newGovStateFetcher(db state.Database) *govStateFetcher { + return &govStateFetcher{ + db: db, + rootByRound: make(map[uint64]common.Hash), + } +} + +func (g *govStateFetcher) SnapshotRound(round uint64, root common.Hash) { + g.rootByRound[round] = root +} + +func (g *govStateFetcher) GetGovStateHelperAtRound(round uint64) *vm.GovernanceStateHelper { + if root, ok := g.rootByRound[round]; ok { + s, err := state.New(root, g.db) + if err != nil { + panic(err) + } + return &vm.GovernanceStateHelper{s} + } + return nil +} diff --git a/dex/governance.go b/dex/governance.go index 0d5a7c926..e9ea1f89c 100644 --- a/dex/governance.go +++ b/dex/governance.go @@ -37,7 +37,6 @@ import ( "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/log" "github.com/dexon-foundation/dexon/params" - "github.com/dexon-foundation/dexon/rlp" ) type DexconGovernance struct { @@ -119,15 +118,12 @@ func (d *DexconGovernance) LenCRS() uint64 { // ProposeCRS send proposals of a new CRS func (d *DexconGovernance) ProposeCRS(round uint64, signedCRS []byte) { - method := vm.GovernanceContractName2Method["proposeCRS"] - - res, err := method.Inputs.Pack(big.NewInt(int64(round)), signedCRS) + data, err := vm.PackProposeCRS(round, signedCRS) if err != nil { log.Error("failed to pack proposeCRS input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send proposeCRS tx", "err", err) @@ -151,16 +147,12 @@ func (d *DexconGovernance) NodeSet(round uint64) []coreCrypto.PublicKey { // NotifyRoundHeight register the mapping between round and height. func (d *DexconGovernance) NotifyRoundHeight(targetRound, consensusHeight uint64) { - method := vm.GovernanceContractName2Method["snapshotRound"] - - res, err := method.Inputs.Pack( - big.NewInt(int64(targetRound)), big.NewInt(int64(consensusHeight))) + data, err := vm.PackNotifyRoundHeight(targetRound, consensusHeight) if err != nil { log.Error("failed to pack snapshotRound input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send snapshotRound tx", "err", err) @@ -169,21 +161,12 @@ func (d *DexconGovernance) NotifyRoundHeight(targetRound, consensusHeight uint64 // AddDKGComplaint adds a DKGComplaint. func (d *DexconGovernance) AddDKGComplaint(round uint64, complaint *dkgTypes.Complaint) { - method := vm.GovernanceContractName2Method["addDKGComplaint"] - - encoded, err := rlp.EncodeToBytes(complaint) - if err != nil { - log.Error("failed to RLP encode complaint to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + data, err := vm.PackAddDKGComplaint(round, complaint) if err != nil { log.Error("failed to pack addDKGComplaint input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send addDKGComplaint tx", "err", err) @@ -192,21 +175,12 @@ func (d *DexconGovernance) AddDKGComplaint(round uint64, complaint *dkgTypes.Com // AddDKGMasterPublicKey adds a DKGMasterPublicKey. func (d *DexconGovernance) AddDKGMasterPublicKey(round uint64, masterPublicKey *dkgTypes.MasterPublicKey) { - method := vm.GovernanceContractName2Method["addDKGMasterPublicKey"] - - encoded, err := rlp.EncodeToBytes(masterPublicKey) - if err != nil { - log.Error("failed to RLP encode mpk to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + data, err := vm.PackAddDKGMasterPublicKey(round, masterPublicKey) if err != nil { log.Error("failed to pack addDKGMasterPublicKey input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send addDKGMasterPublicKey tx", "err", err) @@ -215,21 +189,12 @@ func (d *DexconGovernance) AddDKGMasterPublicKey(round uint64, masterPublicKey * // AddDKGMPKReady adds a DKG mpk ready message. func (d *DexconGovernance) AddDKGMPKReady(round uint64, ready *dkgTypes.MPKReady) { - method := vm.GovernanceContractName2Method["addDKGMPKReady"] - - encoded, err := rlp.EncodeToBytes(ready) - if err != nil { - log.Error("failed to RLP encode mpk ready to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + data, err := vm.PackAddDKGMPKReady(round, ready) if err != nil { log.Error("failed to pack addDKGMPKReady input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send addDKGMPKReady tx", "err", err) @@ -238,21 +203,12 @@ func (d *DexconGovernance) AddDKGMPKReady(round uint64, ready *dkgTypes.MPKReady // AddDKGFinalize adds a DKG finalize message. func (d *DexconGovernance) AddDKGFinalize(round uint64, final *dkgTypes.Finalize) { - method := vm.GovernanceContractName2Method["addDKGFinalize"] - - encoded, err := rlp.EncodeToBytes(final) - if err != nil { - log.Error("failed to RLP encode finalize to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(int64(round)), encoded) + data, err := vm.PackAddDKGFinalize(round, final) if err != nil { log.Error("failed to pack addDKGFinalize input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send addDKGFinalize tx", "err", err) @@ -261,27 +217,12 @@ func (d *DexconGovernance) AddDKGFinalize(round uint64, final *dkgTypes.Finalize // ReportForkVote reports a node for forking votes. func (d *DexconGovernance) ReportForkVote(vote1, vote2 *coreTypes.Vote) { - method := vm.GovernanceContractName2Method["report"] - - vote1Bytes, err := rlp.EncodeToBytes(vote1) - if err != nil { - log.Error("failed to RLP encode vote1 to bytes", "err", err) - return - } - - vote2Bytes, err := rlp.EncodeToBytes(vote2) - if err != nil { - log.Error("failed to RLP encode vote2 to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(vm.ReportTypeForkVote), vote1Bytes, vote2Bytes) + data, err := vm.PackReportForkVote(vote1, vote2) if err != nil { - log.Error("failed to pack report input", "err", err) + log.Error("failed to pack report fork vote input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send report fork vote tx", "err", err) @@ -290,27 +231,12 @@ func (d *DexconGovernance) ReportForkVote(vote1, vote2 *coreTypes.Vote) { // ReportForkBlock reports a node for forking blocks. func (d *DexconGovernance) ReportForkBlock(block1, block2 *coreTypes.Block) { - method := vm.GovernanceContractName2Method["report"] - - block1Bytes, err := rlp.EncodeToBytes(block1) - if err != nil { - log.Error("failed to RLP encode block1 to bytes", "err", err) - return - } - - block2Bytes, err := rlp.EncodeToBytes(block2) - if err != nil { - log.Error("failed to RLP encode block2 to bytes", "err", err) - return - } - - res, err := method.Inputs.Pack(big.NewInt(vm.ReportTypeForkBlock), block1Bytes, block2Bytes) + data, err := vm.PackReportForkBlock(block1, block2) if err != nil { - log.Error("failed to pack report input", "err", err) + log.Error("failed to pack report fork block input", "err", err) return } - data := append(method.Id(), res...) err = d.sendGovTx(context.Background(), data) if err != nil { log.Error("failed to send report fork block tx", "err", err) |