diff --git a/cmd/efsn/chaincmd.go b/cmd/efsn/chaincmd.go index f724f0f2..6cf9dcf4 100644 --- a/cmd/efsn/chaincmd.go +++ b/cmd/efsn/chaincmd.go @@ -454,7 +454,7 @@ func dump(ctx *cli.Context) error { fmt.Println("{}") utils.Fatalf("block not found") } else { - state, err := state.New(block.Root(), state.NewDatabase(chainDb)) + state, err := state.New(block.Root(), block.MixDigest(), state.NewDatabase(chainDb)) if err != nil { utils.Fatalf("could not create new state: %v", err) } diff --git a/common/types.go b/common/types.go index d1f2ed35..62d4ffbd 100644 --- a/common/types.go +++ b/common/types.go @@ -834,13 +834,50 @@ func (s TicketSlice) ToMap() map[Hash]TicketDisplay { } func (s TicketSlice) DeepCopy() TicketSlice { - r := make(TicketSlice, 0, len(s)) - for _, t := range s { - r = append(r, t) + r := make(TicketSlice, len(s)) + for i, t := range s { + r[i] = t } return r } +func (s TicketSlice) AllIds() []Hash { + ids := make([]Hash, len(s)) + for i, t := range s { + ids[i] = t.ID + } + return ids +} + +func (s TicketSlice) Get(id Hash) (*Ticket, error) { + for _, t := range s { + if t.ID == id { + return &t, nil + } + } + return nil, fmt.Errorf("%v ticket not fount", id.String()) +} + +func (s TicketSlice) AddTicket(ticket *Ticket) (TicketSlice, error) { + for _, t := range s { + if t.ID == ticket.ID { + return s, fmt.Errorf("AddTicket: %v ticket exist", t.ID.String()) + } + } + s = append(s, *ticket) + return s, nil +} + +func (s TicketSlice) RemoveTicket(id Hash) (TicketSlice, error) { + for i, t := range s { + if t.ID == id { + s = append(s[:i], s[i+1:]...) + return s, nil + } + } + return nil, fmt.Errorf("RemoveTicket: %v ticket not fount", id.String()) +} + // Swap wacom type Swap struct { ID Hash diff --git a/consensus/datong/consensus.go b/consensus/datong/consensus.go index c0aff6f9..dbb72a32 100644 --- a/consensus/datong/consensus.go +++ b/consensus/datong/consensus.go @@ -227,19 +227,15 @@ func (dt *DaTong) verifySeal(chain consensus.ChainReader, header *types.Header, return err } ticketID := snap.GetVoteTicket() - ticket, err := dt.getTicket(parent, ticketID) - if err != nil { - return err - } - // verify ticket with signer - if header.Coinbase != ticket.Owner() { - return errors.New("Coinbase is not the voted ticket owner") - } // verify ticket: list squence, ID , ticket Info, difficulty diff, tk, listSq, _, errv := dt.calcBlockDifficulty(chain, header, parent) if errv != nil { return errv } + // verify ticket with signer + if tk.Owner() != header.Coinbase { + return errors.New("Coinbase is not the voted ticket owner") + } // check ticket ID if tk.ID != ticketID { return fmt.Errorf("verifySeal ticketID mismatch, have %v, want %v", ticketID.String(), tk.ID.String()) @@ -404,6 +400,15 @@ func (dt *DaTong) Finalize(chain consensus.ChainReader, header *types.Header, st log.Warn("Next block have no ticket, wait buy ticket.") return nil, errors.New("Next block have no ticket, wait buy ticket.") } + ticketsIDHash, err := headerState.UpdateTickets() + if err != nil { + return nil, errors.New("UpdateTickets failed") + } + if header.MixDigest == (common.Hash{}) { + header.MixDigest = ticketsIDHash + } else if header.MixDigest != ticketsIDHash { + return nil, fmt.Errorf("verify MixDigest failed, have %v, want %v", header.MixDigest, ticketsIDHash) + } snap.SetWeight(remainingWeight) snap.SetTicketWeight(remainingWeight) @@ -517,16 +522,8 @@ func (dt *DaTong) Close() error { return nil } -func (dt *DaTong) getTicket(header *types.Header, id common.Hash) (*common.Ticket, error) { - statedb, err := state.New(header.Root, dt.stateCache) - if err != nil { - return nil, fmt.Errorf("getTicket error:%v", err) - } - return statedb.GetTicket(id) -} - func (dt *DaTong) getAllTickets(header *types.Header) (common.TicketSlice, error) { - statedb, err := state.New(header.Root, dt.stateCache) + statedb, err := state.New(header.Root, header.MixDigest, dt.stateCache) if err != nil { return nil, fmt.Errorf("getAllTickets error:%v", err) } diff --git a/core/blockchain.go b/core/blockchain.go index 94c62748..52af7224 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -286,7 +286,7 @@ func (bc *BlockChain) SetHead(head uint64) error { bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())) } if currentBlock := bc.CurrentBlock(); currentBlock != nil { - if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { + if _, err := bc.State(); err != nil { // Rewound state missing, rolled back to before pivot, reset to genesis bc.currentBlock.Store(bc.genesisBlock) } @@ -378,12 +378,12 @@ func (bc *BlockChain) Processor() Processor { // State returns a new mutable state based on the current HEAD block. func (bc *BlockChain) State() (*state.StateDB, error) { - return bc.StateAt(bc.CurrentBlock().Root()) + return bc.StateAt(bc.CurrentBlock().Root(), bc.CurrentBlock().MixDigest()) } // StateAt returns a new mutable state based on a particular point in time. -func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { - return state.New(root, bc.stateCache) +func (bc *BlockChain) StateAt(root common.Hash, mixDigest common.Hash) (*state.StateDB, error) { + return state.New(root, mixDigest, bc.stateCache) } // Reset purges the entire blockchain, restoring it to its genesis state. @@ -428,7 +428,7 @@ func (bc *BlockChain) repair(head **types.Block) error { for { blockNumber := (*head).Number() // Abort if we've rewound to a head block that does have associated state - if _, err := state.New((*head).Root(), bc.stateCache); err == nil { + if _, err := bc.StateAt((*head).Root(), (*head).MixDigest()); err == nil { if rewound { log.Info("Rewound blockchain to past state", "number", blockNumber, "hash", (*head).Hash()) } @@ -1174,7 +1174,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty } else { parent = chain[i-1] } - state, err := state.New(parent.Root(), bc.stateCache) + state, err := bc.StateAt(parent.Root(), parent.MixDigest()) if err != nil { return i, events, coalescedLogs, err } diff --git a/core/chain_makers.go b/core/chain_makers.go index afaf554f..aae2de7e 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -216,7 +216,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse return nil, nil } for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root(), state.NewDatabase(db)) + statedb, err := state.New(parent.Root(), parent.MixDigest(), state.NewDatabase(db)) if err != nil { panic(err) } diff --git a/core/genesis.go b/core/genesis.go index 864066a8..dc0d8f79 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -233,7 +233,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if db == nil { db = ethdb.NewMemDatabase() } - statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) + statedb, _ := state.New(common.Hash{}, common.Hash{}, state.NewDatabase(db)) for addr, account := range g.Alloc { statedb.AddBalance(addr, common.SystemAssetID, account.Balance) statedb.SetCode(addr, account.Code) @@ -246,6 +246,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { blockNumber := new(big.Int).SetUint64(g.Number) timestamp := new(big.Int).SetUint64(g.Timestamp) + var ticketsIDHash common.Hash if g.TicketCreateInfo != nil { var x uint64 for x = 0; x < g.TicketCreateInfo.Count; x++ { @@ -257,6 +258,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { } statedb.AddTicket(ticket) } + ticketsIDHash, _ = statedb.UpdateTickets() } statedb.GenAsset(common.SystemAsset) @@ -271,7 +273,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { GasLimit: g.GasLimit, GasUsed: g.GasUsed, Difficulty: g.Difficulty, - MixDigest: g.Mixhash, + MixDigest: ticketsIDHash, Coinbase: g.Coinbase, Root: root, } diff --git a/core/state/state_object.go b/core/state/state_object.go index 4d27944e..b0045709 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -479,9 +479,6 @@ func (s *stateObject) setTimeLockBalance(assetID common.Hash, amount *common.Tim func (c *stateObject) ReturnGas(gas *big.Int) {} func (self *stateObject) deepCopy(db *StateDB) *stateObject { - if self.address == common.TicketKeyAddress { - self.CommitTrie(db.db) - } stateObject := newObject(db, self.address, self.data) if self.trie != nil { stateObject.trie = db.db.CopyTrie(self.trie) diff --git a/core/state/statedb.go b/core/state/statedb.go index 7ad81ab2..9e938b6c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -81,17 +81,20 @@ type StateDB struct { validRevisions []revision nextRevisionId int + ticketsHash common.Hash + tickets common.TicketSlice + lock sync.Mutex rwlock sync.RWMutex } // Create a new state from a given trie. -func New(root common.Hash, db Database) (*StateDB, error) { +func New(root common.Hash, mixDigest common.Hash, db Database) (*StateDB, error) { tr, err := db.OpenTrie(root) if err != nil { return nil, err } - return &StateDB{ + statedb := &StateDB{ db: db, trie: tr, stateObjects: make(map[common.Address]*stateObject), @@ -99,7 +102,10 @@ func New(root common.Hash, db Database) (*StateDB, error) { logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), journal: newJournal(), - }, nil + ticketsHash: mixDigest, + tickets: nil, + } + return statedb, nil } // setError remembers the first non-nil error it is called with. @@ -130,6 +136,7 @@ func (self *StateDB) Reset(root common.Hash) error { self.logSize = 0 self.preimages = make(map[common.Hash][]byte) self.clearJournalAndRefund() + self.tickets = nil return nil } @@ -570,6 +577,8 @@ func (self *StateDB) Copy() *StateDB { logSize: self.logSize, preimages: make(map[common.Hash][]byte), journal: newJournal(), + ticketsHash: self.ticketsHash, + tickets: self.tickets.DeepCopy(), } // Copy the dirty states, logs, and preimages for addr := range self.journal.dirties { @@ -961,7 +970,13 @@ func (db *StateDB) setTicketState(key, value common.Hash) { // IsTicketExist wacom func (db *StateDB) IsTicketExist(id common.Hash) bool { - _, err := db.GetTicket(id) + tickets, err := db.AllTickets() + if err != nil { + log.Error("IsTicketExist unable to retrieve all tickets") + return false + } + + _, err = tickets.Get(id) return err == nil } @@ -983,58 +998,82 @@ func (db *StateDB) GetTicket(id common.Hash) (*common.Ticket, error) { // AllTickets wacom func (db *StateDB) AllTickets() (common.TicketSlice, error) { + if len(db.tickets) != 0 { + return db.tickets, nil + } + db.rwlock.RLock() defer db.rwlock.RUnlock() - stateObject := db.getStateObject(common.TicketKeyAddress) - if stateObject == nil { - return nil, nil + key := db.ticketsHash + blob := db.GetStructData(common.TicketKeyAddress, key.Bytes()) + if len(blob) == 0 { + return common.TicketSlice{}, nil } - tickets := make(common.TicketSlice, 0, 1000) - tr := stateObject.getTrie(db.db) - it := trie.NewIterator(tr.NodeIterator(nil)) - emptyKey := common.Hash{} - for it.Next() { - key := common.BytesToHash(tr.GetKey(it.Key)) - if key == emptyKey { - continue - } - value, dirty := stateObject.dirtyStorage[key] - if !dirty { - if _, content, _, err := rlp.Split(it.Value); err == nil { - value = common.BytesToHash(content) - } - } - if ticket, err := common.ParseTicket(key, value); err == nil { - tickets = append(tickets, *ticket) + var ids []common.Hash + if err := rlp.DecodeBytes(blob, &ids); err != nil { + log.Error("AllTickets: Unable to decode tickets " + key.String()) + return nil, fmt.Errorf("Unable to decode tickets, err: %v", err) + } + tickets := make(common.TicketSlice, len(ids)) + for i, id := range ids { + ticket, err := db.GetTicket(id) + if err != nil { + log.Error("AllTickets error: " + err.Error()) + return nil, fmt.Errorf("AllTickets error: %v", err) } + tickets[i] = *ticket } - return tickets, nil + db.tickets = tickets + return db.tickets, nil } // AddTicket wacom func (db *StateDB) AddTicket(ticket common.Ticket) error { - id := ticket.ID - if id == (common.Hash{}) { - return fmt.Errorf("AddTicket: empty ticket ID") + tickets, err := db.AllTickets() + if err != nil { + return fmt.Errorf("AddTicket error: %v", err) } - if db.IsTicketExist(id) == true { - return fmt.Errorf("AddTicket: %s Ticket exists", id.String()) + tickets, err = tickets.AddTicket(&ticket) + if err != nil { + return fmt.Errorf("AddTicket error: %v", err) } + db.tickets = tickets value := ticket.ToValHash() - db.setTicketState(id, value) + db.setTicketState(ticket.ID, value) return nil } // RemoveTicket wacom func (db *StateDB) RemoveTicket(id common.Hash) error { - if db.IsTicketExist(id) == false { - return fmt.Errorf("RemoveTicket: %s Ticket not found", id.String()) + tickets, err := db.AllTickets() + if err != nil { + return fmt.Errorf("RemoveTicket error: %v", err) + } + tickets, err = tickets.RemoveTicket(id) + if err != nil { + return fmt.Errorf("RemoveTicket error: %v", err) } - db.setTicketState(id, common.Hash{}) + db.tickets = tickets return nil } +func (db *StateDB) UpdateTickets() (common.Hash, error) { + ids := db.tickets.AllIds() + blob, err := rlp.EncodeToBytes(&ids) + if err != nil { + log.Error("UpdateTickets: Unable to encode tickets") + return common.Hash{}, fmt.Errorf("Unable to encode tickets, err: %v", err) + } + hash := crypto.Keccak256Hash(blob) + + db.rwlock.Lock() + defer db.rwlock.Unlock() + + db.SetStructData(common.TicketKeyAddress, hash.Bytes(), blob) + return hash, nil +} + // AllSwaps wacom func (db *StateDB) AllSwaps() (map[common.Hash]common.Swap, error) { return nil, fmt.Errorf("AllSwaps has been depreciated please use api.fusionnetwork.io") diff --git a/core/tx_pool.go b/core/tx_pool.go index d2771968..315868ab 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -117,7 +117,7 @@ const ( type blockChain interface { CurrentBlock() *types.Block GetBlock(hash common.Hash, number uint64) *types.Block - StateAt(root common.Hash) (*state.StateDB, error) + StateAt(root common.Hash, mixDigest common.Hash) (*state.StateDB, error) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription } @@ -424,7 +424,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { if newHead == nil { newHead = pool.chain.CurrentBlock().Header() // Special case during testing } - statedb, err := pool.chain.StateAt(newHead.Root) + statedb, err := pool.chain.StateAt(newHead.Root, newHead.MixDigest) if err != nil { log.Error("Failed to reset txpool state", "err", err) return diff --git a/eth/api.go b/eth/api.go index 3b2fc36a..7f3435b5 100644 --- a/eth/api.go +++ b/eth/api.go @@ -269,7 +269,7 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error if block == nil { return state.Dump{}, fmt.Errorf("block #%d not found", blockNr) } - stateDb, err := api.eth.BlockChain().StateAt(block.Root()) + stateDb, err := api.eth.BlockChain().StateAt(block.Root(), block.MixDigest()) if err != nil { return state.Dump{}, err } diff --git a/eth/api_backend.go b/eth/api_backend.go index 01613f77..5f5d8892 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -98,7 +98,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. if header == nil || err != nil { return nil, nil, err } - stateDb, err := b.eth.BlockChain().StateAt(header.Root) + stateDb, err := b.eth.BlockChain().StateAt(header.Root, header.MixDigest) return stateDb, header, err } diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 7b96c9e6..cae88e0f 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -146,7 +146,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl return nil, fmt.Errorf("parent block #%d not found", number-1) } } - statedb, err := state.New(start.Root(), database) + statedb, err := state.New(start.Root(), start.MixDigest(), database) if err != nil { // If the starting state is missing, allow some number of blocks to be reexecuted reexec := defaultTraceReexec @@ -159,7 +159,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl if start == nil { break } - if statedb, err = state.New(start.Root(), database); err == nil { + if statedb, err = state.New(start.Root(), start.MixDigest(), database); err == nil { break } } @@ -477,7 +477,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // attempted to be reexecuted to generate the desired state. func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) { // If we have the state fully available, use that - statedb, err := api.eth.blockchain.StateAt(block.Root()) + statedb, err := api.eth.blockchain.StateAt(block.Root(), block.MixDigest()) if err == nil { return statedb, nil } @@ -490,7 +490,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (* if block == nil { break } - if statedb, err = state.New(block.Root(), database); err == nil { + if statedb, err = state.New(block.Root(), block.MixDigest(), database); err == nil { break } } diff --git a/light/trie.go b/light/trie.go index 1e69f33d..0298a0b5 100644 --- a/light/trie.go +++ b/light/trie.go @@ -30,7 +30,7 @@ import ( ) func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB { - state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr)) + state, _ := state.New(head.Root, head.MixDigest, NewStateDatabase(ctx, head, odr)) return state } diff --git a/miner/worker.go b/miner/worker.go index dec97907..518f95a6 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -571,7 +571,7 @@ func (w *worker) resultLoop() { // makeCurrent creates a new environment for the current cycle. func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { - state, err := w.chain.StateAt(parent.Root()) + state, err := w.chain.StateAt(parent.Root(), parent.MixDigest()) if err != nil { return err } @@ -761,6 +761,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) GasLimit: core.CalcGasLimit(parent, w.gasFloor, w.gasCeil), Extra: w.extra, Time: big.NewInt(timestamp), + Difficulty: common.Big0, } // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) if w.isRunning() {