Merge pull request #2643 from nspcc-dev/policy-contract-rpc-wrapper

Policy/GAS/Designation contracts RPC wrappers
This commit is contained in:
Roman Khimov 2022-08-16 13:08:34 +03:00 committed by GitHub
commit f8857c5ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 902 additions and 13 deletions

View file

@ -17,6 +17,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
@ -241,7 +242,8 @@ func getNEP17Balance(ctx *cli.Context) error {
if err != nil { if err != nil {
// Try to get native NEP17 with matching symbol. // Try to get native NEP17 with matching symbol.
var gasSymbol, neoSymbol string var gasSymbol, neoSymbol string
gasSymbol, h, err = getNativeNEP17Symbol(c, nativenames.Gas) g := gas.NewReader(invoker.New(c, nil))
gasSymbol, err = g.Symbol()
if err != nil { if err != nil {
continue continue
} }
@ -253,6 +255,8 @@ func getNEP17Balance(ctx *cli.Context) error {
if neoSymbol != name { if neoSymbol != name {
continue continue
} }
} else {
h = gas.Hash
} }
} }
} }

View file

@ -176,7 +176,7 @@ func NewContractMD(name string, id int32) *ContractMD {
c.NEF.Header.Compiler = "neo-core-v3.0" c.NEF.Header.Compiler = "neo-core-v3.0"
c.NEF.Header.Magic = nef.Magic c.NEF.Header.Magic = nef.Magic
c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling
c.Hash = state.CreateContractHash(util.Uint160{}, 0, c.Name) c.Hash = state.CreateNativeContractHash(c.Name)
c.Manifest = *manifest.DefaultManifest(name) c.Manifest = *manifest.DefaultManifest(name)
return c return c

View file

@ -115,3 +115,9 @@ func CreateContractHash(sender util.Uint160, checksum uint32, name string) util.
} }
return hash.Hash160(w.Bytes()) return hash.Hash160(w.Bytes())
} }
// CreateNativeContractHash calculates the hash for the native contract with the
// given name.
func CreateNativeContractHash(name string) util.Uint160 {
return CreateContractHash(util.Uint160{}, 0, name)
}

26
pkg/rpcclient/gas/gas.go Normal file
View file

@ -0,0 +1,26 @@
/*
Package gas provides a convenience wrapper for GAS contract to use it via RPC.
GAS itself only has standard NEP-17 methods, so this package only contains its
hash and allows to create NEP-17 structures in an easier way.
*/
package gas
import (
"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/rpcclient/nep17"
)
// Hash stores the hash of the native GAS contract.
var Hash = state.CreateNativeContractHash(nativenames.Gas)
// NewReader creates a NEP-17 reader for the GAS contract.
func NewReader(invoker nep17.Invoker) *nep17.TokenReader {
return nep17.NewReader(invoker, Hash)
}
// New creates a NEP-17 contract instance for the native GAS contract.
func New(actor nep17.Actor) *nep17.Token {
return nep17.New(actor, Hash)
}

View file

@ -0,0 +1,40 @@
package gas
import (
"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/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) MakeRun(script []byte) (*transaction.Transaction, error) {
return t.tx, t.err
}
func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) {
return t.tx, t.err
}
func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) {
return t.txh, t.vub, t.err
}
func TestNew(t *testing.T) {
ta := &testAct{}
gr := NewReader(ta)
require.NotNil(t, gr)
g := New(ta)
require.NotNil(t, g)
}

View file

@ -52,6 +52,8 @@ func (c *Client) getFromNEO(meth string) (int64, error) {
} }
// GetDesignatedByRole invokes `getDesignatedByRole` method on a native RoleManagement contract. // GetDesignatedByRole invokes `getDesignatedByRole` method on a native RoleManagement contract.
//
// Deprecated: please use rolemgmt package.
func (c *Client) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) { func (c *Client) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) {
rmHash, err := c.GetNativeContractHash(nativenames.Designation) rmHash, err := c.GetNativeContractHash(nativenames.Designation)
if err != nil { if err != nil {

View file

@ -9,16 +9,22 @@ import (
) )
// GetFeePerByte invokes `getFeePerByte` method on a native Policy contract. // GetFeePerByte invokes `getFeePerByte` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetFeePerByte() (int64, error) { func (c *Client) GetFeePerByte() (int64, error) {
return c.invokeNativePolicyMethod("getFeePerByte") return c.invokeNativePolicyMethod("getFeePerByte")
} }
// GetExecFeeFactor invokes `getExecFeeFactor` method on a native Policy contract. // GetExecFeeFactor invokes `getExecFeeFactor` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetExecFeeFactor() (int64, error) { func (c *Client) GetExecFeeFactor() (int64, error) {
return c.invokeNativePolicyMethod("getExecFeeFactor") return c.invokeNativePolicyMethod("getExecFeeFactor")
} }
// GetStoragePrice invokes `getStoragePrice` method on a native Policy contract. // GetStoragePrice invokes `getStoragePrice` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetStoragePrice() (int64, error) { func (c *Client) GetStoragePrice() (int64, error) {
return c.invokeNativePolicyMethod("getStoragePrice") return c.invokeNativePolicyMethod("getStoragePrice")
} }
@ -46,6 +52,8 @@ func (c *Client) invokeNativeGetMethod(hash util.Uint160, operation string) (int
} }
// IsBlocked invokes `isBlocked` method on native Policy contract. // IsBlocked invokes `isBlocked` method on native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) IsBlocked(hash util.Uint160) (bool, error) { func (c *Client) IsBlocked(hash util.Uint160) (bool, error) {
policyHash, err := c.GetNativeContractHash(nativenames.Policy) policyHash, err := c.GetNativeContractHash(nativenames.Policy)
if err != nil { if err != nil {

View file

@ -0,0 +1,219 @@
/*
Package policy allows to work with the native PolicyContract contract via RPC.
Safe methods are encapsulated into ContractReader structure while Contract provides
various methods to perform PolicyContract state-changing calls.
*/
package policy
import (
"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/smartcontract"
"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)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Hash stores the hash of the native PolicyContract contract.
var Hash = state.CreateNativeContractHash(nativenames.Policy)
const (
execFeeSetter = "setExecFeeFactor"
feePerByteSetter = "setFeePerByte"
storagePriceSetter = "setStoragePrice"
)
// ContractReader provides an interface to call read-only PolicyContract
// contract's methods.
type ContractReader struct {
invoker Invoker
}
// Contract represents a PolicyContract contract client that can be used to
// invoke all of its methods.
type Contract struct {
ContractReader
actor Actor
}
// 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. Notice that PolicyContract's state can be changed
// only by the network's committee, so the Actor provided must be a committee
// actor for all methods to work properly.
func New(actor Actor) *Contract {
return &Contract{*NewReader(actor), actor}
}
// GetExecFeeFactor returns current execution fee factor used by the network.
// This setting affects all executions of all transactions.
func (c *ContractReader) GetExecFeeFactor() (int64, error) {
return unwrap.Int64(c.invoker.Call(Hash, "getExecFeeFactor"))
}
// GetFeePerByte returns current minimal per-byte network fee value which
// affects all transactions on the network.
func (c *ContractReader) GetFeePerByte() (int64, error) {
return unwrap.Int64(c.invoker.Call(Hash, "getFeePerByte"))
}
// GetStoragePrice returns current per-byte storage price. Any contract saving
// data to the storage pays for it according to this value.
func (c *ContractReader) GetStoragePrice() (int64, error) {
return unwrap.Int64(c.invoker.Call(Hash, "getStoragePrice"))
}
// IsBlocked checks if the given account is blocked in the PolicyContract.
func (c *ContractReader) IsBlocked(account util.Uint160) (bool, error) {
return unwrap.Bool(c.invoker.Call(Hash, "isBlocked", account))
}
// SetExecFeeFactor creates and sends a transaction that sets the new
// execution fee factor for the network to use. 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) SetExecFeeFactor(value int64) (util.Uint256, uint32, error) {
return c.actor.SendCall(Hash, execFeeSetter, value)
}
// SetExecFeeFactorTransaction creates a transaction that sets the new execution
// fee factor. This transaction is signed, but not sent to the network,
// instead it's returned to the caller.
func (c *Contract) SetExecFeeFactorTransaction(value int64) (*transaction.Transaction, error) {
return c.actor.MakeCall(Hash, execFeeSetter, value)
}
// SetExecFeeFactorUnsigned creates a transaction that sets the new execution
// fee factor. This transaction is not signed and just returned to the caller.
func (c *Contract) SetExecFeeFactorUnsigned(value int64) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(Hash, execFeeSetter, nil, value)
}
// SetFeePerByte creates and sends a transaction that sets the new minimal
// per-byte network fee value. 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) SetFeePerByte(value int64) (util.Uint256, uint32, error) {
return c.actor.SendCall(Hash, feePerByteSetter, value)
}
// SetFeePerByteTransaction creates a transaction that sets the new minimal
// per-byte network fee value. This transaction is signed, but not sent to the
// network, instead it's returned to the caller.
func (c *Contract) SetFeePerByteTransaction(value int64) (*transaction.Transaction, error) {
return c.actor.MakeCall(Hash, feePerByteSetter, value)
}
// SetFeePerByteUnsigned creates a transaction that sets the new minimal per-byte
// network fee value. This transaction is not signed and just returned to the
// caller.
func (c *Contract) SetFeePerByteUnsigned(value int64) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(Hash, feePerByteSetter, nil, value)
}
// SetStoragePrice creates and sends a transaction that sets the storage price
// for contracts. 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) SetStoragePrice(value int64) (util.Uint256, uint32, error) {
return c.actor.SendCall(Hash, storagePriceSetter, value)
}
// SetStoragePriceTransaction creates a transaction that sets the storage price
// for contracts. This transaction is signed, but not sent to the network,
// instead it's returned to the caller.
func (c *Contract) SetStoragePriceTransaction(value int64) (*transaction.Transaction, error) {
return c.actor.MakeCall(Hash, storagePriceSetter, value)
}
// SetStoragePriceUnsigned creates a transaction that sets the storage price
// for contracts. This transaction is not signed and just returned to the
// caller.
func (c *Contract) SetStoragePriceUnsigned(value int64) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(Hash, storagePriceSetter, nil, value)
}
// BlockAccount creates and sends a transaction that blocks an account on the
// network (via `blockAccount` method), it fails (with FAULT state) if it's not
// successful. The returned values are transaction hash, its
// ValidUntilBlock value and an error if any.
func (c *Contract) BlockAccount(account util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendRun(blockScript(account))
}
// BlockAccountTransaction creates a transaction that blocks an account on the
// network and checks for the result of the appropriate call, failing the
// transaction if it's not true. This transaction is signed, but not sent to the
// network, instead it's returned to the caller.
func (c *Contract) BlockAccountTransaction(account util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeRun(blockScript(account))
}
// BlockAccountUnsigned creates a transaction that blocks an account on the
// network and checks for the result of the appropriate call, failing the
// transaction if it's not true. This transaction is not signed and just returned
// to the caller.
func (c *Contract) BlockAccountUnsigned(account util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedRun(blockScript(account), nil)
}
func blockScript(account util.Uint160) []byte {
// We know parameters exactly (unlike with nep17.Transfer), so this can't fail.
script, _ := smartcontract.CreateCallWithAssertScript(Hash, "blockAccount", account)
return script
}
// UnblockAccount creates and sends a transaction that removes previously blocked
// account from the stop list. It uses `unblockAccount` method and checks for the
// result returned, failing the transaction if it's not true. The returned values
// are transaction hash, its ValidUntilBlock value and an error if any.
func (c *Contract) UnblockAccount(account util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendRun(unblockScript(account))
}
// UnblockAccountTransaction creates a transaction that unblocks previously
// blocked account via `unblockAccount` method and checks for the result returned,
// failing the transaction if it's not true. This transaction is signed, but not
// sent to the network, instead it's returned to the caller.
func (c *Contract) UnblockAccountTransaction(account util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeRun(unblockScript(account))
}
// UnblockAccountUnsigned creates a transaction that unblocks the given account
// if it was blocked previously. It uses `unblockAccount` method and checks for
// its return value, failing the transaction if it's not true. This transaction
// is not signed and just returned to the caller.
func (c *Contract) UnblockAccountUnsigned(account util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedRun(unblockScript(account), nil)
}
func unblockScript(account util.Uint160) []byte {
// We know parameters exactly (unlike with nep17.Transfer), so this can't fail.
script, _ := smartcontract.CreateCallWithAssertScript(Hash, "unblockAccount", account)
return script
}

View file

@ -0,0 +1,182 @@
package policy
import (
"errors"
"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 (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) {
return t.tx, t.err
}
func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) {
return t.tx, t.err
}
func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) {
return t.txh, t.vub, t.err
}
func TestReader(t *testing.T) {
ta := new(testAct)
pc := NewReader(ta)
meth := []func() (int64, error){
pc.GetExecFeeFactor,
pc.GetFeePerByte,
pc.GetStoragePrice,
}
ta.err = errors.New("")
for _, m := range meth {
_, err := m()
require.Error(t, err)
}
_, err := pc.IsBlocked(util.Uint160{1, 2, 3})
require.Error(t, err)
ta.err = nil
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make(42),
},
}
for _, m := range meth {
val, err := m()
require.NoError(t, err)
require.Equal(t, int64(42), val)
}
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make(true),
},
}
val, err := pc.IsBlocked(util.Uint160{1, 2, 3})
require.NoError(t, err)
require.True(t, val)
}
func TestIntSetters(t *testing.T) {
ta := new(testAct)
pc := New(ta)
meth := []func(int64) (util.Uint256, uint32, error){
pc.SetExecFeeFactor,
pc.SetFeePerByte,
pc.SetStoragePrice,
}
ta.err = errors.New("")
for _, m := range meth {
_, _, err := m(42)
require.Error(t, err)
}
ta.err = nil
ta.txh = util.Uint256{1, 2, 3}
ta.vub = 42
for _, m := range meth {
h, vub, err := m(100)
require.NoError(t, err)
require.Equal(t, ta.txh, h)
require.Equal(t, ta.vub, vub)
}
}
func TestUint160Setters(t *testing.T) {
ta := new(testAct)
pc := New(ta)
meth := []func(util.Uint160) (util.Uint256, uint32, error){
pc.BlockAccount,
pc.UnblockAccount,
}
ta.err = errors.New("")
for _, m := range meth {
_, _, err := m(util.Uint160{})
require.Error(t, err)
}
ta.err = nil
ta.txh = util.Uint256{1, 2, 3}
ta.vub = 42
for _, m := range meth {
h, vub, err := m(util.Uint160{})
require.NoError(t, err)
require.Equal(t, ta.txh, h)
require.Equal(t, ta.vub, vub)
}
}
func TestIntTransactions(t *testing.T) {
ta := new(testAct)
pc := New(ta)
for _, fun := range []func(int64) (*transaction.Transaction, error){
pc.SetExecFeeFactorTransaction,
pc.SetExecFeeFactorUnsigned,
pc.SetFeePerByteTransaction,
pc.SetFeePerByteUnsigned,
pc.SetStoragePriceTransaction,
pc.SetStoragePriceUnsigned,
} {
ta.err = errors.New("")
_, err := fun(1)
require.Error(t, err)
ta.err = nil
ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42}
tx, err := fun(1)
require.NoError(t, err)
require.Equal(t, ta.tx, tx)
}
}
func TestUint160Transactions(t *testing.T) {
ta := new(testAct)
pc := New(ta)
for _, fun := range []func(util.Uint160) (*transaction.Transaction, error){
pc.BlockAccountTransaction,
pc.BlockAccountUnsigned,
pc.UnblockAccountTransaction,
pc.UnblockAccountUnsigned,
} {
ta.err = errors.New("")
_, err := fun(util.Uint160{1})
require.Error(t, err)
ta.err = nil
ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42}
tx, err := fun(util.Uint160{1})
require.NoError(t, err)
require.Equal(t, ta.tx, tx)
}
}

View file

@ -0,0 +1,119 @@
/*
Package rolemgmt allows to work with the native RoleManagement contract via RPC.
Safe methods are encapsulated into ContractReader structure while Contract provides
various methods to perform the only RoleManagement state-changing call.
*/
package rolemgmt
import (
"crypto/elliptic"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/crypto/keys"
"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 RoleManagement contract.
var Hash = state.CreateNativeContractHash(nativenames.Designation)
const designateMethod = "designateAsRole"
// ContractReader provides an interface to call read-only RoleManagement
// contract's methods.
type ContractReader struct {
invoker Invoker
}
// Contract represents a RoleManagement contract client that can be used to
// invoke all of its methods.
type Contract struct {
ContractReader
actor Actor
}
// DesignationEvent represents an event emitted by RoleManagement contract when
// a new role designation is done.
type DesignationEvent struct {
Role noderoles.Role
BlockIndex uint32
}
// 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. Notice that RoleManagement's state can be changed
// only by the network's committee, so the Actor provided must be a committee
// actor for designation methods to work properly.
func New(actor Actor) *Contract {
return &Contract{*NewReader(actor), actor}
}
// GetDesignatedByRole returns the list of the keys designated to serve for the
// given role at the given height. The list can be empty if no keys are
// configured for this role/height.
func (c *ContractReader) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) {
arr, err := unwrap.Array(c.invoker.Call(Hash, "getDesignatedByRole", int64(role), index))
if err != nil {
return nil, err
}
pks := make(keys.PublicKeys, len(arr))
for i, item := range arr {
val, err := item.TryBytes()
if err != nil {
return nil, fmt.Errorf("invalid array element #%d: %s", i, item.Type())
}
pks[i], err = keys.NewPublicKeyFromBytes(val, elliptic.P256())
if err != nil {
return nil, err
}
}
return pks, nil
}
// DesignateAsRole creates and sends a transaction that sets the keys used for
// the given node role. 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) DesignateAsRole(role noderoles.Role, pubs keys.PublicKeys) (util.Uint256, uint32, error) {
return c.actor.SendCall(Hash, designateMethod, int(role), pubs)
}
// DesignateAsRoleTransaction creates a transaction that sets the keys for the
// given node role. This transaction is signed, but not sent to the network,
// instead it's returned to the caller.
func (c *Contract) DesignateAsRoleTransaction(role noderoles.Role, pubs keys.PublicKeys) (*transaction.Transaction, error) {
return c.actor.MakeCall(Hash, designateMethod, int(role), pubs)
}
// DesignateAsRoleUnsigned creates a transaction that sets the keys for the
// given node role. This transaction is not signed and just returned to the
// caller.
func (c *Contract) DesignateAsRoleUnsigned(role noderoles.Role, pubs keys.PublicKeys) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(Hash, designateMethod, nil, int(role), pubs)
}

View file

@ -0,0 +1,151 @@
package rolemgmt
import (
"errors"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"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 TestReaderGetDesignatedByRole(t *testing.T) {
ta := new(testAct)
rc := NewReader(ta)
ta.err = errors.New("")
_, err := rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.Error(t, err)
ta.err = nil
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make(100500),
},
}
_, err = rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.Error(t, err)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Null{},
},
}
_, err = rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.Error(t, err)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{}),
},
}
nodes, err := rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.NoError(t, err)
require.NotNil(t, nodes)
require.Equal(t, 0, len(nodes))
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{stackitem.Null{}}),
},
}
_, err = rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.Error(t, err)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{stackitem.Make(42)}),
},
}
_, err = rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.Error(t, err)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{stackitem.Make(k.PublicKey().Bytes())}),
},
}
nodes, err = rc.GetDesignatedByRole(noderoles.Oracle, 0)
require.NoError(t, err)
require.NotNil(t, nodes)
require.Equal(t, 1, len(nodes))
require.Equal(t, k.PublicKey(), nodes[0])
}
func TestDesignateAsRole(t *testing.T) {
ta := new(testAct)
rc := New(ta)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
ks := keys.PublicKeys{k.PublicKey()}
ta.err = errors.New("")
_, _, err = rc.DesignateAsRole(noderoles.Oracle, ks)
require.Error(t, err)
ta.err = nil
ta.txh = util.Uint256{1, 2, 3}
ta.vub = 42
h, vub, err := rc.DesignateAsRole(noderoles.Oracle, ks)
require.NoError(t, err)
require.Equal(t, ta.txh, h)
require.Equal(t, ta.vub, vub)
}
func TestDesignateAsRoleTransaction(t *testing.T) {
ta := new(testAct)
rc := New(ta)
k, err := keys.NewPrivateKey()
require.NoError(t, err)
ks := keys.PublicKeys{k.PublicKey()}
for _, fun := range []func(r noderoles.Role, pubs keys.PublicKeys) (*transaction.Transaction, error){
rc.DesignateAsRoleTransaction,
rc.DesignateAsRoleUnsigned,
} {
ta.err = errors.New("")
_, err := fun(noderoles.P2PNotary, ks)
require.Error(t, err)
ta.err = nil
ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42}
tx, err := fun(noderoles.P2PNotary, ks)
require.NoError(t, err)
require.Equal(t, ta.tx, tx)
}
}

View file

@ -20,7 +20,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -30,9 +30,12 @@ import (
"github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "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/nns"
"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" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
@ -90,6 +93,138 @@ func TestClient_NEP17(t *testing.T) {
}) })
} }
func TestClientRoleManagement(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
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)
height, err := c.GetBlockCount()
require.NoError(t, err)
rm := rolemgmt.New(act)
ks, err := rm.GetDesignatedByRole(noderoles.Oracle, height)
require.NoError(t, err)
require.Equal(t, 0, len(ks))
testKeys := keys.PublicKeys{
testchain.PrivateKeyByID(0).PublicKey(),
testchain.PrivateKeyByID(1).PublicKey(),
testchain.PrivateKeyByID(2).PublicKey(),
testchain.PrivateKeyByID(3).PublicKey(),
}
tx, err := rm.DesignateAsRoleUnsigned(noderoles.Oracle, testKeys)
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)
sort.Sort(testKeys)
ks, err = rm.GetDesignatedByRole(noderoles.Oracle, height+1)
require.NoError(t, err)
require.Equal(t, testKeys, ks)
}
func TestClientPolicyContract(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
polizei := policy.NewReader(invoker.New(c, nil))
val, err := polizei.GetExecFeeFactor()
require.NoError(t, err)
require.Equal(t, int64(30), val)
val, err = polizei.GetFeePerByte()
require.NoError(t, err)
require.Equal(t, int64(1000), val)
val, err = polizei.GetStoragePrice()
require.NoError(t, err)
require.Equal(t, int64(100000), val)
ret, err := polizei.IsBlocked(util.Uint160{})
require.NoError(t, err)
require.False(t, ret)
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)
polis := policy.New(act)
txexec, err := polis.SetExecFeeFactorUnsigned(100)
require.NoError(t, err)
txnetfee, err := polis.SetFeePerByteUnsigned(500)
require.NoError(t, err)
txstorage, err := polis.SetStoragePriceUnsigned(100500)
require.NoError(t, err)
txblock, err := polis.BlockAccountUnsigned(util.Uint160{1, 2, 3})
require.NoError(t, err)
for _, tx := range []*transaction.Transaction{txblock, txstorage, txnetfee, txexec} {
tx.Scripts[0].InvocationScript = testchain.SignCommittee(tx)
}
bl := testchain.NewBlock(t, chain, 1, 0, txblock, txstorage, txnetfee, txexec)
_, err = c.SubmitBlock(*bl)
require.NoError(t, err)
val, err = polizei.GetExecFeeFactor()
require.NoError(t, err)
require.Equal(t, int64(100), val)
val, err = polizei.GetFeePerByte()
require.NoError(t, err)
require.Equal(t, int64(500), val)
val, err = polizei.GetStoragePrice()
require.NoError(t, err)
require.Equal(t, int64(100500), val)
ret, err = polizei.IsBlocked(util.Uint160{1, 2, 3})
require.NoError(t, err)
require.True(t, ret)
}
func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) { func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close() defer chain.Close()
@ -727,14 +862,11 @@ func TestCreateNEP17TransferTx(t *testing.T) {
acc := wallet.NewAccountFromPrivateKey(priv) acc := wallet.NewAccountFromPrivateKey(priv)
addr := priv.PublicKey().GetScriptHash() addr := priv.PublicKey().GetScriptHash()
gasContractHash, err := c.GetNativeContractHash(nativenames.Gas)
require.NoError(t, err)
t.Run("default scope", func(t *testing.T) { t.Run("default scope", func(t *testing.T) {
act, err := actor.NewSimple(c, acc) act, err := actor.NewSimple(c, acc)
require.NoError(t, err) require.NoError(t, err)
gas := nep17.New(act, gasContractHash) gasprom := gas.New(act)
tx, err := gas.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) tx, err := gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx)) require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx)) require.NoError(t, chain.VerifyTx(tx))
@ -751,8 +883,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
Account: acc, Account: acc,
}}) }})
require.NoError(t, err) require.NoError(t, err)
gas := nep17.New(act, gasContractHash) gasprom := gas.New(act)
_, err = gas.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) _, err = gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil)
require.Error(t, err) require.Error(t, err)
}) })
t.Run("customcontracts scope", func(t *testing.T) { t.Run("customcontracts scope", func(t *testing.T) {
@ -760,13 +892,13 @@ func TestCreateNEP17TransferTx(t *testing.T) {
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: priv.PublicKey().GetScriptHash(), Account: priv.PublicKey().GetScriptHash(),
Scopes: transaction.CustomContracts, Scopes: transaction.CustomContracts,
AllowedContracts: []util.Uint160{gasContractHash}, AllowedContracts: []util.Uint160{gas.Hash},
}, },
Account: acc, Account: acc,
}}) }})
require.NoError(t, err) require.NoError(t, err)
gas := nep17.New(act, gasContractHash) gasprom := gas.New(act)
tx, err := gas.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil) tx, err := gasprom.TransferUnsigned(addr, util.Uint160{}, big.NewInt(1000), nil)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx)) require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx)) require.NoError(t, chain.VerifyTx(tx))