github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/oracle.go (about)

     1  package native
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"math/big"
    10  	"strings"
    11  	"sync/atomic"
    12  
    13  	"github.com/nspcc-dev/neo-go/pkg/config"
    14  	"github.com/nspcc-dev/neo-go/pkg/core/dao"
    15  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    16  	"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
    17  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
    18  	"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
    19  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    20  	"github.com/nspcc-dev/neo-go/pkg/core/storage"
    21  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    22  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    23  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    24  	"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
    25  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    26  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    27  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    28  	"github.com/nspcc-dev/neo-go/pkg/util"
    29  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    30  )
    31  
    32  // Oracle represents Oracle native contract.
    33  type Oracle struct {
    34  	interop.ContractMD
    35  	GAS *GAS
    36  	NEO *NEO
    37  
    38  	Desig        *Designate
    39  	oracleScript []byte
    40  
    41  	// Module is an oracle module capable of talking with the external world.
    42  	Module atomic.Value
    43  	// newRequests contains new requests created during the current block.
    44  	newRequests map[uint64]*state.OracleRequest
    45  }
    46  
    47  type OracleCache struct {
    48  	requestPrice int64
    49  }
    50  
    51  // OracleService specifies oracle module interface.
    52  type OracleService interface {
    53  	// AddRequests processes new requests.
    54  	AddRequests(map[uint64]*state.OracleRequest)
    55  	// RemoveRequests removes already processed requests.
    56  	RemoveRequests([]uint64)
    57  	// UpdateOracleNodes updates oracle nodes.
    58  	UpdateOracleNodes(keys.PublicKeys)
    59  	// UpdateNativeContract updates oracle contract native script and hash.
    60  	UpdateNativeContract([]byte, []byte, util.Uint160, int)
    61  	// Start runs oracle module.
    62  	Start()
    63  	// Shutdown shutdowns oracle module.
    64  	Shutdown()
    65  }
    66  
    67  const (
    68  	oracleContractID  = -9
    69  	maxURLLength      = 256
    70  	maxFilterLength   = 128
    71  	maxCallbackLength = 32
    72  	maxUserDataLength = 512
    73  	// maxRequestsCount is the maximum number of requests per URL.
    74  	maxRequestsCount = 256
    75  
    76  	// DefaultOracleRequestPrice is the default amount GAS needed for an oracle request.
    77  	DefaultOracleRequestPrice = 5000_0000
    78  
    79  	// MinimumResponseGas is the minimum response fee permitted for a request.
    80  	MinimumResponseGas = 10_000_000
    81  )
    82  
    83  var (
    84  	prefixRequestPrice = []byte{5}
    85  	prefixIDList       = []byte{6}
    86  	prefixRequest      = []byte{7}
    87  	prefixRequestID    = []byte{9}
    88  )
    89  
    90  // Various validation errors.
    91  var (
    92  	ErrBigArgument      = errors.New("some of the arguments are invalid")
    93  	ErrInvalidWitness   = errors.New("witness check failed")
    94  	ErrLowResponseGas   = errors.New("not enough gas for response")
    95  	ErrNotEnoughGas     = errors.New("gas limit exceeded")
    96  	ErrRequestNotFound  = errors.New("oracle request not found")
    97  	ErrResponseNotFound = errors.New("oracle response not found")
    98  )
    99  
   100  var (
   101  	_ interop.Contract        = (*Oracle)(nil)
   102  	_ dao.NativeContractCache = (*OracleCache)(nil)
   103  )
   104  
   105  // Copy implements NativeContractCache interface.
   106  func (c *OracleCache) Copy() dao.NativeContractCache {
   107  	cp := &OracleCache{}
   108  	copyOracleCache(c, cp)
   109  	return cp
   110  }
   111  
   112  func copyOracleCache(src, dst *OracleCache) {
   113  	*dst = *src
   114  }
   115  
   116  func newOracle() *Oracle {
   117  	o := &Oracle{
   118  		ContractMD:  *interop.NewContractMD(nativenames.Oracle, oracleContractID),
   119  		newRequests: make(map[uint64]*state.OracleRequest),
   120  	}
   121  	defer o.BuildHFSpecificMD(o.ActiveIn())
   122  
   123  	o.oracleScript = CreateOracleResponseScript(o.Hash)
   124  
   125  	desc := newDescriptor("request", smartcontract.VoidType,
   126  		manifest.NewParameter("url", smartcontract.StringType),
   127  		manifest.NewParameter("filter", smartcontract.StringType),
   128  		manifest.NewParameter("callback", smartcontract.StringType),
   129  		manifest.NewParameter("userData", smartcontract.AnyType),
   130  		manifest.NewParameter("gasForResponse", smartcontract.IntegerType))
   131  	md := newMethodAndPrice(o.request, 0, callflag.States|callflag.AllowNotify)
   132  	o.AddMethod(md, desc)
   133  
   134  	desc = newDescriptor("finish", smartcontract.VoidType)
   135  	md = newMethodAndPrice(o.finish, 0, callflag.States|callflag.AllowCall|callflag.AllowNotify)
   136  	o.AddMethod(md, desc)
   137  
   138  	desc = newDescriptor("verify", smartcontract.BoolType)
   139  	md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag)
   140  	o.AddMethod(md, desc)
   141  
   142  	eDesc := newEventDescriptor("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
   143  		manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
   144  		manifest.NewParameter("Url", smartcontract.StringType),
   145  		manifest.NewParameter("Filter", smartcontract.StringType))
   146  	eMD := newEvent(eDesc)
   147  	o.AddEvent(eMD)
   148  
   149  	eDesc = newEventDescriptor("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType),
   150  		manifest.NewParameter("OriginalTx", smartcontract.Hash256Type))
   151  	eMD = newEvent(eDesc)
   152  	o.AddEvent(eMD)
   153  
   154  	desc = newDescriptor("getPrice", smartcontract.IntegerType)
   155  	md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
   156  	o.AddMethod(md, desc)
   157  
   158  	desc = newDescriptor("setPrice", smartcontract.VoidType,
   159  		manifest.NewParameter("price", smartcontract.IntegerType))
   160  	md = newMethodAndPrice(o.setPrice, 1<<15, callflag.States)
   161  	o.AddMethod(md, desc)
   162  
   163  	return o
   164  }
   165  
   166  // GetOracleResponseScript returns a script for the transaction with an oracle response.
   167  func (o *Oracle) GetOracleResponseScript() []byte {
   168  	return bytes.Clone(o.oracleScript)
   169  }
   170  
   171  // OnPersist implements the Contract interface.
   172  func (o *Oracle) OnPersist(ic *interop.Context) error {
   173  	return nil
   174  }
   175  
   176  // PostPersist represents `postPersist` method.
   177  func (o *Oracle) PostPersist(ic *interop.Context) error {
   178  	p := o.getPriceInternal(ic.DAO)
   179  
   180  	var nodes keys.PublicKeys
   181  	var reward []big.Int
   182  	single := big.NewInt(p)
   183  	var removedIDs []uint64
   184  
   185  	orc, _ := o.Module.Load().(*OracleService)
   186  	for _, tx := range ic.Block.Transactions {
   187  		resp := getResponse(tx)
   188  		if resp == nil {
   189  			continue
   190  		}
   191  		reqKey := makeRequestKey(resp.ID)
   192  		req := new(state.OracleRequest)
   193  		if err := o.getConvertibleFromDAO(ic.DAO, reqKey, req); err != nil {
   194  			continue
   195  		}
   196  		ic.DAO.DeleteStorageItem(o.ID, reqKey)
   197  		if orc != nil && *orc != nil {
   198  			removedIDs = append(removedIDs, resp.ID)
   199  		}
   200  
   201  		idKey := makeIDListKey(req.URL)
   202  		idList := new(IDList)
   203  		if err := o.getConvertibleFromDAO(ic.DAO, idKey, idList); err != nil {
   204  			return err
   205  		}
   206  		if !idList.Remove(resp.ID) {
   207  			return errors.New("response ID wasn't found")
   208  		}
   209  
   210  		var err error
   211  		if len(*idList) == 0 {
   212  			ic.DAO.DeleteStorageItem(o.ID, idKey)
   213  		} else {
   214  			err = putConvertibleToDAO(o.ID, ic.DAO, idKey, idList)
   215  		}
   216  		if err != nil {
   217  			return err
   218  		}
   219  
   220  		if nodes == nil {
   221  			nodes, err = o.GetOracleNodes(ic.DAO)
   222  			if err != nil {
   223  				return err
   224  			}
   225  			reward = make([]big.Int, len(nodes))
   226  		}
   227  
   228  		if len(reward) > 0 {
   229  			index := resp.ID % uint64(len(nodes))
   230  			reward[index].Add(&reward[index], single)
   231  		}
   232  	}
   233  	for i := range reward {
   234  		o.GAS.mint(ic, nodes[i].GetScriptHash(), &reward[i], false)
   235  	}
   236  
   237  	if len(removedIDs) != 0 {
   238  		(*orc).RemoveRequests(removedIDs)
   239  	}
   240  	return o.updateCache(ic.DAO)
   241  }
   242  
   243  // Metadata returns contract metadata.
   244  func (o *Oracle) Metadata() *interop.ContractMD {
   245  	return &o.ContractMD
   246  }
   247  
   248  // Initialize initializes an Oracle contract.
   249  func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
   250  	switch hf {
   251  	case o.ActiveIn():
   252  		setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
   253  		setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
   254  
   255  		cache := &OracleCache{
   256  			requestPrice: int64(DefaultOracleRequestPrice),
   257  		}
   258  		ic.DAO.SetCache(o.ID, cache)
   259  	default:
   260  		orc, _ := o.Module.Load().(*OracleService)
   261  		if orc != nil && *orc != nil {
   262  			md, ok := newMD.GetMethod(manifest.MethodVerify, -1)
   263  			if !ok {
   264  				panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
   265  			}
   266  			(*orc).UpdateNativeContract(newMD.NEF.Script, o.GetOracleResponseScript(),
   267  				o.Hash, md.MD.Offset)
   268  		}
   269  	}
   270  
   271  	return nil
   272  }
   273  
   274  func (o *Oracle) InitializeCache(blockHeight uint32, d *dao.Simple) error {
   275  	cache := &OracleCache{}
   276  	cache.requestPrice = getIntWithKey(o.ID, d, prefixRequestPrice)
   277  	d.SetCache(o.ID, cache)
   278  	return nil
   279  }
   280  
   281  // ActiveIn implements the Contract interface.
   282  func (o *Oracle) ActiveIn() *config.Hardfork {
   283  	return nil
   284  }
   285  
   286  func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
   287  	for i := range tx.Attributes {
   288  		if tx.Attributes[i].Type == transaction.OracleResponseT {
   289  			return tx.Attributes[i].Value.(*transaction.OracleResponse)
   290  		}
   291  	}
   292  	return nil
   293  }
   294  
   295  func (o *Oracle) finish(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
   296  	err := o.FinishInternal(ic)
   297  	if err != nil {
   298  		panic(err)
   299  	}
   300  	return stackitem.Null{}
   301  }
   302  
   303  // FinishInternal processes an oracle response.
   304  func (o *Oracle) FinishInternal(ic *interop.Context) error {
   305  	if len(ic.VM.Istack()) != 2 {
   306  		return errors.New("Oracle.finish called from non-entry script")
   307  	}
   308  	if ic.Invocations[o.Hash] != 1 {
   309  		return errors.New("Oracle.finish called multiple times")
   310  	}
   311  	resp := getResponse(ic.Tx)
   312  	if resp == nil {
   313  		return ErrResponseNotFound
   314  	}
   315  	req, err := o.GetRequestInternal(ic.DAO, resp.ID)
   316  	if err != nil {
   317  		return ErrRequestNotFound
   318  	}
   319  	ic.AddNotification(o.Hash, "OracleResponse", stackitem.NewArray([]stackitem.Item{
   320  		stackitem.Make(resp.ID),
   321  		stackitem.Make(req.OriginalTxID.BytesBE()),
   322  	}))
   323  
   324  	origTx, _, err := ic.DAO.GetTransaction(req.OriginalTxID)
   325  	if err != nil {
   326  		return ErrRequestNotFound
   327  	}
   328  	ic.UseSigners(origTx.Signers)
   329  	defer ic.UseSigners(nil)
   330  
   331  	userData, err := stackitem.Deserialize(req.UserData)
   332  	if err != nil {
   333  		return err
   334  	}
   335  	args := []stackitem.Item{
   336  		stackitem.Make(req.URL),
   337  		userData,
   338  		stackitem.Make(resp.Code),
   339  		stackitem.Make(resp.Result),
   340  	}
   341  	cs, err := ic.GetContract(req.CallbackContract)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	return contract.CallFromNative(ic, o.Hash, cs, req.CallbackMethod, args, false)
   346  }
   347  
   348  func (o *Oracle) request(ic *interop.Context, args []stackitem.Item) stackitem.Item {
   349  	url, err := stackitem.ToString(args[0])
   350  	if err != nil {
   351  		panic(err)
   352  	}
   353  	var filter *string
   354  	_, ok := args[1].(stackitem.Null)
   355  	if !ok {
   356  		// Check UTF-8 validity.
   357  		str, err := stackitem.ToString(args[1])
   358  		if err != nil {
   359  			panic(err)
   360  		}
   361  		filter = &str
   362  	}
   363  	cb, err := stackitem.ToString(args[2])
   364  	if err != nil {
   365  		panic(err)
   366  	}
   367  	userData := args[3]
   368  	gas, err := args[4].TryInteger()
   369  	if err != nil {
   370  		panic(err)
   371  	}
   372  	if !ic.VM.AddGas(o.getPriceInternal(ic.DAO)) {
   373  		panic("insufficient gas")
   374  	}
   375  	if err := o.RequestInternal(ic, url, filter, cb, userData, gas); err != nil {
   376  		panic(err)
   377  	}
   378  	return stackitem.Null{}
   379  }
   380  
   381  // RequestInternal processes an oracle request.
   382  func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string, cb string, userData stackitem.Item, gas *big.Int) error {
   383  	if len(url) > maxURLLength || (filter != nil && len(*filter) > maxFilterLength) || len(cb) > maxCallbackLength || !gas.IsInt64() {
   384  		return ErrBigArgument
   385  	}
   386  	if gas.Int64() < MinimumResponseGas {
   387  		return ErrLowResponseGas
   388  	}
   389  	if strings.HasPrefix(cb, "_") {
   390  		return errors.New("disallowed callback method (starts with '_')")
   391  	}
   392  
   393  	if !ic.VM.AddGas(gas.Int64()) {
   394  		return ErrNotEnoughGas
   395  	}
   396  	callingHash := ic.VM.GetCallingScriptHash()
   397  	o.GAS.mint(ic, o.Hash, gas, false)
   398  	si := ic.DAO.GetStorageItem(o.ID, prefixRequestID)
   399  	itemID := bigint.FromBytes(si)
   400  	id := itemID.Uint64()
   401  	itemID.Add(itemID, intOne)
   402  	ic.DAO.PutBigInt(o.ID, prefixRequestID, itemID)
   403  
   404  	// Should be executed from the contract.
   405  	_, err := ic.GetContract(ic.VM.GetCallingScriptHash())
   406  	if err != nil {
   407  		return err
   408  	}
   409  
   410  	data, err := ic.DAO.GetItemCtx().Serialize(userData, false)
   411  	if err != nil {
   412  		return err
   413  	}
   414  	if len(data) > maxUserDataLength {
   415  		return ErrBigArgument
   416  	}
   417  	data = bytes.Clone(data) // Serialization context will be used in PutRequestInternal again.
   418  
   419  	var filterNotif stackitem.Item
   420  	if filter != nil {
   421  		filterNotif = stackitem.Make(*filter)
   422  	} else {
   423  		filterNotif = stackitem.Null{}
   424  	}
   425  	ic.AddNotification(o.Hash, "OracleRequest", stackitem.NewArray([]stackitem.Item{
   426  		stackitem.Make(id),
   427  		stackitem.Make(ic.VM.GetCallingScriptHash().BytesBE()),
   428  		stackitem.Make(url),
   429  		filterNotif,
   430  	}))
   431  	req := &state.OracleRequest{
   432  		OriginalTxID:     o.getOriginalTxID(ic.DAO, ic.Tx),
   433  		GasForResponse:   gas.Uint64(),
   434  		URL:              url,
   435  		Filter:           filter,
   436  		CallbackContract: callingHash,
   437  		CallbackMethod:   cb,
   438  		UserData:         data,
   439  	}
   440  	return o.PutRequestInternal(id, req, ic.DAO)
   441  }
   442  
   443  // PutRequestInternal puts the oracle request with the specified id to d.
   444  func (o *Oracle) PutRequestInternal(id uint64, req *state.OracleRequest, d *dao.Simple) error {
   445  	reqKey := makeRequestKey(id)
   446  	if err := putConvertibleToDAO(o.ID, d, reqKey, req); err != nil {
   447  		return err
   448  	}
   449  	orc, _ := o.Module.Load().(*OracleService)
   450  	if orc != nil && *orc != nil {
   451  		o.newRequests[id] = req
   452  	}
   453  
   454  	// Add request ID to the id list.
   455  	lst := new(IDList)
   456  	key := makeIDListKey(req.URL)
   457  	if err := o.getConvertibleFromDAO(d, key, lst); err != nil && !errors.Is(err, storage.ErrKeyNotFound) {
   458  		return err
   459  	}
   460  	if len(*lst) >= maxRequestsCount {
   461  		return fmt.Errorf("there are too many pending requests for %s url", req.URL)
   462  	}
   463  	*lst = append(*lst, id)
   464  	return putConvertibleToDAO(o.ID, d, key, lst)
   465  }
   466  
   467  // GetScriptHash returns script hash of oracle nodes.
   468  func (o *Oracle) GetScriptHash(d *dao.Simple) (util.Uint160, error) {
   469  	return o.Desig.GetLastDesignatedHash(d, noderoles.Oracle)
   470  }
   471  
   472  // GetOracleNodes returns public keys of oracle nodes.
   473  func (o *Oracle) GetOracleNodes(d *dao.Simple) (keys.PublicKeys, error) {
   474  	nodes, _, err := o.Desig.GetDesignatedByRole(d, noderoles.Oracle, math.MaxUint32)
   475  	return nodes, err
   476  }
   477  
   478  // GetRequestInternal returns the request by ID and key under which it is stored.
   479  func (o *Oracle) GetRequestInternal(d *dao.Simple, id uint64) (*state.OracleRequest, error) {
   480  	key := makeRequestKey(id)
   481  	req := new(state.OracleRequest)
   482  	return req, o.getConvertibleFromDAO(d, key, req)
   483  }
   484  
   485  // GetIDListInternal returns the request by ID and key under which it is stored.
   486  func (o *Oracle) GetIDListInternal(d *dao.Simple, url string) (*IDList, error) {
   487  	key := makeIDListKey(url)
   488  	idList := new(IDList)
   489  	return idList, o.getConvertibleFromDAO(d, key, idList)
   490  }
   491  
   492  func (o *Oracle) verify(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
   493  	return stackitem.NewBool(ic.Tx.HasAttribute(transaction.OracleResponseT))
   494  }
   495  
   496  func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
   497  	return stackitem.NewBigInteger(big.NewInt(o.getPriceInternal(ic.DAO)))
   498  }
   499  
   500  func (o *Oracle) getPriceInternal(d *dao.Simple) int64 {
   501  	cache := d.GetROCache(o.ID).(*OracleCache)
   502  	return cache.requestPrice
   503  }
   504  
   505  func (o *Oracle) setPrice(ic *interop.Context, args []stackitem.Item) stackitem.Item {
   506  	price := toBigInt(args[0])
   507  	if price.Sign() <= 0 || !price.IsInt64() {
   508  		panic("invalid register price")
   509  	}
   510  	if !o.NEO.checkCommittee(ic) {
   511  		panic("invalid committee signature")
   512  	}
   513  	setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64())
   514  	cache := ic.DAO.GetRWCache(o.ID).(*OracleCache)
   515  	cache.requestPrice = price.Int64()
   516  	return stackitem.Null{}
   517  }
   518  
   519  func (o *Oracle) getOriginalTxID(d *dao.Simple, tx *transaction.Transaction) util.Uint256 {
   520  	for i := range tx.Attributes {
   521  		if tx.Attributes[i].Type == transaction.OracleResponseT {
   522  			id := tx.Attributes[i].Value.(*transaction.OracleResponse).ID
   523  			req, _ := o.GetRequestInternal(d, id)
   524  			return req.OriginalTxID
   525  		}
   526  	}
   527  	return tx.Hash()
   528  }
   529  
   530  // GetRequests returns all requests which have not been finished yet.
   531  func (o *Oracle) GetRequests(d *dao.Simple) (map[uint64]*state.OracleRequest, error) {
   532  	var reqs = make(map[uint64]*state.OracleRequest)
   533  	var err error
   534  	d.Seek(o.ID, storage.SeekRange{Prefix: prefixRequest}, func(k, v []byte) bool {
   535  		if len(k) != 8 {
   536  			err = errors.New("invalid request ID")
   537  			return false
   538  		}
   539  		req := new(state.OracleRequest)
   540  		err = stackitem.DeserializeConvertible(v, req)
   541  		if err != nil {
   542  			return false
   543  		}
   544  		id := binary.BigEndian.Uint64(k)
   545  		reqs[id] = req
   546  		return true
   547  	})
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  	return reqs, nil
   552  }
   553  
   554  func makeRequestKey(id uint64) []byte {
   555  	k := make([]byte, 9)
   556  	k[0] = prefixRequest[0]
   557  	binary.BigEndian.PutUint64(k[1:], id)
   558  	return k
   559  }
   560  
   561  func makeIDListKey(url string) []byte {
   562  	return append(prefixIDList, hash.Hash160([]byte(url)).BytesBE()...)
   563  }
   564  
   565  func (o *Oracle) getConvertibleFromDAO(d *dao.Simple, key []byte, item stackitem.Convertible) error {
   566  	return getConvertibleFromDAO(o.ID, d, key, item)
   567  }
   568  
   569  // updateCache updates cached Oracle values if they've been changed.
   570  func (o *Oracle) updateCache(d *dao.Simple) error {
   571  	orc, _ := o.Module.Load().(*OracleService)
   572  	if orc == nil || *orc == nil {
   573  		return nil
   574  	}
   575  
   576  	reqs := o.newRequests
   577  	o.newRequests = make(map[uint64]*state.OracleRequest)
   578  	for id := range reqs {
   579  		key := makeRequestKey(id)
   580  		if si := d.GetStorageItem(o.ID, key); si == nil { // tx has failed
   581  			delete(reqs, id)
   582  		}
   583  	}
   584  	(*orc).AddRequests(reqs)
   585  	return nil
   586  }
   587  
   588  // CreateOracleResponseScript returns a script that is used to create the native Oracle
   589  // response.
   590  func CreateOracleResponseScript(nativeOracleHash util.Uint160) []byte {
   591  	script, err := smartcontract.CreateCallScript(nativeOracleHash, "finish")
   592  	if err != nil {
   593  		panic(fmt.Errorf("failed to create Oracle response script: %w", err))
   594  	}
   595  	return script
   596  }