Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Add custom marshalling and unmarshalling for block Header (#642)
Browse files Browse the repository at this point in the history
* Add custom marshalling and unmarshalling for block Header

* Change header uint64 and []byte to string

* Fix linter

* Add test for encoding decoding uint64

* Add additional test

* Use trimPrefix

* Remove mustDecodeUint64 method

* Inline return value

* Implement RemoveHexPrefixFromByteArray method

* Modify test for encodingUint64

* Fix lint

* Make HeaderJSON non exportable

* Add UnmarshallFromJSON test

* Remove testName

* Simplify uint64 encoding test

* Simplify header JSON test

* Simplify decodeuint64 test

* Add more sleek maxuint64

Co-authored-by: Milos Zivkovic <milos.zivkovic@mvpworkshop.co>
  • Loading branch information
0xAleksaOpacic and zivkovicmilos authored Jul 27, 2022
1 parent 3ec0ea3 commit f786241
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 1 deletion.
12 changes: 12 additions & 0 deletions helper/hex/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ func EncodeUint64(i uint64) string {
return string(strconv.AppendUint(enc, i, 16))
}

// DecodeUint64 decodes a hex string with 0x prefix to uint64
func DecodeUint64(hexStr string) (uint64, error) {
// remove 0x suffix if found in the input string
cleaned := strings.TrimPrefix(hexStr, "0x")

return strconv.ParseUint(cleaned, 16, 64)
}

const BadNibble = ^uint64(0)

// DecodeNibble decodes a byte into a uint64
Expand Down Expand Up @@ -85,3 +93,7 @@ func DecodeHexToBig(hexNum string) *big.Int {

return createdNum
}

func DropHexPrefix(input []byte) []byte {
return input[2:]
}
39 changes: 39 additions & 0 deletions helper/hex/hex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package hex

import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)

// TestDecodeUint64 verifies that uint64 values
// are properly decoded from hex
func TestDecodeUint64(t *testing.T) {
t.Parallel()

uint64Array := []uint64{
0,
1,
11,
67312,
80604,
^uint64(0), // max uint64
}

toHexArr := func(nums []uint64) []string {
numbers := make([]string, len(nums))

for index, num := range nums {
numbers[index] = fmt.Sprintf("0x%x", num)
}

return numbers
}

for index, value := range toHexArr(uint64Array) {
decodedValue, err := DecodeUint64(value)
assert.NoError(t, err)

assert.Equal(t, uint64Array[index], decodedValue)
}
}
104 changes: 104 additions & 0 deletions types/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package types
import (
"database/sql/driver"
"encoding/binary"
goHex "encoding/hex"
"encoding/json"
"errors"
"fmt"
"sync/atomic"
Expand Down Expand Up @@ -30,6 +32,97 @@ type Header struct {
Hash Hash `json:"hash"`
}

// headerJSON represents a block header used for json calls
type headerJSON struct {
ParentHash Hash `json:"parentHash"`
Sha3Uncles Hash `json:"sha3Uncles"`
Miner Address `json:"miner"`
StateRoot Hash `json:"stateRoot"`
TxRoot Hash `json:"transactionsRoot"`
ReceiptsRoot Hash `json:"receiptsRoot"`
LogsBloom Bloom `json:"logsBloom"`
Difficulty string `json:"difficulty"`
Number string `json:"number"`
GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"`
Timestamp string `json:"timestamp"`
ExtraData string `json:"extraData"`
MixHash Hash `json:"mixHash"`
Nonce Nonce `json:"nonce"`
Hash Hash `json:"hash"`
}

func (h *Header) MarshalJSON() ([]byte, error) {
var header headerJSON

header.ParentHash = h.ParentHash
header.Sha3Uncles = h.Sha3Uncles
header.Miner = h.Miner
header.StateRoot = h.StateRoot
header.TxRoot = h.TxRoot
header.ReceiptsRoot = h.ReceiptsRoot
header.LogsBloom = h.LogsBloom

header.MixHash = h.MixHash
header.Nonce = h.Nonce
header.Hash = h.Hash

header.Difficulty = hex.EncodeUint64(h.Difficulty)
header.Number = hex.EncodeUint64(h.Number)
header.GasLimit = hex.EncodeUint64(h.GasLimit)
header.GasUsed = hex.EncodeUint64(h.GasUsed)
header.Timestamp = hex.EncodeUint64(h.Timestamp)
header.ExtraData = hex.EncodeToHex(h.ExtraData)

return json.Marshal(&header)
}

func (h *Header) UnmarshalJSON(input []byte) error {
var header headerJSON
if err := json.Unmarshal(input, &header); err != nil {
return err
}

h.ParentHash = header.ParentHash
h.Sha3Uncles = header.Sha3Uncles
h.Miner = header.Miner
h.StateRoot = header.StateRoot
h.TxRoot = header.TxRoot
h.ReceiptsRoot = header.ReceiptsRoot
h.LogsBloom = header.LogsBloom
h.MixHash = header.MixHash
h.Nonce = header.Nonce
h.Hash = header.Hash

var err error

if h.Difficulty, err = hex.DecodeUint64(header.Difficulty); err != nil {
return err
}

if h.Number, err = hex.DecodeUint64(header.Number); err != nil {
return err
}

if h.GasLimit, err = hex.DecodeUint64(header.GasLimit); err != nil {
return err
}

if h.GasUsed, err = hex.DecodeUint64(header.GasUsed); err != nil {
return err
}

if h.Timestamp, err = hex.DecodeUint64(header.Timestamp); err != nil {
return err
}

if h.ExtraData, err = hex.DecodeHex(header.ExtraData); err != nil {
return err
}

return nil
}

func (h *Header) Equal(hh *Header) bool {
return h.Hash == hh.Hash
}
Expand Down Expand Up @@ -77,6 +170,17 @@ func (n Nonce) MarshalText() ([]byte, error) {
return []byte(n.String()), nil
}

func (n *Nonce) UnmarshalText(input []byte) error {
if _, err := goHex.Decode(
n[:],
hex.DropHexPrefix(input),
); err != nil {
return err
}

return nil
}

func (h *Header) Copy() *Header {
hh := new(Header)
*hh = *h
Expand Down
77 changes: 77 additions & 0 deletions types/header_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package types

import (
"encoding/json"
"github.com/stretchr/testify/assert"
"regexp"
"testing"
)

// TestHeader_JSON makes sure the Header is properly
// marshalled and unmarshalled from JSON
func TestHeader_JSON(t *testing.T) {
t.Parallel()

var (
// nolint:lll
headerJSON = `{
"parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000",
"sha3Uncles" : "0x0200000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0100000000000000000000000000000000000000",
"stateRoot" : "0x0400000000000000000000000000000000000000000000000000000000000000",
"transactionsRoot" : "0x0500000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot" : "0x0600000000000000000000000000000000000000000000000000000000000000",
"logsBloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty" : "0xa",
"number" : "0xb",
"gasLimit" : "0xc",
"gasUsed" : "0xd",
"timestamp" : "0xe",
"extraData":"0x616263646566",
"mixHash" : "0x0700000000000000000000000000000000000000000000000000000000000000",
"nonce" : "0x0a00000000000000",
"hash" : "0x0800000000000000000000000000000000000000000000000000000000000000"
}`
header = Header{
ParentHash: Hash{0x1},
Sha3Uncles: Hash{0x2},
Miner: Address{0x1},
StateRoot: Hash{0x4},
TxRoot: Hash{0x5},
ReceiptsRoot: Hash{0x6},
LogsBloom: Bloom{0x0},
Difficulty: 10,
Number: 11,
GasLimit: 12,
GasUsed: 13,
Timestamp: 14,
ExtraData: []byte{97, 98, 99, 100, 101, 102},
MixHash: Hash{0x7},
Nonce: Nonce{10},
Hash: Hash{0x8},
}
rg = regexp.MustCompile(`(\t|\n| )+`)
)

t.Run("Header marshalled to JSON", func(t *testing.T) {
t.Parallel()

marshalledHeader, err := json.Marshal(&header)
if err != nil {
t.Fatalf("Unable to marshal header, %v", err)
}

assert.Equal(t, rg.ReplaceAllString(headerJSON, ""), string(marshalledHeader))
})

t.Run("Header unmarshalled from JSON", func(t *testing.T) {
t.Parallel()

unmarshalledHeader := Header{}
if err := json.Unmarshal([]byte(headerJSON), &unmarshalledHeader); err != nil {
t.Fatalf("unable to unmarshall JSON, %v", err)
}

assert.Equal(t, header, unmarshalledHeader)
})
}
2 changes: 1 addition & 1 deletion types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const BloomByteLength = 256
type Bloom [BloomByteLength]byte

func (b *Bloom) UnmarshalText(input []byte) error {
input = input[2:]
input = hex.DropHexPrefix(input)
if _, err := goHex.Decode(b[:], input); err != nil {
return err
}
Expand Down

0 comments on commit f786241

Please sign in to comment.