diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index f09c73562..3776b4887 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -433,7 +433,7 @@ func (bc *Blockchain) init() error { return fmt.Errorf("can't init MPT at height %d: %w", bHeight, err) } - err = bc.initializeNativeCache(bc.dao) + err = bc.initializeNativeCache(bc.blockHeight, bc.dao) if err != nil { return fmt.Errorf("can't init natives cache: %w", err) } @@ -570,7 +570,7 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateJumpStage) error Root: block.PrevStateRoot, }) - err = bc.initializeNativeCache(bc.dao) + err = bc.initializeNativeCache(block.Index, bc.dao) if err != nil { return fmt.Errorf("failed to initialize natives cache: %w", err) } @@ -585,8 +585,8 @@ func (bc *Blockchain) jumpToStateInternal(p uint32, stage stateJumpStage) error return nil } -func (bc *Blockchain) initializeNativeCache(d *dao.Simple) error { - err := bc.contracts.NEO.InitializeCache(bc, d) +func (bc *Blockchain) initializeNativeCache(blockHeight uint32, d *dao.Simple) error { + err := bc.contracts.NEO.InitializeCache(blockHeight, d) if err != nil { return fmt.Errorf("can't init cache for NEO native contract: %w", err) } @@ -2143,7 +2143,7 @@ func (bc *Blockchain) GetCommittee() (keys.PublicKeys, error) { // GetValidators returns current validators. func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) { - return bc.contracts.NEO.ComputeNextBlockValidators(bc, bc.dao) + return bc.contracts.NEO.ComputeNextBlockValidators(bc.blockHeight, bc.dao) } // GetNextBlockValidators returns next block validators. @@ -2189,7 +2189,7 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact dTrie.Version = bc.dao.Version // Initialize native cache before passing DAO to interop context constructor, because // the constructor will call BaseExecFee/StoragePrice policy methods on the passed DAO. - err = bc.initializeNativeCache(dTrie) + err = bc.initializeNativeCache(b.Index, dTrie) if err != nil { return nil, fmt.Errorf("failed to initialize native cache backed by historic DAO: %w", err) } diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 9217e8d58..351052918 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/io" @@ -339,3 +340,31 @@ func (ic *Context) Exec() error { defer ic.Finalize() return ic.VM.Run() } + +// BlockHeight returns current block height got from Context's block if it's set. +func (ic *Context) BlockHeight() uint32 { + if ic.Block != nil { + return ic.Block.Index - 1 // Persisting block is not yet stored. + } + return ic.Chain.BlockHeight() +} + +// CurrentBlockHash returns current block hash got from Context's block if it's set. +func (ic *Context) CurrentBlockHash() util.Uint256 { + if ic.Block != nil { + return ic.Chain.GetHeaderHash(int(ic.Block.Index - 1)) // Persisting block is not yet stored. + } + return ic.Chain.CurrentBlockHash() +} + +// GetBlock returns block if it exists and available at the current Context's height. +func (ic *Context) GetBlock(hash util.Uint256) (*block.Block, error) { + block, err := ic.Chain.GetBlock(hash) + if err != nil { + return nil, err + } + if block.Index > ic.BlockHeight() { + return nil, storage.ErrKeyNotFound + } + return block, nil +} diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 809395c78..e57e3c598 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -188,7 +188,7 @@ func (s *Designate) getDesignatedByRole(ic *interop.Context, args []stackitem.It panic(ErrInvalidIndex) } index := ind.Uint64() - if index > uint64(ic.Chain.BlockHeight()+1) { + if index > uint64(ic.BlockHeight()+1) { panic(ErrInvalidIndex) } pubs, _, err := s.GetDesignatedByRole(ic.DAO, r, uint32(index)) diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index 3fd2ab57a..28fd6edc4 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -31,7 +31,7 @@ func Call(ic *interop.Context) error { if len(history) == 0 { return fmt.Errorf("native contract %s is disabled", c.Metadata().Name) } - if history[0] > ic.Chain.BlockHeight() { + if history[0] > ic.BlockHeight() { return fmt.Errorf("native contract %s is active after height = %d", c.Metadata().Name, history[0]) } m, ok := c.Metadata().GetMethodByOffset(ic.VM.Context().IP()) diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go index 64506e506..dabd443a8 100644 --- a/pkg/core/native/ledger.go +++ b/pkg/core/native/ledger.go @@ -103,19 +103,19 @@ func (l *Ledger) PostPersist(ic *interop.Context) error { // currentHash implements currentHash SC method. func (l *Ledger) currentHash(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.Make(ic.Chain.CurrentBlockHash().BytesBE()) + return stackitem.Make(ic.CurrentBlockHash().BytesBE()) } // currentIndex implements currentIndex SC method. func (l *Ledger) currentIndex(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.Make(ic.Chain.BlockHeight()) + return stackitem.Make(ic.BlockHeight()) } // getBlock implements getBlock SC method. func (l *Ledger) getBlock(ic *interop.Context, params []stackitem.Item) stackitem.Item { - hash := getBlockHashFromItem(ic.Chain, params[0]) - block, err := ic.Chain.GetBlock(hash) - if err != nil || !isTraceableBlock(ic.Chain, block.Index) { + hash := getBlockHashFromItem(ic, params[0]) + block, err := ic.GetBlock(hash) + if err != nil || !isTraceableBlock(ic, block.Index) { return stackitem.Null{} } return BlockToStackItem(block) @@ -124,7 +124,7 @@ func (l *Ledger) getBlock(ic *interop.Context, params []stackitem.Item) stackite // getTransaction returns transaction to the SC. func (l *Ledger) getTransaction(ic *interop.Context, params []stackitem.Item) stackitem.Item { tx, h, err := getTransactionAndHeight(ic.DAO, params[0]) - if err != nil || !isTraceableBlock(ic.Chain, h) { + if err != nil || !isTraceableBlock(ic, h) { return stackitem.Null{} } return TransactionToStackItem(tx) @@ -133,7 +133,7 @@ func (l *Ledger) getTransaction(ic *interop.Context, params []stackitem.Item) st // getTransactionHeight returns transaction height to the SC. func (l *Ledger) getTransactionHeight(ic *interop.Context, params []stackitem.Item) stackitem.Item { _, h, err := getTransactionAndHeight(ic.DAO, params[0]) - if err != nil || !isTraceableBlock(ic.Chain, h) { + if err != nil || !isTraceableBlock(ic, h) { return stackitem.Make(-1) } return stackitem.Make(h) @@ -142,10 +142,10 @@ func (l *Ledger) getTransactionHeight(ic *interop.Context, params []stackitem.It // getTransactionFromBlock returns transaction with the given index from the // block with height or hash specified. func (l *Ledger) getTransactionFromBlock(ic *interop.Context, params []stackitem.Item) stackitem.Item { - hash := getBlockHashFromItem(ic.Chain, params[0]) + hash := getBlockHashFromItem(ic, params[0]) index := toUint32(params[1]) - block, err := ic.Chain.GetBlock(hash) - if err != nil || !isTraceableBlock(ic.Chain, block.Index) { + block, err := ic.GetBlock(hash) + if err != nil || !isTraceableBlock(ic, block.Index) { return stackitem.Null{} } if index >= uint32(len(block.Transactions)) { @@ -157,7 +157,7 @@ func (l *Ledger) getTransactionFromBlock(ic *interop.Context, params []stackitem // getTransactionSigners returns transaction signers to the SC. func (l *Ledger) getTransactionSigners(ic *interop.Context, params []stackitem.Item) stackitem.Item { tx, h, err := getTransactionAndHeight(ic.DAO, params[0]) - if err != nil || !isTraceableBlock(ic.Chain, h) { + if err != nil || !isTraceableBlock(ic, h) { return stackitem.Null{} } return SignersToStackItem(tx.Signers) @@ -170,7 +170,7 @@ func (l *Ledger) getTransactionVMState(ic *interop.Context, params []stackitem.I panic(err) } h, _, aer, err := ic.DAO.GetTxExecResult(hash) - if err != nil || !isTraceableBlock(ic.Chain, h) { + if err != nil || !isTraceableBlock(ic, h) { return stackitem.Make(vm.NoneState) } return stackitem.Make(aer.VMState) @@ -178,9 +178,9 @@ func (l *Ledger) getTransactionVMState(ic *interop.Context, params []stackitem.I // isTraceableBlock defines whether we're able to give information about // the block with index specified. -func isTraceableBlock(bc interop.Ledger, index uint32) bool { - height := bc.BlockHeight() - MaxTraceableBlocks := bc.GetConfig().MaxTraceableBlocks +func isTraceableBlock(ic *interop.Context, index uint32) bool { + height := ic.BlockHeight() + MaxTraceableBlocks := ic.Chain.GetConfig().MaxTraceableBlocks return index <= height && index+MaxTraceableBlocks > height } @@ -188,17 +188,17 @@ func isTraceableBlock(bc interop.Ledger, index uint32) bool { // Ledger if needed. Interop functions accept both block numbers and // block hashes as parameters, thus this function is needed. It's supposed to // be called within VM context, so it panics if anything goes wrong. -func getBlockHashFromItem(bc interop.Ledger, item stackitem.Item) util.Uint256 { +func getBlockHashFromItem(ic *interop.Context, item stackitem.Item) util.Uint256 { bigindex, err := item.TryInteger() if err == nil && bigindex.IsUint64() { index := bigindex.Uint64() if index > math.MaxUint32 { panic("bad block index") } - if uint32(index) > bc.BlockHeight() { + if uint32(index) > ic.BlockHeight() { panic(fmt.Errorf("no block with index %d", index)) } - return bc.GetHeaderHash(int(index)) + return ic.Chain.GetHeaderHash(int(index)) } hash, err := getUint256FromItem(item) if err != nil { diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index dae1bfb25..84f66bee0 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -249,7 +249,7 @@ func (n *NEO) Initialize(ic *interop.Context) error { committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)] cvs := toKeysWithVotes(committee0) - err := n.updateCache(cache, cvs, ic.Chain) + err := n.updateCache(cache, cvs, ic.BlockHeight()) if err != nil { return err } @@ -279,7 +279,7 @@ func (n *NEO) Initialize(ic *interop.Context) error { // InitializeCache initializes all NEO cache with the proper values from storage. // Cache initialisation should be done apart from Initialize because Initialize is // called only when deploying native contracts. -func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error { +func (n *NEO) InitializeCache(blockHeight uint32, d *dao.Simple) error { cache := &NeoCache{ gasPerVoteCache: make(map[string]big.Int), votesChanged: true, @@ -290,7 +290,7 @@ func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error { if err := committee.DecodeBytes(si); err != nil { return fmt.Errorf("failed to decode committee: %w", err) } - if err := n.updateCache(cache, committee, bc); err != nil { + if err := n.updateCache(cache, committee, blockHeight); err != nil { return fmt.Errorf("failed to update cache: %w", err) } @@ -309,7 +309,7 @@ func (n *NEO) initConfigCache(cfg config.ProtocolConfiguration) error { return err } -func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, bc interop.Ledger) error { +func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, blockHeight uint32) error { cache.committee = cvs var committee = getCommitteeMembers(cache) @@ -319,8 +319,7 @@ func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, bc interop.Ledger) } cache.committeeHash = hash.Hash160(script) - // TODO: use block height from interop context for proper historical calls handling. - nextVals := committee[:n.cfg.GetNumOfCNs(bc.BlockHeight()+1)].Copy() + nextVals := committee[:n.cfg.GetNumOfCNs(blockHeight+1)].Copy() sort.Sort(nextVals) cache.nextValidators = nextVals return nil @@ -333,11 +332,11 @@ func (n *NEO) updateCommittee(cache *NeoCache, ic *interop.Context) error { return nil } - _, cvs, err := n.computeCommitteeMembers(ic.Chain, ic.DAO) + _, cvs, err := n.computeCommitteeMembers(ic.BlockHeight(), ic.DAO) if err != nil { return err } - if err := n.updateCache(cache, cvs, ic.Chain); err != nil { + if err := n.updateCache(cache, cvs, ic.BlockHeight()); err != nil { return err } cache.votesChanged = false @@ -939,8 +938,8 @@ func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stacki } // ComputeNextBlockValidators returns an actual list of current validators. -func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) { - numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1) +func (n *NEO) ComputeNextBlockValidators(blockHeight uint32, d *dao.Simple) (keys.PublicKeys, error) { + numOfCNs := n.cfg.GetNumOfCNs(blockHeight + 1) // Most of the time it should be OK with RO cache, thus try to retrieve // validators without RW cache creation to avoid cached values copying. cache := d.GetROCache(n.ID).(*NeoCache) @@ -948,7 +947,7 @@ func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys return vals.Copy(), nil } cache = d.GetRWCache(n.ID).(*NeoCache) - result, _, err := n.computeCommitteeMembers(bc, d) + result, _, err := n.computeCommitteeMembers(blockHeight, d) if err != nil { return nil, err } @@ -1007,7 +1006,7 @@ func toKeysWithVotes(pubs keys.PublicKeys) keysWithVotes { } // computeCommitteeMembers returns public keys of nodes in committee. -func (n *NEO) computeCommitteeMembers(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, keysWithVotes, error) { +func (n *NEO) computeCommitteeMembers(blockHeight uint32, d *dao.Simple) (keys.PublicKeys, keysWithVotes, error) { key := []byte{prefixVotersCount} si := d.GetStorageItem(n.ID, key) if si == nil { @@ -1019,7 +1018,7 @@ func (n *NEO) computeCommitteeMembers(bc interop.Ledger, d *dao.Simple) (keys.Pu _, totalSupply := n.getTotalSupply(d) voterTurnout := votersCount.Div(votersCount, totalSupply) - count := n.cfg.GetCommitteeSize(bc.BlockHeight() + 1) + count := n.cfg.GetCommitteeSize(blockHeight + 1) // Can be sorted and/or returned to outside users, thus needs to be copied. sbVals := keys.PublicKeys(n.standbyKeys[:count]).Copy() cs, err := n.getCandidates(d, false) diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index fadf8a372..2d5ab29ec 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -223,7 +223,7 @@ func (n *Notary) onPayment(ic *interop.Context, args []stackitem.Item) stackitem } allowedChangeTill := ic.Tx.Sender() == to - currentHeight := ic.Chain.BlockHeight() + currentHeight := ic.BlockHeight() deposit := n.GetDepositFor(ic.DAO, to) till := toUint32(additionalParams[1]) if till < currentHeight { @@ -266,7 +266,7 @@ func (n *Notary) lockDepositUntil(ic *interop.Context, args []stackitem.Item) st return stackitem.NewBool(false) } till := toUint32(args[1]) - if till < ic.Chain.BlockHeight() { + if till < ic.BlockHeight() { return stackitem.NewBool(false) } deposit := n.GetDepositFor(ic.DAO, addr) @@ -302,7 +302,7 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem. if deposit == nil { return stackitem.NewBool(false) } - if ic.Chain.BlockHeight() < deposit.Till { + if ic.BlockHeight() < deposit.Till { return stackitem.NewBool(false) } cs, err := ic.GetContract(n.GAS.Hash) @@ -416,8 +416,8 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem value := toUint32(args[0]) cfg := ic.Chain.GetConfig() maxInc := cfg.MaxValidUntilBlockIncrement - if value > maxInc/2 || value < uint32(cfg.GetNumOfCNs(ic.Chain.BlockHeight())) { - panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be more than %d or less than %d", maxInc/2, cfg.GetNumOfCNs(ic.Chain.BlockHeight()))) + if value > maxInc/2 || value < uint32(cfg.GetNumOfCNs(ic.BlockHeight())) { + panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be more than %d or less than %d", maxInc/2, cfg.GetNumOfCNs(ic.BlockHeight()))) } if !n.NEO.checkCommittee(ic) { panic("invalid committee signature")