rpcclient: add notary.Actor for seamless notary experience
This commit is contained in:
parent
ac5c609063
commit
07f3023e84
7 changed files with 972 additions and 7 deletions
59
pkg/rpcclient/notary/accounts.go
Normal file
59
pkg/rpcclient/notary/accounts.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package notary
|
||||
|
||||
import (
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
// FakeSimpleAccount creates a fake account belonging to the given public key.
|
||||
// It uses a simple signature contract and this account has SignTx that
|
||||
// returns no error, but at the same time adds no signature (it obviously can't
|
||||
// do that, so CanSign() returns false for it). Use this account for Actor when
|
||||
// simple signatures are needed to be collected.
|
||||
func FakeSimpleAccount(k *keys.PublicKey) *wallet.Account {
|
||||
return &wallet.Account{
|
||||
Address: k.Address(),
|
||||
Contract: &wallet.Contract{
|
||||
Script: k.GetVerificationScript(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FakeMultisigAccount creates a fake account belonging to the given "m out of
|
||||
// len(pkeys)" account for the given set of keys. The account returned has SignTx
|
||||
// that returns no error, but at the same time adds no signatures (it can't
|
||||
// do that, so CanSign() returns false for it). Use this account for Actor when
|
||||
// multisignature account needs to be added into a notary transaction, but you
|
||||
// have no keys at all for it (if you have at least one (which usually is the
|
||||
// case) ordinary multisig account works fine already).
|
||||
func FakeMultisigAccount(m int, pkeys keys.PublicKeys) (*wallet.Account, error) {
|
||||
script, err := smartcontract.CreateMultiSigRedeemScript(m, pkeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &wallet.Account{
|
||||
Address: address.Uint160ToString(hash.Hash160(script)),
|
||||
Contract: &wallet.Contract{
|
||||
Script: script,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FakeContractAccount creates a fake account belonging to some deployed contract.
|
||||
// SignTx can be called on this account with no error, but at the same time it
|
||||
// adds no signature or other data into the invocation script (it obviously can't
|
||||
// do that, so CanSign() returns false for it). Use this account for Actor when
|
||||
// one of the signers is a contract and it doesn't need a signature or you can
|
||||
// provide it externally.
|
||||
func FakeContractAccount(hash util.Uint160) *wallet.Account {
|
||||
return &wallet.Account{
|
||||
Address: address.Uint160ToString(hash),
|
||||
Contract: &wallet.Contract{
|
||||
Deployed: true,
|
||||
},
|
||||
}
|
||||
}
|
36
pkg/rpcclient/notary/accounts_test.go
Normal file
36
pkg/rpcclient/notary/accounts_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package notary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFakeAccounts(t *testing.T) {
|
||||
k, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
fac := FakeSimpleAccount(k.PublicKey())
|
||||
require.False(t, fac.CanSign())
|
||||
|
||||
sh := k.PublicKey().GetScriptHash()
|
||||
tx := transaction.New([]byte{1, 2, 3}, 1)
|
||||
tx.Signers = append(tx.Signers, transaction.Signer{Account: sh})
|
||||
require.NoError(t, fac.SignTx(0, tx))
|
||||
|
||||
fac = FakeContractAccount(sh)
|
||||
require.False(t, fac.CanSign())
|
||||
require.NoError(t, fac.SignTx(0, tx))
|
||||
|
||||
_, err = FakeMultisigAccount(0, keys.PublicKeys{k.PublicKey()})
|
||||
require.Error(t, err)
|
||||
|
||||
fac, err = FakeMultisigAccount(1, keys.PublicKeys{k.PublicKey()})
|
||||
require.NoError(t, err)
|
||||
require.False(t, fac.CanSign())
|
||||
tx.Signers[0].Account = hash.Hash160(fac.Contract.Script)
|
||||
require.NoError(t, fac.SignTx(0, tx))
|
||||
}
|
314
pkg/rpcclient/notary/actor.go
Normal file
314
pkg/rpcclient/notary/actor.go
Normal file
|
@ -0,0 +1,314 @@
|
|||
package notary
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
// Actor encapsulates everything needed to create proper notary requests for
|
||||
// assisted transactions.
|
||||
type Actor struct {
|
||||
// Actor is the main transaction actor, it has appropriate attributes and
|
||||
// transaction modifiers to set ValidUntilBlock. Use it to create main
|
||||
// transactions that have incomplete set of signatures. They can be
|
||||
// signed (using available wallets), but can not be sent directly to the
|
||||
// network. Instead of sending them to the network use Actor methods to
|
||||
// wrap them into notary requests.
|
||||
actor.Actor
|
||||
// FbActor is the fallback transaction actor, it has two required signers
|
||||
// and a set of attributes expected from a fallback transaction. It can
|
||||
// be used to create _unsigned_ transactions with whatever actions
|
||||
// required (but no additional attributes can be added). Signing them
|
||||
// while technically possible (with notary contract signature missing),
|
||||
// will lead to incorrect transaction because NotValidBefore and
|
||||
// Conflicts attributes as well as ValidUntilBlock field can be
|
||||
// correctly set only when some main transaction is available.
|
||||
FbActor actor.Actor
|
||||
|
||||
fbScript []byte
|
||||
reader *ContractReader
|
||||
sender *wallet.Account
|
||||
rpc RPCActor
|
||||
}
|
||||
|
||||
// ActorOptions are used to influence main and fallback actors as well as the
|
||||
// default Notarize behavior.
|
||||
type ActorOptions struct {
|
||||
// FbAttributes are additional attributes to be added into fallback
|
||||
// transaction by an appropriate actor. Irrespective of this setting
|
||||
// (which defaults to nil) NotaryAssisted, NotValidBefore and Conflicts
|
||||
// attributes are always added.
|
||||
FbAttributes []transaction.Attribute
|
||||
// FbScript is the script to use in the Notarize convenience method, it
|
||||
// defaults to a simple RET instruction (doing nothing).
|
||||
FbScript []byte
|
||||
// FbSigner is the second signer to be used for the fallback transaction.
|
||||
// By default it's derived from the account and has None scope, it has
|
||||
// to be a simple signature or deployed contract account, but this setting
|
||||
// allows you to give it some other scope to be used in complex fallback
|
||||
// scripts.
|
||||
FbSigner actor.SignerAccount
|
||||
// MainAttribtues are additional attributes to be added into main
|
||||
// transaction by an appropriate actor. Irrespective of this setting
|
||||
// (which defaults to nil) NotaryAssisted attribute is always added.
|
||||
MainAttributes []transaction.Attribute
|
||||
// MainCheckerModifier will be used by the main Actor when creating
|
||||
// transactions. It defaults to using [actor.DefaultCheckerModifier]
|
||||
// for result check and adds MaxNotValidBeforeDelta to the
|
||||
// ValidUntilBlock transaction's field. Only override it if you know
|
||||
// what you're doing.
|
||||
MainCheckerModifier actor.TransactionCheckerModifier
|
||||
// MainModifier will be used by the main Actor when creating
|
||||
// transactions. By default it adds MaxNotValidBeforeDelta to the
|
||||
// ValidUntilBlock transaction's field. Only override it if you know
|
||||
// what you're doing.
|
||||
MainModifier actor.TransactionModifier
|
||||
}
|
||||
|
||||
// RPCActor is a set of methods required from RPC client to create Actor.
|
||||
type RPCActor interface {
|
||||
actor.RPCActor
|
||||
|
||||
SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error)
|
||||
}
|
||||
|
||||
// NewDefaultActorOptions returns the default Actor options. Internal functions
|
||||
// of it need some data from the contract, so it should be added.
|
||||
func NewDefaultActorOptions(reader *ContractReader, acc *wallet.Account) ActorOptions {
|
||||
opts := ActorOptions{
|
||||
FbScript: []byte{byte(opcode.RET)},
|
||||
FbSigner: actor.SignerAccount{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc.Contract.ScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: acc,
|
||||
},
|
||||
MainModifier: func(t *transaction.Transaction) error {
|
||||
nvbDelta, err := reader.GetMaxNotValidBeforeDelta()
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get MaxNVBDelta: %w", err)
|
||||
}
|
||||
t.ValidUntilBlock += nvbDelta
|
||||
return nil
|
||||
},
|
||||
}
|
||||
opts.MainCheckerModifier = func(r *result.Invoke, t *transaction.Transaction) error {
|
||||
err := actor.DefaultCheckerModifier(r, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return opts.MainModifier(t)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// NewActor creates a new notary.Actor using the given RPC client, the set of
|
||||
// signers for main transactions and the account that will sign notary requests
|
||||
// (one plain signature or contract-based). The set of signers will be extended
|
||||
// by the notary contract signer with the None scope (as required by the notary
|
||||
// protocol) and all transactions created with the resulting Actor will get a
|
||||
// NotaryAssisted attribute with appropriate number of keys specified
|
||||
// (depending on signers). A fallback Actor will be created as well with the
|
||||
// notary contract and simpleAcc signers and a full set of required fallback
|
||||
// transaction attributes (NotaryAssisted, NotValidBefore and Conflicts).
|
||||
func NewActor(c RPCActor, signers []actor.SignerAccount, simpleAcc *wallet.Account) (*Actor, error) {
|
||||
return newTunedActor(c, signers, simpleAcc, nil)
|
||||
}
|
||||
|
||||
// NewTunedActor is the same as NewActor, but allows to override the default
|
||||
// options (see ActorOptions for details). Use with care.
|
||||
func NewTunedActor(c RPCActor, signers []actor.SignerAccount, opts ActorOptions) (*Actor, error) {
|
||||
return newTunedActor(c, signers, opts.FbSigner.Account, &opts)
|
||||
}
|
||||
|
||||
func newTunedActor(c RPCActor, signers []actor.SignerAccount, simpleAcc *wallet.Account, opts *ActorOptions) (*Actor, error) {
|
||||
if len(signers) < 1 {
|
||||
return nil, errors.New("at least one signer (sender) is required")
|
||||
}
|
||||
var nKeys int
|
||||
for _, sa := range signers {
|
||||
if sa.Account.Contract == nil {
|
||||
return nil, fmt.Errorf("empty contract for account %s", sa.Account.Address)
|
||||
}
|
||||
if sa.Account.Contract.Deployed {
|
||||
continue
|
||||
}
|
||||
if vm.IsSignatureContract(sa.Account.Contract.Script) {
|
||||
nKeys++
|
||||
continue
|
||||
}
|
||||
_, pubs, ok := vm.ParseMultiSigContract(sa.Account.Contract.Script)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("signer %s is not a contract- or signature-based", sa.Account.Address)
|
||||
}
|
||||
nKeys += len(pubs)
|
||||
}
|
||||
if nKeys > 255 {
|
||||
return nil, fmt.Errorf("notary subsystem can't handle more than 255 signatures")
|
||||
}
|
||||
if simpleAcc.Contract == nil {
|
||||
return nil, errors.New("bad simple account: no contract")
|
||||
}
|
||||
if !simpleAcc.CanSign() {
|
||||
return nil, errors.New("bad simple account: can't sign")
|
||||
}
|
||||
if !vm.IsSignatureContract(simpleAcc.Contract.Script) && !simpleAcc.Contract.Deployed {
|
||||
return nil, errors.New("bad simple account: neither plain signature, nor contract")
|
||||
}
|
||||
// Not reusing mainActor/fbActor for ContractReader to make requests a bit lighter.
|
||||
reader := NewReader(invoker.New(c, nil))
|
||||
if opts == nil {
|
||||
defOpts := NewDefaultActorOptions(reader, simpleAcc)
|
||||
opts = &defOpts
|
||||
}
|
||||
var notarySA = actor.SignerAccount{
|
||||
Signer: transaction.Signer{
|
||||
Account: Hash,
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: FakeContractAccount(Hash),
|
||||
}
|
||||
|
||||
var mainSigners = make([]actor.SignerAccount, len(signers), len(signers)+1)
|
||||
copy(mainSigners, signers)
|
||||
mainSigners = append(mainSigners, notarySA)
|
||||
|
||||
mainOpts := actor.Options{
|
||||
Attributes: []transaction.Attribute{{
|
||||
Type: transaction.NotaryAssistedT,
|
||||
Value: &transaction.NotaryAssisted{NKeys: uint8(nKeys)},
|
||||
}},
|
||||
CheckerModifier: opts.MainCheckerModifier,
|
||||
Modifier: opts.MainModifier,
|
||||
}
|
||||
mainOpts.Attributes = append(mainOpts.Attributes, opts.MainAttributes...)
|
||||
|
||||
mainActor, err := actor.NewTuned(c, mainSigners, mainOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fbSigners := []actor.SignerAccount{notarySA, opts.FbSigner}
|
||||
fbOpts := actor.Options{
|
||||
Attributes: []transaction.Attribute{{
|
||||
Type: transaction.NotaryAssistedT,
|
||||
Value: &transaction.NotaryAssisted{NKeys: 0},
|
||||
}, {
|
||||
// A stub, it has correct size, but the contents is to be filled per-request.
|
||||
Type: transaction.NotValidBeforeT,
|
||||
Value: &transaction.NotValidBefore{},
|
||||
}, {
|
||||
// A stub, it has correct size, but the contents is to be filled per-request.
|
||||
Type: transaction.ConflictsT,
|
||||
Value: &transaction.Conflicts{},
|
||||
}},
|
||||
}
|
||||
fbOpts.Attributes = append(fbOpts.Attributes, opts.FbAttributes...)
|
||||
fbActor, err := actor.NewTuned(c, fbSigners, fbOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Actor{*mainActor, *fbActor, opts.FbScript, reader, simpleAcc, c}, nil
|
||||
}
|
||||
|
||||
// Notarize is a simple wrapper for transaction-creating functions that allows to
|
||||
// send any partially-signed transaction in a notary request with a fallback
|
||||
// transaction created based on Actor settings and SendRequest adjustment rules.
|
||||
// The values returned are main and fallback transaction hashes, ValidUntilBlock
|
||||
// and error if any.
|
||||
func (a *Actor) Notarize(mainTx *transaction.Transaction, err error) (util.Uint256, util.Uint256, uint32, error) {
|
||||
var (
|
||||
// Just to simplify return values on error.
|
||||
fbHash util.Uint256
|
||||
mainHash util.Uint256
|
||||
vub uint32
|
||||
)
|
||||
if err != nil {
|
||||
return mainHash, fbHash, vub, err
|
||||
}
|
||||
fbTx, err := a.FbActor.MakeUnsignedRun(a.fbScript, nil)
|
||||
if err != nil {
|
||||
return mainHash, fbHash, vub, err
|
||||
}
|
||||
return a.SendRequest(mainTx, fbTx)
|
||||
}
|
||||
|
||||
// SendRequest creates and sends a notary request using the given main and
|
||||
// fallback transactions. It accepts signed main transaction and unsigned fallback
|
||||
// transaction that will be adjusted in its NotValidBefore and Conflicts
|
||||
// attributes as well as ValidUntilBlock value. Conflicts is set to the main
|
||||
// transaction hash, while NotValidBefore is set to the middle of current mainTx
|
||||
// lifetime (between current block and ValidUntilBlock). The values returned are
|
||||
// main and fallback transaction hashes, ValidUntilBlock and error if any.
|
||||
func (a *Actor) SendRequest(mainTx *transaction.Transaction, fbTx *transaction.Transaction) (util.Uint256, util.Uint256, uint32, error) {
|
||||
var (
|
||||
fbHash util.Uint256
|
||||
mainHash = mainTx.Hash()
|
||||
vub = mainTx.ValidUntilBlock
|
||||
)
|
||||
if len(fbTx.Attributes) < 3 {
|
||||
return mainHash, fbHash, vub, errors.New("invalid fallback: missing required attributes")
|
||||
}
|
||||
if fbTx.Attributes[1].Type != transaction.NotValidBeforeT {
|
||||
return mainHash, fbHash, vub, errors.New("invalid fallback: NotValidBefore is missing where expected")
|
||||
}
|
||||
if fbTx.Attributes[2].Type != transaction.ConflictsT {
|
||||
return mainHash, fbHash, vub, errors.New("invalid fallback: Conflicts is missing where expected")
|
||||
}
|
||||
height, err := a.GetBlockCount()
|
||||
if err != nil {
|
||||
return mainHash, fbHash, vub, err
|
||||
}
|
||||
// New values must be created to avoid overwriting originals via a pointer.
|
||||
fbTx.Attributes[1].Value = &transaction.NotValidBefore{Height: (height + vub) / 2}
|
||||
fbTx.Attributes[2].Value = &transaction.Conflicts{Hash: mainHash}
|
||||
fbTx.ValidUntilBlock = vub
|
||||
err = a.FbActor.Sign(fbTx)
|
||||
if err != nil {
|
||||
return mainHash, fbHash, vub, err
|
||||
}
|
||||
fbTx.Scripts[0].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...) // Must be present.
|
||||
return a.SendRequestExactly(mainTx, fbTx)
|
||||
}
|
||||
|
||||
// SendRequestExactly accepts signed and completely prepared main and fallback
|
||||
// transactions, creates a P2P notary request containing them, signs and sends
|
||||
// it to the network. Caller takes full responsibility for transaction
|
||||
// correctness in this case, use this method only if you know exactly that you
|
||||
// need to override some of the other method's behavior and you can do it. The
|
||||
// values returned are main and fallback transaction hashes, ValidUntilBlock
|
||||
// and error if any.
|
||||
func (a *Actor) SendRequestExactly(mainTx *transaction.Transaction, fbTx *transaction.Transaction) (util.Uint256, util.Uint256, uint32, error) {
|
||||
var (
|
||||
fbHash = fbTx.Hash()
|
||||
mainHash = mainTx.Hash()
|
||||
vub = mainTx.ValidUntilBlock
|
||||
)
|
||||
req := &payload.P2PNotaryRequest{
|
||||
MainTransaction: mainTx,
|
||||
FallbackTransaction: fbTx,
|
||||
}
|
||||
req.Witness = transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, a.sender.PrivateKey().SignHashable(uint32(a.GetNetwork()), req)...),
|
||||
VerificationScript: a.sender.GetVerificationScript(),
|
||||
}
|
||||
actualHash, err := a.rpc.SubmitP2PNotaryRequest(req)
|
||||
if err != nil {
|
||||
return mainHash, fbHash, vub, fmt.Errorf("failed to submit notary request: %w", err)
|
||||
}
|
||||
if !actualHash.Equals(fbHash) {
|
||||
return mainHash, fbHash, vub, fmt.Errorf("sent and actual fallback tx hashes mismatch: %v vs %v", fbHash.StringLE(), actualHash.StringLE())
|
||||
}
|
||||
return mainHash, fbHash, vub, nil
|
||||
}
|
522
pkg/rpcclient/notary/actor_test.go
Normal file
522
pkg/rpcclient/notary/actor_test.go
Normal file
|
@ -0,0 +1,522 @@
|
|||
package notary
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type RPCClient struct {
|
||||
err error
|
||||
invRes *result.Invoke
|
||||
netFee int64
|
||||
bCount uint32
|
||||
version *result.Version
|
||||
hash util.Uint256
|
||||
nhash util.Uint256
|
||||
mirror bool
|
||||
}
|
||||
|
||||
func (r *RPCClient) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) {
|
||||
return r.netFee, r.err
|
||||
}
|
||||
func (r *RPCClient) GetBlockCount() (uint32, error) {
|
||||
return r.bCount, r.err
|
||||
}
|
||||
func (r *RPCClient) GetVersion() (*result.Version, error) {
|
||||
verCopy := *r.version
|
||||
return &verCopy, r.err
|
||||
}
|
||||
func (r *RPCClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) {
|
||||
return r.hash, r.err
|
||||
}
|
||||
func (r *RPCClient) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error) {
|
||||
if r.mirror {
|
||||
return req.FallbackTransaction.Hash(), nil
|
||||
}
|
||||
return r.nhash, r.err
|
||||
}
|
||||
func (r *RPCClient) TerminateSession(sessionID uuid.UUID) (bool, error) {
|
||||
return false, nil // Just a stub, unused by actor.
|
||||
}
|
||||
func (r *RPCClient) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount int) ([]stackitem.Item, error) {
|
||||
return nil, nil // Just a stub, unused by actor.
|
||||
}
|
||||
|
||||
func TestNewActor(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
version: &result.Version{
|
||||
Protocol: result.Protocol{
|
||||
Network: netmode.UnitTestNet,
|
||||
MillisecondsPerBlock: 1000,
|
||||
ValidatorsCount: 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := NewActor(rc, nil, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
var (
|
||||
keyz [4]*keys.PrivateKey
|
||||
accs [4]*wallet.Account
|
||||
faccs [4]*wallet.Account
|
||||
pkeys [4]*keys.PublicKey
|
||||
)
|
||||
for i := range accs {
|
||||
keyz[i], err = keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
accs[i] = wallet.NewAccountFromPrivateKey(keyz[i])
|
||||
pkeys[i] = keyz[i].PublicKey()
|
||||
faccs[i] = FakeSimpleAccount(pkeys[i])
|
||||
}
|
||||
var multiAccs [4]*wallet.Account
|
||||
for i := range accs {
|
||||
multiAccs[i] = &wallet.Account{}
|
||||
*multiAccs[i] = *accs[i]
|
||||
require.NoError(t, multiAccs[i].ConvertMultisig(smartcontract.GetDefaultHonestNodeCount(len(pkeys)), pkeys[:]))
|
||||
}
|
||||
|
||||
// nil Contract
|
||||
badMultiAcc0 := &wallet.Account{}
|
||||
*badMultiAcc0 = *multiAccs[0]
|
||||
badMultiAcc0.Contract = nil
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: badMultiAcc0,
|
||||
}}, accs[0])
|
||||
require.Error(t, err)
|
||||
|
||||
// Non-standard script.
|
||||
badMultiAcc0.Contract = &wallet.Contract{}
|
||||
*badMultiAcc0.Contract = *multiAccs[0].Contract
|
||||
badMultiAcc0.Contract.Script = append(badMultiAcc0.Contract.Script, byte(opcode.NOP))
|
||||
badMultiAcc0.Address = address.Uint160ToString(badMultiAcc0.Contract.ScriptHash())
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: badMultiAcc0.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: badMultiAcc0,
|
||||
}}, accs[0])
|
||||
require.Error(t, err)
|
||||
|
||||
// Too many keys
|
||||
var (
|
||||
manyKeys [256]*keys.PrivateKey
|
||||
manyPkeys [256]*keys.PublicKey
|
||||
)
|
||||
for i := range manyKeys {
|
||||
manyKeys[i], err = keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
manyPkeys[i] = manyKeys[i].PublicKey()
|
||||
}
|
||||
bigMultiAcc := &wallet.Account{}
|
||||
*bigMultiAcc = *wallet.NewAccountFromPrivateKey(manyKeys[0])
|
||||
require.NoError(t, bigMultiAcc.ConvertMultisig(129, manyPkeys[:]))
|
||||
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: bigMultiAcc.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: bigMultiAcc,
|
||||
}}, wallet.NewAccountFromPrivateKey(manyKeys[0]))
|
||||
require.Error(t, err)
|
||||
|
||||
// No contract in the simple account.
|
||||
badSimple0 := &wallet.Account{}
|
||||
*badSimple0 = *accs[0]
|
||||
badSimple0.Contract = nil
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, badSimple0)
|
||||
require.Error(t, err)
|
||||
|
||||
// Simple account that can't sign.
|
||||
badSimple0 = FakeSimpleAccount(pkeys[0])
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, badSimple0)
|
||||
require.Error(t, err)
|
||||
|
||||
// Multisig account instead of simple one.
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, multiAccs[0])
|
||||
require.Error(t, err)
|
||||
|
||||
// Main actor freaking out on hash mismatch.
|
||||
_, err = NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: accs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, accs[0])
|
||||
require.Error(t, err)
|
||||
|
||||
// FB actor freaking out on hash mismatch.
|
||||
opts := NewDefaultActorOptions(NewReader(invoker.New(rc, nil)), accs[0])
|
||||
opts.FbSigner.Signer.Account = multiAccs[0].Contract.ScriptHash()
|
||||
_, err = NewTunedActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, opts)
|
||||
require.Error(t, err)
|
||||
|
||||
// Good, one multisig.
|
||||
multi0, err := NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAccs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAccs[0],
|
||||
}}, accs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
script := []byte{byte(opcode.RET)}
|
||||
rc.invRes = &result.Invoke{
|
||||
State: "HALT",
|
||||
GasConsumed: 3,
|
||||
Script: script,
|
||||
Stack: []stackitem.Item{stackitem.Make(42)},
|
||||
}
|
||||
tx, err := multi0.MakeRun(script)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(tx.Attributes))
|
||||
require.Equal(t, transaction.NotaryAssistedT, tx.Attributes[0].Type)
|
||||
require.Equal(t, &transaction.NotaryAssisted{NKeys: 4}, tx.Attributes[0].Value)
|
||||
|
||||
// Good, 4 single sigs with one that can sign and one contract.
|
||||
single4, err := NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: accs[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: accs[0],
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: faccs[1].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: faccs[1],
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: faccs[2].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: faccs[2],
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: accs[3].Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: faccs[3],
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: util.Uint160{1, 2, 3},
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: FakeContractAccount(util.Uint160{1, 2, 3}),
|
||||
}}, accs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
tx, err = single4.MakeRun(script)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(tx.Attributes))
|
||||
require.Equal(t, transaction.NotaryAssistedT, tx.Attributes[0].Type)
|
||||
require.Equal(t, &transaction.NotaryAssisted{NKeys: 4}, tx.Attributes[0].Value) // One account can sign, three need to collect additional sigs.
|
||||
}
|
||||
|
||||
func TestSendRequestExactly(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
version: &result.Version{
|
||||
Protocol: result.Protocol{
|
||||
Network: netmode.UnitTestNet,
|
||||
MillisecondsPerBlock: 1000,
|
||||
ValidatorsCount: 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
key0, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
key1, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
acc0 := wallet.NewAccountFromPrivateKey(key0)
|
||||
facc1 := FakeSimpleAccount(key1.PublicKey())
|
||||
|
||||
act, err := NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc0.Contract.ScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: acc0,
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: facc1.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: facc1,
|
||||
}}, acc0)
|
||||
require.NoError(t, err)
|
||||
|
||||
script := []byte{byte(opcode.RET)}
|
||||
mainTx := transaction.New(script, 1)
|
||||
fbTx := transaction.New(script, 1)
|
||||
|
||||
// Hashes mismatch
|
||||
_, _, _, err = act.SendRequestExactly(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// Error returned
|
||||
rc.err = errors.New("")
|
||||
_, _, _, err = act.SendRequestExactly(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// OK returned
|
||||
rc.err = nil
|
||||
rc.nhash = fbTx.Hash()
|
||||
mHash, fbHash, vub, err := act.SendRequestExactly(mainTx, fbTx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, mainTx.Hash(), mHash)
|
||||
require.Equal(t, fbTx.Hash(), fbHash)
|
||||
require.Equal(t, mainTx.ValidUntilBlock, vub)
|
||||
}
|
||||
|
||||
func TestSendRequest(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
version: &result.Version{
|
||||
Protocol: result.Protocol{
|
||||
Network: netmode.UnitTestNet,
|
||||
MillisecondsPerBlock: 1000,
|
||||
ValidatorsCount: 7,
|
||||
},
|
||||
},
|
||||
bCount: 42,
|
||||
}
|
||||
|
||||
key0, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
key1, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
acc0 := wallet.NewAccountFromPrivateKey(key0)
|
||||
facc0 := FakeSimpleAccount(key0.PublicKey())
|
||||
facc1 := FakeSimpleAccount(key1.PublicKey())
|
||||
|
||||
act, err := NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc0.Contract.ScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: acc0,
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: facc1.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: facc1,
|
||||
}}, acc0)
|
||||
require.NoError(t, err)
|
||||
|
||||
script := []byte{byte(opcode.RET)}
|
||||
rc.invRes = &result.Invoke{
|
||||
State: "HALT",
|
||||
GasConsumed: 3,
|
||||
Script: script,
|
||||
Stack: []stackitem.Item{stackitem.Make(42)},
|
||||
}
|
||||
|
||||
mainTx, err := act.MakeRun(script)
|
||||
require.NoError(t, err)
|
||||
|
||||
// No attributes.
|
||||
fbTx, err := act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
fbTx.Attributes = nil
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// Bad NVB.
|
||||
fbTx, err = act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
fbTx.Attributes[1].Type = transaction.HighPriority
|
||||
fbTx.Attributes[1].Value = nil
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// Bad Conflicts.
|
||||
fbTx, err = act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
fbTx.Attributes[2].Type = transaction.HighPriority
|
||||
fbTx.Attributes[2].Value = nil
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// GetBlockCount error.
|
||||
fbTx, err = act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
rc.err = errors.New("")
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// Can't sign suddenly.
|
||||
rc.err = nil
|
||||
acc0Backup := &wallet.Account{}
|
||||
*acc0Backup = *acc0
|
||||
*acc0 = *facc0
|
||||
fbTx, err = act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
|
||||
// Good.
|
||||
*acc0 = *acc0Backup
|
||||
fbTx, err = act.FbActor.MakeUnsignedRun(script, nil)
|
||||
require.NoError(t, err)
|
||||
_, _, _, err = act.SendRequest(mainTx, fbTx)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNotarize(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
version: &result.Version{
|
||||
Protocol: result.Protocol{
|
||||
Network: netmode.UnitTestNet,
|
||||
MillisecondsPerBlock: 1000,
|
||||
ValidatorsCount: 7,
|
||||
},
|
||||
},
|
||||
bCount: 42,
|
||||
}
|
||||
|
||||
key0, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
key1, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
acc0 := wallet.NewAccountFromPrivateKey(key0)
|
||||
facc1 := FakeSimpleAccount(key1.PublicKey())
|
||||
|
||||
act, err := NewActor(rc, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc0.Contract.ScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: acc0,
|
||||
}, {
|
||||
Signer: transaction.Signer{
|
||||
Account: facc1.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: facc1,
|
||||
}}, acc0)
|
||||
require.NoError(t, err)
|
||||
|
||||
script := []byte{byte(opcode.RET)}
|
||||
|
||||
// Immediate error from MakeRun.
|
||||
rc.invRes = &result.Invoke{
|
||||
State: "FAULT",
|
||||
GasConsumed: 3,
|
||||
Script: script,
|
||||
Stack: []stackitem.Item{stackitem.Make(42)},
|
||||
}
|
||||
_, _, _, err = act.Notarize(act.MakeRun(script))
|
||||
require.Error(t, err)
|
||||
|
||||
// Explicitly good transaction. but failure to create a fallback.
|
||||
rc.invRes.State = "HALT"
|
||||
tx, err := act.MakeRun(script)
|
||||
require.NoError(t, err)
|
||||
|
||||
rc.invRes.State = "FAULT"
|
||||
_, _, _, err = act.Notarize(tx, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
// FB hash mismatch from SendRequestExactly.
|
||||
rc.invRes.State = "HALT"
|
||||
_, _, _, err = act.Notarize(act.MakeRun(script))
|
||||
require.Error(t, err)
|
||||
|
||||
// Good.
|
||||
rc.mirror = true
|
||||
mHash, fbHash, vub, err := act.Notarize(act.MakeRun(script))
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, util.Uint256{}, mHash)
|
||||
require.NotEqual(t, util.Uint256{}, fbHash)
|
||||
require.Equal(t, uint32(92), vub)
|
||||
}
|
||||
|
||||
func TestDefaultActorOptions(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
version: &result.Version{
|
||||
Protocol: result.Protocol{
|
||||
Network: netmode.UnitTestNet,
|
||||
MillisecondsPerBlock: 1000,
|
||||
ValidatorsCount: 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
opts := NewDefaultActorOptions(NewReader(invoker.New(rc, nil)), acc)
|
||||
rc.invRes = &result.Invoke{
|
||||
State: "HALT",
|
||||
GasConsumed: 3,
|
||||
Script: opts.FbScript,
|
||||
Stack: []stackitem.Item{stackitem.Make(42)},
|
||||
}
|
||||
tx := transaction.New(opts.FbScript, 1)
|
||||
require.Error(t, opts.MainCheckerModifier(&result.Invoke{State: "FAULT"}, tx))
|
||||
rc.invRes.State = "FAULT"
|
||||
require.Error(t, opts.MainCheckerModifier(&result.Invoke{State: "HALT"}, tx))
|
||||
rc.invRes.State = "HALT"
|
||||
require.NoError(t, opts.MainCheckerModifier(&result.Invoke{State: "HALT"}, tx))
|
||||
require.Equal(t, uint32(42), tx.ValidUntilBlock)
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
Package notary provides an RPC-based wrapper for the Notary subsystem.
|
||||
|
||||
It provides both regular ContractReader/Contract interfaces for the notary
|
||||
contract and notary-specific functions and interfaces to simplify creation of
|
||||
notary requests.
|
||||
contract and notary-specific Actor as well as some helper functions to simplify
|
||||
creation of notary requests.
|
||||
*/
|
||||
package notary
|
||||
|
||||
|
|
|
@ -866,6 +866,9 @@ func getSigners(sender *wallet.Account, cosigners []SignerAccount) ([]transactio
|
|||
// can be multisignature), or it only should have a partial multisignature.
|
||||
//
|
||||
// Note: client should be initialized before SignAndPushP2PNotaryRequest call.
|
||||
//
|
||||
// Deprecated: please use Actor from the notary subpackage. This method will be
|
||||
// deleted in future versions.
|
||||
func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fallbackScript []byte, fallbackSysFee int64, fallbackNetFee int64, fallbackValidFor uint32, acc *wallet.Account) (*payload.P2PNotaryRequest, error) {
|
||||
var err error
|
||||
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
|
||||
|
|
|
@ -985,6 +985,37 @@ func TestSignAndPushInvocationTx(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestNotaryActor(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
|
||||
require.NoError(t, err)
|
||||
|
||||
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
||||
acc := wallet.NewAccountFromPrivateKey(sender)
|
||||
|
||||
comm, err := c.GetCommittee()
|
||||
require.NoError(t, err)
|
||||
|
||||
multiAcc := &wallet.Account{}
|
||||
*multiAcc = *acc
|
||||
require.NoError(t, multiAcc.ConvertMultisig(smartcontract.GetMajorityHonestNodeCount(len(comm)), comm))
|
||||
|
||||
nact, err := notary.NewActor(c, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiAcc.Contract.ScriptHash(),
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
Account: multiAcc,
|
||||
}}, acc)
|
||||
require.NoError(t, err)
|
||||
neoW := neo.New(nact)
|
||||
_, _, _, err = nact.Notarize(neoW.SetRegisterPriceTransaction(1_0000_0000))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
|
||||
defer chain.Close()
|
||||
|
@ -996,23 +1027,23 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
t.Run("client wasn't initialized", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(transaction.New([]byte{byte(opcode.RET)}, 123), []byte{byte(opcode.RET)}, -1, 0, 100, acc)
|
||||
_, err := c.SignAndPushP2PNotaryRequest(transaction.New([]byte{byte(opcode.RET)}, 123), []byte{byte(opcode.RET)}, -1, 0, 100, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
require.NoError(t, c.Init())
|
||||
t.Run("bad account address", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, nil, 0, 0, 0, &wallet.Account{Address: "not-an-addr"})
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, nil, 0, 0, 0, &wallet.Account{Address: "not-an-addr"}) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("bad fallback script", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc)
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("too large fallbackValidFor", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.RET)}, -1, 0, 141, acc)
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.RET)}, -1, 0, 141, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
|
@ -1031,7 +1062,7 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
|||
}
|
||||
mainTx := expected
|
||||
_ = expected.Hash()
|
||||
req, err := c.SignAndPushP2PNotaryRequest(&mainTx, []byte{byte(opcode.RET)}, -1, 0, 6, acc)
|
||||
req, err := c.SignAndPushP2PNotaryRequest(&mainTx, []byte{byte(opcode.RET)}, -1, 0, 6, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
|
||||
require.NoError(t, err)
|
||||
|
||||
// check that request was correctly completed
|
||||
|
|
Loading…
Reference in a new issue