mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 13:41:37 +00:00
Merge pull request #3098 from nspcc-dev/2951-getrawnotarypool
Implement `getrawnotaryrequest` and `getrawnotarytransaction` RPC extensions, close #2951.
This commit is contained in:
commit
0d30c834d5
11 changed files with 695 additions and 91 deletions
22
docs/rpc.md
22
docs/rpc.md
|
@ -284,7 +284,27 @@ state has all its values got from MPT with the specified stateroot. This allows
|
||||||
to track the contract storage scheme using the specified past chain state. These
|
to track the contract storage scheme using the specified past chain state. These
|
||||||
methods may be useful for debugging purposes.
|
methods may be useful for debugging purposes.
|
||||||
|
|
||||||
#### `submitnotaryrequest` call
|
#### P2PNotary extensions
|
||||||
|
|
||||||
|
The following P2PNotary extensions can be used on P2P Notary enabled networks
|
||||||
|
only.
|
||||||
|
|
||||||
|
##### `getrawnotarypool` call
|
||||||
|
|
||||||
|
`getrawnotarypool` method provides the ability to retrieve the content of the
|
||||||
|
RPC node's notary pool (a map from main transaction hashes to the corresponding
|
||||||
|
fallback transaction hashes for currently processing P2PNotaryRequest payloads).
|
||||||
|
You can use the `getrawnotarytransaction` method to iterate through
|
||||||
|
the results of `getrawnotarypool`, retrieve main/fallback transactions,
|
||||||
|
check their contents and act accordingly.
|
||||||
|
|
||||||
|
##### `getrawnotarytransaction` call
|
||||||
|
|
||||||
|
The `getrawnotarytransaction` method takes a transaction hash and aims to locate
|
||||||
|
the corresponding transaction in the P2PNotaryRequest pool. It performs
|
||||||
|
this search across all the verified main and fallback transactions.
|
||||||
|
|
||||||
|
##### `submitnotaryrequest` call
|
||||||
|
|
||||||
This method can be used on P2P Notary enabled networks to submit new notary
|
This method can be used on P2P Notary enabled networks to submit new notary
|
||||||
payloads to be relayed from RPC to P2P.
|
payloads to be relayed from RPC to P2P.
|
||||||
|
|
|
@ -609,3 +609,19 @@ func (mp *Pool) removeConflictsOf(tx *transaction.Transaction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IterateVerifiedTransactions iterates through verified transactions and invokes
|
||||||
|
// function `cont`. Iterations continue while the function `cont` returns true.
|
||||||
|
// Function `cont` is executed within a read-locked memory pool,
|
||||||
|
// thus IterateVerifiedTransactions will block any write mempool operation,
|
||||||
|
// use it with care. Do not modify transaction or data via `cont`.
|
||||||
|
func (mp *Pool) IterateVerifiedTransactions(cont func(tx *transaction.Transaction, data any) bool) {
|
||||||
|
mp.lock.RLock()
|
||||||
|
defer mp.lock.RUnlock()
|
||||||
|
|
||||||
|
for i := range mp.verifiedTxes {
|
||||||
|
if !cont(mp.verifiedTxes[i].txn, mp.verifiedTxes[i].data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -637,26 +637,18 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
balance: 100,
|
balance: 100,
|
||||||
}
|
}
|
||||||
mp := New(10, 1, false, nil)
|
mp := New(10, 1, false, nil)
|
||||||
newTx := func(t *testing.T, netFee int64) *transaction.Transaction {
|
|
||||||
tx := transaction.New([]byte{byte(opcode.RET)}, 0)
|
|
||||||
tx.Signers = []transaction.Signer{{}, {}}
|
|
||||||
tx.NetworkFee = netFee
|
|
||||||
nonce++
|
|
||||||
tx.Nonce = nonce
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
|
|
||||||
// bad, insufficient deposit
|
// bad, insufficient deposit
|
||||||
r1 := &payload.P2PNotaryRequest{
|
r1 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, fs.balance+1),
|
FallbackTransaction: mkTwoSignersTx(fs.balance+1, &nonce),
|
||||||
}
|
}
|
||||||
require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)
|
require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)
|
||||||
|
|
||||||
// good
|
// good
|
||||||
r2 := &payload.P2PNotaryRequest{
|
r2 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
||||||
require.True(t, mp.ContainsKey(r2.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r2.FallbackTransaction.Hash()))
|
||||||
|
@ -669,8 +661,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
|
// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
|
||||||
r3 := &payload.P2PNotaryRequest{
|
r3 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee+1),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
||||||
require.True(t, mp.ContainsKey(r3.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r3.FallbackTransaction.Hash()))
|
||||||
|
@ -680,8 +672,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
|
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
|
||||||
r4 := &payload.P2PNotaryRequest{
|
r4 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r4.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r4.FallbackTransaction.Hash()))
|
||||||
|
@ -691,8 +683,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
|
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
|
||||||
r5 := &payload.P2PNotaryRequest{
|
r5 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r5.FallbackTransaction, fs, r5))
|
require.NoError(t, mp.Add(r5.FallbackTransaction, fs, r5))
|
||||||
require.True(t, mp.ContainsKey(r5.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r5.FallbackTransaction.Hash()))
|
||||||
|
@ -713,7 +705,7 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
|
|
||||||
// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
|
// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
|
||||||
r6 := newTx(t, smallNetFee)
|
r6 := mkTwoSignersTx(smallNetFee, &nonce)
|
||||||
require.NoError(t, mp.Add(r6, fs, nil))
|
require.NoError(t, mp.Add(r6, fs, nil))
|
||||||
require.True(t, mp.ContainsKey(r6.Hash()))
|
require.True(t, mp.ContainsKey(r6.Hash()))
|
||||||
data, ok = mp.TryGetData(r6.Hash())
|
data, ok = mp.TryGetData(r6.Hash())
|
||||||
|
@ -722,14 +714,14 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// getting data: item is in verifiedMap, but not in verifiedTxes
|
// getting data: item is in verifiedMap, but not in verifiedTxes
|
||||||
r7 := &payload.P2PNotaryRequest{
|
r7 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r7.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r7.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r7.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r7.FallbackTransaction.Hash()))
|
||||||
r8 := &payload.P2PNotaryRequest{
|
r8 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee-1),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee-1, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r8.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r8.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r8.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r8.FallbackTransaction.Hash()))
|
||||||
|
@ -737,3 +729,99 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
_, ok = mp.TryGetData(r7.FallbackTransaction.Hash())
|
_, ok = mp.TryGetData(r7.FallbackTransaction.Hash())
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mkTwoSignersTx(netFee int64, nonce *uint32) *transaction.Transaction {
|
||||||
|
tx := transaction.New([]byte{byte(opcode.RET)}, 0)
|
||||||
|
tx.Signers = []transaction.Signer{{}, {}}
|
||||||
|
tx.NetworkFee = netFee
|
||||||
|
*nonce++
|
||||||
|
tx.Nonce = *nonce
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMempoolIterateVerifiedTransactions(t *testing.T) {
|
||||||
|
var (
|
||||||
|
smallNetFee int64 = 3
|
||||||
|
nonce uint32
|
||||||
|
r1, r2, r3, r4, r5 *payload.P2PNotaryRequest
|
||||||
|
)
|
||||||
|
fs := &FeerStub{
|
||||||
|
feePerByte: 0,
|
||||||
|
p2pSigExt: true,
|
||||||
|
blockHeight: 5,
|
||||||
|
balance: 100,
|
||||||
|
}
|
||||||
|
mp := New(10, 1, false, nil)
|
||||||
|
|
||||||
|
checkRequestsOrder := func(orderedRequests []*payload.P2PNotaryRequest) {
|
||||||
|
var pooledRequests []*payload.P2PNotaryRequest
|
||||||
|
mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
|
||||||
|
d := data.(*payload.P2PNotaryRequest)
|
||||||
|
pooledRequests = append(pooledRequests, d)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
require.Equal(t, orderedRequests, pooledRequests)
|
||||||
|
}
|
||||||
|
|
||||||
|
r1 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r1.FallbackTransaction, fs, r1))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r1})
|
||||||
|
|
||||||
|
// r2 has higher priority than r1. The resulting mp.verifiedTxes: [r2, r1]
|
||||||
|
r2 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1})
|
||||||
|
|
||||||
|
// r3 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3]
|
||||||
|
r3 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3})
|
||||||
|
|
||||||
|
// r4 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3, r4]
|
||||||
|
r4 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3, r4})
|
||||||
|
|
||||||
|
checkPooledRequest := func(t *testing.T, r *payload.P2PNotaryRequest, isPooled bool) {
|
||||||
|
cont := true
|
||||||
|
notaryRequest := &payload.P2PNotaryRequest{}
|
||||||
|
mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
|
||||||
|
if data != nil {
|
||||||
|
notaryRequest = data.(*payload.P2PNotaryRequest)
|
||||||
|
if notaryRequest.MainTransaction.Hash() == r.MainTransaction.Hash() {
|
||||||
|
cont = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cont
|
||||||
|
})
|
||||||
|
|
||||||
|
if isPooled {
|
||||||
|
require.Equal(t, false, cont)
|
||||||
|
require.Equal(t, r, notaryRequest)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, true, cont)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkPooledRequest(t, r1, true)
|
||||||
|
checkPooledRequest(t, r2, true)
|
||||||
|
checkPooledRequest(t, r3, true)
|
||||||
|
checkPooledRequest(t, r4, true)
|
||||||
|
|
||||||
|
r5 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
checkPooledRequest(t, r5, false)
|
||||||
|
}
|
||||||
|
|
48
pkg/neorpc/result/raw_notary_pool.go
Normal file
48
pkg/neorpc/result/raw_notary_pool.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package result
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawNotaryPool represents a result of `getrawnotarypool` RPC call.
|
||||||
|
// The structure consist of `Hashes`. `Hashes` field is a map, where key is
|
||||||
|
// the hash of the main transaction and value is a slice of related fallback
|
||||||
|
// transaction hashes.
|
||||||
|
type RawNotaryPool struct {
|
||||||
|
Hashes map[util.Uint256][]util.Uint256
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawNotaryPoolAux is an auxiliary struct for RawNotaryPool JSON marshalling.
|
||||||
|
type rawNotaryPoolAux struct {
|
||||||
|
Hashes map[string][]util.Uint256 `json:"hashes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
func (p RawNotaryPool) MarshalJSON() ([]byte, error) {
|
||||||
|
var aux rawNotaryPoolAux
|
||||||
|
aux.Hashes = make(map[string][]util.Uint256, len(p.Hashes))
|
||||||
|
for main, fallbacks := range p.Hashes {
|
||||||
|
aux.Hashes["0x"+main.StringLE()] = fallbacks
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (p *RawNotaryPool) UnmarshalJSON(data []byte) error {
|
||||||
|
var aux rawNotaryPoolAux
|
||||||
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Hashes = make(map[util.Uint256][]util.Uint256, len(aux.Hashes))
|
||||||
|
for main, fallbacks := range aux.Hashes {
|
||||||
|
hashMain, err := util.Uint256DecodeStringLE(strings.TrimPrefix(main, "0x"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Hashes[hashMain] = fallbacks
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -96,6 +96,8 @@ Supported methods
|
||||||
Extensions:
|
Extensions:
|
||||||
|
|
||||||
getblocksysfee
|
getblocksysfee
|
||||||
|
getrawnotarypool
|
||||||
|
getrawnotarytransaction
|
||||||
submitnotaryrequest
|
submitnotaryrequest
|
||||||
|
|
||||||
Unsupported methods
|
Unsupported methods
|
||||||
|
|
|
@ -1285,3 +1285,44 @@ func (c *Client) TerminateSession(sessionID uuid.UUID) (bool, error) {
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRawNotaryTransaction returns main or fallback transaction from the
|
||||||
|
// RPC node's notary request pool.
|
||||||
|
func (c *Client) GetRawNotaryTransaction(hash util.Uint256) (*transaction.Transaction, error) {
|
||||||
|
var (
|
||||||
|
params = []any{hash.StringLE()}
|
||||||
|
resp []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if err = c.performRequest("getrawnotarytransaction", params, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return transaction.NewTransactionFromBytes(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawNotaryTransactionVerbose returns main or fallback transaction from the
|
||||||
|
// RPC node's notary request pool.
|
||||||
|
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and
|
||||||
|
// io.GetVarSize(t) respectively.
|
||||||
|
func (c *Client) GetRawNotaryTransactionVerbose(hash util.Uint256) (*transaction.Transaction, error) {
|
||||||
|
var (
|
||||||
|
params = []any{hash.StringLE(), 1} // 1 for verbose.
|
||||||
|
resp = &transaction.Transaction{}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if err = c.performRequest("getrawnotarytransaction", params, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawNotaryPool returns hashes of main P2PNotaryRequest transactions that
|
||||||
|
// are currently in the RPC node's notary request pool with the corresponding
|
||||||
|
// hashes of fallback transactions.
|
||||||
|
func (c *Client) GetRawNotaryPool() (*result.RawNotaryPool, error) {
|
||||||
|
resp := &result.RawNotaryPool{}
|
||||||
|
if err := c.performRequest("getrawnotarypool", nil, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1375,6 +1375,89 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"getrawnotarytransaction": {
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
invoke: func(c *Client) (any, error) {
|
||||||
|
hash, err := util.Uint256DecodeStringLE("ad1c2875de823a54188949490e2d68580fd070fcc5ff409609f478d23d12355f")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.GetRawNotaryTransaction(hash)
|
||||||
|
},
|
||||||
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"AAMAAAAAAAAAAAAAAAAAAAAAAAAAewAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsgABIgEBQAEDAQQHAwMGCQ=="}`,
|
||||||
|
result: func(c *Client) any {
|
||||||
|
return &transaction.Transaction{}
|
||||||
|
},
|
||||||
|
check: func(t *testing.T, c *Client, uns any) {
|
||||||
|
res, ok := uns.(*transaction.Transaction)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.NotNil(t, res)
|
||||||
|
expectHash, err := util.Uint256DecodeStringLE("ad1c2875de823a54188949490e2d68580fd070fcc5ff409609f478d23d12355f")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, expectHash, res.Hash())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "positive verbose",
|
||||||
|
invoke: func(c *Client) (any, error) {
|
||||||
|
hash, err := util.Uint256DecodeStringLE("ad1c2875de823a54188949490e2d68580fd070fcc5ff409609f478d23d12355f")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.GetRawNotaryTransactionVerbose(hash)
|
||||||
|
},
|
||||||
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xad1c2875de823a54188949490e2d68580fd070fcc5ff409609f478d23d12355f","size":61,"version":0,"nonce":3,"sender":"Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn","sysfee":"0","netfee":"0","validuntilblock":123,"attributes":[{"nkeys":1,"type":"NotaryAssisted"}],"signers":[{"account":"0xb248508f4ef7088e10c48f14d04be3272ca29eee","scopes":"None"}],"script":"QA==","witnesses":[{"invocation":"AQQH","verification":"AwYJ"}]}}`,
|
||||||
|
result: func(c *Client) any {
|
||||||
|
return &transaction.Transaction{}
|
||||||
|
},
|
||||||
|
check: func(t *testing.T, c *Client, uns any) {
|
||||||
|
res, ok := uns.(*transaction.Transaction)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.NotNil(t, res)
|
||||||
|
expectHash, err := util.Uint256DecodeStringLE("ad1c2875de823a54188949490e2d68580fd070fcc5ff409609f478d23d12355f")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, expectHash, res.Hash())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"getrawnotarypool": {
|
||||||
|
{
|
||||||
|
name: "empty pool",
|
||||||
|
invoke: func(c *Client) (any, error) {
|
||||||
|
return c.GetRawNotaryPool()
|
||||||
|
},
|
||||||
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{}}`,
|
||||||
|
result: func(c *Client) any {
|
||||||
|
return &result.RawNotaryPool{
|
||||||
|
Hashes: map[util.Uint256][]util.Uint256{},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonempty pool",
|
||||||
|
invoke: func(c *Client) (any, error) {
|
||||||
|
return c.GetRawNotaryPool()
|
||||||
|
},
|
||||||
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hashes":{"0xd86b5346e9bbe6dba845cc4192fa716535a3d05c4f2084431edc99dc3862a299":["0xbb0b2f1d5539dd776637f00e5011d97921a1400d3a63c02977a38446180c6d7c"]}}}`,
|
||||||
|
result: func(c *Client) any {
|
||||||
|
return &result.RawNotaryPool{
|
||||||
|
Hashes: map[util.Uint256][]util.Uint256{},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
check: func(t *testing.T, c *Client, uns any) {
|
||||||
|
res, ok := uns.(*result.RawNotaryPool)
|
||||||
|
require.True(t, ok)
|
||||||
|
mainHash, err := util.Uint256DecodeStringLE("d86b5346e9bbe6dba845cc4192fa716535a3d05c4f2084431edc99dc3862a299")
|
||||||
|
require.NoError(t, err, "can't decode `mainHash` result hash")
|
||||||
|
fallbackHash, err := util.Uint256DecodeStringLE("bb0b2f1d5539dd776637f00e5011d97921a1400d3a63c02977a38446180c6d7c")
|
||||||
|
require.NoError(t, err, "can't decode `fallbackHash` result hash")
|
||||||
|
fallbacks, ok := res.Hashes[mainHash]
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, fallbacks[0], fallbackHash)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcClientErrorCase struct {
|
type rpcClientErrorCase struct {
|
||||||
|
|
|
@ -1135,6 +1135,137 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetRawNotaryPoolAndTransaction(t *testing.T) {
|
||||||
|
var (
|
||||||
|
mainHash1, fallbackHash1, mainHash2, fallbackHash2 util.Uint256
|
||||||
|
tx1, tx2 *transaction.Transaction
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
t.Run("getrawnotarypool", func(t *testing.T) {
|
||||||
|
t.Run("empty pool", func(t *testing.T) {
|
||||||
|
np, err := c.GetRawNotaryPool()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(np.Hashes))
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
// Send the 1st notary request
|
||||||
|
tx1, err = neoW.SetRegisterPriceTransaction(1_0000_0000)
|
||||||
|
require.NoError(t, err)
|
||||||
|
mainHash1, fallbackHash1, _, err = nact.Notarize(tx1, err)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
checkTxInPool := func(t *testing.T, mainHash, fallbackHash util.Uint256, res *result.RawNotaryPool) {
|
||||||
|
actFallbacks, ok := res.Hashes[mainHash]
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
require.Equal(t, 1, len(actFallbacks))
|
||||||
|
require.Equal(t, fallbackHash, actFallbacks[0])
|
||||||
|
}
|
||||||
|
t.Run("nonempty pool", func(t *testing.T) {
|
||||||
|
actNotaryPool, err := c.GetRawNotaryPool()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(actNotaryPool.Hashes))
|
||||||
|
checkTxInPool(t, mainHash1, fallbackHash1, actNotaryPool)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Send the 2nd notary request
|
||||||
|
tx2, err = neoW.SetRegisterPriceTransaction(2_0000_0000)
|
||||||
|
require.NoError(t, err)
|
||||||
|
mainHash2, fallbackHash2, _, err = nact.Notarize(tx2, err)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("pool with 2", func(t *testing.T) {
|
||||||
|
actNotaryPool, err := c.GetRawNotaryPool()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(actNotaryPool.Hashes))
|
||||||
|
checkTxInPool(t, mainHash1, fallbackHash1, actNotaryPool)
|
||||||
|
checkTxInPool(t, mainHash2, fallbackHash2, actNotaryPool)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("getrawnotarytransaction", func(t *testing.T) {
|
||||||
|
t.Run("client GetRawNotaryTransaction", func(t *testing.T) {
|
||||||
|
t.Run("unknown transaction", func(t *testing.T) {
|
||||||
|
_, err := c.GetRawNotaryTransaction(util.Uint256{0, 0, 0})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorIs(t, err, neorpc.ErrUnknownTransaction)
|
||||||
|
})
|
||||||
|
_ = tx1.Size()
|
||||||
|
_ = tx2.Size()
|
||||||
|
// RPC server returns empty scripts in transaction.Witness,
|
||||||
|
// thus here the nil-value was changed to empty value.
|
||||||
|
if tx1.Scripts[1].InvocationScript == nil && tx1.Scripts[1].VerificationScript == nil {
|
||||||
|
tx1.Scripts[1] = transaction.Witness{
|
||||||
|
InvocationScript: []byte{},
|
||||||
|
VerificationScript: []byte{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tx2.Scripts[1].InvocationScript == nil && tx2.Scripts[1].VerificationScript == nil {
|
||||||
|
tx2.Scripts[1] = transaction.Witness{
|
||||||
|
InvocationScript: []byte{},
|
||||||
|
VerificationScript: []byte{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("transactions from pool", func(t *testing.T) {
|
||||||
|
mainTx1, err := c.GetRawNotaryTransaction(mainHash1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tx1, mainTx1)
|
||||||
|
_, err = c.GetRawNotaryTransaction(fallbackHash1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mainTx2, err := c.GetRawNotaryTransaction(mainHash2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tx2, mainTx2)
|
||||||
|
_, err = c.GetRawNotaryTransaction(fallbackHash2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("client GetRawNotaryTransactionVerbose", func(t *testing.T) {
|
||||||
|
t.Run("unknown transaction", func(t *testing.T) {
|
||||||
|
_, err := c.GetRawNotaryTransactionVerbose(util.Uint256{0, 0, 0})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorIs(t, err, neorpc.ErrUnknownTransaction)
|
||||||
|
})
|
||||||
|
t.Run("transactions from pool", func(t *testing.T) {
|
||||||
|
mainTx1, err := c.GetRawNotaryTransactionVerbose(mainHash1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tx1, mainTx1)
|
||||||
|
_, err = c.GetRawNotaryTransactionVerbose(fallbackHash1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mainTx2, err := c.GetRawNotaryTransactionVerbose(mainHash2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tx2, mainTx2)
|
||||||
|
_, err = c.GetRawNotaryTransactionVerbose(fallbackHash2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestCalculateNotaryFee(t *testing.T) {
|
func TestCalculateNotaryFee(t *testing.T) {
|
||||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
|
|
@ -229,6 +229,8 @@ var rpcHandlers = map[string]func(*Server, params.Params) (any, *neorpc.Error){
|
||||||
"getpeers": (*Server).getPeers,
|
"getpeers": (*Server).getPeers,
|
||||||
"getproof": (*Server).getProof,
|
"getproof": (*Server).getProof,
|
||||||
"getrawmempool": (*Server).getRawMempool,
|
"getrawmempool": (*Server).getRawMempool,
|
||||||
|
"getrawnotarypool": (*Server).getRawNotaryPool,
|
||||||
|
"getrawnotarytransaction": (*Server).getRawNotaryTransaction,
|
||||||
"getrawtransaction": (*Server).getrawtransaction,
|
"getrawtransaction": (*Server).getrawtransaction,
|
||||||
"getstate": (*Server).getState,
|
"getstate": (*Server).getState,
|
||||||
"getstateheight": (*Server).getStateHeight,
|
"getstateheight": (*Server).getStateHeight,
|
||||||
|
@ -3090,3 +3092,54 @@ func (s *Server) Addresses() []string {
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) getRawNotaryPool(_ params.Params) (any, *neorpc.Error) {
|
||||||
|
if !s.chain.P2PSigExtensionsEnabled() {
|
||||||
|
return nil, neorpc.NewInternalServerError("P2PSignatureExtensions are disabled")
|
||||||
|
}
|
||||||
|
nrp := s.coreServer.GetNotaryPool()
|
||||||
|
res := &result.RawNotaryPool{Hashes: make(map[util.Uint256][]util.Uint256)}
|
||||||
|
nrp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
|
||||||
|
if data != nil {
|
||||||
|
d := data.(*payload.P2PNotaryRequest)
|
||||||
|
mainHash := d.MainTransaction.Hash()
|
||||||
|
fallbackHash := d.FallbackTransaction.Hash()
|
||||||
|
res.Hashes[mainHash] = append(res.Hashes[mainHash], fallbackHash)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getRawNotaryTransaction(reqParams params.Params) (any, *neorpc.Error) {
|
||||||
|
if !s.chain.P2PSigExtensionsEnabled() {
|
||||||
|
return nil, neorpc.NewInternalServerError("P2PSignatureExtensions are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
txHash, err := reqParams.Value(0).GetUint256()
|
||||||
|
if err != nil {
|
||||||
|
return nil, neorpc.ErrInvalidParams
|
||||||
|
}
|
||||||
|
nrp := s.coreServer.GetNotaryPool()
|
||||||
|
// Try to find fallback transaction.
|
||||||
|
tx, ok := nrp.TryGetValue(txHash)
|
||||||
|
if !ok {
|
||||||
|
// Try to find main transaction.
|
||||||
|
nrp.IterateVerifiedTransactions(func(t *transaction.Transaction, data any) bool {
|
||||||
|
if data != nil && data.(*payload.P2PNotaryRequest).MainTransaction.Hash().Equals(txHash) {
|
||||||
|
tx = data.(*payload.P2PNotaryRequest).MainTransaction
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
// The transaction was not found.
|
||||||
|
if tx == nil {
|
||||||
|
return nil, neorpc.ErrUnknownTransaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
return tx.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
|
@ -2275,8 +2275,11 @@ func TestSubmitOracle(t *testing.T) {
|
||||||
t.Run("Valid", runCase(t, false, 0, pubStr, `1`, txSigStr, msgSigStr))
|
t.Run("Valid", runCase(t, false, 0, pubStr, `1`, txSigStr, msgSigStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubmitNotaryRequest(t *testing.T) {
|
func TestNotaryRequestRPC(t *testing.T) {
|
||||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitnotaryrequest", "params": %s}`
|
var notaryRequest1, notaryRequest2 *payload.P2PNotaryRequest
|
||||||
|
rpcSubmit := `{"jsonrpc": "2.0", "id": 1, "method": "submitnotaryrequest", "params": %s}`
|
||||||
|
rpcPool := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarypool", "params": []}`
|
||||||
|
rpcTx := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarytransaction", "params": ["%s", %d]}`
|
||||||
|
|
||||||
t.Run("disabled P2PSigExtensions", func(t *testing.T) {
|
t.Run("disabled P2PSigExtensions", func(t *testing.T) {
|
||||||
chain, rpcSrv, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
|
chain, rpcSrv, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
|
||||||
|
@ -2284,26 +2287,90 @@ func TestSubmitNotaryRequest(t *testing.T) {
|
||||||
})
|
})
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
defer rpcSrv.Shutdown()
|
defer rpcSrv.Shutdown()
|
||||||
req := fmt.Sprintf(rpc, "[]")
|
t.Run("submitnotaryrequest", func(t *testing.T) {
|
||||||
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
body := doRPCCallOverHTTP(fmt.Sprintf(rpcSubmit, "[]"), httpSrv.URL, t)
|
||||||
checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
|
checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
|
||||||
})
|
})
|
||||||
|
t.Run("getrawnotarypool", func(t *testing.T) {
|
||||||
|
body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
|
||||||
|
checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
|
||||||
|
})
|
||||||
|
t.Run("getrawnotarytransaction", func(t *testing.T) {
|
||||||
|
body := doRPCCallOverHTTP(fmt.Sprintf(rpcTx, " ", 1), httpSrv.URL, t)
|
||||||
|
checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
|
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
defer rpcSrv.Shutdown()
|
defer rpcSrv.Shutdown()
|
||||||
|
|
||||||
runCase := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) {
|
submitNotaryRequest := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
ps := `[` + strings.Join(params, ",") + `]`
|
ps := `[` + strings.Join(params, ",") + `]`
|
||||||
req := fmt.Sprintf(rpc, ps)
|
req := fmt.Sprintf(rpcSubmit, ps)
|
||||||
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||||
checkErrGetResult(t, body, fail, errCode)
|
checkErrGetResult(t, body, fail, errCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Run("missing request", runCase(t, true, neorpc.InvalidParamsCode))
|
|
||||||
t.Run("not a base64", runCase(t, true, neorpc.InvalidParamsCode, `"not-a-base64$"`))
|
t.Run("getrawnotarypool", func(t *testing.T) {
|
||||||
t.Run("invalid request bytes", runCase(t, true, neorpc.InvalidParamsCode, `"not-a-request"`))
|
t.Run("empty pool", func(t *testing.T) {
|
||||||
|
body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
|
||||||
|
res := checkErrGetResult(t, body, false, 0)
|
||||||
|
actual := new(result.RawNotaryPool)
|
||||||
|
require.NoError(t, json.Unmarshal(res, actual))
|
||||||
|
require.Equal(t, 0, len(actual.Hashes))
|
||||||
|
})
|
||||||
|
|
||||||
|
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
||||||
|
notaryRequest1 = createValidNotaryRequest(chain, sender, 1, 2_0000_0000, nil)
|
||||||
|
nrBytes, err := notaryRequest1.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
|
||||||
|
submitNotaryRequest(t, false, 0, str)(t)
|
||||||
|
|
||||||
|
t.Run("nonempty pool", func(t *testing.T) {
|
||||||
|
//get notary pool & check tx hashes
|
||||||
|
body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
|
||||||
|
res := checkErrGetResult(t, body, false, 0)
|
||||||
|
actual := new(result.RawNotaryPool)
|
||||||
|
require.NoError(t, json.Unmarshal(res, actual))
|
||||||
|
require.Equal(t, 1, len(actual.Hashes))
|
||||||
|
for actMain, actFallbacks := range actual.Hashes {
|
||||||
|
require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain)
|
||||||
|
require.Equal(t, 1, len(actFallbacks))
|
||||||
|
require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
notaryRequest2 = createValidNotaryRequest(chain, sender, 2, 3_0000_0000, notaryRequest1.MainTransaction)
|
||||||
|
nrBytes2, err := notaryRequest2.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
str2 := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes2))
|
||||||
|
submitNotaryRequest(t, false, 0, str2)(t)
|
||||||
|
|
||||||
|
t.Run("pool with 2", func(t *testing.T) {
|
||||||
|
//get notary pool & check tx hashes
|
||||||
|
body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
|
||||||
|
res := checkErrGetResult(t, body, false, 0)
|
||||||
|
actual := new(result.RawNotaryPool)
|
||||||
|
require.NoError(t, json.Unmarshal(res, actual))
|
||||||
|
require.Equal(t, 1, len(actual.Hashes))
|
||||||
|
for actMain, actFallbacks := range actual.Hashes {
|
||||||
|
require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain)
|
||||||
|
require.Equal(t, 2, len(actFallbacks))
|
||||||
|
// The second fallback transaction has higher priority, so it's first in the slice.
|
||||||
|
require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[1])
|
||||||
|
require.Equal(t, notaryRequest2.FallbackTransaction.Hash(), actFallbacks[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("submitnotaryrequest", func(t *testing.T) {
|
||||||
|
t.Run("missing request", submitNotaryRequest(t, true, neorpc.InvalidParamsCode))
|
||||||
|
t.Run("not a base64", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-base64$"`))
|
||||||
|
t.Run("invalid request bytes", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-request"`))
|
||||||
t.Run("invalid request", func(t *testing.T) {
|
t.Run("invalid request", func(t *testing.T) {
|
||||||
mainTx := &transaction.Transaction{
|
mainTx := &transaction.Transaction{
|
||||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||||
|
@ -2336,26 +2403,80 @@ func TestSubmitNotaryRequest(t *testing.T) {
|
||||||
VerificationScript: []byte{7, 8, 9},
|
VerificationScript: []byte{7, 8, 9},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bytes, err := p.Bytes()
|
nrBytes, err := p.Bytes()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(bytes))
|
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
|
||||||
runCase(t, true, neorpc.ErrVerificationFailedCode, str)(t)
|
submitNotaryRequest(t, true, neorpc.ErrVerificationFailedCode, str)(t)
|
||||||
})
|
})
|
||||||
t.Run("valid request", func(t *testing.T) {
|
t.Run("valid request", func(t *testing.T) {
|
||||||
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
|
||||||
p := createValidNotaryRequest(chain, sender, 1)
|
notaryRequest1 = createValidNotaryRequest(chain, sender, 3, 2_0000_0000, nil)
|
||||||
bytes, err := p.Bytes()
|
nrBytes, err := notaryRequest1.Bytes()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(bytes))
|
str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
|
||||||
runCase(t, false, 0, str)(t)
|
submitNotaryRequest(t, false, 0, str)(t)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("getrawnotarytransaction", func(t *testing.T) {
|
||||||
|
t.Run("invalid param", func(t *testing.T) {
|
||||||
|
req := fmt.Sprintf(rpcTx, "invalid", 1)
|
||||||
|
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||||
|
checkErrGetResult(t, body, true, neorpc.InvalidParamsCode)
|
||||||
|
})
|
||||||
|
t.Run("unknown transaction", func(t *testing.T) {
|
||||||
|
req := fmt.Sprintf(rpcTx, (util.Uint256{0, 0, 0}).StringLE(), 1)
|
||||||
|
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||||
|
checkErrGetResult(t, body, true, neorpc.ErrUnknownTransactionCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
checkGetTxVerbose := func(t *testing.T, tx *transaction.Transaction) {
|
||||||
|
req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 1)
|
||||||
|
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||||
|
res := checkErrGetResult(t, body, false, 0)
|
||||||
|
actual := new(transaction.Transaction)
|
||||||
|
require.NoError(t, json.Unmarshal(res, actual))
|
||||||
|
_ = tx.Size()
|
||||||
|
require.Equal(t, tx, actual)
|
||||||
|
}
|
||||||
|
t.Run("mainTx verbose", func(t *testing.T) {
|
||||||
|
checkGetTxVerbose(t, notaryRequest1.MainTransaction)
|
||||||
|
})
|
||||||
|
t.Run("fallbackTx verbose", func(t *testing.T) {
|
||||||
|
checkGetTxVerbose(t, notaryRequest1.FallbackTransaction)
|
||||||
|
checkGetTxVerbose(t, notaryRequest2.FallbackTransaction)
|
||||||
|
})
|
||||||
|
|
||||||
|
checkGetTxBytes := func(t *testing.T, tx *transaction.Transaction) {
|
||||||
|
req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 0)
|
||||||
|
body := doRPCCallOverHTTP(req, httpSrv.URL, t)
|
||||||
|
res := checkErrGetResult(t, body, false, 0)
|
||||||
|
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(res, &s)
|
||||||
|
require.NoErrorf(t, err, "could not parse response: %s", res)
|
||||||
|
txBin, err := testserdes.EncodeBinary(tx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
expected := base64.StdEncoding.EncodeToString(txBin)
|
||||||
|
assert.Equal(t, expected, s)
|
||||||
|
}
|
||||||
|
t.Run("mainTx bytes", func(t *testing.T) {
|
||||||
|
checkGetTxBytes(t, notaryRequest1.MainTransaction)
|
||||||
|
})
|
||||||
|
t.Run("fallbackTx bytes", func(t *testing.T) {
|
||||||
|
checkGetTxBytes(t, notaryRequest1.FallbackTransaction)
|
||||||
|
checkGetTxBytes(t, notaryRequest2.FallbackTransaction)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// createValidNotaryRequest creates and signs P2PNotaryRequest payload which can
|
// createValidNotaryRequest creates and signs P2PNotaryRequest payload which can
|
||||||
// pass verification.
|
// pass verification. It uses the provided mainTx if it's a nonempty structure.
|
||||||
func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, nonce uint32) *payload.P2PNotaryRequest {
|
func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, nonce uint32, networkFee int64, mainTx *transaction.Transaction) *payload.P2PNotaryRequest {
|
||||||
h := chain.BlockHeight()
|
h := chain.BlockHeight()
|
||||||
mainTx := &transaction.Transaction{
|
// If mainTx is nil, then generate it.
|
||||||
|
if mainTx == nil {
|
||||||
|
mainTx = &transaction.Transaction{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
|
||||||
Script: []byte{byte(opcode.RET)},
|
Script: []byte{byte(opcode.RET)},
|
||||||
|
@ -2366,6 +2487,7 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n
|
||||||
VerificationScript: []byte{3, 6, 9},
|
VerificationScript: []byte{3, 6, 9},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fallbackTx := &transaction.Transaction{
|
fallbackTx := &transaction.Transaction{
|
||||||
Script: []byte{byte(opcode.RET)},
|
Script: []byte{byte(opcode.RET)},
|
||||||
ValidUntilBlock: h + 100,
|
ValidUntilBlock: h + 100,
|
||||||
|
@ -2378,7 +2500,7 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n
|
||||||
Scripts: []transaction.Witness{
|
Scripts: []transaction.Witness{
|
||||||
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}},
|
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}},
|
||||||
},
|
},
|
||||||
NetworkFee: 2_0000_0000,
|
NetworkFee: networkFee,
|
||||||
}
|
}
|
||||||
fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{
|
fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{
|
||||||
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...),
|
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...),
|
||||||
|
|
|
@ -144,7 +144,7 @@ func TestSubscriptions(t *testing.T) {
|
||||||
|
|
||||||
// We should manually add NotaryRequest to test notification.
|
// We should manually add NotaryRequest to test notification.
|
||||||
sender := testchain.PrivateKeyByID(0)
|
sender := testchain.PrivateKeyByID(0)
|
||||||
err := rpcSrv.coreServer.RelayP2PNotaryRequest(createValidNotaryRequest(chain, sender, 1))
|
err := rpcSrv.coreServer.RelayP2PNotaryRequest(createValidNotaryRequest(chain, sender, 1, 2_0000_0000, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for {
|
for {
|
||||||
resp := getNotification(t, respMsgs)
|
resp := getNotification(t, respMsgs)
|
||||||
|
@ -390,7 +390,7 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
subID := callSubscribe(t, c, respMsgs, this.params)
|
subID := callSubscribe(t, c, respMsgs, this.params)
|
||||||
|
|
||||||
err := rpcSrv.coreServer.RelayP2PNotaryRequest(createValidNotaryRequest(chain, priv0, nonce))
|
err := rpcSrv.coreServer.RelayP2PNotaryRequest(createValidNotaryRequest(chain, priv0, nonce, 2_0000_0000, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
nonce++
|
nonce++
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue