Repository: NethermindEth/juno
Pull request #541 (2023-02-02 10:31:40)

Configure cbor to allow unmarshaling specified types to interfaces they implement
For example;

``` golang

type A struct {
	B string
	C uint
}

func TestA(t *testing.T) {
	inst := A{"hello", 42}

//	encoder.RegisterType(reflect.TypeOf(inst))

	data, _ := encoder.Marshal(inst)
	var recovered any
	encoder.Unmarshal(data, &recovered)

	switch v := recovered.(type) {
	case A:
		t.Error("success:", v)
	default:
		panic(v)
	}
}
```

if `RegisterType` is not called, test will panic at the type switch. If type `A` is registered, we will be able to cast `recovered` to `A` in runtime.

This helps us to have a `[]Transaction` in our block body and allows us to cast them to our individual transaction types in runtime.

codecov[bot]: # [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) Report
Base: **74.42**% // Head: **74.03**% // Decreases project coverage by **`-0.39%`** :warning:
> Coverage data is based on head [(`4557887`)](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) compared to base [(`d15139f`)](https://codecov.io/gh/NethermindEth/juno/commit/d15139f46f1982045525e199fb3677237c1a2d1e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).
> Patch coverage: 76.92% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop     #541      +/-   ##
===========================================
- Coverage    74.42%   74.03%   -0.39%     
===========================================
  Files           22       23       +1     
  Lines         1642     1633       -9     
===========================================
- Hits          1222     1209      -13     
- Misses         315      316       +1     
- Partials       105      108       +3     
```


| [Impacted Files](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) | Coverage Δ | |
|---|---|---|
| [core/transaction.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-Y29yZS90cmFuc2FjdGlvbi5nbw==) | `84.93% <40.00%> (-7.14%)` | :arrow_down: |
| [blockchain/blockchain.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-YmxvY2tjaGFpbi9ibG9ja2NoYWluLmdv) | `78.86% <50.00%> (ø)` | |
| [encoder/encoder.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-ZW5jb2Rlci9lbmNvZGVyLmdv) | `79.41% <79.41%> (ø)` | |
| [core/trie/trie.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-Y29yZS90cmllL3RyaWUuZ28=) | `71.83% <85.18%> (ø)` | |
| [core/trie/node.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-Y29yZS90cmllL25vZGUuZ28=) | `82.60% <100.00%> (-0.29%)` | :arrow_down: |
| [core/trie/trietxn.go](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-Y29yZS90cmllL3RyaWV0eG4uZ28=) | `60.52% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/NethermindEth/juno/pull/541?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).

omerfirmak: When requesting cosmetic or nitpick changes, please approve the PR. So that we don't have to wait for a second round of reviews to merge a PR.

Patch:
diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go
index e3d14af0..7a381191 100644
--- a/blockchain/blockchain.go
+++ b/blockchain/blockchain.go
@@ -9,8 +9,8 @@ import (
 	"github.com/NethermindEth/juno/core/felt"
 	"github.com/NethermindEth/juno/core/state"
 	"github.com/NethermindEth/juno/db"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/NethermindEth/juno/utils"
-	"github.com/fxamacker/cbor/v2"
 )
 
 type ErrIncompatibleBlockAndStateUpdate struct {
@@ -94,7 +94,7 @@ func (b *Blockchain) head(txn db.Transaction) (*core.Block, error) {
 	}
 
 	headBlock := new(core.Block)
-	if err = cbor.Unmarshal(headBlockBin, headBlock); err != nil {
+	if err = encoder.Unmarshal(headBlockBin, headBlock); err != nil {
 		return nil, err
 	}
 	return headBlock, nil
@@ -112,7 +112,7 @@ func (b *Blockchain) Store(block *core.Block, stateUpdate *core.StateUpdate) err
 			return err
 		}
 
-		blockBinary, err := cbor.Marshal(block)
+		blockBinary, err := encoder.Marshal(block)
 		if err != nil {
 			return err
 		}
diff --git a/core/transaction.go b/core/transaction.go
index 14328a8e..bbc90f9f 100644
--- a/core/transaction.go
+++ b/core/transaction.go
@@ -3,9 +3,11 @@ package core
 import (
 	"errors"
 	"math/big"
+	"reflect"
 
 	"github.com/NethermindEth/juno/core/crypto"
 	"github.com/NethermindEth/juno/core/felt"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/NethermindEth/juno/utils"
 	"github.com/ethereum/go-ethereum/common"
 )
@@ -57,6 +59,21 @@ const (
 	L1Handler
 )
 
+func init() {
+	err := encoder.RegisterType(reflect.TypeOf(DeclareTransaction{}))
+	if err != nil {
+		panic(err)
+	}
+	err = encoder.RegisterType(reflect.TypeOf(DeployTransaction{}))
+	if err != nil {
+		panic(err)
+	}
+	err = encoder.RegisterType(reflect.TypeOf(InvokeTransaction{}))
+	if err != nil {
+		panic(err)
+	}
+}
+
 type TransactionReceipt struct {
 	ActualFee          *felt.Felt
 	Events             []*Event
diff --git a/core/trie/node.go b/core/trie/node.go
index 071d44b8..994acb5b 100644
--- a/core/trie/node.go
+++ b/core/trie/node.go
@@ -1,7 +1,6 @@
 package trie
 
 import (
-	"bytes"
 	"encoding/binary"
 	"fmt"
 
@@ -20,15 +19,15 @@ func (e ErrMalformedNode) Error() string {
 
 // A Node represents a node in the [Trie]
 type Node struct {
-	value *felt.Felt
-	left  *bitset.BitSet
-	right *bitset.BitSet
+	Value *felt.Felt
+	Left  *bitset.BitSet
+	Right *bitset.BitSet
 }
 
 // Hash calculates the hash of a [Node]
 func (n *Node) Hash(path *bitset.BitSet) *felt.Felt {
 	if path.Len() == 0 {
-		return n.value
+		return n.Value
 	}
 
 	pathWords := path.Bytes()
@@ -45,7 +44,7 @@ func (n *Node) Hash(path *bitset.BitSet) *felt.Felt {
 	pathFelt := new(felt.Felt).SetBytes(pathBytes[:])
 
 	// https://docs.starknet.io/documentation/develop/State/starknet-state/
-	hash := crypto.Pedersen(n.value, pathFelt)
+	hash := crypto.Pedersen(n.Value, pathFelt)
 
 	pathFelt.SetUint64(uint64(path.Len()))
 	return hash.Add(hash, pathFelt)
@@ -53,73 +52,5 @@ func (n *Node) Hash(path *bitset.BitSet) *felt.Felt {
 
 // Equal checks for equality of two [Node]s
 func (n *Node) Equal(other *Node) bool {
-	return n.value.Equal(other.value) && n.left.Equal(other.left) && n.right.Equal(n.right)
-}
-
-// MarshalBinary serializes a [Node] into a byte array
-func (n *Node) MarshalBinary() ([]byte, error) {
-	if n.value == nil {
-		return nil, ErrMalformedNode{"cannot marshal node with nil value"}
-	}
-
-	var ret []byte
-	valueB := n.value.Bytes()
-	ret = append(ret, valueB[:]...)
-
-	if n.left != nil {
-		ret = append(ret, 'l')
-		leftB, err := n.left.MarshalBinary()
-		if err != nil {
-			return nil, err
-		}
-		ret = append(ret, leftB...)
-	}
-
-	if n.right != nil {
-		ret = append(ret, 'r')
-		rightB, err := n.right.MarshalBinary()
-		if err != nil {
-			return nil, err
-		}
-		ret = append(ret, rightB...)
-	}
-	return ret, nil
-}
-
-// UnmarshalBinary deserializes a [Node] from a byte array
-func (n *Node) UnmarshalBinary(data []byte) error {
-	if len(data) < felt.Bytes {
-		return ErrMalformedNode{"size of input data is less than felt size"}
-	}
-	n.value = new(felt.Felt).SetBytes(data[:felt.Bytes])
-	data = data[felt.Bytes:]
-
-	stream := bytes.NewReader(data)
-	for stream.Len() > 0 {
-		head, err := stream.ReadByte()
-		if err != nil {
-			return err
-		}
-
-		var pathP **bitset.BitSet
-		switch head {
-		case 'l':
-			pathP = &(n.left)
-		case 'r':
-			pathP = &(n.right)
-		default:
-			return ErrMalformedNode{"unknown child node prefix"}
-		}
-		if *pathP != nil {
-			return ErrMalformedNode{"multiple children are not supported"}
-		}
-
-		*pathP = new(bitset.BitSet)
-		_, err = (*pathP).ReadFrom(stream)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
+	return n.Value.Equal(other.Value) && n.Left.Equal(other.Left) && n.Right.Equal(n.Right)
 }
diff --git a/core/trie/trie.go b/core/trie/trie.go
index 27450db9..3fc9477e 100644
--- a/core/trie/trie.go
+++ b/core/trie/trie.go
@@ -133,9 +133,9 @@ func (t *Trie) nodesFromRoot(key *bitset.BitSet) ([]storageNode, error) {
 		}
 
 		if key.Test(key.Len() - cur.Len() - 1) {
-			cur = node.right
+			cur = node.Right
 		} else {
-			cur = node.left
+			cur = node.Left
 		}
 	}
 
@@ -148,7 +148,7 @@ func (t *Trie) Get(key *felt.Felt) (*felt.Felt, error) {
 	if err != nil {
 		return nil, err
 	}
-	return value.value, nil
+	return value.Value, nil
 }
 
 // Put updates the corresponding `value` for a `key`
@@ -157,7 +157,7 @@ func (t *Trie) Put(key *felt.Felt, value *felt.Felt) (*felt.Felt, error) {
 	old := new(felt.Felt)
 	nodeKey := t.FeltToBitSet(key)
 	node := &Node{
-		value: value,
+		Value: value,
 	}
 
 	// empty trie, make new value root
@@ -183,7 +183,7 @@ func (t *Trie) Put(key *felt.Felt, value *felt.Felt) (*felt.Felt, error) {
 	// Replace if key already exist
 	sibling := &nodes[len(nodes)-1]
 	if nodeKey.Equal(sibling.key) {
-		old = sibling.node.value // record old value to return to caller
+		old = sibling.node.Value // record old value to return to caller
 		sibling.node = node
 		if value.IsZero() {
 			if err = t.deleteLast(nodes); err != nil {
@@ -202,12 +202,12 @@ func (t *Trie) Put(key *felt.Felt, value *felt.Felt) (*felt.Felt, error) {
 
 	commonKey, _ := FindCommonKey(nodeKey, sibling.key)
 	newParent := &Node{
-		value: new(felt.Felt),
+		Value: new(felt.Felt),
 	}
 	if nodeKey.Test(nodeKey.Len() - commonKey.Len() - 1) {
-		newParent.left, newParent.right = sibling.key, nodeKey
+		newParent.Left, newParent.Right = sibling.key, nodeKey
 	} else {
-		newParent.left, newParent.right = nodeKey, sibling.key
+		newParent.Left, newParent.Right = nodeKey, sibling.key
 	}
 
 	makeRoot := len(nodes) == 1
@@ -215,10 +215,10 @@ func (t *Trie) Put(key *felt.Felt, value *felt.Felt) (*felt.Felt, error) {
 		siblingParent := &nodes[len(nodes)-2]
 
 		// replace the link to our sibling with the new parent
-		if siblingParent.node.left.Equal(sibling.key) {
-			siblingParent.node.left = commonKey
+		if siblingParent.node.Left.Equal(sibling.key) {
+			siblingParent.node.Left = commonKey
 		} else {
-			siblingParent.node.right = commonKey
+			siblingParent.node.Right = commonKey
 		}
 	}
 
@@ -257,10 +257,10 @@ func (t *Trie) deleteLast(affectedNodes []storageNode) error {
 		}
 
 		var siblingKey *bitset.BitSet
-		if parent.node.left.Equal(last.key) {
-			siblingKey = parent.node.right
+		if parent.node.Left.Equal(last.key) {
+			siblingKey = parent.node.Right
 		} else {
-			siblingKey = parent.node.left
+			siblingKey = parent.node.Left
 		}
 
 		if len(affectedNodes) == 2 { // sibling should become root
@@ -268,10 +268,10 @@ func (t *Trie) deleteLast(affectedNodes []storageNode) error {
 		} else { // sibling should link to grandparent (len(affectedNodes) > 2)
 			grandParent := &affectedNodes[len(affectedNodes)-3]
 			// replace link to parent with a link to sibling
-			if grandParent.node.left.Equal(parent.key) {
-				grandParent.node.left = siblingKey
+			if grandParent.node.Left.Equal(parent.key) {
+				grandParent.node.Left = siblingKey
 			} else {
-				grandParent.node.right = siblingKey
+				grandParent.node.Right = siblingKey
 			}
 
 			if sibling, err := t.storage.Get(siblingKey); err != nil {
@@ -301,26 +301,26 @@ func (t *Trie) propagateValues(affectedNodes []storageNode) error {
 	for idx := len(affectedNodes) - 1; idx >= 0; idx-- {
 		cur := affectedNodes[idx]
 
-		if (cur.node.left == nil) != (cur.node.right == nil) {
+		if (cur.node.Left == nil) != (cur.node.Right == nil) {
 			panic("should not happen")
 		}
 
-		if cur.node.left != nil || cur.node.right != nil {
+		if cur.node.Left != nil || cur.node.Right != nil {
 			// todo: one of the children is already in affectedNodes, use that instead of fetching from storage
-			left, err := t.storage.Get(cur.node.left)
+			left, err := t.storage.Get(cur.node.Left)
 			if err != nil {
 				return err
 			}
 
-			right, err := t.storage.Get(cur.node.right)
+			right, err := t.storage.Get(cur.node.Right)
 			if err != nil {
 				return err
 			}
 
-			leftPath := Path(cur.node.left, cur.key)
-			rightPath := Path(cur.node.right, cur.key)
+			leftPath := Path(cur.node.Left, cur.key)
+			rightPath := Path(cur.node.Right, cur.key)
 
-			cur.node.value = crypto.Pedersen(left.Hash(leftPath), right.Hash(rightPath))
+			cur.node.Value = crypto.Pedersen(left.Hash(leftPath), right.Hash(rightPath))
 		}
 
 		if err := t.storage.Put(cur.key, cur.node); err != nil {
@@ -377,16 +377,16 @@ func (t *Trie) dump(level int, parentP *bitset.BitSet) {
 
 	root, err := t.storage.Get(t.rootKey)
 	path := Path(t.rootKey, parentP)
-	fmt.Printf("%sstorage : \"%s\" %d spec: \"%s\" %d bottom: \"%s\" \n", strings.Repeat("\t", level), t.rootKey.String(), t.rootKey.Len(), path.String(), path.Len(), root.value.Text(16))
+	fmt.Printf("%sstorage : \"%s\" %d spec: \"%s\" %d bottom: \"%s\" \n", strings.Repeat("\t", level), t.rootKey.String(), t.rootKey.Len(), path.String(), path.Len(), root.Value.Text(16))
 	if err != nil {
 		return
 	}
 	(&Trie{
-		rootKey: root.left,
+		rootKey: root.Left,
 		storage: t.storage,
 	}).dump(level+1, t.rootKey)
 	(&Trie{
-		rootKey: root.right,
+		rootKey: root.Right,
 		storage: t.storage,
 	}).dump(level+1, t.rootKey)
 }
diff --git a/core/trie/trietxn.go b/core/trie/trietxn.go
index e4d658f2..9a8de554 100644
--- a/core/trie/trietxn.go
+++ b/core/trie/trietxn.go
@@ -2,6 +2,7 @@ package trie
 
 import (
 	"github.com/NethermindEth/juno/db"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/bits-and-blooms/bitset"
 )
 
@@ -35,7 +36,7 @@ func (t *TrieTxn) Put(key *bitset.BitSet, value *Node) error {
 		return err
 	}
 
-	valueBytes, err := value.MarshalBinary()
+	valueBytes, err := encoder.Marshal(value)
 	if err != nil {
 		return err
 	}
@@ -53,7 +54,7 @@ func (t *TrieTxn) Get(key *bitset.BitSet) (*Node, error) {
 		return nil, err
 	} else {
 		node := new(Node)
-		return node, node.UnmarshalBinary(val)
+		return node, encoder.Unmarshal(val, node)
 	}
 }
 
diff --git a/encoder/encoder.go b/encoder/encoder.go
new file mode 100644
index 00000000..336b1042
--- /dev/null
+++ b/encoder/encoder.go
@@ -0,0 +1,69 @@
+package encoder
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/fxamacker/cbor/v2"
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	ts = cbor.NewTagSet()
+	// https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+	// 65536-15309735 	Unassigned
+	tagNum  = uint64(65536)
+	encMode cbor.EncMode
+	decMode cbor.DecMode
+)
+
+func initEncModes() {
+	var err error
+	encMode, err = cbor.CanonicalEncOptions().EncModeWithTags(ts)
+	if err != nil {
+		panic(err)
+	}
+
+	decMode, err = cbor.DecOptions{}.DecModeWithTags(ts)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func RegisterType(rType reflect.Type) error {
+	if err := ts.Add(
+		cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired},
+		rType,
+		tagNum,
+	); err != nil {
+		return err
+	}
+	initEncModes()
+	tagNum++
+	return nil
+}
+
+func init() {
+	initEncModes()
+}
+
+// Marshal returns encoding of param v
+func Marshal(v any) ([]byte, error) {
+	return encMode.Marshal(v)
+}
+
+// Unmarshal decodes param v from []byte b
+func Unmarshal(b []byte, v any) error {
+	return decMode.Unmarshal(b, v)
+}
+
+// TestSymmetry checks if a type can be marshaled and unmarshalled with no issues
+func TestSymmetry(t *testing.T, value any) {
+	cborBytes, err := cbor.Marshal(value)
+	assert.NoError(t, err)
+
+	unmarshaled := reflect.New(reflect.TypeOf(value))
+	err = cbor.Unmarshal(cborBytes, unmarshaled.Interface())
+	assert.NoError(t, err)
+	assert.Equal(t, value, unmarshaled.Elem().Interface())
+}


Test patch:
diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go
index cc2171f7..cb31d537 100644
--- a/blockchain/blockchain_test.go
+++ b/blockchain/blockchain_test.go
@@ -11,9 +11,9 @@ import (
 	"github.com/NethermindEth/juno/core/felt"
 	"github.com/NethermindEth/juno/core/state"
 	"github.com/NethermindEth/juno/db"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/NethermindEth/juno/starknetdata/gateway"
 	"github.com/NethermindEth/juno/utils"
-	"github.com/fxamacker/cbor/v2"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -158,7 +158,7 @@ func TestVerifyBlock(t *testing.T) {
 			incomingBlock := &core.Block{Number: 10, ParentHash: h2}
 
 			assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-				blockBinary, err := cbor.Marshal(headBlock)
+				blockBinary, err := encoder.Marshal(headBlock)
 				if err != nil {
 					return err
 				}
@@ -174,7 +174,7 @@ func TestVerifyBlock(t *testing.T) {
 		headBlock := &core.Block{Hash: h1, Number: 1}
 		incomingBlock := &core.Block{ParentHash: h2, Number: 2}
 		assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-			blockBinary, err := cbor.Marshal(headBlock)
+			blockBinary, err := encoder.Marshal(headBlock)
 			if err != nil {
 				return err
 			}
@@ -191,7 +191,7 @@ func TestVerifyBlock(t *testing.T) {
 		block := &core.Block{Number: 1, ParentHash: h1, Hash: h2}
 		stateUpdate := &core.StateUpdate{BlockHash: h3}
 		assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-			blockBinary, err := cbor.Marshal(headBlock)
+			blockBinary, err := encoder.Marshal(headBlock)
 			if err != nil {
 				return err
 			}
@@ -207,7 +207,7 @@ func TestVerifyBlock(t *testing.T) {
 			block := &core.Block{Number: 1, ParentHash: h1, Hash: h2, GlobalStateRoot: sr1}
 			stateUpdate := &core.StateUpdate{BlockHash: h2, NewRoot: sr2}
 			assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-				blockBinary, err := cbor.Marshal(headBlock)
+				blockBinary, err := encoder.Marshal(headBlock)
 				if err != nil {
 					return err
 				}
@@ -223,7 +223,7 @@ func TestVerifyBlock(t *testing.T) {
 		block := &core.Block{Number: 119802, ParentHash: h1, Hash: h2, GlobalStateRoot: sr1}
 		stateUpdate := &core.StateUpdate{BlockHash: h2, NewRoot: sr1}
 		assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-			blockBinary, err := cbor.Marshal(headBlock)
+			blockBinary, err := encoder.Marshal(headBlock)
 			if err != nil {
 				return err
 			}
@@ -243,7 +243,7 @@ func TestVerifyBlock(t *testing.T) {
 		}
 		stateUpdate := &core.StateUpdate{BlockHash: h2, NewRoot: sr1}
 		assert.NoError(t, chain.database.Update(func(txn db.Transaction) error {
-			blockBinary, err := cbor.Marshal(headBlock)
+			blockBinary, err := encoder.Marshal(headBlock)
 			if err != nil {
 				return err
 			}
@@ -296,7 +296,7 @@ func TestStore(t *testing.T) {
 			}
 
 			headBlock := new(core.Block)
-			if err = cbor.Unmarshal(databaseHeadBlock, headBlock); err != nil {
+			if err = encoder.Unmarshal(databaseHeadBlock, headBlock); err != nil {
 				return err
 			}
 			assert.Equal(t, headBlock, block0)
@@ -313,7 +313,7 @@ func TestStore(t *testing.T) {
 			}
 
 			block := new(core.Block)
-			if err = cbor.Unmarshal(databaseBlock0, block); err != nil {
+			if err = encoder.Unmarshal(databaseBlock0, block); err != nil {
 				return err
 			}
 			assert.Equal(t, block, block0)
@@ -358,7 +358,7 @@ func TestStore(t *testing.T) {
 			}
 
 			headBlock := new(core.Block)
-			if err = cbor.Unmarshal(databaseHeadBlock, headBlock); err != nil {
+			if err = encoder.Unmarshal(databaseHeadBlock, headBlock); err != nil {
 				return err
 			}
 			assert.Equal(t, headBlock, block1)
@@ -375,7 +375,7 @@ func TestStore(t *testing.T) {
 			}
 
 			block := new(core.Block)
-			if err = cbor.Unmarshal(databaseBlock0, block); err != nil {
+			if err = encoder.Unmarshal(databaseBlock0, block); err != nil {
 				return err
 			}
 			assert.Equal(t, block, block1)
diff --git a/core/felt/felt_test.go b/core/felt/felt_test.go
index f3573d6a..0de79459 100644
--- a/core/felt/felt_test.go
+++ b/core/felt/felt_test.go
@@ -3,8 +3,7 @@ package felt
 import (
 	"testing"
 
-	"github.com/fxamacker/cbor/v2"
-
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -22,10 +21,5 @@ func TestFeltCbor(t *testing.T) {
 	_, err := val.SetRandom()
 	assert.NoError(t, err)
 
-	bytes, err := cbor.Marshal(val)
-	assert.NoError(t, err)
-
-	var unmarshaledFelt Felt
-	assert.NoError(t, cbor.Unmarshal(bytes, &unmarshaledFelt))
-	assert.Equal(t, val, unmarshaledFelt)
+	encoder.TestSymmetry(t, val)
 }
diff --git a/core/state/state_test.go b/core/state/state_test.go
index b5b0e12d..67ee9cd0 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -52,11 +52,8 @@ func TestState_Root(t *testing.T) {
 	newRootPath = storage.FeltToBitSet(key)
 
 	expectedRootNode := new(trie.Node)
-	if err := expectedRootNode.UnmarshalBinary(value.Marshal()); err != nil {
-		t.Error(err)
-	}
+	expectedRootNode.Value = value
 
-	expectedRootNode.UnmarshalBinary(value.Marshal())
 	expectedRoot := expectedRootNode.Hash(trie.Path(newRootPath, nil))
 
 	actualRoot, err := state.Root()
diff --git a/core/transaction_test.go b/core/transaction_test.go
index 859e3513..55bd84b1 100644
--- a/core/transaction_test.go
+++ b/core/transaction_test.go
@@ -6,7 +6,9 @@ import (
 	"testing"
 
 	"github.com/NethermindEth/juno/core/felt"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/NethermindEth/juno/utils"
+	"github.com/stretchr/testify/assert"
 )
 
 var (
@@ -52,6 +54,8 @@ func TestDeployTransactions(t *testing.T) {
 			if !transactionHash.Equal(test.want) {
 				t.Errorf("wrong hash: got %s, want %s", transactionHash.Text(16), test.want.Text(16))
 			}
+
+			checkTransactionSymmetry(t, &test.input)
 		})
 	}
 }
@@ -125,6 +129,8 @@ func TestInvokeTransactions(t *testing.T) {
 			if !transactionHash.Equal(test.want) {
 				t.Errorf("wrong hash: got %s, want %s", transactionHash.Text(16), test.want.Text(16))
 			}
+
+			checkTransactionSymmetry(t, &test.input)
 		})
 	}
 }
@@ -185,6 +191,27 @@ func TestDeclareTransaction(t *testing.T) {
 			if !transactionHash.Equal(test.want) {
 				t.Errorf("wrong hash: got %s, want %s", transactionHash.Text(16), test.want.Text(16))
 			}
+
+			checkTransactionSymmetry(t, &test.input)
 		})
 	}
 }
+
+func checkTransactionSymmetry(t *testing.T, input Transaction) {
+	data, err := encoder.Marshal(input)
+	assert.NoError(t, err)
+
+	var txn Transaction
+	assert.NoError(t, encoder.Unmarshal(data, &txn))
+
+	switch v := txn.(type) {
+	case *DeclareTransaction:
+		assert.Equal(t, input, v)
+	case *DeployTransaction:
+		assert.Equal(t, input, v)
+	case *InvokeTransaction:
+		assert.Equal(t, input, v)
+	default:
+		t.Error("not a transaction")
+	}
+}
diff --git a/core/trie/node_test.go b/core/trie/node_test.go
index 51dc6674..d9c8e25c 100644
--- a/core/trie/node_test.go
+++ b/core/trie/node_test.go
@@ -6,19 +6,12 @@ import (
 	"testing"
 
 	"github.com/NethermindEth/juno/core/felt"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/bits-and-blooms/bitset"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestNodeMarshalAndUnmarshalBinary(t *testing.T) {
-	t.Run("error when marshalling node with nil value", func(t *testing.T) {
-		n := new(Node)
-		_, err := n.MarshalBinary()
-
-		if err == nil {
-			t.Fatal("expected error but got no error")
-		}
-	})
 	t.Run("node with non nil value", func(t *testing.T) {
 		value, err := new(felt.Felt).SetRandom()
 		if err != nil {
@@ -34,52 +27,52 @@ func TestNodeMarshalAndUnmarshalBinary(t *testing.T) {
 			{
 				name: "node with both children nil",
 				node: Node{
-					value: value,
-					left:  nil,
-					right: nil,
+					Value: value,
+					Left:  nil,
+					Right: nil,
 				},
 			},
 			{
 				name: "node with left child",
 				node: Node{
-					value: value,
-					left:  path1,
-					right: nil,
+					Value: value,
+					Left:  path1,
+					Right: nil,
 				},
 			},
 			{
 				name: "node with right child",
 				node: Node{
-					value: value,
-					left:  nil,
-					right: path2,
+					Value: value,
+					Left:  nil,
+					Right: path2,
 				},
 			},
 			{
 				name: "node with both children (l: path1, r: path2)",
 				node: Node{
-					value: value,
-					left:  path1,
-					right: path2,
+					Value: value,
+					Left:  path1,
+					Right: path2,
 				},
 			},
 			{
 				name: "node with both children (l: path2, r: path1)",
 				node: Node{
-					value: value,
-					left:  path2,
-					right: path1,
+					Value: value,
+					Left:  path2,
+					Right: path1,
 				},
 			},
 		}
 		for _, test := range tests {
 			t.Run(test.name, func(t *testing.T) {
-				data, err := test.node.MarshalBinary()
+				data, err := encoder.Marshal(test.node)
 				if err != nil {
 					t.Fatalf("expected no error but got %s", err)
 				}
 				unmarshalled := new(Node)
-				err = unmarshalled.UnmarshalBinary(data)
+				err = encoder.Unmarshal(data, unmarshalled)
 				if err != nil {
 					t.Fatalf("expected no error but got %s", err)
 				}
@@ -137,7 +130,7 @@ func TestNodeMarshalAndUnmarshalBinary(t *testing.T) {
 		unmarshalled := new(Node)
 		for _, test := range tests {
 			t.Run(test.name, func(t *testing.T) {
-				err = unmarshalled.UnmarshalBinary(test.marshalBin)
+				err = encoder.Unmarshal(test.marshalBin, unmarshalled)
 				if errors.Is(err, ErrMalformedNode{}) {
 					t.Errorf("expected error not right: got %s, wanted ErrMalformedNode", err)
 				}
@@ -159,7 +152,7 @@ func TestNodeMarshalAndUnmarshalBinary(t *testing.T) {
 		}
 		for _, test := range tests {
 			t.Run(test.name, func(t *testing.T) {
-				if err := new(Node).UnmarshalBinary(test.node); err == nil {
+				if err := encoder.Unmarshal(test.node, new(Node)); err == nil {
 					t.Errorf("expected error but got no error")
 				}
 			})
@@ -173,7 +166,7 @@ func TestNodeHash(t *testing.T) {
 	expected, _ := new(felt.Felt).SetString("0x1d937094c09b5f8e26a662d21911871e3cbc6858d55cc49af9848ea6fed4e9")
 
 	node := Node{
-		value: new(felt.Felt).SetBytes(valueBytes),
+		Value: new(felt.Felt).SetBytes(valueBytes),
 	}
 	path := bitset.FromWithLength(6, []uint64{42})
 
diff --git a/core/trie/trie_test.go b/core/trie/trie_test.go
index 4d781ea5..12a001e3 100644
--- a/core/trie/trie_test.go
+++ b/core/trie/trie_test.go
@@ -187,12 +187,12 @@ func TestPathOnTrie(t *testing.T) {
 			t.Error()
 		}
 
-		assert.Equal(t, true, rootNode.left != nil && rootNode.right != nil)
+		assert.Equal(t, true, rootNode.Left != nil && rootNode.Right != nil)
 
 		expectedLeftPath := bitset.New(2).Set(1)
 		expectedRightPath := bitset.New(2).Set(0)
-		assert.Equal(t, true, Path(rootNode.left, trie.rootKey).Equal(expectedLeftPath))
-		assert.Equal(t, true, Path(rootNode.right, trie.rootKey).Equal(expectedRightPath))
+		assert.Equal(t, true, Path(rootNode.Left, trie.rootKey).Equal(expectedLeftPath))
+		assert.Equal(t, true, Path(rootNode.Right, trie.rootKey).Equal(expectedRightPath))
 		return nil
 	})
 }
diff --git a/core/trie/trietxn_test.go b/core/trie/trietxn_test.go
index 8a672da2..c85b951a 100644
--- a/core/trie/trietxn_test.go
+++ b/core/trie/trietxn_test.go
@@ -16,8 +16,7 @@ func TestTrieTxn(t *testing.T) {
 
 	key := bitset.New(44)
 	node := new(Node)
-	value, _ := new(felt.Felt).SetRandom()
-	assert.NoError(t, node.UnmarshalBinary(value.Marshal()))
+	node.Value, _ = new(felt.Felt).SetRandom()
 
 	// put a node
 	assert.NoError(t, testDb.Update(func(txn db.Transaction) error {
diff --git a/sync/sync_test.go b/sync/sync_test.go
index bfd3a325..864fb926 100644
--- a/sync/sync_test.go
+++ b/sync/sync_test.go
@@ -11,9 +11,9 @@ import (
 	"github.com/NethermindEth/juno/core"
 	"github.com/NethermindEth/juno/core/felt"
 	"github.com/NethermindEth/juno/db"
+	"github.com/NethermindEth/juno/encoder"
 	"github.com/NethermindEth/juno/starknetdata/gateway"
 	"github.com/NethermindEth/juno/utils"
-	"github.com/fxamacker/cbor/v2"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -26,7 +26,7 @@ func TestSyncBlocks(t *testing.T) {
 			}
 
 			headBlock := new(core.Block)
-			if err = cbor.Unmarshal(headBlockBinary, headBlock); err != nil {
+			if err = encoder.Unmarshal(headBlockBinary, headBlock); err != nil {
 				return err
 			}
 
@@ -49,7 +49,7 @@ func TestSyncBlocks(t *testing.T) {
 				}
 
 				block := new(core.Block)
-				if err = cbor.Unmarshal(blockBinary, block); err != nil {
+				if err = encoder.Unmarshal(blockBinary, block); err != nil {
 					return err
 				}