diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 097543292..4641f2517 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -264,17 +264,12 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ result: func(c *Client) interface{} { b := getResultBlock1() return &result.Header{ - Hash: b.Hash(), - Size: 457, - Version: b.Version, - NextBlockHash: b.NextBlockHash, - PrevBlockHash: b.PrevHash, - MerkleRoot: b.MerkleRoot, - Timestamp: b.Timestamp, - Index: b.Index, - NextConsensus: address.Uint160ToString(b.NextConsensus), - Witnesses: []transaction.Witness{b.Script}, - Confirmations: b.Confirmations, + Header: b.Header, + BlockMetadata: result.BlockMetadata{ + Size: 457, + NextBlockHash: b.NextBlockHash, + Confirmations: b.Confirmations, + }, } }, }, diff --git a/pkg/rpc/response/result/block_header.go b/pkg/rpc/response/result/block_header.go index 6e087883e..c68e7b38a 100644 --- a/pkg/rpc/response/result/block_header.go +++ b/pkg/rpc/response/result/block_header.go @@ -1,10 +1,11 @@ package result import ( + "encoding/json" + "errors" + "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" - "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" ) @@ -13,33 +14,19 @@ type ( // Header wrapper used for the representation of // block header on the RPC Server. Header struct { - Hash util.Uint256 `json:"hash"` - Size int `json:"size"` - Version uint32 `json:"version"` - PrevBlockHash util.Uint256 `json:"previousblockhash"` - MerkleRoot util.Uint256 `json:"merkleroot"` - Timestamp uint64 `json:"time"` - Index uint32 `json:"index"` - NextConsensus string `json:"nextconsensus"` - Witnesses []transaction.Witness `json:"witnesses"` - Confirmations uint32 `json:"confirmations"` - NextBlockHash *util.Uint256 `json:"nextblockhash,omitempty"` + block.Header + BlockMetadata } ) // NewHeader creates a new Header wrapper. func NewHeader(h *block.Header, chain blockchainer.Blockchainer) Header { res := Header{ - Hash: h.Hash(), - Size: io.GetVarSize(h), - Version: h.Version, - PrevBlockHash: h.PrevHash, - MerkleRoot: h.MerkleRoot, - Timestamp: h.Timestamp, - Index: h.Index, - NextConsensus: address.Uint160ToString(h.NextConsensus), - Witnesses: []transaction.Witness{h.Script}, - Confirmations: chain.BlockHeight() - h.Index + 1, + Header: *h, + BlockMetadata: BlockMetadata{ + Size: io.GetVarSize(h), + Confirmations: chain.BlockHeight() - h.Index + 1, + }, } hash := chain.GetHeaderHash(int(h.Index) + 1) @@ -48,3 +35,42 @@ func NewHeader(h *block.Header, chain blockchainer.Blockchainer) Header { } return res } + +// MarshalJSON implements json.Marshaler interface. +func (h Header) MarshalJSON() ([]byte, error) { + output, err := json.Marshal(h.BlockMetadata) + if err != nil { + return nil, err + } + baseBytes, err := json.Marshal(h.Header) + 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 (h *Header) UnmarshalJSON(data []byte) error { + // As block.Block and BlockMetadata are at the same level in json, + // do unmarshalling separately for both structs. + meta := new(BlockMetadata) + err := json.Unmarshal(data, meta) + if err != nil { + return err + } + err = json.Unmarshal(data, &h.Header) + if err != nil { + return err + } + h.BlockMetadata = *meta + return nil +} diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index c7f11b944..66260c311 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -1455,17 +1455,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] t.Run("verbose != 0", func(t *testing.T) { nextHash := chain.GetHeaderHash(int(hdr.Index) + 1) expected := &result.Header{ - Hash: hdr.Hash(), - Size: io.GetVarSize(hdr), - Version: hdr.Version, - PrevBlockHash: hdr.PrevHash, - MerkleRoot: hdr.MerkleRoot, - Timestamp: hdr.Timestamp, - Index: hdr.Index, - NextConsensus: address.Uint160ToString(hdr.NextConsensus), - Witnesses: []transaction.Witness{hdr.Script}, - Confirmations: e.chain.BlockHeight() - hdr.Index + 1, - NextBlockHash: &nextHash, + Header: *hdr, + BlockMetadata: result.BlockMetadata{ + Size: io.GetVarSize(hdr), + NextBlockHash: &nextHash, + Confirmations: e.chain.BlockHeight() - hdr.Index + 1, + }, } rpc := fmt.Sprintf(rpc, `["`+testHeaderHash+`", 2]`)