native: allow to set oracle request price

This commit is contained in:
Evgeniy Stratonikov 2021-03-05 14:38:16 +03:00
parent 27fc28bd69
commit 256cd09228
4 changed files with 84 additions and 6 deletions

View file

@ -120,7 +120,9 @@ func TestNativeHelpersCompile(t *testing.T) {
}, nep17TestCases...))
runNativeTestCases(t, cs.GAS.ContractMD, "gas", nep17TestCases)
runNativeTestCases(t, cs.Oracle.ContractMD, "oracle", []nativeTestCase{
{"getPrice", nil},
{"request", []string{`"url"`, "nil", `"callback"`, "nil", "123"}},
{"setPrice", []string{"10"}},
})
runNativeTestCases(t, cs.Designate.ContractMD, "roles", []nativeTestCase{
{"designateAsRole", []string{"1", "[]interop.PublicKey{}"}},

View file

@ -40,6 +40,9 @@ type Oracle struct {
Desig *Designate
oracleScript []byte
requestPrice atomic.Value
requestPriceChanged atomic.Value
// Module is an oracle module capable of talking with the external world.
Module atomic.Value
// newRequests contains new requests created during current block.
@ -55,13 +58,15 @@ const (
// maxRequestsCount is the maximum number of requests per URL
maxRequestsCount = 256
oracleRequestPrice = 5000_0000
// DefaultOracleRequestPrice is default amount GAS needed for oracle request.
DefaultOracleRequestPrice = 5000_0000
)
var (
prefixIDList = []byte{6}
prefixRequest = []byte{7}
prefixRequestID = []byte{9}
prefixRequestPrice = []byte{5}
prefixIDList = []byte{6}
prefixRequest = []byte{7}
prefixRequestID = []byte{9}
)
// Various validation errors.
@ -109,6 +114,17 @@ func newOracle() *Oracle {
o.AddEvent("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType),
manifest.NewParameter("OriginalTx", smartcontract.Hash256Type))
desc = newDescriptor("getPrice", smartcontract.IntegerType)
md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
o.AddMethod(md, desc)
desc = newDescriptor("setPrice", smartcontract.VoidType,
manifest.NewParameter("price", smartcontract.IntegerType))
md = newMethodAndPrice(o.setPrice, 1<<15, callflag.States)
o.AddMethod(md, desc)
o.requestPriceChanged.Store(true)
return o
}
@ -130,9 +146,15 @@ func (o *Oracle) OnPersist(ic *interop.Context) error {
// PostPersist represents `postPersist` method.
func (o *Oracle) PostPersist(ic *interop.Context) error {
p := o.getPriceInternal(ic.DAO)
if o.requestPriceChanged.Load().(bool) {
o.requestPrice.Store(p)
o.requestPriceChanged.Store(false)
}
var nodes keys.PublicKeys
var reward []big.Int
single := new(big.Int).SetUint64(oracleRequestPrice)
single := big.NewInt(p)
var removedIDs []uint64
orc, _ := o.Module.Load().(services.Oracle)
@ -202,7 +224,15 @@ func (o *Oracle) Metadata() *interop.ContractMD {
// Initialize initializes Oracle contract.
func (o *Oracle) Initialize(ic *interop.Context) error {
return setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
if err := setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0); err != nil {
return err
}
if err := setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice); err != nil {
return err
}
o.requestPrice.Store(int64(DefaultOracleRequestPrice))
o.requestPriceChanged.Store(false)
return nil
}
func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
@ -281,6 +311,9 @@ func (o *Oracle) request(ic *interop.Context, args []stackitem.Item) stackitem.I
if err != nil {
panic(err)
}
if !ic.VM.AddGas(o.getPriceInternal(ic.DAO)) {
panic("insufficient gas")
}
if err := o.RequestInternal(ic, url, filter, cb, userData, gas); err != nil {
panic(err)
}
@ -404,6 +437,32 @@ func (o *Oracle) verify(ic *interop.Context, _ []stackitem.Item) stackitem.Item
return stackitem.NewBool(ic.Tx.HasAttribute(transaction.OracleResponseT))
}
func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewBigInteger(big.NewInt(o.getPriceInternal(ic.DAO)))
}
func (o *Oracle) getPriceInternal(d dao.DAO) int64 {
if !o.requestPriceChanged.Load().(bool) {
return o.requestPrice.Load().(int64)
}
return getIntWithKey(o.ID, d, prefixRequestPrice)
}
func (o *Oracle) setPrice(ic *interop.Context, args []stackitem.Item) stackitem.Item {
price := toBigInt(args[0])
if price.Sign() <= 0 || !price.IsInt64() {
panic("invalid register price")
}
if !o.NEO.checkCommittee(ic) {
panic("invalid committee signature")
}
if err := setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64()); err != nil {
panic(err)
}
o.requestPriceChanged.Store(true)
return stackitem.Null{}
}
func (o *Oracle) getOriginalTxID(d dao.DAO, tx *transaction.Transaction) util.Uint256 {
for i := range tx.Attributes {
if tx.Attributes[i].Type == transaction.OracleResponseT {

View file

@ -2,6 +2,7 @@ package core
import (
"errors"
"math"
"math/big"
"testing"
@ -248,3 +249,9 @@ func TestOracle_Request(t *testing.T) {
})
})
}
func TestGetSetPrice(t *testing.T) {
bc := newTestChain(t)
testGetSet(t, bc, bc.contracts.Oracle.Hash, "Price",
native.DefaultOracleRequestPrice, 1, math.MaxInt64)
}

View file

@ -14,3 +14,13 @@ func Request(url string, filter []byte, cb string, userData interface{}, gasForR
contract.States|contract.AllowNotify,
url, filter, cb, userData, gasForResponse)
}
// GetPrice represents `getPrice` method of Oracle native contract.
func GetPrice() int {
return contract.Call(interop.Hash160(Hash), "getPrice", contract.ReadStates).(int)
}
// SetPrice represents `setPrice` method of Oracle native contract.
func SetPrice(amount int) {
contract.Call(interop.Hash160(Hash), "setPrice", contract.States, amount)
}