forked from TrueCloudLab/neoneo-go
rpc/block: rework the way Block is JSONized
Our block.Block was JSONized in a bit different fashion than result.Block in its Nonce and NextConsensus fields. It's not good for notifications because third-party clients would probably expect to see the same format. Also, using completely different Block representation in result is probably making our client a bit weaker as this representation is harder to use with other neo-go components. So use the same approach we took for Transactions and wrap block.Base which is to be serialized in proper way.
This commit is contained in:
parent
83febead59
commit
9546e021a9
8 changed files with 267 additions and 94 deletions
|
@ -1,6 +1,7 @@
|
|||
package block
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
|
@ -17,10 +18,15 @@ type Block struct {
|
|||
Base
|
||||
|
||||
// Transaction list.
|
||||
Transactions []*transaction.Transaction `json:"tx"`
|
||||
Transactions []*transaction.Transaction
|
||||
|
||||
// True if this block is created from trimmed data.
|
||||
Trimmed bool `json:"-"`
|
||||
Trimmed bool
|
||||
}
|
||||
|
||||
// auxTxes is used for JSON i/o.
|
||||
type auxTxes struct {
|
||||
Transactions []*transaction.Transaction `json:"tx"`
|
||||
}
|
||||
|
||||
// Header returns the Header of the Block.
|
||||
|
@ -149,3 +155,42 @@ func (b *Block) Compare(item queue.Item) int {
|
|||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (b Block) MarshalJSON() ([]byte, error) {
|
||||
txes, err := json.Marshal(auxTxes{b.Transactions})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseBytes, err := json.Marshal(b.Base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stitch them together.
|
||||
if baseBytes[len(baseBytes)-1] != '}' || txes[0] != '{' {
|
||||
return nil, errors.New("can't merge internal jsons")
|
||||
}
|
||||
baseBytes[len(baseBytes)-1] = ','
|
||||
baseBytes = append(baseBytes, txes[1:]...)
|
||||
return baseBytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (b *Block) UnmarshalJSON(data []byte) error {
|
||||
// As Base and txes are at the same level in json,
|
||||
// do unmarshalling separately for both structs.
|
||||
txes := new(auxTxes)
|
||||
err := json.Unmarshal(data, txes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base := new(Base)
|
||||
err = json.Unmarshal(data, base)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Base = *base
|
||||
b.Transactions = txes.Transactions
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package block
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
@ -12,33 +16,33 @@ import (
|
|||
// Base holds the base info of a block
|
||||
type Base struct {
|
||||
// Version of the block.
|
||||
Version uint32 `json:"version"`
|
||||
Version uint32
|
||||
|
||||
// hash of the previous block.
|
||||
PrevHash util.Uint256 `json:"previousblockhash"`
|
||||
PrevHash util.Uint256
|
||||
|
||||
// Root hash of a transaction list.
|
||||
MerkleRoot util.Uint256 `json:"merkleroot"`
|
||||
MerkleRoot util.Uint256
|
||||
|
||||
// The time stamp of each block must be later than previous block's time stamp.
|
||||
// Generally the difference of two block's time stamp is about 15 seconds and imprecision is allowed.
|
||||
// The height of the block must be exactly equal to the height of the previous block plus 1.
|
||||
Timestamp uint32 `json:"time"`
|
||||
Timestamp uint32
|
||||
|
||||
// index/height of the block
|
||||
Index uint32 `json:"height"`
|
||||
Index uint32
|
||||
|
||||
// Random number also called nonce
|
||||
ConsensusData uint64 `json:"nonce"`
|
||||
ConsensusData uint64
|
||||
|
||||
// Contract address of the next miner
|
||||
NextConsensus util.Uint160 `json:"next_consensus"`
|
||||
NextConsensus util.Uint160
|
||||
|
||||
// Padding that is fixed to 1
|
||||
_ uint8
|
||||
|
||||
// Script used to validate the block
|
||||
Script transaction.Witness `json:"script"`
|
||||
Script transaction.Witness
|
||||
|
||||
// Hash of this block, created when binary encoded (double SHA256).
|
||||
hash util.Uint256
|
||||
|
@ -47,6 +51,21 @@ type Base struct {
|
|||
verificationHash util.Uint256
|
||||
}
|
||||
|
||||
// baseAux is used to marshal/unmarshal to/from JSON, it's almost the same
|
||||
// as original Base, but with Nonce and NextConsensus fields differing and
|
||||
// Hash added.
|
||||
type baseAux struct {
|
||||
Hash util.Uint256 `json:"hash"`
|
||||
Version uint32 `json:"version"`
|
||||
PrevHash util.Uint256 `json:"previousblockhash"`
|
||||
MerkleRoot util.Uint256 `json:"merkleroot"`
|
||||
Timestamp uint32 `json:"time"`
|
||||
Index uint32 `json:"index"`
|
||||
Nonce string `json:"nonce"`
|
||||
NextConsensus string `json:"nextconsensus"`
|
||||
Script transaction.Witness `json:"script"`
|
||||
}
|
||||
|
||||
// Verify verifies the integrity of the Base.
|
||||
func (b *Base) Verify() bool {
|
||||
// TODO: Need a persisted blockchain for this.
|
||||
|
@ -140,3 +159,56 @@ func (b *Base) decodeHashableFields(br *io.BinReader) {
|
|||
b.createHash()
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (b Base) MarshalJSON() ([]byte, error) {
|
||||
nonce := strconv.FormatUint(b.ConsensusData, 16)
|
||||
for len(nonce) < 16 {
|
||||
nonce = "0" + nonce
|
||||
}
|
||||
aux := baseAux{
|
||||
Hash: b.Hash(),
|
||||
Version: b.Version,
|
||||
PrevHash: b.PrevHash,
|
||||
MerkleRoot: b.MerkleRoot,
|
||||
Timestamp: b.Timestamp,
|
||||
Index: b.Index,
|
||||
Nonce: nonce,
|
||||
NextConsensus: address.Uint160ToString(b.NextConsensus),
|
||||
Script: b.Script,
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (b *Base) UnmarshalJSON(data []byte) error {
|
||||
var aux = new(baseAux)
|
||||
var nonce uint64
|
||||
var nextC util.Uint160
|
||||
|
||||
err := json.Unmarshal(data, aux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce, err = strconv.ParseUint(aux.Nonce, 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nextC, err = address.StringToUint160(aux.NextConsensus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Version = aux.Version
|
||||
b.PrevHash = aux.PrevHash
|
||||
b.MerkleRoot = aux.MerkleRoot
|
||||
b.Timestamp = aux.Timestamp
|
||||
b.Index = aux.Index
|
||||
b.ConsensusData = nonce
|
||||
b.NextConsensus = nextC
|
||||
b.Script = aux.Script
|
||||
if !aux.Hash.Equals(b.Hash()) {
|
||||
return errors.New("json 'hash' doesn't match block hash")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -188,6 +188,8 @@ func TestBinBlockDecodeEncode(t *testing.T) {
|
|||
data, err := testserdes.EncodeBinary(&b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rawtx, hex.EncodeToString(data))
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, &b, new(Block))
|
||||
}
|
||||
|
||||
func TestBlockSizeCalculation(t *testing.T) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"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/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -158,10 +160,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xe93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c","size":452,"version":0,"nextblockhash":"0xcc37d5bc460e72c9423015cb8d579c13e7b03b93bfaa1a23cf4fa777988e035f","previousblockhash":"0x996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099","merkleroot":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","time":1541215200,"index":1,"nonce":"51b484a2fe49ed4d","nextconsensus":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","confirmations":10534,"script":{"invocation":"40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"txid":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","size":10,"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[],"sys_fee":"0","net_fee":"0","nonce":4266257741}]}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nextBlockHash, err := util.Uint256DecodeStringLE("cc37d5bc460e72c9423015cb8d579c13e7b03b93bfaa1a23cf4fa777988e035f")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -192,31 +190,48 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
Scripts: []transaction.Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
return &result.Block{
|
||||
Hash: hash,
|
||||
Size: 452,
|
||||
Version: 0,
|
||||
NextBlockHash: &nextBlockHash,
|
||||
PreviousBlockHash: prevBlockHash,
|
||||
MerkleRoot: merkleRoot,
|
||||
Time: 1541215200,
|
||||
Index: 1,
|
||||
Nonce: "51b484a2fe49ed4d",
|
||||
NextConsensus: "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU",
|
||||
Confirmations: 10534,
|
||||
var nonce uint64
|
||||
i, err := fmt.Sscanf("51b484a2fe49ed4d", "%016x", &nonce)
|
||||
if i != 1 {
|
||||
panic("can't decode nonce")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nextCon, err := address.StringToUint160("AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
base := &block.Base{
|
||||
Version: 0,
|
||||
PrevHash: prevBlockHash,
|
||||
MerkleRoot: merkleRoot,
|
||||
Timestamp: 1541215200,
|
||||
Index: 1,
|
||||
ConsensusData: nonce,
|
||||
NextConsensus: nextCon,
|
||||
Script: transaction.Witness{
|
||||
InvocationScript: invScript,
|
||||
VerificationScript: verifScript,
|
||||
},
|
||||
Tx: []result.Tx{{
|
||||
Transaction: tx,
|
||||
Fees: result.Fees{
|
||||
SysFee: 0,
|
||||
NetFee: 0,
|
||||
},
|
||||
}},
|
||||
}
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
_ = base.Hash()
|
||||
return &result.Block{
|
||||
Base: base,
|
||||
BlockMetadataAndTx: result.BlockMetadataAndTx{
|
||||
Size: 452,
|
||||
Confirmations: 10534,
|
||||
NextBlockHash: &nextBlockHash,
|
||||
Tx: []result.Tx{{
|
||||
Transaction: tx,
|
||||
Fees: result.Fees{
|
||||
SysFee: 0,
|
||||
NetFee: 0,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -253,10 +268,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xe93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c","size":452,"version":0,"nextblockhash":"0xcc37d5bc460e72c9423015cb8d579c13e7b03b93bfaa1a23cf4fa777988e035f","previousblockhash":"0x996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099","merkleroot":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","time":1541215200,"index":1,"nonce":"51b484a2fe49ed4d","nextconsensus":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","confirmations":10534,"script":{"invocation":"40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"txid":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","size":10,"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[],"sys_fee":"0","net_fee":"0","nonce":4266257741}]}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nextBlockHash, err := util.Uint256DecodeStringLE("cc37d5bc460e72c9423015cb8d579c13e7b03b93bfaa1a23cf4fa777988e035f")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -277,6 +288,18 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var nonce uint64
|
||||
i, err := fmt.Sscanf("51b484a2fe49ed4d", "%016x", &nonce)
|
||||
if i != 1 {
|
||||
panic("can't decode nonce")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nextCon, err := address.StringToUint160("AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tx := &transaction.Transaction{
|
||||
Type: transaction.MinerType,
|
||||
Version: 0,
|
||||
|
@ -287,31 +310,36 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
Scripts: []transaction.Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
return &result.Block{
|
||||
Hash: hash,
|
||||
Size: 452,
|
||||
Version: 0,
|
||||
NextBlockHash: &nextBlockHash,
|
||||
PreviousBlockHash: prevBlockHash,
|
||||
MerkleRoot: merkleRoot,
|
||||
Time: 1541215200,
|
||||
Index: 1,
|
||||
Nonce: "51b484a2fe49ed4d",
|
||||
NextConsensus: "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU",
|
||||
Confirmations: 10534,
|
||||
base := &block.Base{
|
||||
Version: 0,
|
||||
PrevHash: prevBlockHash,
|
||||
MerkleRoot: merkleRoot,
|
||||
Timestamp: 1541215200,
|
||||
Index: 1,
|
||||
ConsensusData: nonce,
|
||||
NextConsensus: nextCon,
|
||||
Script: transaction.Witness{
|
||||
InvocationScript: invScript,
|
||||
VerificationScript: verifScript,
|
||||
},
|
||||
Tx: []result.Tx{{
|
||||
Transaction: tx,
|
||||
Fees: result.Fees{
|
||||
SysFee: 0,
|
||||
NetFee: 0,
|
||||
},
|
||||
}},
|
||||
}
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
_ = base.Hash()
|
||||
return &result.Block{
|
||||
Base: base,
|
||||
BlockMetadataAndTx: result.BlockMetadataAndTx{
|
||||
Size: 452,
|
||||
Confirmations: 10534,
|
||||
NextBlockHash: &nextBlockHash,
|
||||
Tx: []result.Tx{{
|
||||
Transaction: tx,
|
||||
Fees: result.Fees{
|
||||
SysFee: 0,
|
||||
NetFee: 0,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -116,7 +116,7 @@ func TestWSClientEvents(t *testing.T) {
|
|||
`{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7","executions":[{"trigger":"Application","contract":"0x0000000000000000000000000000000000000000","vmstate":"HALT","gas_consumed":"1.048","stack":[{"type":"Integer","value":"1"}],"notifications":[{"contract":"0xc2789e5ab9bab828743833965b1df0d5fbcc206f","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"507574"},{"type":"Array","value":[{"type":"ByteArray","value":"746573746b6579"},{"type":"ByteArray","value":"7465737476616c7565"}]}]}}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0xc2789e5ab9bab828743833965b1df0d5fbcc206f","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"507574"},{"type":"Array","value":[{"type":"ByteArray","value":"746573746b6579"},{"type":"ByteArray","value":"7465737476616c7565"}]}]}}]}`,
|
||||
`{"jsonrpc":"2.0","method":"transaction_added","params":[{"txid":"0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7","size":60,"type":"InvocationTransaction","version":1,"attributes":[],"vin":[],"vout":[],"scripts":[],"script":"097465737476616c756507746573746b657952c103507574676f20ccfbd5f01d5b9633387428b8bab95a9e78c2"}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"version":0,"previousblockhash":"0x33f3e0e24542b2ec3b6420e6881c31f6460a39a4e733d88f7557cbcc3b5ed560","merkleroot":"0x9d922c5cfd4c8cd1da7a6b2265061998dc438bd0dea7145192e2858155e6c57a","time":1586154525,"height":205,"nonce":1111,"next_consensus":"0xa21e4f7178607089e4fe9fab1300d1f5a3d348be","script":{"invocation":"4047a444a51218ac856f1cbc629f251c7c88187910534d6ba87847c86a9a73ed4951d203fd0a87f3e65657a7259269473896841f65c0a0c8efc79d270d917f4ff640435ee2f073c94a02f0276dfe4465037475e44e1c34c0decb87ec9c2f43edf688059fc4366a41c673d72ba772b4782c39e79f01cb981247353216d52d2df1651140527eb0dfd80a800fdd7ac8fbe68fc9366db2d71655d8ba235525a97a69a7181b1e069b82091be711c25e504a17c3c55eee6e76e6af13cb488fbe35d5c5d025c34041f39a02ebe9bb08be0e4aaa890f447dc9453209bbfb4705d8f2d869c2b55ee2d41dbec2ee476a059d77fb7c26400284328d05aece5f3168b48f1db1c6f7be0b","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"txid":"0xf9adfde059810f37b3d0686d67f6b29034e0c669537df7e59b40c14a0508b9ed","size":10,"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[]},{"txid":"0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7","size":60,"type":"InvocationTransaction","version":1,"attributes":[],"vin":[],"vout":[],"scripts":[],"script":"097465737476616c756507746573746b657952c103507574676f20ccfbd5f01d5b9633387428b8bab95a9e78c2"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x48fba8aebf88278818a3dc0caecb230873d1d4ce1ea8bf473634317f94a609e5","version":0,"previousblockhash":"0x33f3e0e24542b2ec3b6420e6881c31f6460a39a4e733d88f7557cbcc3b5ed560","merkleroot":"0x9d922c5cfd4c8cd1da7a6b2265061998dc438bd0dea7145192e2858155e6c57a","time":1586154525,"index":205,"nonce":"0000000000000457","nextconsensus":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","script":{"invocation":"4047a444a51218ac856f1cbc629f251c7c88187910534d6ba87847c86a9a73ed4951d203fd0a87f3e65657a7259269473896841f65c0a0c8efc79d270d917f4ff640435ee2f073c94a02f0276dfe4465037475e44e1c34c0decb87ec9c2f43edf688059fc4366a41c673d72ba772b4782c39e79f01cb981247353216d52d2df1651140527eb0dfd80a800fdd7ac8fbe68fc9366db2d71655d8ba235525a97a69a7181b1e069b82091be711c25e504a17c3c55eee6e76e6af13cb488fbe35d5c5d025c34041f39a02ebe9bb08be0e4aaa890f447dc9453209bbfb4705d8f2d869c2b55ee2d41dbec2ee476a059d77fb7c26400284328d05aece5f3168b48f1db1c6f7be0b","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"txid":"0xf9adfde059810f37b3d0686d67f6b29034e0c669537df7e59b40c14a0508b9ed","size":10,"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[]},{"txid":"0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7","size":60,"type":"InvocationTransaction","version":1,"attributes":[],"vin":[],"vout":[],"scripts":[],"script":"097465737476616c756507746573746b657952c103507574676f20ccfbd5f01d5b9633387428b8bab95a9e78c2"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -3,12 +3,10 @@ package result
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
@ -30,42 +28,29 @@ type (
|
|||
// Block wrapper used for the representation of
|
||||
// block.Block / block.Base on the RPC Server.
|
||||
Block struct {
|
||||
Hash util.Uint256 `json:"hash"`
|
||||
Size int `json:"size"`
|
||||
Version uint32 `json:"version"`
|
||||
NextBlockHash *util.Uint256 `json:"nextblockhash,omitempty"`
|
||||
PreviousBlockHash util.Uint256 `json:"previousblockhash"`
|
||||
MerkleRoot util.Uint256 `json:"merkleroot"`
|
||||
Time uint32 `json:"time"`
|
||||
Index uint32 `json:"index"`
|
||||
Nonce string `json:"nonce"`
|
||||
NextConsensus string `json:"nextconsensus"`
|
||||
*block.Base
|
||||
BlockMetadataAndTx
|
||||
}
|
||||
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
|
||||
Script transaction.Witness `json:"script"`
|
||||
|
||||
Tx []Tx `json:"tx"`
|
||||
// BlockMetadataAndTx is an additional metadata added to standard
|
||||
// block.Base plus specially encoded transactions.
|
||||
BlockMetadataAndTx struct {
|
||||
Size int `json:"size"`
|
||||
NextBlockHash *util.Uint256 `json:"nextblockhash,omitempty"`
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
Tx []Tx `json:"tx"`
|
||||
}
|
||||
)
|
||||
|
||||
// NewBlock creates a new Block wrapper.
|
||||
func NewBlock(b *block.Block, chain core.Blockchainer) Block {
|
||||
res := Block{
|
||||
Version: b.Version,
|
||||
Hash: b.Hash(),
|
||||
Size: io.GetVarSize(b),
|
||||
PreviousBlockHash: b.PrevHash,
|
||||
MerkleRoot: b.MerkleRoot,
|
||||
Time: b.Timestamp,
|
||||
Index: b.Index,
|
||||
Nonce: fmt.Sprintf("%016x", b.ConsensusData),
|
||||
NextConsensus: address.Uint160ToString(b.NextConsensus),
|
||||
Confirmations: chain.BlockHeight() - b.Index - 1,
|
||||
|
||||
Script: b.Script,
|
||||
|
||||
Tx: make([]Tx, 0, len(b.Transactions)),
|
||||
Base: &b.Base,
|
||||
BlockMetadataAndTx: BlockMetadataAndTx{
|
||||
Size: io.GetVarSize(b),
|
||||
Confirmations: chain.BlockHeight() - b.Index - 1,
|
||||
Tx: make([]Tx, 0, len(b.Transactions)),
|
||||
},
|
||||
}
|
||||
|
||||
hash := chain.GetHeaderHash(int(b.Index) + 1)
|
||||
|
@ -130,3 +115,44 @@ func (t *Tx) UnmarshalJSON(data []byte) error {
|
|||
t.Transaction = transaction
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (b Block) MarshalJSON() ([]byte, error) {
|
||||
output, err := json.Marshal(b.BlockMetadataAndTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseBytes, err := json.Marshal(b.Base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We have to keep both "fields" at the same level in json in order to
|
||||
// match C# API, so there's no way to marshall Block correctly with
|
||||
// standard json.Marshaller tool.
|
||||
if output[len(output)-1] != '}' || baseBytes[0] != '{' {
|
||||
return nil, errors.New("can't merge internal jsons")
|
||||
}
|
||||
output[len(output)-1] = ','
|
||||
output = append(output, baseBytes[1:]...)
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (b *Block) UnmarshalJSON(data []byte) error {
|
||||
// As block.Base and BlockMetadataAndTx are at the same level in json,
|
||||
// do unmarshalling separately for both structs.
|
||||
metaTx := new(BlockMetadataAndTx)
|
||||
base := new(block.Base)
|
||||
err := json.Unmarshal(data, metaTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, base)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Base = base
|
||||
b.BlockMetadataAndTx = *metaTx
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -336,7 +336,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(2))
|
||||
require.NoErrorf(t, err, "could not get block")
|
||||
|
||||
assert.Equal(t, block.Hash(), res.Hash)
|
||||
assert.Equal(t, block.Hash(), res.Hash())
|
||||
for i := range res.Tx {
|
||||
tx := res.Tx[i]
|
||||
require.Equal(t, transaction.MinerType, tx.Transaction.Type)
|
||||
|
|
|
@ -199,7 +199,7 @@ func TestFilteredSubscriptions(t *testing.T) {
|
|||
resp := getNotification(t, respMsgs)
|
||||
rmap := resp.Payload[0].(map[string]interface{})
|
||||
if resp.Event == response.BlockEventID {
|
||||
index := rmap["height"].(float64)
|
||||
index := rmap["index"].(float64)
|
||||
if uint32(index) == lastBlock {
|
||||
break
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue