rpcclient: add oracle package for OracleContract
This commit is contained in:
parent
5c8f3a99dc
commit
0dbe8b6ce2
4 changed files with 236 additions and 3 deletions
|
@ -20,6 +20,8 @@ import (
|
|||
)
|
||||
|
||||
// GetOraclePrice invokes `getPrice` method on a native Oracle contract.
|
||||
//
|
||||
// Deprecated: please use oracle subpackage.
|
||||
func (c *Client) GetOraclePrice() (int64, error) {
|
||||
oracleHash, err := c.GetNativeContractHash(nativenames.Oracle)
|
||||
if err != nil {
|
||||
|
|
113
pkg/rpcclient/oracle/oracle.go
Normal file
113
pkg/rpcclient/oracle/oracle.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
Package oracle allows to work with the native OracleContract contract via RPC.
|
||||
|
||||
Safe methods are encapsulated into ContractReader structure while Contract provides
|
||||
various methods to perform state-changing calls.
|
||||
*/
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Invoker is used by ContractReader to call various methods.
|
||||
type Invoker interface {
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// Actor is used by Contract to create and send transactions.
|
||||
type Actor interface {
|
||||
Invoker
|
||||
|
||||
MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error)
|
||||
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error)
|
||||
SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error)
|
||||
}
|
||||
|
||||
// Hash stores the hash of the native OracleContract contract.
|
||||
var Hash = state.CreateNativeContractHash(nativenames.Oracle)
|
||||
|
||||
const priceSetter = "setPrice"
|
||||
|
||||
// ContractReader provides an interface to call read-only OracleContract
|
||||
// contract's methods. "verify" method is not exposed since it's very specific
|
||||
// and can't be executed successfully outside of the proper oracle response
|
||||
// transaction.
|
||||
type ContractReader struct {
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
// Contract represents the OracleContract contract client that can be used to
|
||||
// invoke its "setPrice" method. Other methods are useless for direct calls,
|
||||
// "request" requires a callback that entry script can't provide and "finish"
|
||||
// will only work in an oracle transaction. Since "setPrice" can be called
|
||||
// successfully only by the network's committee, an appropriate Actor is needed
|
||||
// for Contract.
|
||||
type Contract struct {
|
||||
ContractReader
|
||||
|
||||
actor Actor
|
||||
}
|
||||
|
||||
// RequestEvent represents an OracleRequest notification event emitted from
|
||||
// the OracleContract contract.
|
||||
type RequestEvent struct {
|
||||
ID int64
|
||||
Contract util.Uint160
|
||||
URL string
|
||||
Filter string
|
||||
}
|
||||
|
||||
// ResponseEvent represents an OracleResponse notification event emitted from
|
||||
// the OracleContract contract.
|
||||
type ResponseEvent struct {
|
||||
ID int64
|
||||
OriginalTx util.Uint256
|
||||
}
|
||||
|
||||
// NewReader creates an instance of ContractReader that can be used to read
|
||||
// data from the contract.
|
||||
func NewReader(invoker Invoker) *ContractReader {
|
||||
return &ContractReader{invoker}
|
||||
}
|
||||
|
||||
// New creates an instance of Contract to perform actions using
|
||||
// the given Actor.
|
||||
func New(actor Actor) *Contract {
|
||||
return &Contract{*NewReader(actor), actor}
|
||||
}
|
||||
|
||||
// GetPrice returns current price of the oracle request call.
|
||||
func (c *ContractReader) GetPrice() (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(Hash, "getPrice"))
|
||||
}
|
||||
|
||||
// SetPrice creates and sends a transaction that sets the new price for the
|
||||
// oracle request call. The action is successful when transaction ends in HALT
|
||||
// state. The returned values are transaction hash, its ValidUntilBlock value and
|
||||
// an error if any.
|
||||
func (c *Contract) SetPrice(value *big.Int) (util.Uint256, uint32, error) {
|
||||
return c.actor.SendCall(Hash, priceSetter, value)
|
||||
}
|
||||
|
||||
// SetPriceTransaction creates a transaction that sets the new price for the
|
||||
// oracle request call. The action is successful when transaction ends in HALT
|
||||
// state. The transaction is signed, but not sent to the network, instead it's
|
||||
// returned to the caller.
|
||||
func (c *Contract) SetPriceTransaction(value *big.Int) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeCall(Hash, priceSetter, value)
|
||||
}
|
||||
|
||||
// SetPriceUnsigned creates a transaction that sets the new price for the
|
||||
// oracle request call. The action is successful when transaction ends in HALT
|
||||
// state. The transaction is not signed and just returned to the caller.
|
||||
func (c *Contract) SetPriceUnsigned(value *big.Int) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeUnsignedCall(Hash, priceSetter, nil, value)
|
||||
}
|
86
pkg/rpcclient/oracle/oracle_test.go
Normal file
86
pkg/rpcclient/oracle/oracle_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package oracle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testAct struct {
|
||||
err error
|
||||
res *result.Invoke
|
||||
tx *transaction.Transaction
|
||||
txh util.Uint256
|
||||
vub uint32
|
||||
}
|
||||
|
||||
func (t *testAct) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) {
|
||||
return t.res, t.err
|
||||
}
|
||||
func (t *testAct) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) {
|
||||
return t.tx, t.err
|
||||
}
|
||||
func (t *testAct) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) {
|
||||
return t.tx, t.err
|
||||
}
|
||||
func (t *testAct) SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) {
|
||||
return t.txh, t.vub, t.err
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
ta := new(testAct)
|
||||
ora := NewReader(ta)
|
||||
|
||||
ta.err = errors.New("")
|
||||
_, err := ora.GetPrice()
|
||||
require.Error(t, err)
|
||||
|
||||
ta.err = nil
|
||||
ta.res = &result.Invoke{
|
||||
State: "HALT",
|
||||
Stack: []stackitem.Item{
|
||||
stackitem.Make(42),
|
||||
},
|
||||
}
|
||||
price, err := ora.GetPrice()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, big.NewInt(42), price)
|
||||
}
|
||||
|
||||
func TestPriceSetter(t *testing.T) {
|
||||
ta := new(testAct)
|
||||
ora := New(ta)
|
||||
|
||||
big42 := big.NewInt(42)
|
||||
|
||||
ta.err = errors.New("")
|
||||
_, _, err := ora.SetPrice(big42)
|
||||
require.Error(t, err)
|
||||
_, err = ora.SetPriceTransaction(big42)
|
||||
require.Error(t, err)
|
||||
_, err = ora.SetPriceUnsigned(big42)
|
||||
require.Error(t, err)
|
||||
|
||||
ta.err = nil
|
||||
ta.txh = util.Uint256{1, 2, 3}
|
||||
ta.vub = 42
|
||||
ta.tx = transaction.New([]byte{1, 2, 3}, 100500)
|
||||
|
||||
h, vub, err := ora.SetPrice(big42)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ta.txh, h)
|
||||
require.Equal(t, ta.vub, vub)
|
||||
|
||||
tx, err := ora.SetPriceTransaction(big42)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ta.tx, tx)
|
||||
tx, err = ora.SetPriceUnsigned(big42)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ta.tx, tx)
|
||||
}
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nns"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/oracle"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -1453,7 +1454,7 @@ func TestClient_GetNotaryServiceFeePerKey(t *testing.T) {
|
|||
require.Equal(t, defaultNotaryServiceFeePerKey, actual)
|
||||
}
|
||||
|
||||
func TestClient_GetOraclePrice(t *testing.T) {
|
||||
func TestClientOracle(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
@ -1462,10 +1463,41 @@ func TestClient_GetOraclePrice(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NoError(t, c.Init())
|
||||
|
||||
var defaultOracleRequestPrice int64 = 5000_0000
|
||||
actual, err := c.GetOraclePrice()
|
||||
oraRe := oracle.NewReader(invoker.New(c, nil))
|
||||
|
||||
var defaultOracleRequestPrice = big.NewInt(5000_0000)
|
||||
actual, err := oraRe.GetPrice()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, defaultOracleRequestPrice, actual)
|
||||
|
||||
act, err := actor.New(c, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: testchain.CommitteeScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: &wallet.Account{
|
||||
Address: testchain.CommitteeAddress(),
|
||||
Contract: &wallet.Contract{
|
||||
Script: testchain.CommitteeVerificationScript(),
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
ora := oracle.New(act)
|
||||
|
||||
newPrice := big.NewInt(1_0000_0000)
|
||||
tx, err := ora.SetPriceUnsigned(newPrice)
|
||||
require.NoError(t, err)
|
||||
|
||||
tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx)
|
||||
bl := testchain.NewBlock(t, chain, 1, 0, tx)
|
||||
_, err = c.SubmitBlock(*bl)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err = ora.GetPrice()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newPrice, actual)
|
||||
}
|
||||
|
||||
func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue