forked from TrueCloudLab/neoneo-go
Merge pull request #1726 from nspcc-dev/signature_collection/rpc
*: add `submitnotaryrequest` RPC call and associated logic
This commit is contained in:
commit
97da0feb98
20 changed files with 671 additions and 91 deletions
|
@ -468,7 +468,7 @@ func transferNEP17(ctx *cli.Context) error {
|
|||
func signAndSendTransfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, recipients []client.TransferTarget) error {
|
||||
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||
|
||||
tx, err := c.CreateNEP17MultiTransferTx(acc, int64(gas), recipients...)
|
||||
tx, err := c.CreateNEP17MultiTransferTx(acc, int64(gas), recipients, nil)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ func claimGas(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
hash, err := c.TransferNEP17(acc, scriptHash, neoContractHash, 0, 0)
|
||||
hash, err := c.TransferNEP17(acc, scriptHash, neoContractHash, 0, 0, nil)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -25,6 +26,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"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/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -296,9 +298,11 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
|||
gasHash := bc.contracts.GAS.Hash
|
||||
neoHash := bc.contracts.NEO.Hash
|
||||
policyHash := bc.contracts.Policy.Hash
|
||||
notaryHash := bc.contracts.Notary.Hash
|
||||
t.Logf("native GAS hash: %v", gasHash)
|
||||
t.Logf("native NEO hash: %v", neoHash)
|
||||
t.Logf("native Policy hash: %v", policyHash)
|
||||
t.Logf("native Notary hash: %v", notaryHash)
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
priv0ScriptHash := priv0.GetScriptHash()
|
||||
|
@ -426,6 +430,31 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
|||
require.NoError(t, acc0.SignTx(txDeploy2))
|
||||
b = bc.newBlock(txDeploy2)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
|
||||
// Deposit some GAS to notary contract for priv0
|
||||
transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000))
|
||||
transferTx.Nonce = getNextNonce()
|
||||
transferTx.ValidUntilBlock = validUntilBlock
|
||||
transferTx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
||||
transferTx.SystemFee += 10_0000
|
||||
require.NoError(t, acc0.SignTx(transferTx))
|
||||
|
||||
b = bc.newBlock(transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE())
|
||||
|
||||
// Designate new Notary node
|
||||
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, ntr.Accounts[0].Decrypt("one"))
|
||||
bc.setNodesByRole(t, true, native.RoleP2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()})
|
||||
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
|
||||
}
|
||||
|
||||
func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
|
||||
|
|
|
@ -86,7 +86,7 @@ func newNotary() *Notary {
|
|||
|
||||
desc = newDescriptor("verify", smartcontract.BoolType,
|
||||
manifest.NewParameter("signature", smartcontract.SignatureType))
|
||||
md = newMethodAndPrice(n.verify, 100_0000, callflag.ReadStates)
|
||||
md = newMethodAndPrice(n.verify, NotaryVerificationPrice, callflag.ReadStates)
|
||||
n.AddMethod(md, desc)
|
||||
|
||||
desc = newDescriptor("getMaxNotValidBeforeDelta", smartcontract.IntegerType)
|
||||
|
@ -339,6 +339,7 @@ func (n *Notary) verify(ic *interop.Context, args []stackitem.Item) stackitem.It
|
|||
if signer.Scopes != transaction.None {
|
||||
return stackitem.NewBool(false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if tx.Sender() == n.Hash {
|
||||
|
|
|
@ -95,7 +95,7 @@ func TestNotary(t *testing.T) {
|
|||
|
||||
bc.SetNotary(ntr1)
|
||||
bc.RegisterPostBlock(func(bc blockchainer.Blockchainer, pool *mempool.Pool, b *block.Block) {
|
||||
ntr1.PostPersist(bc, pool, b)
|
||||
ntr1.PostPersist()
|
||||
})
|
||||
|
||||
notaryNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
|
||||
|
|
|
@ -24,6 +24,31 @@ type P2PNotaryRequest struct {
|
|||
signedHash util.Uint256
|
||||
}
|
||||
|
||||
// NewP2PNotaryRequestFromBytes decodes P2PNotaryRequest from the given bytes.
|
||||
func NewP2PNotaryRequestFromBytes(network netmode.Magic, b []byte) (*P2PNotaryRequest, error) {
|
||||
req := &P2PNotaryRequest{Network: network}
|
||||
br := io.NewBinReaderFromBuf(b)
|
||||
req.DecodeBinary(br)
|
||||
if br.Err != nil {
|
||||
return nil, br.Err
|
||||
}
|
||||
_ = br.ReadB()
|
||||
if br.Err == nil {
|
||||
return nil, errors.New("additional data after the payload")
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Bytes returns serialized P2PNotaryRequest payload.
|
||||
func (r *P2PNotaryRequest) Bytes() ([]byte, error) {
|
||||
buf := io.NewBufBinWriter()
|
||||
r.EncodeBinary(buf.BinWriter)
|
||||
if buf.Err != nil {
|
||||
return nil, buf.Err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Hash returns payload's hash.
|
||||
func (r *P2PNotaryRequest) Hash() util.Uint256 {
|
||||
if r.hash.Equals(util.Uint256{}) {
|
||||
|
@ -74,8 +99,8 @@ func (r *P2PNotaryRequest) updateHashes(b []byte) {
|
|||
|
||||
// DecodeBinaryUnsigned reads payload from w excluding signature.
|
||||
func (r *P2PNotaryRequest) decodeHashableFields(br *io.BinReader) {
|
||||
r.MainTransaction = new(transaction.Transaction)
|
||||
r.FallbackTransaction = new(transaction.Transaction)
|
||||
r.MainTransaction = &transaction.Transaction{Network: r.Network}
|
||||
r.FallbackTransaction = &transaction.Transaction{Network: r.Network}
|
||||
r.MainTransaction.DecodeBinary(br)
|
||||
r.FallbackTransaction.DecodeBinary(br)
|
||||
if br.Err == nil {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"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/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -143,8 +143,9 @@ func TestNotaryRequestIsValid(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestNotaryRequestEncodeDecodeBinary(t *testing.T) {
|
||||
func TestNotaryRequestBytesFromBytes(t *testing.T) {
|
||||
mainTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||
Script: []byte{0, 1, 2},
|
||||
ValidUntilBlock: 123,
|
||||
|
@ -157,6 +158,7 @@ func TestNotaryRequestEncodeDecodeBinary(t *testing.T) {
|
|||
_ = mainTx.Hash()
|
||||
_ = mainTx.Size()
|
||||
fallbackTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Script: []byte{3, 2, 1},
|
||||
ValidUntilBlock: 123,
|
||||
Attributes: []transaction.Attribute{
|
||||
|
@ -172,6 +174,7 @@ func TestNotaryRequestEncodeDecodeBinary(t *testing.T) {
|
|||
_ = fallbackTx.Hash()
|
||||
_ = fallbackTx.Size()
|
||||
p := &P2PNotaryRequest{
|
||||
Network: netmode.UnitTestNet,
|
||||
MainTransaction: mainTx,
|
||||
FallbackTransaction: fallbackTx,
|
||||
Witness: transaction.Witness{
|
||||
|
@ -180,5 +183,10 @@ func TestNotaryRequestEncodeDecodeBinary(t *testing.T) {
|
|||
},
|
||||
}
|
||||
require.Equal(t, hash.Sha256(p.GetSignedHash().BytesBE()), p.Hash())
|
||||
testserdes.EncodeDecodeBinary(t, p, new(P2PNotaryRequest))
|
||||
|
||||
bytes, err := p.Bytes()
|
||||
require.NoError(t, err)
|
||||
actual, err := NewP2PNotaryRequestFromBytes(netmode.UnitTestNet, bytes)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, p, actual)
|
||||
}
|
||||
|
|
|
@ -158,9 +158,6 @@ func newServerFromConstructors(config ServerConfig, chain blockchainer.Blockchai
|
|||
}
|
||||
s.notaryModule = n
|
||||
chain.SetNotary(n)
|
||||
chain.RegisterPostBlock(func(bc blockchainer.Blockchainer, pool *mempool.Pool, b *block.Block) {
|
||||
s.notaryModule.PostPersist(bc, pool, b)
|
||||
})
|
||||
}
|
||||
} else if chain.GetConfig().P2PNotary.Enabled {
|
||||
return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enable")
|
||||
|
@ -835,10 +832,18 @@ func (s *Server) handleP2PNotaryRequestCmd(r *payload.P2PNotaryRequest) error {
|
|||
if !s.chain.P2PSigExtensionsEnabled() {
|
||||
return errors.New("P2PNotaryRequestCMD was received, but P2PSignatureExtensions are disabled")
|
||||
}
|
||||
if s.verifyAndPoolNotaryRequest(r) == RelaySucceed {
|
||||
s.RelayP2PNotaryRequest(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelayP2PNotaryRequest adds given request to the pool and relays. It does not check
|
||||
// P2PSigExtensions enabled.
|
||||
func (s *Server) RelayP2PNotaryRequest(r *payload.P2PNotaryRequest) RelayReason {
|
||||
ret := s.verifyAndPoolNotaryRequest(r)
|
||||
if ret == RelaySucceed {
|
||||
s.broadcastP2PNotaryRequestPayload(nil, r)
|
||||
}
|
||||
return nil
|
||||
return ret
|
||||
}
|
||||
|
||||
// verifyAndPoolNotaryRequest verifies NotaryRequest payload and adds it to the payload mempool.
|
||||
|
@ -877,7 +882,7 @@ func verifyNotaryRequest(bc blockchainer.Blockchainer, _ *transaction.Transactio
|
|||
}
|
||||
|
||||
func (s *Server) broadcastP2PNotaryRequestPayload(_ *transaction.Transaction, data interface{}) {
|
||||
r := data.(payload.P2PNotaryRequest) // we can guarantee that cast is successful
|
||||
r := data.(*payload.P2PNotaryRequest) // we can guarantee that cast is successful
|
||||
msg := NewMessage(CMDInv, payload.NewInventory(payload.P2PNotaryRequestType, []util.Uint256{r.FallbackTransaction.Hash()}))
|
||||
s.broadcastMessage(msg)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/atomic"
|
||||
|
@ -524,6 +523,7 @@ func TestGetData(t *testing.T) {
|
|||
})
|
||||
t.Run("p2pNotaryRequest", func(t *testing.T) {
|
||||
mainTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||
Script: []byte{0, 1, 2},
|
||||
ValidUntilBlock: 123,
|
||||
|
@ -533,6 +533,7 @@ func TestGetData(t *testing.T) {
|
|||
mainTx.Size()
|
||||
mainTx.Hash()
|
||||
fallbackTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Script: []byte{1, 2, 3},
|
||||
ValidUntilBlock: 123,
|
||||
Attributes: []transaction.Attribute{
|
||||
|
|
|
@ -104,27 +104,38 @@ func (c *Client) NEP17TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
|
|||
// method of a given contract (token) to move specified amount of NEP17 assets
|
||||
// (in FixedN format using contract's number of decimals) to given account and
|
||||
// returns it. The returned transaction is not signed.
|
||||
func (c *Client) CreateNEP17TransferTx(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas int64) (*transaction.Transaction, error) {
|
||||
return c.CreateNEP17MultiTransferTx(acc, gas, TransferTarget{
|
||||
Token: token,
|
||||
Address: to,
|
||||
Amount: amount,
|
||||
})
|
||||
func (c *Client) CreateNEP17TransferTx(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas int64, data interface{}) (*transaction.Transaction, error) {
|
||||
return c.CreateNEP17MultiTransferTx(acc, gas, []TransferTarget{
|
||||
{Token: token,
|
||||
Address: to,
|
||||
Amount: amount,
|
||||
},
|
||||
}, []interface{}{data})
|
||||
}
|
||||
|
||||
// CreateNEP17MultiTransferTx creates an invocation transaction for performing NEP17 transfers
|
||||
// from a single sender to multiple recipients.
|
||||
func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, recipients ...TransferTarget) (*transaction.Transaction, error) {
|
||||
// from a single sender to multiple recipients with the given data.
|
||||
func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, recipients []TransferTarget, data []interface{}) (*transaction.Transaction, error) {
|
||||
from, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad account address: %w", err)
|
||||
}
|
||||
if data == nil {
|
||||
data = make([]interface{}, len(recipients))
|
||||
} else {
|
||||
if len(data) != len(recipients) {
|
||||
return nil, fmt.Errorf("data and recipients number mismatch: %d vs %d", len(data), len(recipients))
|
||||
}
|
||||
}
|
||||
w := io.NewBufBinWriter()
|
||||
for i := range recipients {
|
||||
emit.AppCall(w.BinWriter, recipients[i].Token, "transfer",
|
||||
callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify, from, recipients[i].Address, recipients[i].Amount, nil)
|
||||
callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify, from, recipients[i].Address, recipients[i].Amount, data[i])
|
||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||
}
|
||||
if w.Err != nil {
|
||||
return nil, fmt.Errorf("failed to create transfer script: %w", w.Err)
|
||||
}
|
||||
accAddr, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad account address: %v", err)
|
||||
|
@ -178,10 +189,10 @@ func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee,
|
|||
|
||||
// TransferNEP17 creates an invocation transaction that invokes 'transfer' method
|
||||
// on a given token to move specified amount of NEP17 assets (in FixedN format
|
||||
// using contract's number of decimals) to given account and sends it to the
|
||||
// network returning just a hash of it.
|
||||
func (c *Client) TransferNEP17(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas int64) (util.Uint256, error) {
|
||||
tx, err := c.CreateNEP17TransferTx(acc, to, token, amount, gas)
|
||||
// using contract's number of decimals) to given account with data specified and
|
||||
// sends it to the network returning just a hash of it.
|
||||
func (c *Client) TransferNEP17(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas int64, data interface{}) (util.Uint256, error) {
|
||||
tx, err := c.CreateNEP17TransferTx(acc, to, token, amount, gas, data)
|
||||
if err != nil {
|
||||
return util.Uint256{}, err
|
||||
}
|
||||
|
@ -194,8 +205,8 @@ func (c *Client) TransferNEP17(acc *wallet.Account, to util.Uint160, token util.
|
|||
}
|
||||
|
||||
// MultiTransferNEP17 is similar to TransferNEP17, buf allows to have multiple recipients.
|
||||
func (c *Client) MultiTransferNEP17(acc *wallet.Account, gas int64, recipients ...TransferTarget) (util.Uint256, error) {
|
||||
tx, err := c.CreateNEP17MultiTransferTx(acc, gas, recipients...)
|
||||
func (c *Client) MultiTransferNEP17(acc *wallet.Account, gas int64, recipients []TransferTarget, data []interface{}) (util.Uint256, error) {
|
||||
tx, err := c.CreateNEP17MultiTransferTx(acc, gas, recipients, data)
|
||||
if err != nil {
|
||||
return util.Uint256{}, err
|
||||
}
|
||||
|
|
|
@ -22,27 +22,46 @@ func (c *Client) GetMaxBlockSize() (int64, error) {
|
|||
|
||||
// GetFeePerByte invokes `getFeePerByte` method on a native Policy contract.
|
||||
func (c *Client) GetFeePerByte() (int64, error) {
|
||||
if !c.initDone {
|
||||
return 0, errNetworkNotInitialized
|
||||
}
|
||||
return c.invokeNativePolicyMethod("getFeePerByte")
|
||||
}
|
||||
|
||||
// GetExecFeeFactor invokes `getExecFeeFactor` method on a native Policy contract.
|
||||
func (c *Client) GetExecFeeFactor() (int64, error) {
|
||||
if !c.initDone {
|
||||
return 0, errNetworkNotInitialized
|
||||
}
|
||||
return c.invokeNativePolicyMethod("getExecFeeFactor")
|
||||
}
|
||||
|
||||
// GetMaxNotValidBeforeDelta invokes `getMaxNotValidBeforeDelta` method on a native Notary contract.
|
||||
func (c *Client) GetMaxNotValidBeforeDelta() (int64, error) {
|
||||
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get native Notary hash: %w", err)
|
||||
}
|
||||
return c.invokeNativeGetMethod(notaryHash, "getMaxNotValidBeforeDelta")
|
||||
}
|
||||
|
||||
// invokeNativePolicy method invokes Get* method on a native Policy contract.
|
||||
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
||||
if !c.initDone {
|
||||
return 0, errNetworkNotInitialized
|
||||
}
|
||||
result, err := c.InvokeFunction(c.cache.nativeHashes[nativenames.Policy], operation, []smartcontract.Parameter{}, nil)
|
||||
return c.invokeNativeGetMethod(c.cache.nativeHashes[nativenames.Policy], operation)
|
||||
}
|
||||
|
||||
func (c *Client) invokeNativeGetMethod(hash util.Uint160, operation string) (int64, error) {
|
||||
result, err := c.InvokeFunction(hash, operation, []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = getInvocationError(result)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to invoke %s Policy method: %w", operation, err)
|
||||
return 0, fmt.Errorf("failed to invoke %s method of native contract %s: %w", operation, hash.StringLE(), err)
|
||||
}
|
||||
|
||||
return topIntFromStack(result.Stack)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,16 +9,21 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||
"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/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
|
@ -560,6 +565,146 @@ func getSigners(sender util.Uint160, cosigners []transaction.Signer) []transacti
|
|||
return append([]transaction.Signer{s}, cosigners...)
|
||||
}
|
||||
|
||||
// SignAndPushP2PNotaryRequest creates and pushes P2PNotary request constructed from the main
|
||||
// and fallback transactions using given wif to sign it. It returns the request and an error.
|
||||
// Fallback transaction is constructed from the given script using the amount of gas specified.
|
||||
// For successful fallback transaction validation at least 2*transaction.NotaryServiceFeePerKey
|
||||
// GAS should be deposited to Notary contract.
|
||||
// Main transaction should be constructed by the user. Several rules need to be met for
|
||||
// successful main transaction acceptance:
|
||||
// 1. Native Notary contract should be a signer of the main transaction.
|
||||
// 2. Main transaction should have dummy contract witness for Notary signer.
|
||||
// 3. Main transaction should have NotaryAssisted attribute with NKeys specified.
|
||||
// 4. NotaryAssisted attribute and dummy Notary witness (as long as the other incomplete witnesses)
|
||||
// should be paid for. Use CalculateNotaryWitness to calculate the amount of network fee to pay
|
||||
// for the attribute and Notary witness.
|
||||
// 5. Main transaction either shouldn't have all witnesses attached (in this case none of them
|
||||
// can be multisignature), or it only should have a partial multisignature.
|
||||
// Note: client should be initialized before SignAndPushP2PNotaryRequest call.
|
||||
func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fallbackScript []byte, fallbackSysFee int64, fallbackNetFee int64, fallbackValidFor uint32, acc *wallet.Account) (*payload.P2PNotaryRequest, error) {
|
||||
var err error
|
||||
if !c.initDone {
|
||||
return nil, errNetworkNotInitialized
|
||||
}
|
||||
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get native Notary hash: %w", err)
|
||||
}
|
||||
from, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad account address: %v", err)
|
||||
}
|
||||
signers := []transaction.Signer{{Account: notaryHash}, {Account: from}}
|
||||
if fallbackSysFee < 0 {
|
||||
result, err := c.InvokeScript(fallbackScript, signers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't add system fee to fallback transaction: %w", err)
|
||||
}
|
||||
if result.State != "HALT" {
|
||||
return nil, fmt.Errorf("can't add system fee to fallback transaction: bad vm state %s due to an error: %s", result.State, result.FaultException)
|
||||
}
|
||||
fallbackSysFee = result.GasConsumed
|
||||
}
|
||||
|
||||
maxNVBDelta, err := c.GetMaxNotValidBeforeDelta()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get MaxNotValidBeforeDelta")
|
||||
}
|
||||
if int64(fallbackValidFor) > maxNVBDelta {
|
||||
return nil, fmt.Errorf("fallback transaction should be valid for not more than %d blocks", maxNVBDelta)
|
||||
}
|
||||
fallbackTx := transaction.New(c.GetNetwork(), fallbackScript, fallbackSysFee)
|
||||
fallbackTx.Signers = signers
|
||||
fallbackTx.ValidUntilBlock = mainTx.ValidUntilBlock
|
||||
fallbackTx.Attributes = []transaction.Attribute{
|
||||
{
|
||||
Type: transaction.NotaryAssistedT,
|
||||
Value: &transaction.NotaryAssisted{NKeys: 0},
|
||||
},
|
||||
{
|
||||
Type: transaction.NotValidBeforeT,
|
||||
Value: &transaction.NotValidBefore{Height: fallbackTx.ValidUntilBlock - fallbackValidFor + 1},
|
||||
},
|
||||
{
|
||||
Type: transaction.ConflictsT,
|
||||
Value: &transaction.Conflicts{Hash: mainTx.Hash()},
|
||||
},
|
||||
}
|
||||
extraNetFee, err := c.CalculateNotaryFee(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fallbackNetFee += extraNetFee
|
||||
|
||||
dummyAccount := &wallet.Account{Contract: &wallet.Contract{Deployed: false}} // don't call `verify` for Notary contract witness, because it will fail
|
||||
err = c.AddNetworkFee(fallbackTx, fallbackNetFee, dummyAccount, acc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add network fee: %w", err)
|
||||
}
|
||||
fallbackTx.Scripts = []transaction.Witness{
|
||||
{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64)...),
|
||||
VerificationScript: []byte{},
|
||||
},
|
||||
}
|
||||
if err = acc.SignTx(fallbackTx); err != nil {
|
||||
return nil, fmt.Errorf("failed to sign fallback tx: %w", err)
|
||||
}
|
||||
fallbackHash := fallbackTx.Hash()
|
||||
req := &payload.P2PNotaryRequest{
|
||||
MainTransaction: mainTx,
|
||||
FallbackTransaction: fallbackTx,
|
||||
Network: c.GetNetwork(),
|
||||
}
|
||||
req.Witness = transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().Sign(req.GetSignedPart())...),
|
||||
VerificationScript: acc.GetVerificationScript(),
|
||||
}
|
||||
actualHash, err := c.SubmitP2PNotaryRequest(req)
|
||||
if err != nil {
|
||||
return req, fmt.Errorf("failed to submit notary request: %w", err)
|
||||
}
|
||||
if !actualHash.Equals(fallbackHash) {
|
||||
return req, fmt.Errorf("sent and actual fallback tx hashes mismatch:\n\tsent: %v\n\tactual: %v", fallbackHash.StringLE(), actualHash.StringLE())
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// CalculateNotaryFee calculates network fee for one dummy Notary witness and NotaryAssisted attribute with NKeys specified.
|
||||
// The result should be added to the transaction's net fee for successful verification.
|
||||
func (c *Client) CalculateNotaryFee(nKeys uint8) (int64, error) {
|
||||
baseExecFee, err := c.GetExecFeeFactor()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get BaseExecFeeFactor: %w", err)
|
||||
}
|
||||
feePerByte, err := c.GetFeePerByte()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get FeePerByte: %w", err)
|
||||
}
|
||||
return int64((nKeys+1))*transaction.NotaryServiceFeePerKey + // fee for NotaryAssisted attribute
|
||||
fee.Opcode(baseExecFee, // Notary node witness
|
||||
opcode.PUSHDATA1, opcode.RET, // invocation script
|
||||
opcode.PUSHINT8, opcode.SYSCALL, opcode.RET) + // System.Contract.CallNative
|
||||
native.NotaryVerificationPrice + // Notary witness verification price
|
||||
feePerByte*int64(io.GetVarSize(make([]byte, 66))) + // invocation script per-byte fee
|
||||
feePerByte*int64(io.GetVarSize([]byte{})), // verification script per-byte fee
|
||||
nil
|
||||
}
|
||||
|
||||
// SubmitP2PNotaryRequest submits given P2PNotaryRequest payload to the RPC node.
|
||||
func (c *Client) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error) {
|
||||
var resp = new(result.RelayResult)
|
||||
bytes, err := req.Bytes()
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("failed to encode request: %w", err)
|
||||
}
|
||||
params := request.NewRawParams(bytes)
|
||||
if err := c.performRequest("submitnotaryrequest", params, resp); err != nil {
|
||||
return util.Uint256{}, err
|
||||
}
|
||||
return resp.Hash, nil
|
||||
}
|
||||
|
||||
// ValidateAddress verifies that the address is a correct NEO address.
|
||||
func (c *Client) ValidateAddress(address string) error {
|
||||
var (
|
||||
|
|
|
@ -419,6 +419,18 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"getExecFeeFactor": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetExecFeeFactor()
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return int64(1000)
|
||||
},
|
||||
},
|
||||
},
|
||||
"getMaxTransacctionsPerBlock": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -443,6 +455,18 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"getMaxNotValidBeforeDelta": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetMaxNotValidBeforeDelta()
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMD2dldE1heEJsb2NrU2l6ZQwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return int64(262144)
|
||||
},
|
||||
},
|
||||
},
|
||||
"isBlocked": {
|
||||
{
|
||||
name: "positive",
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"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/transaction"
|
||||
|
@ -17,6 +18,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"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"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -214,6 +216,111 @@ func TestSignAndPushInvocationTx(t *testing.T) {
|
|||
require.EqualValues(t, 30, tx.SystemFee)
|
||||
}
|
||||
|
||||
func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("client wasn't initialized", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, nil, 0, 0, 0, nil)
|
||||
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"})
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
t.Run("bad fallback script", func(t *testing.T) {
|
||||
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc)
|
||||
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)
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
||||
acc := wallet.NewAccountFromPrivateKey(sender)
|
||||
expected := transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||
Script: []byte{byte(opcode.RET)},
|
||||
ValidUntilBlock: chain.BlockHeight() + 5,
|
||||
Signers: []transaction.Signer{{Account: util.Uint160{1, 5, 9}}},
|
||||
Scripts: []transaction.Witness{{
|
||||
InvocationScript: []byte{1, 4, 7},
|
||||
VerificationScript: []byte{3, 6, 9},
|
||||
}},
|
||||
}
|
||||
mainTx := expected
|
||||
_ = expected.Hash()
|
||||
req, err := c.SignAndPushP2PNotaryRequest(&mainTx, []byte{byte(opcode.RET)}, -1, 0, 6, acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// check that request was correctly completed
|
||||
require.Equal(t, expected, *req.MainTransaction) // main tx should be the same
|
||||
require.ElementsMatch(t, []transaction.Attribute{
|
||||
{
|
||||
Type: transaction.NotaryAssistedT,
|
||||
Value: &transaction.NotaryAssisted{NKeys: 0},
|
||||
},
|
||||
{
|
||||
Type: transaction.NotValidBeforeT,
|
||||
Value: &transaction.NotValidBefore{Height: chain.BlockHeight()},
|
||||
},
|
||||
{
|
||||
Type: transaction.ConflictsT,
|
||||
Value: &transaction.Conflicts{Hash: mainTx.Hash()},
|
||||
},
|
||||
}, req.FallbackTransaction.Attributes)
|
||||
require.Equal(t, []transaction.Signer{
|
||||
{Account: chain.GetNotaryContractScriptHash()},
|
||||
{Account: acc.PrivateKey().GetScriptHash()},
|
||||
}, req.FallbackTransaction.Signers)
|
||||
|
||||
// it shouldn't be an error to add completed fallback to the chain
|
||||
w, err := wallet.NewWalletFromFile(notaryPath)
|
||||
require.NoError(t, err)
|
||||
ntr := w.Accounts[0]
|
||||
ntr.Decrypt(notaryPass)
|
||||
req.FallbackTransaction.Scripts[0] = transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, ntr.PrivateKey().Sign(req.FallbackTransaction.GetSignedPart())...),
|
||||
VerificationScript: []byte{},
|
||||
}
|
||||
b := testchain.NewBlock(t, chain, 1, 0, req.FallbackTransaction)
|
||||
require.NoError(t, chain.AddBlock(b))
|
||||
appLogs, err := chain.GetAppExecResults(req.FallbackTransaction.Hash(), trigger.Application)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(appLogs))
|
||||
appLog := appLogs[0]
|
||||
require.Equal(t, vm.HaltState, appLog.VMState)
|
||||
require.Equal(t, appLog.GasConsumed, req.FallbackTransaction.SystemFee)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCalculateNotaryFee(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("client not initialized", func(t *testing.T) {
|
||||
_, err := c.CalculateNotaryFee(0)
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
||||
defer chain.Close()
|
||||
|
@ -272,7 +379,7 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
|||
gasContractHash, err := c.GetNativeContractHash(nativenames.Gas)
|
||||
require.NoError(t, err)
|
||||
|
||||
tx, err := c.CreateNEP17TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0)
|
||||
tx, err := c.CreateNEP17TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0, nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, acc.SignTx(tx))
|
||||
require.NoError(t, chain.VerifyTx(tx))
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
||||
|
@ -122,6 +123,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon
|
|||
"invokecontractverify": (*Server).invokeContractVerify,
|
||||
"sendrawtransaction": (*Server).sendrawtransaction,
|
||||
"submitblock": (*Server).submitBlock,
|
||||
"submitnotaryrequest": (*Server).submitNotaryRequest,
|
||||
"submitoracleresponse": (*Server).submitOracleResponse,
|
||||
"validateaddress": (*Server).validateAddress,
|
||||
"verifyproof": (*Server).verifyProof,
|
||||
|
@ -1243,6 +1245,48 @@ func (s *Server) submitBlock(reqParams request.Params) (interface{}, *response.E
|
|||
}, nil
|
||||
}
|
||||
|
||||
// submitNotaryRequest broadcasts P2PNotaryRequest over the NEO network.
|
||||
func (s *Server) submitNotaryRequest(ps request.Params) (interface{}, *response.Error) {
|
||||
if !s.chain.P2PSigExtensionsEnabled() {
|
||||
return nil, response.NewInternalServerError("P2PNotaryRequest was received, but P2PSignatureExtensions are disabled", nil)
|
||||
}
|
||||
|
||||
if len(ps) < 1 {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
bytePayload, err := ps[0].GetBytesBase64()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
r, err := payload.NewP2PNotaryRequestFromBytes(s.network, bytePayload)
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
return getRelayResult(s.coreServer.RelayP2PNotaryRequest(r), r.FallbackTransaction.Hash())
|
||||
}
|
||||
|
||||
// getRelayResult returns successful relay result or an error.
|
||||
func getRelayResult(relayReason network.RelayReason, hash util.Uint256) (interface{}, *response.Error) {
|
||||
switch relayReason {
|
||||
case network.RelaySucceed:
|
||||
return result.RelayResult{
|
||||
Hash: hash,
|
||||
}, nil
|
||||
case network.RelayAlreadyExists:
|
||||
return nil, response.ErrAlreadyExists
|
||||
case network.RelayOutOfMemory:
|
||||
return nil, response.ErrOutOfMemory
|
||||
case network.RelayUnableToVerify:
|
||||
return nil, response.ErrUnableToVerify
|
||||
case network.RelayInvalid:
|
||||
return nil, response.ErrValidationFailed
|
||||
case network.RelayPolicyFail:
|
||||
return nil, response.ErrPolicyFail
|
||||
default:
|
||||
return nil, response.ErrUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) submitOracleResponse(ps request.Params) (interface{}, *response.Error) {
|
||||
if s.oracle == nil {
|
||||
return nil, response.NewInternalServerError("oracle is not enabled", nil)
|
||||
|
@ -1276,40 +1320,18 @@ func (s *Server) submitOracleResponse(ps request.Params) (interface{}, *response
|
|||
}
|
||||
|
||||
func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, *response.Error) {
|
||||
var resultsErr *response.Error
|
||||
var results interface{}
|
||||
|
||||
if len(reqParams) < 1 {
|
||||
return nil, response.ErrInvalidParams
|
||||
} else if byteTx, err := reqParams[0].GetBytesBase64(); err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
} else {
|
||||
tx, err := transaction.NewTransactionFromBytes(s.network, byteTx)
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
relayReason := s.coreServer.RelayTxn(tx)
|
||||
switch relayReason {
|
||||
case network.RelaySucceed:
|
||||
results = result.RelayResult{
|
||||
Hash: tx.Hash(),
|
||||
}
|
||||
case network.RelayAlreadyExists:
|
||||
resultsErr = response.ErrAlreadyExists
|
||||
case network.RelayOutOfMemory:
|
||||
resultsErr = response.ErrOutOfMemory
|
||||
case network.RelayUnableToVerify:
|
||||
resultsErr = response.ErrUnableToVerify
|
||||
case network.RelayInvalid:
|
||||
resultsErr = response.ErrValidationFailed
|
||||
case network.RelayPolicyFail:
|
||||
resultsErr = response.ErrPolicyFail
|
||||
default:
|
||||
resultsErr = response.ErrUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return results, resultsErr
|
||||
byteTx, err := reqParams[0].GetBytesBase64()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx, err := transaction.NewTransactionFromBytes(s.network, byteTx)
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
return getRelayResult(s.coreServer.RelayTxn(tx), tx.Hash())
|
||||
}
|
||||
|
||||
// subscribe handles subscription requests from websocket clients.
|
||||
|
|
|
@ -23,7 +23,12 @@ import (
|
|||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
func getUnitTestChain(t *testing.T, enableOracle bool) (*core.Blockchain, *oracle.Oracle, config.Config, *zap.Logger) {
|
||||
const (
|
||||
notaryPath = "../../services/notary/testdata/notary1.json"
|
||||
notaryPass = "one"
|
||||
)
|
||||
|
||||
func getUnitTestChain(t *testing.T, enableOracle bool, enableNotary bool) (*core.Blockchain, *oracle.Oracle, config.Config, *zap.Logger) {
|
||||
net := netmode.UnitTestNet
|
||||
configPath := "../../../config"
|
||||
cfg, err := config.Load(configPath, net)
|
||||
|
@ -31,6 +36,19 @@ func getUnitTestChain(t *testing.T, enableOracle bool) (*core.Blockchain, *oracl
|
|||
|
||||
memoryStore := storage.NewMemoryStore()
|
||||
logger := zaptest.NewLogger(t)
|
||||
if enableNotary {
|
||||
cfg.ProtocolConfiguration.P2PSigExtensions = true
|
||||
cfg.ProtocolConfiguration.P2PNotaryRequestPayloadPoolSize = 1000
|
||||
cfg.ProtocolConfiguration.P2PNotary = config.P2PNotary{
|
||||
Enabled: true,
|
||||
UnlockWallet: config.Wallet{
|
||||
Path: notaryPath,
|
||||
Password: notaryPass,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
cfg.ProtocolConfiguration.P2PNotary.Enabled = false
|
||||
}
|
||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
|
||||
require.NoError(t, err, "could not create chain")
|
||||
|
||||
|
@ -79,8 +97,8 @@ func getTestBlocks(t *testing.T) []*block.Block {
|
|||
return blocks
|
||||
}
|
||||
|
||||
func initClearServerWithOracle(t *testing.T, needOracle bool) (*core.Blockchain, *Server, *httptest.Server) {
|
||||
chain, orc, cfg, logger := getUnitTestChain(t, needOracle)
|
||||
func initClearServerWithServices(t *testing.T, needOracle bool, needNotary bool) (*core.Blockchain, *Server, *httptest.Server) {
|
||||
chain, orc, cfg, logger := getUnitTestChain(t, needOracle, needNotary)
|
||||
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
server, err := network.NewServer(serverConfig, chain, logger)
|
||||
|
@ -96,7 +114,7 @@ func initClearServerWithOracle(t *testing.T, needOracle bool) (*core.Blockchain,
|
|||
}
|
||||
|
||||
func initClearServerWithInMemoryChain(t *testing.T) (*core.Blockchain, *Server, *httptest.Server) {
|
||||
return initClearServerWithOracle(t, false)
|
||||
return initClearServerWithServices(t, false, false)
|
||||
}
|
||||
|
||||
func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, *Server, *httptest.Server) {
|
||||
|
@ -108,6 +126,15 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, *Server, *http
|
|||
return chain, rpcServer, srv
|
||||
}
|
||||
|
||||
func initServerWithInMemoryChainAndServices(t *testing.T, needOracle bool, needNotary bool) (*core.Blockchain, *Server, *httptest.Server) {
|
||||
chain, rpcServer, srv := initClearServerWithServices(t, needOracle, needNotary)
|
||||
|
||||
for _, b := range getTestBlocks(t) {
|
||||
require.NoError(t, chain.AddBlock(b))
|
||||
}
|
||||
return chain, rpcServer, srv
|
||||
}
|
||||
|
||||
type FeerStub struct{}
|
||||
|
||||
func (fs *FeerStub) FeePerByte() int64 {
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
|
@ -27,6 +28,7 @@ import (
|
|||
"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/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
|
||||
|
@ -58,7 +60,7 @@ type rpcTestCase struct {
|
|||
}
|
||||
|
||||
const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a"
|
||||
const deploymentTxHash = "9a9d6b0876d1e6cfd68efadd0facaaba7e07efbe7b24282d094a0893645581f3"
|
||||
const deploymentTxHash = "d0de42d5d23211174a50d74fbd4a919631236a63f16431a5a7e7126759e7ba23"
|
||||
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
|
||||
|
||||
const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943"
|
||||
|
@ -649,7 +651,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
require.True(t, ok)
|
||||
expected := result.UnclaimedGas{
|
||||
Address: testchain.MultisigScriptHash(),
|
||||
Unclaimed: *big.NewInt(3500),
|
||||
Unclaimed: *big.NewInt(4500),
|
||||
}
|
||||
assert.Equal(t, expected, *actual)
|
||||
},
|
||||
|
@ -918,6 +920,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
fail: true,
|
||||
},
|
||||
},
|
||||
"submitnotaryrequest": {
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"validateaddress": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -954,7 +963,7 @@ func TestRPC(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSubmitOracle(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initClearServerWithOracle(t, true)
|
||||
chain, rpcSrv, httpSrv := initClearServerWithServices(t, true, false)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
|
@ -986,6 +995,121 @@ func TestSubmitOracle(t *testing.T) {
|
|||
t.Run("Valid", runCase(t, false, pubStr, `1`, txSigStr, msgSigStr))
|
||||
}
|
||||
|
||||
func TestSubmitNotaryRequest(t *testing.T) {
|
||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitnotaryrequest", "params": %s}`
|
||||
|
||||
t.Run("disabled P2PSigExtensions", func(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
req := fmt.Sprintf(rpc, "[]")
|
||||
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||
checkErrGetResult(t, body, true)
|
||||
})
|
||||
|
||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
runCase := func(t *testing.T, fail bool, params ...string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ps := `[` + strings.Join(params, ",") + `]`
|
||||
req := fmt.Sprintf(rpc, ps)
|
||||
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||
checkErrGetResult(t, body, fail)
|
||||
}
|
||||
}
|
||||
t.Run("missing request", runCase(t, true))
|
||||
t.Run("not a base64", runCase(t, true, `"not-a-base64$"`))
|
||||
t.Run("invalid request bytes", runCase(t, true, `"not-a-request"`))
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
mainTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||
Script: []byte{byte(opcode.RET)},
|
||||
ValidUntilBlock: 123,
|
||||
Signers: []transaction.Signer{{Account: util.Uint160{1, 5, 9}}},
|
||||
Scripts: []transaction.Witness{{
|
||||
InvocationScript: []byte{1, 4, 7},
|
||||
VerificationScript: []byte{3, 6, 9},
|
||||
}},
|
||||
}
|
||||
fallbackTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Script: []byte{byte(opcode.RET)},
|
||||
ValidUntilBlock: 123,
|
||||
Attributes: []transaction.Attribute{
|
||||
{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}},
|
||||
{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
|
||||
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
|
||||
},
|
||||
Signers: []transaction.Signer{{Account: util.Uint160{1, 4, 7}}, {Account: util.Uint160{9, 8, 7}}},
|
||||
Scripts: []transaction.Witness{
|
||||
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64, 64)...), VerificationScript: make([]byte, 0)},
|
||||
{InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}},
|
||||
}
|
||||
p := &payload.P2PNotaryRequest{
|
||||
Network: netmode.UnitTestNet,
|
||||
MainTransaction: mainTx,
|
||||
FallbackTransaction: fallbackTx,
|
||||
Witness: transaction.Witness{
|
||||
InvocationScript: []byte{1, 2, 3},
|
||||
VerificationScript: []byte{7, 8, 9},
|
||||
},
|
||||
}
|
||||
bytes, err := p.Bytes()
|
||||
require.NoError(t, err)
|
||||
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(bytes))
|
||||
runCase(t, true, str)(t)
|
||||
})
|
||||
t.Run("valid request", func(t *testing.T) {
|
||||
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
||||
mainTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||
Script: []byte{byte(opcode.RET)},
|
||||
ValidUntilBlock: 123,
|
||||
Signers: []transaction.Signer{{Account: util.Uint160{1, 5, 9}}},
|
||||
Scripts: []transaction.Witness{{
|
||||
InvocationScript: []byte{1, 4, 7},
|
||||
VerificationScript: []byte{3, 6, 9},
|
||||
}},
|
||||
}
|
||||
fallbackTx := &transaction.Transaction{
|
||||
Network: netmode.UnitTestNet,
|
||||
Script: []byte{byte(opcode.RET)},
|
||||
ValidUntilBlock: 123,
|
||||
Attributes: []transaction.Attribute{
|
||||
{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}},
|
||||
{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
|
||||
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
|
||||
},
|
||||
Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}},
|
||||
Scripts: []transaction.Witness{
|
||||
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, make([]byte, 64, 64)...), VerificationScript: []byte{}},
|
||||
},
|
||||
NetworkFee: 2_0000_0000,
|
||||
}
|
||||
fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, sender.Sign(fallbackTx.GetSignedPart())...),
|
||||
VerificationScript: sender.PublicKey().GetVerificationScript(),
|
||||
})
|
||||
p := &payload.P2PNotaryRequest{
|
||||
Network: netmode.UnitTestNet,
|
||||
MainTransaction: mainTx,
|
||||
FallbackTransaction: fallbackTx,
|
||||
}
|
||||
p.Witness = transaction.Witness{
|
||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, sender.Sign(p.GetSignedPart())...),
|
||||
VerificationScript: sender.PublicKey().GetVerificationScript(),
|
||||
}
|
||||
bytes, err := p.Bytes()
|
||||
require.NoError(t, err)
|
||||
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(bytes))
|
||||
runCase(t, false, str)(t)
|
||||
})
|
||||
}
|
||||
|
||||
// testRPCProtocol runs a full set of tests using given callback to make actual
|
||||
// calls. Some tests change the chain state, thus we reinitialize the chain from
|
||||
// scratch here.
|
||||
|
@ -1246,7 +1370,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
||||
|
||||
assert.Equal(t, *block.Transactions[0], actual.Transaction)
|
||||
assert.Equal(t, 8, actual.Confirmations)
|
||||
assert.Equal(t, 10, actual.Confirmations)
|
||||
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
||||
})
|
||||
|
||||
|
@ -1364,12 +1488,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoError(t, json.Unmarshal(res, actual))
|
||||
checkNep17TransfersAux(t, e, actual, sent, rcvd)
|
||||
}
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{3, 4, 5, 6}, []int{1, 2}) })
|
||||
t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{5, 6, 7, 8}, []int{1, 2}) })
|
||||
t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{0, 1}, []int{0}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{3}, []int{1}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{2, 3}, []int{1}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{4, 5}, []int{2}) })
|
||||
t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{2, 3}, []int{0}) })
|
||||
t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{5}, []int{1}) })
|
||||
t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{4, 5}, []int{1}) })
|
||||
t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{6, 7}, []int{2}) })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1474,8 +1598,8 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
|||
},
|
||||
{
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Amount: "80006665650",
|
||||
LastUpdated: 7,
|
||||
Amount: "78994306100",
|
||||
LastUpdated: 8,
|
||||
}},
|
||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||
}
|
||||
|
@ -1484,7 +1608,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
|||
}
|
||||
|
||||
func checkNep17Transfers(t *testing.T, e *executor, acc interface{}) {
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}, []int{0, 1, 2, 3, 4, 5, 6})
|
||||
checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []int{0, 1, 2, 3, 4, 5, 6})
|
||||
}
|
||||
|
||||
func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rcvd []int) {
|
||||
|
@ -1493,6 +1617,11 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockDepositGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(8))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockDepositGAS.Transactions))
|
||||
txDepositGAS := blockDepositGAS.Transactions[0]
|
||||
|
||||
blockDeploy2, err := e.chain.GetBlock(e.chain.GetHeaderHash(7))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(blockDeploy2.Transactions))
|
||||
|
@ -1541,6 +1670,23 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc interface{}, sent, rc
|
|||
// duplicate the Server method.
|
||||
expected := result.NEP17Transfers{
|
||||
Sent: []result.NEP17Transfer{
|
||||
{
|
||||
Timestamp: blockDepositGAS.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: address.Uint160ToString(e.chain.GetNotaryContractScriptHash()),
|
||||
Amount: "1000000000",
|
||||
Index: 8,
|
||||
NotifyIndex: 0,
|
||||
TxHash: txDepositGAS.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockDepositGAS.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
Address: "", // burn
|
||||
Amount: big.NewInt(txDepositGAS.SystemFee + txDepositGAS.NetworkFee).String(),
|
||||
Index: 8,
|
||||
TxHash: blockDepositGAS.Hash(),
|
||||
},
|
||||
{
|
||||
Timestamp: blockDeploy2.Timestamp,
|
||||
Asset: e.chain.UtilityTokenHash(),
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -44,8 +44,9 @@ type (
|
|||
|
||||
mp *mempool.Pool
|
||||
// requests channel
|
||||
reqCh chan mempool.Event
|
||||
stopCh chan struct{}
|
||||
reqCh chan mempool.Event
|
||||
blocksCh chan *block.Block
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// Config represents external configuration for Notary module.
|
||||
|
@ -109,17 +110,20 @@ func NewNotary(bc blockchainer.Blockchainer, mp *mempool.Pool, log *zap.Logger,
|
|||
onTransaction: onTransaction,
|
||||
mp: mp,
|
||||
reqCh: make(chan mempool.Event),
|
||||
blocksCh: make(chan *block.Block),
|
||||
stopCh: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Run runs Notary module and should be called in a separate goroutine.
|
||||
func (n *Notary) Run() {
|
||||
n.Config.Chain.SubscribeForBlocks(n.blocksCh)
|
||||
n.mp.SubscribeForTransactions(n.reqCh)
|
||||
for {
|
||||
select {
|
||||
case <-n.stopCh:
|
||||
n.mp.UnsubscribeFromTransactions(n.reqCh)
|
||||
n.Config.Chain.UnsubscribeFromBlocks(n.blocksCh)
|
||||
return
|
||||
case event := <-n.reqCh:
|
||||
if req, ok := event.Data.(*payload.P2PNotaryRequest); ok {
|
||||
|
@ -130,6 +134,9 @@ func (n *Notary) Run() {
|
|||
n.OnRequestRemoval(req)
|
||||
}
|
||||
}
|
||||
case <-n.blocksCh:
|
||||
// new block was added, need to check for valid fallbacks
|
||||
n.PostPersist()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,16 +266,18 @@ func (n *Notary) OnRequestRemoval(pld *payload.P2PNotaryRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
// PostPersist is a callback which is called after new block is persisted.
|
||||
func (n *Notary) PostPersist(bc blockchainer.Blockchainer, pool *mempool.Pool, b *block.Block) {
|
||||
// PostPersist is a callback which is called after new block event is received.
|
||||
// PostPersist must not be called under the blockchain lock, because it uses finalization function.
|
||||
func (n *Notary) PostPersist() {
|
||||
if n.getAccount() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n.reqMtx.Lock()
|
||||
defer n.reqMtx.Unlock()
|
||||
currHeight := n.Config.Chain.BlockHeight()
|
||||
for h, r := range n.requests {
|
||||
if !r.isSent && r.typ != Unknown && r.nSigs == r.nSigsCollected && r.minNotValidBefore > bc.BlockHeight() {
|
||||
if !r.isSent && r.typ != Unknown && r.nSigs == r.nSigsCollected && r.minNotValidBefore > currHeight {
|
||||
if err := n.finalize(r.main); err != nil {
|
||||
n.Config.Log.Error("failed to finalize main transaction", zap.Error(err))
|
||||
} else {
|
||||
|
@ -276,10 +285,10 @@ func (n *Notary) PostPersist(bc blockchainer.Blockchainer, pool *mempool.Pool, b
|
|||
}
|
||||
continue
|
||||
}
|
||||
if r.minNotValidBefore <= bc.BlockHeight() { // then at least one of the fallbacks can already be sent.
|
||||
if r.minNotValidBefore <= currHeight { // then at least one of the fallbacks can already be sent.
|
||||
newFallbacks := r.fallbacks[:0]
|
||||
for _, fb := range r.fallbacks {
|
||||
if nvb := fb.GetAttributes(transaction.NotValidBeforeT)[0].Value.(*transaction.NotValidBefore).Height; nvb <= bc.BlockHeight() {
|
||||
if nvb := fb.GetAttributes(transaction.NotValidBeforeT)[0].Value.(*transaction.NotValidBefore).Height; nvb <= currHeight {
|
||||
if err := n.finalize(fb); err != nil {
|
||||
newFallbacks = append(newFallbacks, fb) // wait for the next block to resend them
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ func (a *Account) SignTx(t *transaction.Transaction) error {
|
|||
}
|
||||
sign := a.privateKey.Sign(data)
|
||||
|
||||
verif := a.getVerificationScript()
|
||||
verif := a.GetVerificationScript()
|
||||
invoc := append([]byte{byte(opcode.PUSHDATA1), 64}, sign...)
|
||||
for i := range t.Scripts {
|
||||
if bytes.Equal(t.Scripts[i].VerificationScript, verif) {
|
||||
|
@ -124,7 +124,8 @@ func (a *Account) SignTx(t *transaction.Transaction) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *Account) getVerificationScript() []byte {
|
||||
// GetVerificationScript returns account's verification script.
|
||||
func (a *Account) GetVerificationScript() []byte {
|
||||
if a.Contract != nil {
|
||||
return a.Contract.Script
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue