core: use proper current block height/hash for interop API

This commit is contained in:
Anna Shaleva 2022-04-29 18:00:46 +03:00
parent 9cc41528ef
commit 473955c2d6
7 changed files with 72 additions and 44 deletions

View file

@ -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)
}

View file

@ -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
}

View file

@ -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))

View file

@ -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())

View file

@ -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 {

View file

@ -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)

View file

@ -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")