2020-04-08 10:35:39 +00:00
|
|
|
package interop
|
|
|
|
|
|
|
|
import (
|
2021-10-07 11:27:55 +00:00
|
|
|
"context"
|
2021-07-14 12:05:28 +00:00
|
|
|
"encoding/binary"
|
2020-07-28 13:38:00 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
2021-02-15 13:40:44 +00:00
|
|
|
"strings"
|
2020-07-28 13:38:00 +00:00
|
|
|
|
2022-01-12 22:08:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
2020-04-08 10:35:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
2021-02-15 13:40:44 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
2020-04-08 10:35:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2022-04-29 15:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
2020-04-08 10:35:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2021-03-25 12:22:16 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
2021-02-15 13:40:44 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2020-12-29 10:45:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2020-04-22 20:00:18 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
2021-01-13 12:34:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
2020-04-08 10:35:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
2020-04-22 20:00:18 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-04-08 10:35:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
2021-02-15 13:40:44 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2020-06-03 12:55:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2020-04-08 10:35:39 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2020-12-11 12:22:49 +00:00
|
|
|
const (
|
2022-04-20 18:30:09 +00:00
|
|
|
// DefaultBaseExecFee specifies the default multiplier for opcode and syscall prices.
|
2020-12-11 12:22:49 +00:00
|
|
|
DefaultBaseExecFee = 30
|
|
|
|
)
|
|
|
|
|
2022-01-12 22:08:53 +00:00
|
|
|
// Ledger is the interface to Blockchain required for Context functionality.
|
|
|
|
type Ledger interface {
|
|
|
|
BlockHeight() uint32
|
|
|
|
CurrentBlockHash() util.Uint256
|
|
|
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
2022-12-06 13:34:38 +00:00
|
|
|
GetConfig() config.Blockchain
|
2022-11-18 20:19:50 +00:00
|
|
|
GetHeaderHash(uint32) util.Uint256
|
2022-01-12 22:08:53 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 10:35:39 +00:00
|
|
|
// Context represents context in which interops are executed.
|
|
|
|
type Context struct {
|
2022-05-26 10:27:24 +00:00
|
|
|
Chain Ledger
|
|
|
|
Container hash.Hashable
|
|
|
|
Network uint32
|
|
|
|
Hardforks map[string]uint32
|
|
|
|
Natives []Contract
|
|
|
|
Trigger trigger.Type
|
|
|
|
Block *block.Block
|
|
|
|
NonceData [16]byte
|
|
|
|
Tx *transaction.Transaction
|
|
|
|
DAO *dao.Simple
|
|
|
|
Notifications []state.NotificationEvent
|
|
|
|
Log *zap.Logger
|
|
|
|
VM *vm.VM
|
|
|
|
Functions []Function
|
|
|
|
Invocations map[util.Uint160]int
|
|
|
|
cancelFuncs []context.CancelFunc
|
|
|
|
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error)
|
|
|
|
baseExecFee int64
|
|
|
|
baseStorageFee int64
|
2022-06-06 18:53:03 +00:00
|
|
|
loadToken func(ic *Context, id int32) error
|
2022-05-26 10:27:24 +00:00
|
|
|
GetRandomCounter uint32
|
|
|
|
signers []transaction.Signer
|
2020-04-08 10:35:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewContext returns new interop context.
|
2022-04-08 09:27:25 +00:00
|
|
|
func NewContext(trigger trigger.Type, bc Ledger, d *dao.Simple, baseExecFee, baseStorageFee int64,
|
2022-02-16 15:04:47 +00:00
|
|
|
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error), natives []Contract,
|
2022-06-06 18:53:03 +00:00
|
|
|
loadTokenFunc func(ic *Context, id int32) error,
|
2020-12-13 15:26:35 +00:00
|
|
|
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
2022-02-16 16:13:06 +00:00
|
|
|
dao := d.GetPrivate()
|
2022-12-06 13:34:38 +00:00
|
|
|
cfg := bc.GetConfig().ProtocolConfiguration
|
2020-04-08 10:35:39 +00:00
|
|
|
return &Context{
|
2022-04-08 09:27:25 +00:00
|
|
|
Chain: bc,
|
2022-05-06 12:19:17 +00:00
|
|
|
Network: uint32(cfg.Magic),
|
|
|
|
Hardforks: cfg.Hardforks,
|
2022-04-08 09:27:25 +00:00
|
|
|
Natives: natives,
|
|
|
|
Trigger: trigger,
|
|
|
|
Block: block,
|
|
|
|
Tx: tx,
|
|
|
|
DAO: dao,
|
|
|
|
Log: log,
|
|
|
|
Invocations: make(map[util.Uint160]int),
|
|
|
|
getContract: getContract,
|
|
|
|
baseExecFee: baseExecFee,
|
|
|
|
baseStorageFee: baseStorageFee,
|
2022-06-06 18:53:03 +00:00
|
|
|
loadToken: loadTokenFunc,
|
2020-04-08 10:35:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-14 12:05:28 +00:00
|
|
|
// InitNonceData initializes nonce to be used in `GetRandom` calculations.
|
|
|
|
func (ic *Context) InitNonceData() {
|
|
|
|
if tx, ok := ic.Container.(*transaction.Transaction); ok {
|
|
|
|
copy(ic.NonceData[:], tx.Hash().BytesBE())
|
|
|
|
}
|
|
|
|
if ic.Block != nil {
|
|
|
|
nonce := ic.Block.Nonce
|
|
|
|
nonce ^= binary.LittleEndian.Uint64(ic.NonceData[:])
|
|
|
|
binary.LittleEndian.PutUint64(ic.NonceData[:], nonce)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 16:29:26 +00:00
|
|
|
// UseSigners allows overriding signers used in this context.
|
|
|
|
func (ic *Context) UseSigners(s []transaction.Signer) {
|
|
|
|
ic.signers = s
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Signers returns signers witnessing the current execution context.
|
2021-11-15 16:29:26 +00:00
|
|
|
func (ic *Context) Signers() []transaction.Signer {
|
|
|
|
if ic.signers != nil {
|
|
|
|
return ic.signers
|
|
|
|
}
|
|
|
|
if ic.Tx != nil {
|
|
|
|
return ic.Tx.Signers
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Function binds function name, id with the function itself and the price,
|
2020-04-08 10:35:39 +00:00
|
|
|
// it's supposed to be inited once for all interopContexts, so it doesn't use
|
|
|
|
// vm.InteropFuncPrice directly.
|
|
|
|
type Function struct {
|
2020-07-28 10:17:38 +00:00
|
|
|
ID uint32
|
|
|
|
Name string
|
2020-08-07 11:37:49 +00:00
|
|
|
Func func(*Context) error
|
2020-07-28 10:17:38 +00:00
|
|
|
// ParamCount is a number of function parameters.
|
|
|
|
ParamCount int
|
|
|
|
Price int64
|
2020-06-10 14:21:26 +00:00
|
|
|
// RequiredFlags is a set of flags which must be set during script invocations.
|
|
|
|
// Default value is NoneFlag i.e. no flags are required.
|
2020-12-29 10:45:49 +00:00
|
|
|
RequiredFlags callflag.CallFlag
|
2020-04-08 10:35:39 +00:00
|
|
|
}
|
2020-04-22 20:00:18 +00:00
|
|
|
|
|
|
|
// Method is a signature for a native method.
|
2020-06-03 12:55:06 +00:00
|
|
|
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
2020-04-22 20:00:18 +00:00
|
|
|
|
|
|
|
// MethodAndPrice is a native-contract method descriptor.
|
|
|
|
type MethodAndPrice struct {
|
|
|
|
Func Method
|
2020-09-21 14:34:40 +00:00
|
|
|
MD *manifest.Method
|
2021-03-05 10:30:16 +00:00
|
|
|
CPUFee int64
|
2021-03-05 11:53:46 +00:00
|
|
|
StorageFee int64
|
2021-02-15 13:40:44 +00:00
|
|
|
SyscallOffset int
|
2020-12-29 10:45:49 +00:00
|
|
|
RequiredFlags callflag.CallFlag
|
2020-04-22 20:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Contract is an interface for all native contracts.
|
|
|
|
type Contract interface {
|
|
|
|
Initialize(*Context) error
|
2023-11-21 09:35:18 +00:00
|
|
|
// ActiveIn returns the hardfork native contract is active from or nil in case
|
|
|
|
// it's always active.
|
|
|
|
ActiveIn() *config.Hardfork
|
2023-04-26 09:52:59 +00:00
|
|
|
// InitializeCache aimed to initialize contract's cache when the contract has
|
|
|
|
// been deployed, but in-memory cached data were lost due to the node reset.
|
|
|
|
// It should be called each time after node restart iff the contract was
|
|
|
|
// deployed and no Initialize method was called.
|
|
|
|
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
2020-04-22 20:00:18 +00:00
|
|
|
Metadata() *ContractMD
|
2020-12-13 18:36:06 +00:00
|
|
|
OnPersist(*Context) error
|
|
|
|
PostPersist(*Context) error
|
2020-04-22 20:00:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// ContractMD represents a native contract instance.
|
2020-04-22 20:00:18 +00:00
|
|
|
type ContractMD struct {
|
2021-02-09 09:29:41 +00:00
|
|
|
state.NativeContract
|
|
|
|
Name string
|
2021-02-15 13:40:44 +00:00
|
|
|
Methods []MethodAndPrice
|
2020-04-22 20:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewContractMD returns Contract with the specified list of methods.
|
2021-01-15 21:17:31 +00:00
|
|
|
func NewContractMD(name string, id int32) *ContractMD {
|
2021-02-15 13:40:44 +00:00
|
|
|
c := &ContractMD{Name: name}
|
2020-04-22 20:00:18 +00:00
|
|
|
|
2021-02-09 09:29:41 +00:00
|
|
|
c.ID = id
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// NEF is now stored in the contract state and affects state dump.
|
|
|
|
// Therefore, values are taken from C# node.
|
2021-01-19 08:08:34 +00:00
|
|
|
c.NEF.Header.Compiler = "neo-core-v3.0"
|
2021-01-13 12:34:10 +00:00
|
|
|
c.NEF.Header.Magic = nef.Magic
|
2021-09-09 14:03:11 +00:00
|
|
|
c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling
|
2022-08-15 07:54:29 +00:00
|
|
|
c.Hash = state.CreateNativeContractHash(c.Name)
|
2020-11-18 09:43:51 +00:00
|
|
|
c.Manifest = *manifest.DefaultManifest(name)
|
2020-04-22 20:00:18 +00:00
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// UpdateHash creates a native contract script and updates hash.
|
2021-02-15 13:40:44 +00:00
|
|
|
func (c *ContractMD) UpdateHash() {
|
|
|
|
w := io.NewBufBinWriter()
|
|
|
|
for i := range c.Methods {
|
|
|
|
offset := w.Len()
|
|
|
|
c.Methods[i].MD.Offset = offset
|
|
|
|
c.Manifest.ABI.Methods[i].Offset = offset
|
|
|
|
emit.Int(w.BinWriter, 0)
|
|
|
|
c.Methods[i].SyscallOffset = w.Len()
|
|
|
|
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
|
|
|
}
|
|
|
|
if w.Err != nil {
|
|
|
|
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
|
|
|
}
|
|
|
|
|
|
|
|
c.NEF.Script = w.Bytes()
|
|
|
|
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// AddMethod adds a new method to a native contract.
|
2020-12-08 10:27:41 +00:00
|
|
|
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
2020-09-21 14:34:40 +00:00
|
|
|
md.MD = desc
|
2020-12-29 10:45:49 +00:00
|
|
|
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
2021-01-28 13:55:03 +00:00
|
|
|
|
|
|
|
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool {
|
|
|
|
md := c.Manifest.ABI.Methods[i]
|
|
|
|
if md.Name != desc.Name {
|
2022-06-06 09:07:10 +00:00
|
|
|
return md.Name >= desc.Name
|
2021-01-28 13:55:03 +00:00
|
|
|
}
|
|
|
|
return len(md.Parameters) > len(desc.Parameters)
|
|
|
|
})
|
|
|
|
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, manifest.Method{})
|
|
|
|
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
|
|
|
|
c.Manifest.ABI.Methods[index] = *desc
|
|
|
|
|
2021-02-15 13:40:44 +00:00
|
|
|
// Cache follows the same order.
|
|
|
|
c.Methods = append(c.Methods, MethodAndPrice{})
|
|
|
|
copy(c.Methods[index+1:], c.Methods[index:])
|
|
|
|
c.Methods[index] = *md
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// GetMethodByOffset returns method with the provided offset.
|
2021-02-15 13:40:44 +00:00
|
|
|
// Offset is offset of `System.Contract.CallNative` syscall.
|
|
|
|
func (c *ContractMD) GetMethodByOffset(offset int) (MethodAndPrice, bool) {
|
|
|
|
for k := range c.Methods {
|
|
|
|
if c.Methods[k].SyscallOffset == offset {
|
|
|
|
return c.Methods[k], true
|
|
|
|
}
|
2021-01-28 12:36:37 +00:00
|
|
|
}
|
2021-02-15 13:40:44 +00:00
|
|
|
return MethodAndPrice{}, false
|
2021-01-28 12:36:37 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// GetMethod returns method `name` with the specified number of parameters.
|
2021-01-28 12:36:37 +00:00
|
|
|
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
|
2021-02-15 13:40:44 +00:00
|
|
|
index := sort.Search(len(c.Methods), func(i int) bool {
|
|
|
|
md := c.Methods[i]
|
2022-06-06 09:07:10 +00:00
|
|
|
res := strings.Compare(name, md.MD.Name)
|
2021-02-15 13:40:44 +00:00
|
|
|
switch res {
|
|
|
|
case -1, 1:
|
|
|
|
return res == -1
|
|
|
|
default:
|
|
|
|
return paramCount <= len(md.MD.Parameters)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if index < len(c.Methods) {
|
|
|
|
md := c.Methods[index]
|
|
|
|
if md.MD.Name == name && (paramCount == -1 || len(md.MD.Parameters) == paramCount) {
|
|
|
|
return md, true
|
|
|
|
}
|
2021-01-28 12:36:37 +00:00
|
|
|
}
|
2021-02-15 13:40:44 +00:00
|
|
|
return MethodAndPrice{}, false
|
2020-04-22 20:00:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// AddEvent adds a new event to the native contract.
|
2020-04-22 20:00:18 +00:00
|
|
|
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
|
|
|
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
|
|
|
Name: name,
|
|
|
|
Parameters: ps,
|
|
|
|
})
|
|
|
|
}
|
2020-07-28 13:38:00 +00:00
|
|
|
|
|
|
|
// Sort sorts interop functions by id.
|
|
|
|
func Sort(fs []Function) {
|
|
|
|
sort.Slice(fs, func(i, j int) bool { return fs[i].ID < fs[j].ID })
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// GetContract returns a contract by its hash in the current interop context.
|
2020-12-13 15:26:35 +00:00
|
|
|
func (ic *Context) GetContract(hash util.Uint160) (*state.Contract, error) {
|
|
|
|
return ic.getContract(ic.DAO, hash)
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
// GetFunction returns metadata for interop with the specified id.
|
|
|
|
func (ic *Context) GetFunction(id uint32) *Function {
|
2021-05-11 14:40:03 +00:00
|
|
|
n := sort.Search(len(ic.Functions), func(i int) bool {
|
|
|
|
return ic.Functions[i].ID >= id
|
|
|
|
})
|
|
|
|
if n < len(ic.Functions) && ic.Functions[n].ID == id {
|
|
|
|
return &ic.Functions[n]
|
2020-07-28 13:38:00 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:22:49 +00:00
|
|
|
// BaseExecFee represents factor to multiply syscall prices with.
|
|
|
|
func (ic *Context) BaseExecFee() int64 {
|
2021-08-11 12:42:23 +00:00
|
|
|
return ic.baseExecFee
|
2020-12-11 12:22:49 +00:00
|
|
|
}
|
|
|
|
|
2022-04-08 09:27:25 +00:00
|
|
|
// BaseStorageFee represents price for storing one byte of data in the contract storage.
|
|
|
|
func (ic *Context) BaseStorageFee() int64 {
|
|
|
|
return ic.baseStorageFee
|
|
|
|
}
|
|
|
|
|
2022-06-06 18:53:03 +00:00
|
|
|
// LoadToken wraps externally provided load-token loading function providing it with context,
|
|
|
|
// this function can then be easily used by VM.
|
|
|
|
func (ic *Context) LoadToken(id int32) error {
|
|
|
|
return ic.loadToken(ic, id)
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:38:00 +00:00
|
|
|
// SyscallHandler handles syscall with id.
|
2020-08-07 11:37:49 +00:00
|
|
|
func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
|
2020-07-28 13:38:00 +00:00
|
|
|
f := ic.GetFunction(id)
|
|
|
|
if f == nil {
|
|
|
|
return errors.New("syscall not found")
|
|
|
|
}
|
2020-08-07 11:37:49 +00:00
|
|
|
cf := ic.VM.Context().GetCallFlags()
|
2020-07-28 13:38:00 +00:00
|
|
|
if !cf.Has(f.RequiredFlags) {
|
|
|
|
return fmt.Errorf("missing call flags: %05b vs %05b", cf, f.RequiredFlags)
|
|
|
|
}
|
2020-12-11 12:22:49 +00:00
|
|
|
if !ic.VM.AddGas(f.Price * ic.BaseExecFee()) {
|
2020-07-28 13:38:00 +00:00
|
|
|
return errors.New("insufficient amount of gas")
|
|
|
|
}
|
2020-08-07 11:37:49 +00:00
|
|
|
return f.Func(ic)
|
2020-07-28 13:38:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// SpawnVM spawns a new VM with the specified gas limit and set context.VM field.
|
2020-07-28 13:38:00 +00:00
|
|
|
func (ic *Context) SpawnVM() *vm.VM {
|
|
|
|
v := vm.NewWithTrigger(ic.Trigger)
|
2022-06-06 19:48:10 +00:00
|
|
|
ic.initVM(v)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ic *Context) initVM(v *vm.VM) {
|
2022-06-06 18:53:03 +00:00
|
|
|
v.LoadToken = ic.LoadToken
|
2020-07-28 13:38:00 +00:00
|
|
|
v.GasLimit = -1
|
|
|
|
v.SyscallHandler = ic.SyscallHandler
|
2022-06-06 19:00:16 +00:00
|
|
|
v.SetPriceGetter(ic.GetPrice)
|
2020-08-07 11:37:49 +00:00
|
|
|
ic.VM = v
|
2022-06-06 19:48:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReuseVM resets given VM and allows to reuse it in the current context.
|
|
|
|
func (ic *Context) ReuseVM(v *vm.VM) {
|
|
|
|
v.Reset(ic.Trigger)
|
|
|
|
ic.initVM(v)
|
2020-07-28 13:38:00 +00:00
|
|
|
}
|
2021-10-07 11:27:55 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// RegisterCancelFunc adds the given function to the list of functions to be called after the VM
|
2021-10-07 11:27:55 +00:00
|
|
|
// finishes script execution.
|
|
|
|
func (ic *Context) RegisterCancelFunc(f context.CancelFunc) {
|
|
|
|
if f != nil {
|
|
|
|
ic.cancelFuncs = append(ic.cancelFuncs, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finalize calls all registered cancel functions to release the occupied resources.
|
|
|
|
func (ic *Context) Finalize() {
|
|
|
|
for _, f := range ic.cancelFuncs {
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
ic.cancelFuncs = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exec executes loaded VM script and calls registered finalizers to release the occupied resources.
|
|
|
|
func (ic *Context) Exec() error {
|
|
|
|
defer ic.Finalize()
|
|
|
|
return ic.VM.Run()
|
|
|
|
}
|
2022-04-29 15:00:46 +00:00
|
|
|
|
2023-11-21 07:49:05 +00:00
|
|
|
// BlockHeight returns the latest persisted and stored block height/index.
|
|
|
|
// Persisting block index is not taken into account. If Context's block is set,
|
|
|
|
// then BlockHeight calculations relies on persisting block index.
|
2022-04-29 15:00:46 +00:00
|
|
|
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 {
|
2022-11-18 20:19:50 +00:00
|
|
|
return ic.Chain.GetHeaderHash(ic.Block.Index - 1) // Persisting block is not yet stored.
|
2022-04-29 15:00:46 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2023-11-21 07:49:05 +00:00
|
|
|
if block.Index > ic.BlockHeight() { // persisting block is not reachable.
|
2022-04-29 15:00:46 +00:00
|
|
|
return nil, storage.ErrKeyNotFound
|
|
|
|
}
|
|
|
|
return block, nil
|
|
|
|
}
|
2022-05-06 12:19:17 +00:00
|
|
|
|
|
|
|
// IsHardforkEnabled tells whether specified hard-fork enabled at the current context height.
|
|
|
|
func (ic *Context) IsHardforkEnabled(hf config.Hardfork) bool {
|
|
|
|
height, ok := ic.Hardforks[hf.String()]
|
|
|
|
if ok {
|
2023-11-21 07:49:05 +00:00
|
|
|
return (ic.BlockHeight() + 1) >= height // persisting block should be taken into account.
|
2022-05-06 12:19:17 +00:00
|
|
|
}
|
2023-08-24 16:24:34 +00:00
|
|
|
// Completely rely on proper hardforks initialisation made by core.NewBlockchain.
|
|
|
|
return false
|
2022-05-06 12:19:17 +00:00
|
|
|
}
|
2022-05-16 08:19:15 +00:00
|
|
|
|
2023-11-21 09:35:18 +00:00
|
|
|
// IsHardforkActivation denotes whether current block height is the height of
|
|
|
|
// specified hardfork activation.
|
|
|
|
func (ic *Context) IsHardforkActivation(hf config.Hardfork) bool {
|
|
|
|
// Completely rely on proper hardforks initialisation made by core.NewBlockchain.
|
|
|
|
height, ok := ic.Hardforks[hf.String()]
|
|
|
|
return ok && ic.Block.Index == height
|
|
|
|
}
|
|
|
|
|
2022-05-16 08:19:15 +00:00
|
|
|
// AddNotification creates notification event and appends it to the notification list.
|
|
|
|
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) {
|
|
|
|
ic.Notifications = append(ic.Notifications, state.NotificationEvent{
|
|
|
|
ScriptHash: hash,
|
|
|
|
Name: name,
|
|
|
|
Item: item,
|
|
|
|
})
|
|
|
|
}
|