diff --git a/consensus/consensus.go b/consensus/consensus.go index e8135d2c..2f51b5c2 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -83,7 +83,7 @@ func (s *consensus) Init() error { } s.blockchain = blockchain // - vmService, err := vm.NewService(s.Config(), s.Events()) + vmService, err := vm.NewService(s) if err != nil { return err } diff --git a/consensus/model/block_chain.go b/consensus/model/block_chain.go index 03ef6fa0..d0a33835 100644 --- a/consensus/model/block_chain.go +++ b/consensus/model/block_chain.go @@ -14,4 +14,5 @@ type BlockChain interface { BlockByOrder(blockOrder uint64) (*types.SerializedBlock, error) Rebuild() error GetMiningTips(expectPriority int) []*hash.Hash + GetBlockState(order uint64) BlockState } diff --git a/consensus/model/block_state.go b/consensus/model/block_state.go index 62172a02..3ffb7607 100644 --- a/consensus/model/block_state.go +++ b/consensus/model/block_state.go @@ -1,6 +1,10 @@ package model -import "github.com/Qitmeer/qng/common/hash" +import ( + "github.com/Qitmeer/qng/common/hash" + "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" +) type BlockState interface { GetID() uint64 @@ -14,4 +18,9 @@ type BlockState interface { Invalid() Root() *hash.Hash Bytes() ([]byte, error) + GetEVMRoot() common.Hash + GetEVMHash() common.Hash + GetEVMNumber() uint64 + SetEVM(header *etypes.Header) + GetDuplicateTxs() []int } diff --git a/consensus/model/vm_service.go b/consensus/model/vm_service.go index 86a60872..de0ea1f8 100644 --- a/consensus/model/vm_service.go +++ b/consensus/model/vm_service.go @@ -4,13 +4,14 @@ import ( "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/core/types" "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" ) type VMI interface { VerifyTx(tx Tx) (int64, error) VerifyTxSanity(tx Tx) error - CheckConnectBlock(block *types.SerializedBlock) error - ConnectBlock(block *types.SerializedBlock) (uint64, error) + CheckConnectBlock(block *types.SerializedBlock, state BlockState) error + ConnectBlock(block *types.SerializedBlock, state BlockState) (uint64, error) DisconnectBlock(block *types.SerializedBlock) (uint64, error) AddTxToMempool(tx *types.Transaction, local bool) (int64, error) RemoveTxFromMempool(tx *types.Transaction) error @@ -18,11 +19,12 @@ type VMI interface { GetMempoolSize() int64 ResetTemplate() error Genesis(txs []*types.Tx) *hash.Hash - GetBlockID(bh *hash.Hash) uint64 GetBlockIDByTxHash(txhash *hash.Hash) uint64 GetBalance(addr string) (int64, error) SetLogLevel(level string) GetBlockByNumber(num uint64) (interface{}, error) GetCurStateRoot() common.Hash + GetCurHeader() *etypes.Header IsShutdown() bool + RewindTo(state BlockState) error } diff --git a/consensus/vm/block.go b/consensus/vm/block.go index 0512c0ee..86667714 100644 --- a/consensus/vm/block.go +++ b/consensus/vm/block.go @@ -13,9 +13,10 @@ import ( ) type Block struct { - Id *hash.Hash - Txs []model.Tx - Time time.Time + Id *hash.Hash + Txs []model.Tx + Time time.Time + ParentBlockState model.BlockState } func (b *Block) ID() *hash.Hash { @@ -69,3 +70,7 @@ func (b *Block) String() string { func (b *Block) Transactions() []model.Tx { return b.Txs } + +func (b *Block) ParentState() model.BlockState { + return b.ParentBlockState +} diff --git a/consensus/vm/context.go b/consensus/vm/context.go index 35331d39..7b333d5c 100644 --- a/consensus/vm/context.go +++ b/consensus/vm/context.go @@ -9,9 +9,10 @@ import ( type Context struct { context.Context - Cfg *config.Config - Tp model.TxPool - Notify consensus.Notify + Cfg *config.Config + Tp model.TxPool + Notify consensus.Notify + Consensus model.Consensus } func (ctx *Context) GetConfig() *config.Config { @@ -24,4 +25,8 @@ func (ctx *Context) GetTxPool() model.TxPool { func (ctx *Context) GetNotify() consensus.Notify { return ctx.Notify -} \ No newline at end of file +} + +func (ctx Context) GetConsensus() model.Consensus { + return ctx.Consensus +} diff --git a/consensus/vm/util.go b/consensus/vm/util.go new file mode 100644 index 00000000..7b29eda1 --- /dev/null +++ b/consensus/vm/util.go @@ -0,0 +1,49 @@ +package vm + +import ( + "github.com/Qitmeer/qng/consensus/model" + "github.com/Qitmeer/qng/core/types" + "github.com/Qitmeer/qng/vm/consensus" +) + +func BuildEVMBlock(block *types.SerializedBlock, prevState model.BlockState) (consensus.Block, error) { + result := &Block{Id: block.Hash(), Txs: []model.Tx{}, Time: block.Block().Header.Timestamp, ParentBlockState: prevState} + + for idx, tx := range block.Transactions() { + if idx == 0 { + continue + } + if tx.IsDuplicate { + continue + } + + if types.IsCrossChainExportTx(tx.Tx) { + ctx, err := NewExportTx(tx.Tx) + if err != nil { + return nil, err + } + result.Txs = append(result.Txs, ctx) + } else if types.IsCrossChainImportTx(tx.Tx) { + ctx, err := NewImportTx(tx.Tx) + if err != nil { + return nil, err + } + err = ctx.SetCoinbaseTx(block.Transactions()[0].Tx) + if err != nil { + return nil, err + } + result.Txs = append(result.Txs, ctx) + } else if types.IsCrossChainVMTx(tx.Tx) { + ctx, err := NewVMTx(tx.Tx) + if err != nil { + return nil, err + } + err = ctx.SetCoinbaseTx(block.Transactions()[0].Tx) + if err != nil { + return nil, err + } + result.Txs = append(result.Txs, ctx) + } + } + return result, nil +} diff --git a/core/blockchain/api.go b/core/blockchain/api.go index 3dd9043a..b59f2b11 100644 --- a/core/blockchain/api.go +++ b/core/blockchain/api.go @@ -13,7 +13,6 @@ import ( "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/engine/txscript" "github.com/Qitmeer/qng/meerdag" - "github.com/Qitmeer/qng/meerevm/evm" rapi "github.com/Qitmeer/qng/rpc/api" "github.com/Qitmeer/qng/rpc/client/cmds" "strconv" @@ -489,27 +488,18 @@ func (api *PublicBlockAPI) GetStateRoot(order int64, verbose *bool) (interface{} if ib == nil { return nil, internalError(fmt.Errorf("no block").Error(), fmt.Sprintf("Block not found: %d", order)) } - eb, err := api.chain.GetMeerBlock(ib.GetOrder()) - if err != nil { - return nil, err - } - sr := "" - num := uint64(0) - if eblock, ok := eb.(*evm.Block); ok { - sr = eblock.StateRoot().String() - num = eblock.Number() - } if vb { ret := qjson.OrderedResult{ qjson.KV{Key: "Hash", Val: ib.GetHash().String()}, qjson.KV{Key: "Order", Val: order}, qjson.KV{Key: "Height", Val: ib.GetHeight()}, qjson.KV{Key: "Valid", Val: !ib.GetState().GetStatus().KnownInvalid()}, - qjson.KV{Key: "EVMStateRoot", Val: sr}, - qjson.KV{Key: "EVMHeight", Val: num}, + qjson.KV{Key: "EVMStateRoot", Val: ib.GetState().GetEVMRoot().String()}, + qjson.KV{Key: "EVMHeight", Val: ib.GetState().GetEVMNumber()}, + qjson.KV{Key: "EVMHead", Val: ib.GetState().GetEVMHash().String()}, qjson.KV{Key: "StateRoot", Val: ib.GetState().Root().String()}, } return ret, nil } - return sr, nil + return ib.GetState().Root().String(), nil } diff --git a/core/blockchain/blockchain.go b/core/blockchain/blockchain.go index 8a3cdf62..ba1d9fd2 100644 --- a/core/blockchain/blockchain.go +++ b/core/blockchain/blockchain.go @@ -321,13 +321,14 @@ func (b *BlockChain) createChainState() error { header := &genesisBlock.Block().Header node := NewBlockNode(genesisBlock) _, _, ib, _ := b.bd.AddBlock(node) + ib.GetState().SetEVM(b.VMService().GetCurHeader()) //node.FlushToDB(b) // Initialize the state related to the best block. Since it is the // genesis block, use its timestamp for the median time. numTxns := uint64(len(genesisBlock.Block().Transactions)) blockSize := uint64(genesisBlock.Block().SerializeSize()) b.stateSnapshot = newBestState(node.GetHash(), node.Difficulty(), blockSize, numTxns, - time.Unix(node.GetTimestamp(), 0), numTxns, 0, b.bd.GetGraphState(), node.GetHash(), hash.ZeroHash) + time.Unix(node.GetTimestamp(), 0), numTxns, 0, b.bd.GetGraphState(), node.GetHash(), *ib.GetState().Root()) b.TokenTipID = 0 // Create the initial the database chain state including creating the // necessary index buckets and inserting the genesis block. @@ -665,7 +666,7 @@ func (b *BlockChain) reorganizeChain(ib meerdag.IBlock, detachNodes *list.List, if err != nil { panic(err) } - + log.Debug("detach block", "hash", n.Block.GetHash().String(), "old order", n.OldOrder, "status", n.Block.GetState().GetStatus().String()) block.SetOrder(uint64(n.OldOrder)) // Load all of the utxos referenced by the block that aren't // already in the view. @@ -705,6 +706,18 @@ func (b *BlockChain) reorganizeChain(ib meerdag.IBlock, detachNodes *list.List, return err } } + for e := attachNodes.Front(); e != nil; e = e.Next() { + nodeBlock := e.Value.(meerdag.IBlock) + if !nodeBlock.IsOrdered() { + continue + } + startState := b.bd.GetBlockByOrder(nodeBlock.GetOrder() - 1).GetState() + err = b.VMService().RewindTo(startState) + if err != nil { + return err + } + break + } for e := attachNodes.Front(); e != nil; e = e.Next() { nodeBlock := e.Value.(meerdag.IBlock) @@ -755,6 +768,7 @@ func (b *BlockChain) reorganizeChain(ib meerdag.IBlock, detachNodes *list.List, if er != nil { log.Error(er.Error()) } + log.Debug("attach block", "hash", nodeBlock.GetHash().String(), "order", nodeBlock.GetOrder(), "status", nodeBlock.GetState().GetStatus().String()) } // Log the point where the chain forked and old and new best chain @@ -1173,6 +1187,14 @@ func (b *BlockChain) Rebuild() error { return nil } +func (b *BlockChain) GetBlockState(order uint64) model.BlockState { + block := b.BlockDAG().GetBlockByOrder(uint(order)) + if block == nil { + return nil + } + return block.GetState() +} + // New returns a BlockChain instance using the provided configuration details. func New(consensus model.Consensus) (*BlockChain, error) { // Enforce required config fields. diff --git a/core/blockchain/blockindex.go b/core/blockchain/blockindex.go index 903cd353..5846d26c 100644 --- a/core/blockchain/blockindex.go +++ b/core/blockchain/blockindex.go @@ -191,29 +191,3 @@ func (b *BlockChain) GetMiningTips(expectPriority int) []*hash.Hash { func (b *BlockChain) HasTx(txid *hash.Hash) bool { return b.indexManager.HasTx(txid) } - -func (b *BlockChain) GetMeerBlock(order uint) (interface{}, error) { - if b.VMService() == nil { - return nil, nil - } - number := uint64(0) - for i := uint(order); i > 0; i-- { - ib := b.bd.GetBlockByOrder(i) - if ib == nil { - return nil, fmt.Errorf("No meer block number:%d", i) - } - if forks.IsBeforeMeerEVMForkHeight(int64(ib.GetHeight())) { - break - } - num := b.VMService().GetBlockID(ib.GetHash()) - if num != 0 { - number = num - break - } - } - eb, err := b.VMService().GetBlockByNumber(number) - if err != nil { - return nil, err - } - return eb, nil -} diff --git a/core/blockchain/process.go b/core/blockchain/process.go index c37ec02e..ef4f0dfa 100644 --- a/core/blockchain/process.go +++ b/core/blockchain/process.go @@ -431,7 +431,8 @@ func (b *BlockChain) connectBlock(node meerdag.IBlock, block *types.SerializedBl pkss = append(pkss, stxo.PkScript) } if !node.GetState().GetStatus().KnownInvalid() { - vmbid, err := b.VMService().ConnectBlock(block) + prevState := b.bd.GetBlockByOrder(node.GetOrder() - 1).GetState() + vmbid, err := b.VMService().ConnectBlock(block, prevState) if err != nil { return err } @@ -494,12 +495,8 @@ func (b *BlockChain) connectBlock(node meerdag.IBlock, block *types.SerializedBl // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) disconnectBlock(ib meerdag.IBlock, block *types.SerializedBlock, view *utxo.UtxoViewpoint, stxos []utxo.SpentTxOut) error { - vmbid, err := b.VMService().DisconnectBlock(block) - if err != nil { - return err - } // Calculate the exact subsidy produced by adding the block. - err = b.db.Update(func(dbTx database.Tx) error { + err := b.db.Update(func(dbTx database.Tx) error { // Update the utxo set using the state of the utxo view. This // entails restoring all of the utxos spent and removing the new // ones created by the block. @@ -526,7 +523,7 @@ func (b *BlockChain) disconnectBlock(ib meerdag.IBlock, block *types.SerializedB for _, stxo := range stxos { pkss = append(pkss, stxo.PkScript) } - err := b.indexManager.DisconnectBlock(block, pkss, ib, vmbid) + err := b.indexManager.DisconnectBlock(block, pkss, ib, 0) if err != nil { return fmt.Errorf("%v. (Attempt to execute --droptxindex)", err) } @@ -753,18 +750,22 @@ func (b *BlockChain) updateBestState(ib meerdag.IBlock, block *types.SerializedB } func (b *BlockChain) updateBlockState(ib meerdag.IBlock, block *types.SerializedBlock) error { - if ib.GetState() == nil { + if !ib.IsOrdered() { + return b.updateDefaultBlockState(ib) + } + if ib.GetState() == nil || + ib.GetOrder() <= 0 { return fmt.Errorf("block state is nill:%d %s", ib.GetID(), ib.GetHash().String()) } bs, ok := ib.GetState().(*state.BlockState) if !ok { return fmt.Errorf("block state is nill:%d %s", ib.GetID(), ib.GetHash().String()) } - mp := b.bd.GetBlockById(ib.GetMainParent()) - if mp == nil { - return fmt.Errorf("No main parent:%d %s", ib.GetID(), ib.GetHash().String()) + prev := b.bd.GetBlockByOrder(ib.GetOrder() - 1) + if prev == nil { + return fmt.Errorf("No prev block:%d %s", ib.GetID(), ib.GetHash().String()) } - bs.Update(block, mp.GetState().Root(), b.VMService().GetCurStateRoot()) + bs.Update(block, prev.GetState().(*state.BlockState), b.VMService().GetCurHeader()) b.BlockDAG().AddToCommit(ib) return nil } @@ -781,7 +782,7 @@ func (b *BlockChain) updateDefaultBlockState(ib meerdag.IBlock) error { if mp == nil { return fmt.Errorf("No main parent:%d %s", ib.GetID(), ib.GetHash().String()) } - bs.SetRoot(mp.GetState().Root()) + bs.SetDefault(mp.GetState().(*state.BlockState)) b.BlockDAG().AddToCommit(ib) return nil } diff --git a/core/blockchain/validate.go b/core/blockchain/validate.go index 33c4e404..2c9c1f6b 100644 --- a/core/blockchain/validate.go +++ b/core/blockchain/validate.go @@ -19,6 +19,7 @@ import ( "github.com/Qitmeer/qng/core/blockchain/utxo" "github.com/Qitmeer/qng/core/merkle" "github.com/Qitmeer/qng/core/protocol" + "github.com/Qitmeer/qng/core/state" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/core/types/pow" "github.com/Qitmeer/qng/engine/txscript" @@ -940,7 +941,8 @@ func (b *BlockChain) checkConnectBlock(ib meerdag.IBlock, block *types.Serialize if err != nil { return err } - return b.VMService().CheckConnectBlock(block) + prevState := b.bd.GetBlockByOrder(ib.GetOrder() - 1).GetState() + return b.VMService().CheckConnectBlock(block, prevState) } // consensusScriptVerifyFlags returns the script flags that must be used when @@ -1361,7 +1363,6 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *types.SerializedBlock) err if virBlock == nil { return ruleError(ErrPrevBlockNotBest, "tipsNode") } - virBlock.SetOrder(uint(block.Order())) if virBlock.GetHeight() != block.Height() { return ruleError(ErrPrevBlockNotBest, "tipsNode height") } @@ -1369,7 +1370,9 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *types.SerializedBlock) err if mainParent == nil { return ruleError(ErrPrevBlockNotBest, "main parent") } - + block.SetOrder(uint64(mainParent.GetOrder() + 1)) + virBlock.SetOrder(uint(block.Order())) + virBlock.GetState().(*state.BlockState).SetDefault(mainParent.GetState().(*state.BlockState)) err = b.checkBlockContext(block, mainParent, flags) if err != nil { return err diff --git a/core/state/blockstate.go b/core/state/blockstate.go index fc73e55c..8f85c485 100644 --- a/core/state/blockstate.go +++ b/core/state/blockstate.go @@ -9,6 +9,7 @@ import ( "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/meerdag" "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "io" ) @@ -20,6 +21,8 @@ type BlockState struct { status model.BlockStatus duplicateTxs []int evmRoot common.Hash + evmHash common.Hash + evmNumber uint64 root hash.Hash } @@ -29,10 +32,6 @@ func (b *BlockState) GetID() uint64 { func (b *BlockState) SetOrder(o uint64) { b.order = o - - if !b.IsOrdered() { - b.Reset() - } } func (b *BlockState) GetOrder() uint64 { @@ -79,20 +78,47 @@ func (b *BlockState) SetRoot(root *hash.Hash) { b.root = *root } -func (b *BlockState) Reset() { - b.root = hash.ZeroHash +func (b *BlockState) SetDefault(parent *BlockState) { + b.root = parent.root + b.evmHash = parent.evmHash + b.evmNumber = parent.evmNumber + b.evmRoot = parent.evmRoot +} + +func (b *BlockState) GetEVMRoot() common.Hash { + return b.evmRoot +} + +func (b *BlockState) GetEVMHash() common.Hash { + return b.evmHash +} + +func (b *BlockState) GetEVMNumber() uint64 { + return b.evmNumber +} + +func (b *BlockState) GetDuplicateTxs() []int { + return b.duplicateTxs } -func (b *BlockState) Update(block *types.SerializedBlock, mainParentRoot *hash.Hash, evmRoot common.Hash) { +func (b *BlockState) SetEVM(header *etypes.Header) { + b.evmNumber = header.Number.Uint64() + b.evmHash = header.Hash() + b.evmRoot = header.Root +} + +func (b *BlockState) Update(block *types.SerializedBlock, prev *BlockState, header *etypes.Header) { defer func() { log.Trace("Update block state", "id", b.id, "order", b.order, "root", b.root.String()) }() - b.root = *mainParentRoot - b.evmRoot = evmRoot - if b.status.KnownInvalid() || - !b.IsOrdered() { + b.SetDefault(prev) + if b.status.KnownInvalid() { return } + b.evmRoot = header.Root + b.evmHash = header.Hash() + b.evmNumber = header.Number.Uint64() + // b.duplicateTxs = []int{} txs := []*types.Tx{} txRoot := block.Block().Header.TxRoot @@ -108,7 +134,7 @@ func (b *BlockState) Update(block *types.SerializedBlock, mainParentRoot *hash.H txRoot = *merkles[len(merkles)-1] } // - data := mainParentRoot.Bytes() + data := prev.Root().Bytes() data = append(data, serialization.SerializeUint64(b.order)...) data = append(data, serialization.SerializeUint64(b.weight)...) data = append(data, byte(b.status)) @@ -130,6 +156,8 @@ func (b *BlockState) EncodeRLP(_w io.Writer) error { } w.ListEnd(_tmp1) w.WriteBytes(b.evmRoot.Bytes()) + w.WriteBytes(b.evmHash.Bytes()) + w.WriteUint64(b.evmNumber) w.WriteBytes(b.root.Bytes()) w.ListEnd(_tmp0) return w.Flush() @@ -187,12 +215,24 @@ func (b *BlockState) DecodeRLP(dec *rlp.Stream) error { return err } _tmp0.evmRoot = _tmp7 - // Root: - var _tmp8 hash.Hash + // EvmHash: + var _tmp8 common.Hash if err := dec.ReadBytes(_tmp8[:]); err != nil { return err } - _tmp0.root = _tmp8 + _tmp0.evmHash = _tmp8 + // evmNumber: + _tmp9, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.evmNumber = _tmp9 + // Root: + var _tmp10 hash.Hash + if err := dec.ReadBytes(_tmp10[:]); err != nil { + return err + } + _tmp0.root = _tmp10 if err := dec.ListEnd(); err != nil { return err } diff --git a/meerdag/ghostdag.go b/meerdag/ghostdag.go index 9ce0b4bc..6a5b4eef 100644 --- a/meerdag/ghostdag.go +++ b/meerdag/ghostdag.go @@ -44,7 +44,7 @@ func (gd *GhostDAG) Init(bd *MeerDAG) bool { gd.algorithm = ghostdag.New(nil, gd, gd, gd, model.KType(gd.anticoneSize), params.ActiveNetParams.GenesisHash) //vb - gd.virtualBlock = &Block{hash: model.VirtualBlockHash, layer: 0, mainParent: MaxId, parents: NewIdSet()} + gd.virtualBlock = &Block{hash: model.VirtualBlockHash, layer: 0, mainParent: MaxId, parents: NewIdSet(),state: createMockBlockState(uint64(MaxId))} return true } diff --git a/meerdag/mockblockstate.go b/meerdag/mockblockstate.go index 680ea98d..ca8f974b 100644 --- a/meerdag/mockblockstate.go +++ b/meerdag/mockblockstate.go @@ -4,6 +4,8 @@ import ( "bytes" "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/consensus/model" + "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "io" ) @@ -116,6 +118,22 @@ func (b *mockBlockState) Root() *hash.Hash { return nil } +func (b *mockBlockState) GetEVMRoot() common.Hash { + return common.Hash{} +} +func (b *mockBlockState) GetEVMHash() common.Hash { + return common.Hash{} +} +func (b *mockBlockState) GetEVMNumber() uint64 { + return 0 +} +func (b *mockBlockState) SetEVM(header *etypes.Header) { + +} +func (b *mockBlockState) GetDuplicateTxs() []int { + return nil +} + func createMockBlockState(id uint64) model.BlockState { return &mockBlockState{id: id, status: model.StatusNone, order: uint64(MaxBlockOrder)} } diff --git a/meerevm/evm/block.go b/meerevm/evm/block.go index 38a61ffd..4f606bb5 100644 --- a/meerevm/evm/block.go +++ b/meerevm/evm/block.go @@ -88,3 +88,7 @@ func (b *Block) StateRoot() common.Hash { func (b *Block) Number() uint64 { return b.ethBlock.NumberU64() } + +func (b *Block) ParentState() model.BlockState { + return nil +} diff --git a/meerevm/evm/vm.go b/meerevm/evm/vm.go index 56193c91..475a7861 100644 --- a/meerevm/evm/vm.go +++ b/meerevm/evm/vm.go @@ -173,7 +173,11 @@ func (vm *VM) ConnectBlock(block consensus.Block) (uint64, error) { } func (vm *VM) DisconnectBlock(block consensus.Block) (uint64, error) { - return vm.mchain.DisconnectBlock(block) + return 0, nil +} + +func (vm *VM) RewindTo(state model.BlockState) error { + return vm.mchain.RewindTo(state) } func (vm *VM) ParseBlock([]byte) (consensus.Block, error) { @@ -332,14 +336,6 @@ func (vm *VM) Genesis() *hash.Hash { return nmbb } -func (vm *VM) GetBlockID(bh *hash.Hash) uint64 { - bn := meer.ReadBlockNumber(vm.chain.Ether().ChainDb(), qcommon.ToEVMHash(bh)) - if bn == nil { - return 0 - } - return *bn -} - func (vm *VM) GetBlockIDByTxHash(txhash *hash.Hash) uint64 { tx, _, blockNumber, _, _ := vm.chain.Backend().GetTransaction(nil, qcommon.ToEVMHash(txhash)) if tx == nil { @@ -352,6 +348,10 @@ func (vm *VM) GetCurStateRoot() common.Hash { return vm.chain.Ether().BlockChain().CurrentBlock().Root } +func (vm *VM) GetCurHeader() *types.Header { + return vm.chain.Ether().BlockChain().CurrentBlock() +} + func New() *VM { return &VM{} } diff --git a/meerevm/meer/meerchain.go b/meerevm/meer/meerchain.go index 665898a7..ddf87a61 100644 --- a/meerevm/meer/meerchain.go +++ b/meerevm/meer/meerchain.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "fmt" "github.com/Qitmeer/qng/consensus/model" + "github.com/Qitmeer/qng/consensus/vm" qtypes "github.com/Qitmeer/qng/core/types" qcommon "github.com/Qitmeer/qng/meerevm/common" "github.com/Qitmeer/qng/meerevm/eth" @@ -28,12 +29,17 @@ import ( ) type MeerChain struct { - chain *eth.ETHChain - meerpool *MeerPool + chain *eth.ETHChain + meerpool *MeerPool + consensus model.Consensus } func (b *MeerChain) CheckConnectBlock(block qconsensus.Block) error { - _, _, _, err := b.buildBlock(block.Transactions(), block.Timestamp().Unix()) + parent, err := b.prepareEnvironment(block.ParentState()) + if err != nil { + return err + } + _, _, _, err = b.buildBlock(parent, block.Transactions(), block.Timestamp().Unix()) if err != nil { return err } @@ -41,8 +47,11 @@ func (b *MeerChain) CheckConnectBlock(block qconsensus.Block) error { } func (b *MeerChain) ConnectBlock(block qconsensus.Block) (uint64, error) { - var err error - mblock, _, _, err := b.buildBlock(block.Transactions(), block.Timestamp().Unix()) + parent, err := b.prepareEnvironment(block.ParentState()) + if err != nil { + return 0, err + } + mblock, _, _, err := b.buildBlock(parent, block.Transactions(), block.Timestamp().Unix()) if err != nil { return 0, err } @@ -57,62 +66,25 @@ func (b *MeerChain) ConnectBlock(block qconsensus.Block) (uint64, error) { // mbh := qcommon.ToEVMHash(block.ID()) // - WriteBlockNumber(b.chain.Ether().ChainDb(), mbh, mblock.NumberU64()) - // log.Debug(fmt.Sprintf("MeerEVM Block:number=%d hash=%s txs=%d => blockHash(%s) txs=%d", mblock.Number().Uint64(), mblock.Hash().String(), len(mblock.Transactions()), mbh.String(), len(block.Transactions()))) return mblock.NumberU64(), nil } -func (b *MeerChain) DisconnectBlock(block qconsensus.Block) (uint64, error) { - curBlock := b.chain.Ether().BlockChain().CurrentBlock() - if curBlock == nil { - log.Error("Can't find current block") - return 0, nil - } - - mbh := qcommon.ToEVMHash(block.ID()) - - bn := ReadBlockNumber(b.chain.Ether().ChainDb(), mbh) - if bn == nil { - return 0, nil - } - defer func() { - DeleteBlockNumber(b.chain.Ether().ChainDb(), mbh) - }() - - if *bn > curBlock.Number.Uint64() { - return *bn, nil - } - parentNumber := *bn - 1 - err := b.chain.Ether().BlockChain().SetHead(parentNumber) - if err != nil { - log.Error(err.Error()) - return *bn, nil - } - newParent := b.chain.Ether().BlockChain().CurrentBlock() - if newParent == nil { - log.Error("Can't find current block") - return *bn, nil - } - log.Debug(fmt.Sprintf("Reorganize:%s(%d) => %s(%d)", curBlock.Hash().String(), curBlock.Number.Uint64(), newParent.Hash().String(), newParent.Number.Uint64())) - return *bn, nil -} - -func (b *MeerChain) buildBlock(qtxs []model.Tx, timestamp int64) (*types.Block, types.Receipts, *state.StateDB, error) { +func (b *MeerChain) buildBlock(parent *types.Header, qtxs []model.Tx, timestamp int64) (*types.Block, types.Receipts, *state.StateDB, error) { config := b.chain.Config().Eth.Genesis.Config engine := b.chain.Ether().Engine() - parent := types.NewBlockWithHeader(b.chain.Ether().BlockChain().CurrentBlock()) + parentBlock := types.NewBlockWithHeader(parent) uncles := []*types.Header{} chainreader := &fakeChainReader{config: config} - statedb, err := b.chain.Ether().BlockChain().StateAt(parent.Root()) + statedb, err := b.chain.Ether().BlockChain().StateAt(parentBlock.Root()) if err != nil { return nil, nil, nil, err } - gaslimit := core.CalcGasLimit(parent.GasLimit(), b.meerpool.config.GasCeil) + gaslimit := core.CalcGasLimit(parentBlock.GasLimit(), b.meerpool.config.GasCeil) // --------Will be discard in the future -------------------- if config.ChainID.Int64() == mparams.QngMainnetChainConfig.ChainID.Int64() { @@ -120,7 +92,7 @@ func (b *MeerChain) buildBlock(qtxs []model.Tx, timestamp int64) (*types.Block, } // ---------------------------------------------------------- - header := makeHeader(&b.chain.Config().Eth, parent, statedb, timestamp, gaslimit) + header := makeHeader(&b.chain.Config().Eth, parentBlock, statedb, timestamp, gaslimit) if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(header.Number) == 0 { misc.ApplyDAOHardFork(statedb) @@ -286,6 +258,118 @@ func (b *MeerChain) ETHChain() *eth.ETHChain { return b.chain } +func (b *MeerChain) prepareEnvironment(state model.BlockState) (*types.Header, error) { + curBlockHeader := b.chain.Ether().BlockChain().CurrentBlock() + if curBlockHeader.Number.Uint64() > state.GetEVMNumber() { + err := b.RewindTo(state) + if err != nil { + return nil, err + } + curBlockHeader = b.chain.Ether().BlockChain().CurrentBlock() + } + if curBlockHeader.Hash() == state.GetEVMHash() && + curBlockHeader.Number.Uint64() == state.GetEVMNumber() { + return curBlockHeader, nil + } + getError := func(msg string) error { + return fmt.Errorf("meer chain env error:targetEVM.number=%d, targetEVM.hash=%s, targetState.order=%d, cur.number=%d, cur.hash=%s, %s", state.GetEVMNumber(), state.GetEVMHash().String(), state.GetOrder(), curBlockHeader.Number, curBlockHeader.Hash().String(), msg) + } + if state.GetOrder() <= 0 { + return nil, getError("reach genesis") + } + log.Info("Start to find cur block state", "state.order", state.GetOrder(), "evm.Number", state.GetEVMNumber(), "cur.number", curBlockHeader.Number.Uint64()) + var curBlockState model.BlockState + list := []model.BlockState{state} + startState := b.consensus.BlockChain().GetBlockState(state.GetOrder() - 1) + for startState != nil && startState.GetEVMNumber() >= curBlockHeader.Number.Uint64() { + if startState.GetEVMNumber() == curBlockHeader.Number.Uint64() && + startState.GetEVMHash() == curBlockHeader.Hash() { + curBlockState = startState + break + } + list = append(list, startState) + if startState.GetOrder() <= 0 { + break + } + startState = b.consensus.BlockChain().GetBlockState(startState.GetOrder() - 1) + } + if curBlockState == nil { + return nil, getError("Can't find cur block state") + } + log.Info("Find cur block state", "state.order", curBlockState.GetOrder(), "evm.Number", curBlockState.GetEVMNumber()) + for i := len(list) - 1; i >= 0; i-- { + if list[i].GetStatus().KnownInvalid() { + continue + } + cur := b.chain.Ether().BlockChain().CurrentBlock() + if list[i].GetEVMNumber() == cur.Number.Uint64() { + continue + } + log.Info("Try to restore block state for EVM", "evm.hash", list[i].GetEVMHash().String(), "evm.number", list[i].GetEVMNumber(), "state.order", list[i].GetOrder()) + block := b.chain.Ether().BlockChain().GetBlock(list[i].GetEVMHash(), list[i].GetEVMNumber()) + if block != nil { + log.Info("Try to rebuild evm block", "state.order", list[i].GetOrder()) + sb, err := b.consensus.BlockChain().BlockByOrder(list[i].GetOrder()) + if err != nil { + return nil, getError(err.Error()) + } + parentState := curBlockState + if i != len(list)-1 { + parentState = list[i+1] + } + dtxs := list[i].GetDuplicateTxs() + if len(dtxs) > 0 { + for _, index := range dtxs { + sb.Transactions()[index].IsDuplicate = true + } + } + + eb, err := vm.BuildEVMBlock(sb, parentState) + if err != nil { + return nil, getError(err.Error()) + } + if len(eb.Transactions()) <= 0 { + return nil, getError("transactions is empty") + } + block, _, _, err = b.buildBlock(cur, eb.Transactions(), eb.Timestamp().Unix()) + if err != nil { + return nil, getError(err.Error()) + } + } + st, err := b.chain.Ether().BlockChain().InsertChain(types.Blocks{block}) + if err != nil { + return nil, err + } + if st != 1 { + return nil, getError("insert chain") + } + } + cur := b.chain.Ether().BlockChain().CurrentBlock() + if cur.Hash() == state.GetEVMHash() && + cur.Number.Uint64() == state.GetEVMNumber() { + return cur, nil + } + return nil, getError("prepare environment") +} + +func (b *MeerChain) RewindTo(state model.BlockState) error { + curBlockHeader := b.chain.Ether().BlockChain().CurrentBlock() + if curBlockHeader.Number.Uint64() <= state.GetEVMNumber() { + return nil + } + log.Info("Try to rewind", "cur.number", curBlockHeader.Number.Uint64(), "cur.hash", curBlockHeader.Hash().String(), "target.evm.root", state.GetEVMRoot(), "target.evm.number", state.GetEVMNumber(), "target.evm.hash", state.GetEVMHash()) + err := b.chain.Ether().BlockChain().SetHead(state.GetEVMNumber()) + if err != nil { + return err + } + cur := b.chain.Ether().BlockChain().CurrentBlock() + if cur.Number.Uint64() <= state.GetEVMNumber() { + log.Info("Rewound", "cur.number", cur.Number.Uint64(), "cur.hash", cur.Hash().String(), "target.evm.root", state.GetEVMRoot(), "target.evm.number", state.GetEVMNumber(), "target.evm.hash", state.GetEVMHash()) + return nil + } + return fmt.Errorf("Rewind fail:cur.number=%d, cur.hash=%s, target.evm.root=%s, target.evm.number=%d, target.evm.hash=%s", cur.Number.Uint64(), cur.Hash().String(), state.GetEVMRoot(), state.GetEVMNumber(), state.GetEVMHash()) +} + func NewMeerChain(ctx qconsensus.Context) (*MeerChain, error) { cfg := ctx.GetConfig() eth.InitLog(cfg.DebugLevel, cfg.DebugPrintOrigins) @@ -302,8 +386,9 @@ func NewMeerChain(ctx qconsensus.Context) (*MeerChain, error) { } mc := &MeerChain{ - chain: chain, - meerpool: chain.Config().Eth.Miner.External.(*MeerPool), + chain: chain, + meerpool: chain.Config().Eth.Miner.External.(*MeerPool), + consensus: ctx.GetConsensus(), } mc.meerpool.init(&chain.Config().Eth.Miner, chain.Config().Eth.Genesis.Config, chain.Ether().Engine(), chain.Ether(), chain.Ether().EventMux(), ctx) return mc, nil diff --git a/services/index/vmblock_index.go b/services/index/vmblock_index.go index a8f6f076..9a605a46 100644 --- a/services/index/vmblock_index.go +++ b/services/index/vmblock_index.go @@ -79,15 +79,15 @@ func (idx *VMBlockIndex) caughtUpFrom(startOrder uint) error { if i == 0 { continue } - bh := bc.GetBlockHashByOrder(i) - if bh == nil { + ob := bc.(*blockchain.BlockChain).BlockDAG().GetBlockByOrder(i) + if ob == nil { return fmt.Errorf("No block in order:%d", i) } - bid := bc.(*blockchain.BlockChain).VMService().GetBlockID(bh) + bid := ob.GetState().GetEVMNumber() if bid == 0 { continue } - err := idx.ConnectBlock(bh, bid) + err := idx.ConnectBlock(ob.GetHash(), bid) if err != nil { return err } diff --git a/vm/consensus/block.go b/vm/consensus/block.go index 692bf892..95c48db7 100644 --- a/vm/consensus/block.go +++ b/vm/consensus/block.go @@ -18,6 +18,7 @@ type Block interface { Height() uint64 Timestamp() time.Time Transactions() []model.Tx + ParentState() model.BlockState } type BlockHeader interface { diff --git a/vm/consensus/chainvm.go b/vm/consensus/chainvm.go index ca38ed92..2e4ce9c4 100644 --- a/vm/consensus/chainvm.go +++ b/vm/consensus/chainvm.go @@ -9,6 +9,7 @@ import ( "github.com/Qitmeer/qng/consensus/model" "github.com/Qitmeer/qng/core/types" "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" ) type ChainVM interface { @@ -41,13 +42,14 @@ type ChainVM interface { ConnectBlock(block Block) (uint64, error) DisconnectBlock(block Block) (uint64, error) + RewindTo(state model.BlockState) error ResetTemplate() error Genesis() *hash.Hash - GetBlockID(bh *hash.Hash) uint64 GetBlockIDByTxHash(txhash *hash.Hash) uint64 GetCurStateRoot() common.Hash + GetCurHeader() *etypes.Header } diff --git a/vm/consensus/context.go b/vm/consensus/context.go index bc0d201c..6b7971e9 100644 --- a/vm/consensus/context.go +++ b/vm/consensus/context.go @@ -11,4 +11,5 @@ type Context interface { GetConfig() *config.Config GetTxPool() model.TxPool GetNotify() Notify + GetConsensus() model.Consensus } diff --git a/vm/service.go b/vm/service.go index e02f5c5a..2eecf948 100644 --- a/vm/service.go +++ b/vm/service.go @@ -7,6 +7,7 @@ import ( qconfig "github.com/Qitmeer/qng/config" "github.com/Qitmeer/qng/consensus/model" "github.com/Qitmeer/qng/consensus/vm" + vmc "github.com/Qitmeer/qng/consensus/vm" "github.com/Qitmeer/qng/core/blockchain" "github.com/Qitmeer/qng/core/event" "github.com/Qitmeer/qng/core/types" @@ -15,6 +16,7 @@ import ( "github.com/Qitmeer/qng/rpc/api" "github.com/Qitmeer/qng/vm/consensus" "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" ) type Factory interface { @@ -225,33 +227,32 @@ func (s *Service) GetMempoolSize() int64 { return v.GetMempoolSize() } -func (s *Service) CheckConnectBlock(block *types.SerializedBlock) error { +func (s *Service) CheckConnectBlock(block *types.SerializedBlock, state model.BlockState) error { vm, err := s.GetVM(evm.MeerEVMID) if err != nil { return err } - b, err := s.normalizeBlock(block, true) + b, err := vmc.BuildEVMBlock(block, state) if err != nil { return err } - if len(b.Txs) <= 0 { + if len(b.Transactions()) <= 0 { return nil } return vm.CheckConnectBlock(b) } -func (s *Service) ConnectBlock(block *types.SerializedBlock) (uint64, error) { +func (s *Service) ConnectBlock(block *types.SerializedBlock, state model.BlockState) (uint64, error) { vm, err := s.GetVM(evm.MeerEVMID) if err != nil { return 0, err } - b, err := s.normalizeBlock(block, true) + b, err := vmc.BuildEVMBlock(block, state) if err != nil { return 0, err } - - if len(b.Txs) <= 0 { + if len(b.Transactions()) <= 0 { return 0, nil } return vm.ConnectBlock(b) @@ -262,57 +263,15 @@ func (s *Service) DisconnectBlock(block *types.SerializedBlock) (uint64, error) if err != nil { return 0, err } - b, err := s.normalizeBlock(block, false) - if err != nil { - return 0, err - } - - if len(b.Txs) <= 0 { - return 0, nil - } - return vm.DisconnectBlock(b) + return vm.DisconnectBlock(nil) } -func (s *Service) normalizeBlock(block *types.SerializedBlock, checkDup bool) (*vm.Block, error) { - result := &vm.Block{Id: block.Hash(), Txs: []model.Tx{}, Time: block.Block().Header.Timestamp} - - for idx, tx := range block.Transactions() { - if idx == 0 { - continue - } - if tx.IsDuplicate && checkDup { - continue - } - - if types.IsCrossChainExportTx(tx.Tx) { - ctx, err := vm.NewExportTx(tx.Tx) - if err != nil { - return nil, err - } - result.Txs = append(result.Txs, ctx) - } else if types.IsCrossChainImportTx(tx.Tx) { - ctx, err := vm.NewImportTx(tx.Tx) - if err != nil { - return nil, err - } - err = ctx.SetCoinbaseTx(block.Transactions()[0].Tx) - if err != nil { - return nil, err - } - result.Txs = append(result.Txs, ctx) - } else if types.IsCrossChainVMTx(tx.Tx) { - ctx, err := vm.NewVMTx(tx.Tx) - if err != nil { - return nil, err - } - err = ctx.SetCoinbaseTx(block.Transactions()[0].Tx) - if err != nil { - return nil, err - } - result.Txs = append(result.Txs, ctx) - } +func (s *Service) RewindTo(state model.BlockState) error { + vm, err := s.GetVM(evm.MeerEVMID) + if err != nil { + return err } - return result, nil + return vm.RewindTo(state) } func (s *Service) RegisterAPIs(apis []api.API) { @@ -357,14 +316,6 @@ func (s *Service) Genesis(txs []*types.Tx) *hash.Hash { return vm.Genesis() } -func (s *Service) GetBlockID(bh *hash.Hash) uint64 { - vm, err := s.GetVM(evm.MeerEVMID) - if err != nil { - return 0 - } - return vm.GetBlockID(bh) -} - func (s *Service) GetBlockIDByTxHash(txhash *hash.Hash) uint64 { vm, err := s.GetVM(evm.MeerEVMID) if err != nil { @@ -405,9 +356,18 @@ func (s *Service) GetCurStateRoot() common.Hash { return vm.GetCurStateRoot() } -func NewService(cfg *config.Config, events *event.Feed) (*Service, error) { +func (s *Service) GetCurHeader() *etypes.Header { + vm, err := s.GetVM(evm.MeerEVMID) + if err != nil { + return nil + } + return vm.GetCurHeader() +} + +func NewService(cons model.Consensus) (*Service, error) { + cfg := cons.Config() ser := Service{ - events: events, + events: cons.Events(), vms: make(map[string]consensus.ChainVM), cfg: cfg, apis: []api.API{}, @@ -421,6 +381,7 @@ func NewService(cfg *config.Config, events *event.Feed) (*Service, error) { DebugPrintOrigins: cfg.DebugPrintOrigins, EVMEnv: cfg.EVMEnv, }, + Consensus: cons, } if err := ser.registerVMs(); err != nil { log.Error(err.Error())