2020-12-08 15:28:00 +00:00
|
|
|
package native
|
|
|
|
|
|
|
|
import (
|
2020-12-13 15:26:35 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2020-12-08 15:28:00 +00:00
|
|
|
"fmt"
|
2020-12-13 15:26:35 +00:00
|
|
|
"math"
|
|
|
|
"math/big"
|
2021-02-08 20:14:35 +00:00
|
|
|
"unicode/utf8"
|
2020-12-08 15:28:00 +00:00
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
2020-12-08 15:28:00 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
2020-12-13 18:25:04 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
2020-12-08 15:28:00 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2020-12-15 10:53:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-12-29 10:45:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2021-02-09 18:42:39 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util/bitfield"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
2020-12-13 15:26:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2020-12-08 15:28:00 +00:00
|
|
|
)
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Management is a contract-managing native contract.
|
2020-12-08 15:28:00 +00:00
|
|
|
type Management struct {
|
|
|
|
interop.ContractMD
|
2022-05-04 10:27:41 +00:00
|
|
|
NEO *NEO
|
|
|
|
Policy *Policy
|
2022-04-12 14:29:11 +00:00
|
|
|
}
|
2020-12-15 10:53:35 +00:00
|
|
|
|
2022-04-12 14:29:11 +00:00
|
|
|
type ManagementCache struct {
|
2020-12-15 10:53:35 +00:00
|
|
|
contracts map[util.Uint160]*state.Contract
|
2021-11-16 10:28:59 +00:00
|
|
|
// nep11 is a map of NEP11-compliant contracts which is updated with every PostPersist.
|
|
|
|
nep11 map[util.Uint160]struct{}
|
2021-11-18 13:37:42 +00:00
|
|
|
// nep17 is a map of NEP-17-compliant contracts which is updated with every PostPersist.
|
2021-07-23 12:52:30 +00:00
|
|
|
nep17 map[util.Uint160]struct{}
|
2020-12-08 15:28:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2021-10-07 09:03:37 +00:00
|
|
|
ManagementContractID = -1
|
2021-01-15 21:17:31 +00:00
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
prefixContract = 8
|
2020-12-16 13:11:12 +00:00
|
|
|
|
2020-12-16 13:41:55 +00:00
|
|
|
defaultMinimumDeploymentFee = 10_00000000
|
|
|
|
contractDeployNotificationName = "Deploy"
|
|
|
|
contractUpdateNotificationName = "Update"
|
|
|
|
contractDestroyNotificationName = "Destroy"
|
2020-12-08 15:28:00 +00:00
|
|
|
)
|
|
|
|
|
2020-12-16 13:11:12 +00:00
|
|
|
var (
|
|
|
|
errGasLimitExceeded = errors.New("gas limit exceeded")
|
|
|
|
|
|
|
|
keyNextAvailableID = []byte{15}
|
|
|
|
keyMinimumDeploymentFee = []byte{20}
|
|
|
|
)
|
2020-12-13 15:26:35 +00:00
|
|
|
|
2022-04-15 14:48:58 +00:00
|
|
|
var (
|
2022-04-20 14:47:48 +00:00
|
|
|
_ interop.Contract = (*Management)(nil)
|
|
|
|
_ dao.NativeContractCache = (*ManagementCache)(nil)
|
2022-04-15 14:48:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Copy implements NativeContractCache interface.
|
2022-04-20 14:47:48 +00:00
|
|
|
func (c *ManagementCache) Copy() dao.NativeContractCache {
|
2022-04-15 14:48:58 +00:00
|
|
|
cp := &ManagementCache{
|
|
|
|
contracts: make(map[util.Uint160]*state.Contract),
|
|
|
|
nep11: make(map[util.Uint160]struct{}),
|
|
|
|
nep17: make(map[util.Uint160]struct{}),
|
|
|
|
}
|
|
|
|
// Copy the whole set of contracts is too expensive. We will create a separate map
|
|
|
|
// holding the same set of pointers to contracts, and in case if some contract is
|
|
|
|
// supposed to be changed, Management will create the copy in-place.
|
|
|
|
for hash, ctr := range c.contracts {
|
|
|
|
cp.contracts[hash] = ctr
|
|
|
|
}
|
|
|
|
for hash := range c.nep17 {
|
|
|
|
cp.nep17[hash] = struct{}{}
|
|
|
|
}
|
|
|
|
for hash := range c.nep11 {
|
|
|
|
cp.nep11[hash] = struct{}{}
|
|
|
|
}
|
|
|
|
return cp
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// MakeContractKey creates a key from the account script hash.
|
2021-10-07 09:03:37 +00:00
|
|
|
func MakeContractKey(h util.Uint160) []byte {
|
2020-12-13 15:26:35 +00:00
|
|
|
return makeUint160Key(prefixContract, h)
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// newManagement creates a new Management native contract.
|
2020-12-08 15:28:00 +00:00
|
|
|
func newManagement() *Management {
|
2020-12-15 10:53:35 +00:00
|
|
|
var m = &Management{
|
2021-10-07 09:03:37 +00:00
|
|
|
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
2020-12-15 10:53:35 +00:00
|
|
|
}
|
2021-02-15 13:40:44 +00:00
|
|
|
defer m.UpdateHash()
|
2020-12-08 15:28:00 +00:00
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
2021-01-12 15:06:27 +00:00
|
|
|
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
2021-03-05 10:30:16 +00:00
|
|
|
md := newMethodAndPrice(m.getContract, 1<<15, callflag.ReadStates)
|
2020-12-13 15:26:35 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
|
|
|
desc = newDescriptor("deploy", smartcontract.ArrayType,
|
2021-02-12 13:46:17 +00:00
|
|
|
manifest.NewParameter("nefFile", smartcontract.ByteArrayType),
|
2020-12-13 15:26:35 +00:00
|
|
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType))
|
2022-03-21 11:28:28 +00:00
|
|
|
md = newMethodAndPrice(m.deploy, 0, callflag.All)
|
2020-12-13 15:26:35 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2021-01-28 13:31:50 +00:00
|
|
|
desc = newDescriptor("deploy", smartcontract.ArrayType,
|
2021-02-12 13:46:17 +00:00
|
|
|
manifest.NewParameter("nefFile", smartcontract.ByteArrayType),
|
2021-01-28 13:31:50 +00:00
|
|
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
|
|
|
|
manifest.NewParameter("data", smartcontract.AnyType))
|
2022-03-21 11:28:28 +00:00
|
|
|
md = newMethodAndPrice(m.deployWithData, 0, callflag.All)
|
2021-01-28 13:31:50 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
desc = newDescriptor("update", smartcontract.VoidType,
|
2021-02-12 13:46:17 +00:00
|
|
|
manifest.NewParameter("nefFile", smartcontract.ByteArrayType),
|
2020-12-13 15:26:35 +00:00
|
|
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType))
|
2022-03-21 11:28:28 +00:00
|
|
|
md = newMethodAndPrice(m.update, 0, callflag.All)
|
2020-12-13 15:26:35 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2021-01-28 13:31:50 +00:00
|
|
|
desc = newDescriptor("update", smartcontract.VoidType,
|
2021-02-12 13:46:17 +00:00
|
|
|
manifest.NewParameter("nefFile", smartcontract.ByteArrayType),
|
2021-01-28 13:31:50 +00:00
|
|
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
|
|
|
|
manifest.NewParameter("data", smartcontract.AnyType))
|
2022-03-21 11:28:28 +00:00
|
|
|
md = newMethodAndPrice(m.updateWithData, 0, callflag.All)
|
2021-01-28 13:31:50 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
desc = newDescriptor("destroy", smartcontract.VoidType)
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(m.destroy, 1<<15, callflag.States|callflag.AllowNotify)
|
2020-12-13 15:26:35 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2020-12-16 13:11:12 +00:00
|
|
|
desc = newDescriptor("getMinimumDeploymentFee", smartcontract.IntegerType)
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(m.getMinimumDeploymentFee, 1<<15, callflag.ReadStates)
|
2020-12-16 13:11:12 +00:00
|
|
|
m.AddMethod(md, desc)
|
|
|
|
|
2021-01-28 15:01:30 +00:00
|
|
|
desc = newDescriptor("setMinimumDeploymentFee", smartcontract.VoidType,
|
2020-12-16 13:11:12 +00:00
|
|
|
manifest.NewParameter("value", smartcontract.IntegerType))
|
2021-03-05 10:30:16 +00:00
|
|
|
md = newMethodAndPrice(m.setMinimumDeploymentFee, 1<<15, callflag.States)
|
2020-12-16 13:11:12 +00:00
|
|
|
m.AddMethod(md, desc)
|
2020-12-16 13:41:55 +00:00
|
|
|
|
|
|
|
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
|
|
|
m.AddEvent(contractDeployNotificationName, hashParam)
|
|
|
|
m.AddEvent(contractUpdateNotificationName, hashParam)
|
|
|
|
m.AddEvent(contractDestroyNotificationName, hashParam)
|
2020-12-08 15:28:00 +00:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
// getContract is an implementation of public getContract method, it's run under
|
|
|
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
|
|
|
func (m *Management) getContract(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
|
|
hashBytes, err := args[0].TryBytes()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
hash, err := util.Uint160DecodeBytesBE(hashBytes)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
ctr, err := m.GetContract(ic.DAO, hash)
|
|
|
|
if err != nil {
|
2021-03-19 17:05:05 +00:00
|
|
|
if err == storage.ErrKeyNotFound {
|
|
|
|
return stackitem.Null{}
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return contractToStack(ctr)
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// GetContract returns a contract with the given hash from the given DAO.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := d.GetROCache(m.ID).(*ManagementCache)
|
2022-04-12 14:29:11 +00:00
|
|
|
cs, ok := cache.contracts[hash]
|
2020-12-15 10:53:35 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, storage.ErrKeyNotFound
|
|
|
|
}
|
2022-04-19 14:12:03 +00:00
|
|
|
return cs, nil
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getLimitedSlice(arg stackitem.Item, max int) ([]byte, error) {
|
|
|
|
_, isNull := arg.(stackitem.Null)
|
|
|
|
if isNull {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
b, err := arg.TryBytes()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
l := len(b)
|
|
|
|
if l == 0 {
|
|
|
|
return nil, errors.New("empty")
|
|
|
|
} else if l > max {
|
|
|
|
return nil, fmt.Errorf("len is %d (max %d)", l, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getNefAndManifestFromItems converts input arguments into NEF and manifest
|
2022-04-20 18:30:09 +00:00
|
|
|
// adding an appropriate deployment GAS price and sanitizing inputs.
|
2020-12-16 13:11:12 +00:00
|
|
|
func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stackitem.Item, isDeploy bool) (*nef.File, *manifest.Manifest, error) {
|
2020-12-13 15:26:35 +00:00
|
|
|
nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization.
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
|
|
|
}
|
|
|
|
manifestBytes, err := getLimitedSlice(args[1], manifest.MaxManifestSize)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-04-08 09:27:25 +00:00
|
|
|
gas := ic.BaseStorageFee() * int64(len(nefBytes)+len(manifestBytes))
|
2020-12-16 13:11:12 +00:00
|
|
|
if isDeploy {
|
2022-03-11 08:34:37 +00:00
|
|
|
fee := m.minimumDeploymentFee(ic.DAO)
|
2020-12-16 13:11:12 +00:00
|
|
|
if fee > gas {
|
|
|
|
gas = fee
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ic.VM.AddGas(gas) {
|
2020-12-13 15:26:35 +00:00
|
|
|
return nil, nil, errGasLimitExceeded
|
|
|
|
}
|
|
|
|
var resManifest *manifest.Manifest
|
|
|
|
var resNef *nef.File
|
|
|
|
if nefBytes != nil {
|
|
|
|
nf, err := nef.FileFromBytes(nefBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
|
|
|
}
|
|
|
|
resNef = &nf
|
|
|
|
}
|
|
|
|
if manifestBytes != nil {
|
2021-02-08 20:14:35 +00:00
|
|
|
if !utf8.Valid(manifestBytes) {
|
|
|
|
return nil, nil, errors.New("manifest is not UTF-8 compliant")
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
resManifest = new(manifest.Manifest)
|
|
|
|
err := json.Unmarshal(manifestBytes, resManifest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resNef, resManifest, nil
|
|
|
|
}
|
|
|
|
|
2021-01-28 13:31:50 +00:00
|
|
|
// deploy is an implementation of public 2-argument deploy method.
|
2020-12-13 15:26:35 +00:00
|
|
|
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
2021-01-28 13:31:50 +00:00
|
|
|
return m.deployWithData(ic, append(args, stackitem.Null{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
// deployWithData is an implementation of public 3-argument deploy method.
|
|
|
|
// It's run under VM protections, so it's OK for it to panic instead of returning errors.
|
|
|
|
func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
2020-12-16 13:11:12 +00:00
|
|
|
neff, manif, err := m.getNefAndManifestFromItems(ic, args, true)
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if neff == nil {
|
|
|
|
panic(errors.New("no valid NEF provided"))
|
|
|
|
}
|
|
|
|
if manif == nil {
|
|
|
|
panic(errors.New("no valid manifest provided"))
|
|
|
|
}
|
|
|
|
if ic.Tx == nil {
|
|
|
|
panic(errors.New("no transaction provided"))
|
|
|
|
}
|
|
|
|
newcontract, err := m.Deploy(ic.DAO, ic.Tx.Sender(), neff, manif)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-01-28 13:31:50 +00:00
|
|
|
m.callDeploy(ic, newcontract, args[2], false)
|
2020-12-16 13:41:55 +00:00
|
|
|
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
|
2020-12-13 15:26:35 +00:00
|
|
|
return contractToStack(newcontract)
|
2020-12-15 10:53:35 +00:00
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
|
2022-04-19 14:12:03 +00:00
|
|
|
func (m *Management) markUpdated(d *dao.Simple, hash util.Uint160, cs *state.Contract) {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := d.GetRWCache(m.ID).(*ManagementCache)
|
2022-04-19 14:12:03 +00:00
|
|
|
delete(cache.nep11, hash)
|
|
|
|
delete(cache.nep17, hash)
|
|
|
|
if cs == nil {
|
|
|
|
delete(cache.contracts, hash)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
updateContractCache(cache, cs)
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Deploy creates a contract's hash/ID and saves a new contract into the given DAO.
|
2020-12-16 13:41:55 +00:00
|
|
|
// It doesn't run _deploy method and doesn't emit notification.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) Deploy(d *dao.Simple, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
|
2021-01-22 09:22:48 +00:00
|
|
|
h := state.CreateContractHash(sender, neff.Checksum, manif.Name)
|
2022-05-04 10:27:41 +00:00
|
|
|
if m.Policy.IsBlocked(d, h) {
|
|
|
|
return nil, fmt.Errorf("the contract %s has been blocked", h.StringLE())
|
|
|
|
}
|
2022-04-20 15:34:56 +00:00
|
|
|
_, err := m.GetContract(d, h)
|
|
|
|
if err == nil {
|
2020-12-13 15:26:35 +00:00
|
|
|
return nil, errors.New("contract already exists")
|
|
|
|
}
|
|
|
|
id, err := m.getNextContractID(d)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-08 09:04:57 +00:00
|
|
|
err = manif.IsValid(h)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid manifest: %w", err)
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
2021-02-09 18:42:39 +00:00
|
|
|
err = checkScriptAndMethods(neff.Script, manif.ABI.Methods)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
newcontract := &state.Contract{
|
2021-02-09 09:02:38 +00:00
|
|
|
ContractBase: state.ContractBase{
|
|
|
|
ID: id,
|
|
|
|
Hash: h,
|
|
|
|
NEF: *neff,
|
|
|
|
Manifest: *manif,
|
|
|
|
},
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
err = m.PutContractState(d, newcontract)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newcontract, nil
|
|
|
|
}
|
|
|
|
|
2021-01-28 13:31:50 +00:00
|
|
|
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
|
|
return m.updateWithData(ic, append(args, stackitem.Null{}))
|
|
|
|
}
|
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
// update is an implementation of public update method, it's run under
|
|
|
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
2021-01-28 13:31:50 +00:00
|
|
|
func (m *Management) updateWithData(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
2020-12-16 13:11:12 +00:00
|
|
|
neff, manif, err := m.getNefAndManifestFromItems(ic, args, false)
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if neff == nil && manif == nil {
|
|
|
|
panic(errors.New("both NEF and manifest are nil"))
|
|
|
|
}
|
|
|
|
contract, err := m.Update(ic.DAO, ic.VM.GetCallingScriptHash(), neff, manif)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-01-28 13:31:50 +00:00
|
|
|
m.callDeploy(ic, contract, args[2], true)
|
2020-12-16 13:41:55 +00:00
|
|
|
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
|
2020-12-13 15:26:35 +00:00
|
|
|
return stackitem.Null{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update updates contract's script and/or manifest in the given DAO.
|
2020-12-16 13:41:55 +00:00
|
|
|
// It doesn't run _deploy method and doesn't emit notification.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) Update(d *dao.Simple, hash util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
|
2021-07-07 20:17:18 +00:00
|
|
|
var contract state.Contract
|
|
|
|
|
|
|
|
oldcontract, err := m.GetContract(d, hash)
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New("contract doesn't exist")
|
|
|
|
}
|
2022-05-04 10:37:18 +00:00
|
|
|
if oldcontract.UpdateCounter == math.MaxUint16 {
|
|
|
|
return nil, errors.New("the contract reached the maximum number of updates")
|
|
|
|
}
|
2021-07-07 20:17:18 +00:00
|
|
|
|
|
|
|
contract = *oldcontract // Make a copy, don't ruin (potentially) cached contract.
|
2020-12-13 15:26:35 +00:00
|
|
|
// if NEF was provided, update the contract script
|
|
|
|
if neff != nil {
|
2021-01-13 12:34:10 +00:00
|
|
|
contract.NEF = *neff
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
// if manifest was provided, update the contract manifest
|
|
|
|
if manif != nil {
|
2021-01-28 13:43:41 +00:00
|
|
|
if manif.Name != contract.Manifest.Name {
|
|
|
|
return nil, errors.New("contract name can't be changed")
|
|
|
|
}
|
2021-02-08 09:04:57 +00:00
|
|
|
err = manif.IsValid(contract.Hash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid manifest: %w", err)
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
2020-12-15 10:53:35 +00:00
|
|
|
contract.Manifest = *manif
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
2021-02-09 18:42:39 +00:00
|
|
|
err = checkScriptAndMethods(contract.NEF.Script, contract.Manifest.ABI.Methods)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
contract.UpdateCounter++
|
2021-07-07 20:17:18 +00:00
|
|
|
err = m.PutContractState(d, &contract)
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-07-07 20:17:18 +00:00
|
|
|
return &contract, nil
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// destroy is an implementation of destroy update method, it's run under
|
|
|
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
|
|
|
func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackitem.Item {
|
|
|
|
hash := ic.VM.GetCallingScriptHash()
|
|
|
|
err := m.Destroy(ic.DAO, hash)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-12-16 13:41:55 +00:00
|
|
|
m.emitNotification(ic, contractDestroyNotificationName, hash)
|
2020-12-13 15:26:35 +00:00
|
|
|
return stackitem.Null{}
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Destroy drops the given contract from DAO along with its storage. It doesn't emit notification.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error {
|
2020-12-13 15:26:35 +00:00
|
|
|
contract, err := m.GetContract(d, hash)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-07 09:03:37 +00:00
|
|
|
key := MakeContractKey(hash)
|
2022-02-16 14:48:15 +00:00
|
|
|
d.DeleteStorageItem(m.ID, key)
|
|
|
|
d.DeleteContractID(contract.ID)
|
2022-03-31 15:14:11 +00:00
|
|
|
|
|
|
|
d.Seek(contract.ID, storage.SeekRange{}, func(k, _ []byte) bool {
|
|
|
|
d.DeleteStorageItem(contract.ID, k)
|
|
|
|
return true
|
|
|
|
})
|
2022-05-04 10:27:41 +00:00
|
|
|
m.Policy.blockAccountInternal(d, hash)
|
2022-04-19 14:12:03 +00:00
|
|
|
m.markUpdated(d, hash, nil)
|
2020-12-13 15:26:35 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-16 13:11:12 +00:00
|
|
|
func (m *Management) getMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
2022-03-11 08:34:37 +00:00
|
|
|
return stackitem.NewBigInteger(big.NewInt(m.minimumDeploymentFee(ic.DAO)))
|
2020-12-16 13:11:12 +00:00
|
|
|
}
|
|
|
|
|
2022-03-11 08:34:37 +00:00
|
|
|
// minimumDeploymentFee returns the minimum required fee for contract deploy.
|
|
|
|
func (m *Management) minimumDeploymentFee(dao *dao.Simple) int64 {
|
2021-02-09 09:26:25 +00:00
|
|
|
return getIntWithKey(m.ID, dao, keyMinimumDeploymentFee)
|
2020-12-16 13:11:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
2021-05-12 16:27:02 +00:00
|
|
|
value := toBigInt(args[0])
|
|
|
|
if value.Sign() < 0 {
|
|
|
|
panic("MinimumDeploymentFee cannot be negative")
|
2020-12-16 13:11:12 +00:00
|
|
|
}
|
2021-01-21 12:05:15 +00:00
|
|
|
if !m.NEO.checkCommittee(ic) {
|
2021-01-28 15:01:30 +00:00
|
|
|
panic("invalid committee signature")
|
2020-12-16 13:11:12 +00:00
|
|
|
}
|
2022-02-16 14:48:15 +00:00
|
|
|
ic.DAO.PutStorageItem(m.ID, keyMinimumDeploymentFee, bigint.ToBytes(value))
|
2021-01-28 15:01:30 +00:00
|
|
|
return stackitem.Null{}
|
2020-12-16 13:11:12 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 13:31:50 +00:00
|
|
|
func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, data stackitem.Item, isUpdate bool) {
|
|
|
|
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy, 2)
|
2020-12-13 15:26:35 +00:00
|
|
|
if md != nil {
|
2020-12-29 10:44:07 +00:00
|
|
|
err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy,
|
2021-01-28 13:31:50 +00:00
|
|
|
[]stackitem.Item{data, stackitem.NewBool(isUpdate)}, false)
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func contractToStack(cs *state.Contract) stackitem.Item {
|
|
|
|
si, err := cs.ToStackItem()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("contract to stack item: %w", err))
|
|
|
|
}
|
|
|
|
return si
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Metadata implements the Contract interface.
|
2020-12-08 15:28:00 +00:00
|
|
|
func (m *Management) Metadata() *interop.ContractMD {
|
|
|
|
return &m.ContractMD
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// updateContractCache saves the contract in the common and NEP-related caches. It's
|
2021-11-16 10:28:59 +00:00
|
|
|
// an internal method that must be called with m.mtx lock taken.
|
2022-04-12 14:29:11 +00:00
|
|
|
func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
|
|
|
cache.contracts[cs.Hash] = cs
|
2021-11-16 10:28:59 +00:00
|
|
|
if cs.Manifest.IsStandardSupported(manifest.NEP11StandardName) {
|
2022-04-12 14:29:11 +00:00
|
|
|
cache.nep11[cs.Hash] = struct{}{}
|
2021-11-16 10:28:59 +00:00
|
|
|
}
|
|
|
|
if cs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
2022-04-12 14:29:11 +00:00
|
|
|
cache.nep17[cs.Hash] = struct{}{}
|
2021-11-16 10:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// OnPersist implements the Contract interface.
|
2020-12-08 15:28:00 +00:00
|
|
|
func (m *Management) OnPersist(ic *interop.Context) error {
|
2022-04-12 14:29:11 +00:00
|
|
|
var cache *ManagementCache
|
2020-12-08 15:28:00 +00:00
|
|
|
for _, native := range ic.Natives {
|
|
|
|
md := native.Metadata()
|
2021-03-11 11:39:51 +00:00
|
|
|
history := md.UpdateHistory
|
|
|
|
if len(history) == 0 || history[0] != ic.Block.Index {
|
|
|
|
continue
|
|
|
|
}
|
2020-12-08 15:28:00 +00:00
|
|
|
|
|
|
|
cs := &state.Contract{
|
2021-02-09 09:29:41 +00:00
|
|
|
ContractBase: md.ContractBase,
|
2020-12-08 15:28:00 +00:00
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
if err := native.Initialize(ic); err != nil {
|
|
|
|
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
|
|
|
}
|
2022-04-19 14:12:03 +00:00
|
|
|
err := m.putContractState(ic.DAO, cs, false) // Perform cache update manually.
|
2020-12-13 15:26:35 +00:00
|
|
|
if err != nil {
|
2020-12-08 15:28:00 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
if cache == nil {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
2020-12-08 15:28:00 +00:00
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
updateContractCache(cache, cs)
|
2020-12-08 15:28:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:53:35 +00:00
|
|
|
// InitializeCache initializes contract cache with the proper values from storage.
|
2022-04-20 18:30:09 +00:00
|
|
|
// Cache initialization should be done apart from Initialize because Initialize is
|
2020-12-15 10:53:35 +00:00
|
|
|
// called only when deploying native contracts.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) InitializeCache(d *dao.Simple) error {
|
2022-04-12 14:29:11 +00:00
|
|
|
cache := &ManagementCache{
|
|
|
|
contracts: make(map[util.Uint160]*state.Contract),
|
|
|
|
nep11: make(map[util.Uint160]struct{}),
|
|
|
|
nep17: make(map[util.Uint160]struct{}),
|
|
|
|
}
|
2020-12-15 10:53:35 +00:00
|
|
|
|
|
|
|
var initErr error
|
2022-01-17 17:41:51 +00:00
|
|
|
d.Seek(m.ID, storage.SeekRange{Prefix: []byte{prefixContract}}, func(_, v []byte) bool {
|
2021-07-17 15:37:33 +00:00
|
|
|
var cs = new(state.Contract)
|
|
|
|
initErr = stackitem.DeserializeConvertible(v, cs)
|
|
|
|
if initErr != nil {
|
2022-01-17 17:41:51 +00:00
|
|
|
return false
|
2020-12-15 10:53:35 +00:00
|
|
|
}
|
2022-04-12 14:29:11 +00:00
|
|
|
updateContractCache(cache, cs)
|
2022-01-17 17:41:51 +00:00
|
|
|
return true
|
2020-12-15 10:53:35 +00:00
|
|
|
})
|
2022-04-12 14:29:11 +00:00
|
|
|
if initErr != nil {
|
|
|
|
return initErr
|
|
|
|
}
|
2022-04-20 14:47:48 +00:00
|
|
|
d.SetCache(m.ID, cache)
|
2022-04-12 14:29:11 +00:00
|
|
|
return nil
|
2020-12-15 10:53:35 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PostPersist implements the Contract interface.
|
2020-12-15 10:53:35 +00:00
|
|
|
func (m *Management) PostPersist(ic *interop.Context) error {
|
2020-12-08 15:28:00 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-16 10:28:59 +00:00
|
|
|
// GetNEP11Contracts returns hashes of all deployed contracts that support NEP-11 standard. The list
|
|
|
|
// is updated every PostPersist, so until PostPersist is called, the result for the previous block
|
|
|
|
// is returned.
|
2022-04-12 14:29:11 +00:00
|
|
|
func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := d.GetROCache(m.ID).(*ManagementCache)
|
2022-04-12 14:29:11 +00:00
|
|
|
result := make([]util.Uint160, 0, len(cache.nep11))
|
|
|
|
for h := range cache.nep11 {
|
2021-11-16 10:28:59 +00:00
|
|
|
result = append(result, h)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2021-11-18 13:37:42 +00:00
|
|
|
// GetNEP17Contracts returns hashes of all deployed contracts that support NEP-17 standard. The list
|
2021-07-23 12:52:30 +00:00
|
|
|
// is updated every PostPersist, so until PostPersist is called, the result for the previous block
|
|
|
|
// is returned.
|
2022-04-12 14:29:11 +00:00
|
|
|
func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
2022-04-20 14:47:48 +00:00
|
|
|
cache := d.GetROCache(m.ID).(*ManagementCache)
|
2022-04-12 14:29:11 +00:00
|
|
|
result := make([]util.Uint160, 0, len(cache.nep17))
|
|
|
|
for h := range cache.nep17 {
|
2021-07-23 12:52:30 +00:00
|
|
|
result = append(result, h)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Initialize implements the Contract interface.
|
2020-12-16 13:11:12 +00:00
|
|
|
func (m *Management) Initialize(ic *interop.Context) error {
|
2022-02-16 14:48:15 +00:00
|
|
|
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
|
|
|
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
2022-04-12 14:29:11 +00:00
|
|
|
|
|
|
|
cache := &ManagementCache{
|
|
|
|
contracts: make(map[util.Uint160]*state.Contract),
|
|
|
|
nep11: make(map[util.Uint160]struct{}),
|
|
|
|
nep17: make(map[util.Uint160]struct{}),
|
|
|
|
}
|
2022-04-20 14:47:48 +00:00
|
|
|
ic.DAO.SetCache(m.ID, cache)
|
2022-02-16 14:48:15 +00:00
|
|
|
return nil
|
2020-12-08 15:28:00 +00:00
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
|
|
|
|
// PutContractState saves given contract state into given DAO.
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) PutContractState(d *dao.Simple, cs *state.Contract) error {
|
2022-04-19 14:12:03 +00:00
|
|
|
return m.putContractState(d, cs, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// putContractState is an internal PutContractState representation.
|
|
|
|
func (m *Management) putContractState(d *dao.Simple, cs *state.Contract, updateCache bool) error {
|
2021-10-07 09:03:37 +00:00
|
|
|
key := MakeContractKey(cs.Hash)
|
2021-07-17 15:37:33 +00:00
|
|
|
if err := putConvertibleToDAO(m.ID, d, key, cs); err != nil {
|
2020-12-13 15:26:35 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-04-19 14:12:03 +00:00
|
|
|
if updateCache {
|
|
|
|
m.markUpdated(d, cs.Hash, cs)
|
|
|
|
}
|
2020-12-13 15:26:35 +00:00
|
|
|
if cs.UpdateCounter != 0 { // Update.
|
|
|
|
return nil
|
|
|
|
}
|
2022-02-16 14:48:15 +00:00
|
|
|
d.PutContractID(cs.ID, cs.Hash)
|
|
|
|
return nil
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-16 15:04:47 +00:00
|
|
|
func (m *Management) getNextContractID(d *dao.Simple) (int32, error) {
|
2021-02-09 09:26:25 +00:00
|
|
|
si := d.GetStorageItem(m.ID, keyNextAvailableID)
|
2021-02-02 15:46:43 +00:00
|
|
|
if si == nil {
|
|
|
|
return 0, errors.New("nextAvailableID is not initialized")
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
2021-03-05 14:06:54 +00:00
|
|
|
id := bigint.FromBytes(si)
|
2020-12-13 15:26:35 +00:00
|
|
|
ret := int32(id.Int64())
|
2021-02-02 15:46:43 +00:00
|
|
|
id.Add(id, intOne)
|
2022-05-31 20:10:56 +00:00
|
|
|
d.PutBigInt(m.ID, keyNextAvailableID, id)
|
2022-02-16 14:48:15 +00:00
|
|
|
return ret, nil
|
2020-12-13 15:26:35 +00:00
|
|
|
}
|
2020-12-16 13:41:55 +00:00
|
|
|
|
|
|
|
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
|
2022-05-16 08:19:15 +00:00
|
|
|
ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
|
2020-12-16 13:41:55 +00:00
|
|
|
}
|
2021-02-09 18:42:39 +00:00
|
|
|
|
|
|
|
func checkScriptAndMethods(script []byte, methods []manifest.Method) error {
|
|
|
|
l := len(script)
|
|
|
|
offsets := bitfield.New(l)
|
|
|
|
for i := range methods {
|
|
|
|
if methods[i].Offset >= l {
|
core: adjust contract script check on deploy
Reference implementation doesn't panic if the method offset is out of
the contract script bounds, see:
https://github.com/neo-project/neo/blob/736c346b9d8b1404f10023eeecb3a8e92ae0c542/src/neo/SmartContract/Helper.cs#L82
and
https://github.com/neo-project/neo-vm/blob/a65487fa56be3eccb2c1dbfec5dcdd71b8a05fde/src/Neo.VM/Script.cs#L146.
This commit fixes T5 statediff at block #125000. Neo-go node FAULTed the
deploying transaction:
```
{
"version" : 0,
"sysfee" : "1000106065",
"validuntilblock" : 130758,
"script" : "DdMDeyJuYW1lIjoiTmVwMTdUb2tlbiIsImdyb3VwcyI6W10sImZlYXR1cmVzIjp7fSwic3VwcG9ydGVkc3RhbmRhcmRzIjpbIk5FUC0xNyJdLCJhYmkiOnsibWV0aG9kcyI6W3sibmFtZSI6InN5bWJvbCIsInBhcmFtZXRlcnMiOltdLCJyZXR1cm50eXBlIjoiU3RyaW5nIiwib2Zmc2V0IjoyMiwic2FmZSI6dHJ1ZX0seyJuYW1lIjoiZGVjaW1hbHMiLCJwYXJhbWV0ZXJzIjpbXSwicmV0dXJudHlwZSI6IkludGVnZXIiLCJvZmZzZXQiOjIyLCJzYWZlIjp0cnVlfSx7Im5hbWUiOiJ0b3RhbFN1cHBseSIsInBhcmFtZXRlcnMiOltdLCJyZXR1cm50eXBlIjoiSW50ZWdlciIsIm9mZnNldCI6MjIsInNhZmUiOnRydWV9LHsibmFtZSI6ImJhbGFuY2VPZiIsInBhcmFtZXRlcnMiOlt7Im5hbWUiOiJvd25lciIsInR5cGUiOiJIYXNoMTYwIn1dLCJyZXR1cm50eXBlIjoiSW50ZWdlciIsIm9mZnNldCI6MjIsInNhZmUiOnRydWV9LHsibmFtZSI6InRyYW5zZmVyIiwicGFyYW1ldGVycyI6W3sibmFtZSI6ImZyb20iLCJ0eXBlIjoiSGFzaDE2MCJ9LHsibmFtZSI6InRvIiwidHlwZSI6Ikhhc2gxNjAifSx7Im5hbWUiOiJhbW91bnQiLCJ0eXBlIjoiSW50ZWdlciJ9LHsibmFtZSI6ImRhdGEiLCJ0eXBlIjoiQW55In1dLCJyZXR1cm50eXBlIjoiQm9vbGVhbiIsIm9mZnNldCI6MjIsInNhZmUiOmZhbHNlfV0sImV2ZW50cyI6W3sibmFtZSI6IlRyYW5zZmVyIiwicGFyYW1ldGVycyI6W3sibmFtZSI6ImZyb20iLCJ0eXBlIjoiSGFzaDE2MCJ9LHsibmFtZSI6InRvIiwidHlwZSI6Ikhhc2gxNjAifSx7Im5hbWUiOiJhbW91bnQiLCJ0eXBlIjoiSW50ZWdlciJ9XX1dfSwicGVybWlzc2lvbnMiOlt7ImNvbnRyYWN0IjoiKiIsIm1ldGhvZHMiOiIqIn1dLCJ0cnVzdHMiOltdLCJleHRyYSI6eyJlbWFpbCI6ImRldmVsb3BlckBuZW8ub3JnIiwiYXV0aG9yIjoibGF6eW5vZGUiLCJkZXNjcmlwdGlvbiI6IkEgU2ltcGxlIE5lcC0xNyBDb250cmFjdCJ9fQyyTkVGM25lb21sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOaHR0cHM6Ly9naXRodWIuY29tL2xhenlub2RlL25lb21sL2Jsb2IvZGV2L2V4YW1wbGVzL2EuZnVuY3Rpb24uc2ltcGxlbmVwMTcueG1sAAAAABYMBU5lb01MQBhAAgDh9QVAATkFQBFAkA456BLAHwwGZGVwbG95DBT9o/pDRupTKiWPxJfdrdtkN8n9/0FifVtS",
"hash" : "0x40302bcf2021f63a1c24f6009e154c3200f73ad2fe1462d7d599145823dbfa7e",
"witnesses" : [
{
"verification" : "DCECExn08eznGBdguHbcwI+R2//EtVdDx4qf6CeizHqOJgBBVuezJw==",
"invocation" : "DEBtoq+T9NrammQjuYnifco7KHCTk2v+woEJqJCUMr9IscS7PaZaN3FNzSt11yUglIi3T0CJ17KwArBOBvJ8kwq2"
}
],
"attributes" : [],
"signers" : [
{
"scopes" : "None",
"account" : "0x13a192c56738900f9918d7f1ec07d9d8c278b804"
}
],
"size" : 1360,
"nonce" : 1829882407,
"sender" : "NLLvsqs7AyBNmQT6NThUxYWDFwV5b1evaK",
"netfee" : "234352"
}
```
Transaction script contains malformed contract manifest (all methods
offsets are set to be 22, while the contract script lenght is 22):
```
{
"name" : "Nep17Token",
"groups" : [],
"extra" : {
"description" : "A Simple Nep-17 Contract",
"email" : "developer@neo.org",
"author" : "lazynode"
},
"permissions" : [
{
"contract" : "*",
"methods" : "*"
}
],
"features" : {},
"supportedstandards" : [
"NEP-17"
],
"abi" : {
"events" : [
{
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"type" : "Hash160",
"name" : "to"
},
{
"name" : "amount",
"type" : "Integer"
}
],
"name" : "Transfer"
}
],
"methods" : [
{
"safe" : true,
"offset" : 22,
"name" : "symbol",
"returntype" : "String",
"parameters" : []
},
{
"returntype" : "Integer",
"parameters" : [],
"safe" : true,
"offset" : 22,
"name" : "decimals"
},
{
"parameters" : [],
"returntype" : "Integer",
"name" : "totalSupply",
"safe" : true,
"offset" : 22
},
{
"parameters" : [
{
"name" : "owner",
"type" : "Hash160"
}
],
"returntype" : "Integer",
"name" : "balanceOf",
"offset" : 22,
"safe" : true
},
{
"name" : "transfer",
"offset" : 22,
"safe" : false,
"parameters" : [
{
"type" : "Hash160",
"name" : "from"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"name" : "amount",
"type" : "Integer"
},
{
"name" : "data",
"type" : "Any"
}
],
"returntype" : "Boolean"
}
]
},
"trusts" : []
}
```
2022-06-02 06:00:59 +00:00
|
|
|
continue
|
2021-02-09 18:42:39 +00:00
|
|
|
}
|
|
|
|
offsets.Set(methods[i].Offset)
|
|
|
|
}
|
|
|
|
return vm.IsScriptCorrect(script, offsets)
|
|
|
|
}
|