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

Add custom marshalling and unmarshalling for block Header #642

Merged
merged 20 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions helper/hex/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,31 @@ 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")

// base 16 for hexadecimal
result, err := strconv.ParseUint(cleaned, 16, 64)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return 0, err
}

return result, nil
}

// MustDecodeUint64 decodes a hex string with 0x prefix to uint64
// Returns panic if there is an error
func MustDecodeUint64(hexStr string) uint64 {
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
decodedValue, err := DecodeUint64(hexStr)
if err != nil {
panic(fmt.Errorf("could not decode hex: %w", err))
}

return decodedValue
}

const BadNibble = ^uint64(0)

// DecodeNibble decodes a byte into a uint64
Expand Down
42 changes: 42 additions & 0 deletions helper/hex/hex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package hex

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

func TestEncodingUint64(t *testing.T) {
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
testTable := []struct {
name string
value uint64
shouldFail bool
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
}{
{
"Successfully encode and decode uint64",
245,
false,
},
{
"Successfully encode and decode uint64",
0,
false,
},
}

for _, testCase := range testTable {
t.Run(testCase.name, func(t *testing.T) {
encodedValue := EncodeUint64(testCase.value)

decodedValue, err := DecodeUint64(encodedValue)
if err != nil && !testCase.shouldFail {
t.Fatalf("Unable to decode uint64, %v", err)
}

assert.Equal(t, testCase.value, decodedValue)

decodedValue = MustDecodeUint64(encodedValue)

assert.Equal(t, testCase.value, decodedValue)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
})
}
}
108 changes: 108 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,103 @@ type Header struct {
Hash Hash `json:"hash"`
}

// HeaderJson represents a block header used for json calls
type HeaderJSON struct {
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
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

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

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

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

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

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

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

return nil
}

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

func (n *Nonce) UnmarshalText(input []byte) error {
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
input = input[2:]
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
if _, err := goHex.Decode(n[:], input); err != nil {
return err
}

return nil
}

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

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

func TestHeaderType_Encode(t *testing.T) {
header := Header{
ParentHash: Hash{0x1},
Sha3Uncles: Hash{0x2},
Miner: Address{0x1},
StateRoot: Hash{0x4},
TxRoot: Hash{0x5},
ReceiptsRoot: Hash{0x6},
LogsBloom: Bloom{0x1},
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},
}

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

unmarshallHeader := Header{}
assert.NoError(t, json.Unmarshal(headerJSON, &unmarshallHeader))

if !reflect.DeepEqual(unmarshallHeader, header) {
t.Fatal("Resulting data and expected values are not equal")
}
}
Kourin1996 marked this conversation as resolved.
Show resolved Hide resolved