diff --git a/pkg/ipfs/dag_putters/btc_header.go b/pkg/ipfs/dag_putters/btc_header.go
index 4687becb..bf938aee 100644
--- a/pkg/ipfs/dag_putters/btc_header.go
+++ b/pkg/ipfs/dag_putters/btc_header.go
@@ -19,8 +19,6 @@ package dag_putters
import (
"fmt"
- "github.com/btcsuite/btcd/wire"
-
"github.com/vulcanize/vulcanizedb/pkg/ipfs"
"github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
)
@@ -34,16 +32,12 @@ func NewBtcHeaderDagPutter(adder *ipfs.IPFS) *BtcHeaderDagPutter {
}
func (bhdp *BtcHeaderDagPutter) DagPut(raw interface{}) ([]string, error) {
- header, ok := raw.(*wire.BlockHeader)
+ header, ok := raw.(*ipld.BtcHeader)
if !ok {
- return nil, fmt.Errorf("BtcHeaderDagPutter expected input type %T got %T", &wire.BlockHeader{}, raw)
+ return nil, fmt.Errorf("BtcHeaderDagPutter expected input type %T got %T", &ipld.BtcHeader{}, raw)
}
- node, err := ipld.NewBtcHeader(header)
- if err != nil {
+ if err := bhdp.adder.Add(header); err != nil {
return nil, err
}
- if err := bhdp.adder.Add(node); err != nil {
- return nil, err
- }
- return []string{node.Cid().String()}, nil
+ return []string{header.Cid().String()}, nil
}
diff --git a/pkg/ipfs/dag_putters/btc_tx.go b/pkg/ipfs/dag_putters/btc_tx.go
index 77f0dcb7..8d38f675 100644
--- a/pkg/ipfs/dag_putters/btc_tx.go
+++ b/pkg/ipfs/dag_putters/btc_tx.go
@@ -19,8 +19,6 @@ package dag_putters
import (
"fmt"
- "github.com/btcsuite/btcutil"
-
"github.com/vulcanize/vulcanizedb/pkg/ipfs"
"github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
)
@@ -34,20 +32,16 @@ func NewBtcTxDagPutter(adder *ipfs.IPFS) *BtcTxDagPutter {
}
func (etdp *BtcTxDagPutter) DagPut(raw interface{}) ([]string, error) {
- transactions, ok := raw.([]*btcutil.Tx)
+ transactions, ok := raw.([]*ipld.BtcTx)
if !ok {
- return nil, fmt.Errorf("BtcTxDagPutter expected input type %T got %T", []*btcutil.Tx{}, raw)
+ return nil, fmt.Errorf("BtcTxDagPutter expected input type %T got %T", []*ipld.BtcTx{}, raw)
}
cids := make([]string, len(transactions))
for i, transaction := range transactions {
- node, err := ipld.NewBtcTx(transaction.MsgTx())
- if err != nil {
+ if err := etdp.adder.Add(transaction); err != nil {
return nil, err
}
- if err := etdp.adder.Add(node); err != nil {
- return nil, err
- }
- cids[i] = node.Cid().String()
+ cids[i] = transaction.Cid().String()
}
return cids, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_header.go b/pkg/ipfs/dag_putters/eth_header.go
index b06fd086..191b577c 100644
--- a/pkg/ipfs/dag_putters/eth_header.go
+++ b/pkg/ipfs/dag_putters/eth_header.go
@@ -19,8 +19,6 @@ package dag_putters
import (
"fmt"
- "github.com/ethereum/go-ethereum/core/types"
-
"github.com/vulcanize/vulcanizedb/pkg/ipfs"
"github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
)
@@ -34,16 +32,12 @@ func NewEthBlockHeaderDagPutter(adder *ipfs.IPFS) *EthHeaderDagPutter {
}
func (bhdp *EthHeaderDagPutter) DagPut(raw interface{}) ([]string, error) {
- header, ok := raw.(*types.Header)
+ header, ok := raw.(*ipld.EthHeader)
if !ok {
- return nil, fmt.Errorf("EthHeaderDagPutter expected input type %T got %T", &types.Header{}, raw)
+ return nil, fmt.Errorf("EthHeaderDagPutter expected input type %T got %T", &ipld.EthHeader{}, raw)
}
- node, err := ipld.NewEthHeader(header)
- if err != nil {
+ if err := bhdp.adder.Add(header); err != nil {
return nil, err
}
- if err := bhdp.adder.Add(node); err != nil {
- return nil, err
- }
- return []string{node.Cid().String()}, nil
+ return []string{header.Cid().String()}, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_receipt.go b/pkg/ipfs/dag_putters/eth_receipt.go
index bb8bbb64..ad40283e 100644
--- a/pkg/ipfs/dag_putters/eth_receipt.go
+++ b/pkg/ipfs/dag_putters/eth_receipt.go
@@ -19,8 +19,6 @@ package dag_putters
import (
"fmt"
- "github.com/ethereum/go-ethereum/core/types"
-
"github.com/vulcanize/vulcanizedb/pkg/ipfs"
"github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
)
@@ -34,20 +32,16 @@ func NewEthReceiptDagPutter(adder *ipfs.IPFS) *EthReceiptDagPutter {
}
func (erdp *EthReceiptDagPutter) DagPut(raw interface{}) ([]string, error) {
- receipts, ok := raw.(types.Receipts)
+ receipts, ok := raw.([]*ipld.EthReceipt)
if !ok {
- return nil, fmt.Errorf("EthReceiptDagPutter expected input type %T got type %T", types.Receipts{}, raw)
+ return nil, fmt.Errorf("EthReceiptDagPutter expected input type %T got type %T", []*ipld.EthReceipt{}, raw)
}
cids := make([]string, len(receipts))
for i, receipt := range receipts {
- node, err := ipld.NewReceipt(receipt)
- if err != nil {
+ if err := erdp.adder.Add(receipt); err != nil {
return nil, err
}
- if err := erdp.adder.Add(node); err != nil {
- return nil, err
- }
- cids[i] = node.Cid().String()
+ cids[i] = receipt.Cid().String()
}
return cids, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_receipt_trie.go b/pkg/ipfs/dag_putters/eth_receipt_trie.go
new file mode 100644
index 00000000..a8f22865
--- /dev/null
+++ b/pkg/ipfs/dag_putters/eth_receipt_trie.go
@@ -0,0 +1,47 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package dag_putters
+
+import (
+ "fmt"
+
+ "github.com/vulcanize/vulcanizedb/pkg/ipfs"
+ "github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
+)
+
+type EthRctTrieDagPutter struct {
+ adder *ipfs.IPFS
+}
+
+func NewEthRctTrieDagPutter(adder *ipfs.IPFS) *EthRctTrieDagPutter {
+ return &EthRctTrieDagPutter{adder: adder}
+}
+
+func (etdp *EthRctTrieDagPutter) DagPut(raw interface{}) ([]string, error) {
+ rctTrieNodes, ok := raw.([]*ipld.EthRctTrie)
+ if !ok {
+ return nil, fmt.Errorf("EthRctTrieDagPutter expected input type %T got %T", []*ipld.EthRctTrie{}, raw)
+ }
+ cids := make([]string, len(rctTrieNodes))
+ for i, rctNode := range rctTrieNodes {
+ if err := etdp.adder.Add(rctNode); err != nil {
+ return nil, err
+ }
+ cids[i] = rctNode.Cid().String()
+ }
+ return cids, nil
+}
diff --git a/pkg/ipfs/dag_putters/eth_state.go b/pkg/ipfs/dag_putters/eth_state.go
index b031f66c..f32fe9b7 100644
--- a/pkg/ipfs/dag_putters/eth_state.go
+++ b/pkg/ipfs/dag_putters/eth_state.go
@@ -32,16 +32,12 @@ func NewEthStateDagPutter(adder *ipfs.IPFS) *EthStateDagPutter {
}
func (erdp *EthStateDagPutter) DagPut(raw interface{}) ([]string, error) {
- stateNodeRLP, ok := raw.([]byte)
+ stateNode, ok := raw.(*ipld.EthStateTrie)
if !ok {
- return nil, fmt.Errorf("EthStateDagPutter expected input type %T got %T", []byte{}, raw)
+ return nil, fmt.Errorf("EthStateDagPutter expected input type %T got %T", &ipld.EthStateTrie{}, raw)
}
- node, err := ipld.FromStateTrieRLP(stateNodeRLP)
- if err != nil {
+ if err := erdp.adder.Add(stateNode); err != nil {
return nil, err
}
- if err := erdp.adder.Add(node); err != nil {
- return nil, err
- }
- return []string{node.Cid().String()}, nil
+ return []string{stateNode.Cid().String()}, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_storage.go b/pkg/ipfs/dag_putters/eth_storage.go
index f1c20c9b..bb0930d9 100644
--- a/pkg/ipfs/dag_putters/eth_storage.go
+++ b/pkg/ipfs/dag_putters/eth_storage.go
@@ -32,16 +32,12 @@ func NewEthStorageDagPutter(adder *ipfs.IPFS) *EthStorageDagPutter {
}
func (erdp *EthStorageDagPutter) DagPut(raw interface{}) ([]string, error) {
- storageNodeRLP, ok := raw.([]byte)
+ storageNode, ok := raw.(*ipld.EthStorageTrie)
if !ok {
- return nil, fmt.Errorf("EthStorageDagPutter expected input type %T got %T", []byte{}, raw)
+ return nil, fmt.Errorf("EthStorageDagPutter expected input type %T got %T", &ipld.EthStorageTrie{}, raw)
}
- node, err := ipld.FromStorageTrieRLP(storageNodeRLP)
- if err != nil {
+ if err := erdp.adder.Add(storageNode); err != nil {
return nil, err
}
- if err := erdp.adder.Add(node); err != nil {
- return nil, err
- }
- return []string{node.Cid().String()}, nil
+ return []string{storageNode.Cid().String()}, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_tx.go b/pkg/ipfs/dag_putters/eth_tx.go
index b4339ac0..65a3cb63 100644
--- a/pkg/ipfs/dag_putters/eth_tx.go
+++ b/pkg/ipfs/dag_putters/eth_tx.go
@@ -19,8 +19,6 @@ package dag_putters
import (
"fmt"
- "github.com/ethereum/go-ethereum/core/types"
-
"github.com/vulcanize/vulcanizedb/pkg/ipfs"
"github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
)
@@ -34,20 +32,16 @@ func NewEthTxsDagPutter(adder *ipfs.IPFS) *EthTxsDagPutter {
}
func (etdp *EthTxsDagPutter) DagPut(raw interface{}) ([]string, error) {
- transactions, ok := raw.(types.Transactions)
+ transactions, ok := raw.([]*ipld.EthTx)
if !ok {
- return nil, fmt.Errorf("EthTxsDagPutter expected input type %T got %T", types.Transactions{}, raw)
+ return nil, fmt.Errorf("EthTxsDagPutter expected input type %T got %T", []*ipld.EthTx{}, raw)
}
cids := make([]string, len(transactions))
for i, transaction := range transactions {
- node, err := ipld.NewEthTx(transaction)
- if err != nil {
+ if err := etdp.adder.Add(transaction); err != nil {
return nil, err
}
- if err := etdp.adder.Add(node); err != nil {
- return nil, err
- }
- cids[i] = node.Cid().String()
+ cids[i] = transaction.Cid().String()
}
return cids, nil
}
diff --git a/pkg/ipfs/dag_putters/eth_tx_trie.go b/pkg/ipfs/dag_putters/eth_tx_trie.go
new file mode 100644
index 00000000..c805ad9a
--- /dev/null
+++ b/pkg/ipfs/dag_putters/eth_tx_trie.go
@@ -0,0 +1,47 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package dag_putters
+
+import (
+ "fmt"
+
+ "github.com/vulcanize/vulcanizedb/pkg/ipfs"
+ "github.com/vulcanize/vulcanizedb/pkg/ipfs/ipld"
+)
+
+type EthTxTrieDagPutter struct {
+ adder *ipfs.IPFS
+}
+
+func NewEthTxTrieDagPutter(adder *ipfs.IPFS) *EthTxTrieDagPutter {
+ return &EthTxTrieDagPutter{adder: adder}
+}
+
+func (etdp *EthTxTrieDagPutter) DagPut(raw interface{}) ([]string, error) {
+ txTrieNodes, ok := raw.([]*ipld.EthTxTrie)
+ if !ok {
+ return nil, fmt.Errorf("EthTxTrieDagPutter expected input type %T got %T", []*ipld.EthTxTrie{}, raw)
+ }
+ cids := make([]string, len(txTrieNodes))
+ for i, txNode := range txTrieNodes {
+ if err := etdp.adder.Add(txNode); err != nil {
+ return nil, err
+ }
+ cids[i] = txNode.Cid().String()
+ }
+ return cids, nil
+}
diff --git a/pkg/ipfs/ipld/btc_block.go b/pkg/ipfs/ipld/btc_block.go
new file mode 100644
index 00000000..a554b667
--- /dev/null
+++ b/pkg/ipfs/ipld/btc_block.go
@@ -0,0 +1,74 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "github.com/btcsuite/btcd/wire"
+ "github.com/btcsuite/btcutil"
+ node "github.com/ipfs/go-ipld-format"
+)
+
+// FromHeaderAndTxs takes a block header and txs and processes it
+// to return it a set of IPLD nodes for further processing.
+func FromHeaderAndTxs(header *wire.BlockHeader, txs []*btcutil.Tx) (*BtcHeader, []*BtcTx, []*BtcTxTrie, error) {
+ var txNodes []*BtcTx
+ for _, tx := range txs {
+ txNode, err := NewBtcTx(tx.MsgTx())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ txNodes = append(txNodes, txNode)
+ }
+ txTrie, err := mkMerkleTree(txNodes)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ headerNode, err := NewBtcHeader(header)
+ return headerNode, txNodes, txTrie, err
+}
+
+func mkMerkleTree(txs []*BtcTx) ([]*BtcTxTrie, error) {
+ layer := make([]node.Node, len(txs))
+ for i, tx := range txs {
+ layer[i] = tx
+ }
+ var out []*BtcTxTrie
+ var next []node.Node
+ for len(layer) > 1 {
+ if len(layer)%2 != 0 {
+ layer = append(layer, layer[len(layer)-1])
+ }
+ for i := 0; i < len(layer)/2; i++ {
+ var left, right node.Node
+ left = layer[i*2]
+ right = layer[(i*2)+1]
+
+ t := &BtcTxTrie{
+ Left: &node.Link{Cid: left.Cid()},
+ Right: &node.Link{Cid: right.Cid()},
+ }
+
+ out = append(out, t)
+ next = append(next, t)
+ }
+
+ layer = next
+ next = nil
+ }
+
+ return out, nil
+}
diff --git a/pkg/ipfs/ipld/btc_tx.go b/pkg/ipfs/ipld/btc_tx.go
index 02cd5bc4..f37332d3 100644
--- a/pkg/ipfs/ipld/btc_tx.go
+++ b/pkg/ipfs/ipld/btc_tx.go
@@ -1,3 +1,19 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
package ipld
import (
@@ -7,7 +23,7 @@ import (
"strconv"
"github.com/btcsuite/btcd/wire"
- cid "github.com/ipfs/go-cid"
+ "github.com/ipfs/go-cid"
node "github.com/ipfs/go-ipld-format"
mh "github.com/multiformats/go-multihash"
)
diff --git a/pkg/ipfs/ipld/btc_tx_trie.go b/pkg/ipfs/ipld/btc_tx_trie.go
new file mode 100644
index 00000000..b88194a8
--- /dev/null
+++ b/pkg/ipfs/ipld/btc_tx_trie.go
@@ -0,0 +1,110 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "fmt"
+
+ "github.com/ipfs/go-cid"
+ node "github.com/ipfs/go-ipld-format"
+ mh "github.com/multiformats/go-multihash"
+)
+
+type BtcTxTrie struct {
+ Left *node.Link
+ Right *node.Link
+}
+
+func (t *BtcTxTrie) BTCSha() []byte {
+ return cidToHash(t.Cid())
+}
+
+func (t *BtcTxTrie) Cid() cid.Cid {
+ h, _ := mh.Sum(t.RawData(), mh.DBL_SHA2_256, -1)
+ return cid.NewCidV1(cid.BitcoinTx, h)
+}
+
+func (t *BtcTxTrie) Links() []*node.Link {
+ return []*node.Link{t.Left, t.Right}
+}
+
+func (t *BtcTxTrie) RawData() []byte {
+ out := make([]byte, 64)
+ lbytes := cidToHash(t.Left.Cid)
+ copy(out[:32], lbytes)
+
+ rbytes := cidToHash(t.Right.Cid)
+ copy(out[32:], rbytes)
+
+ return out
+}
+
+func (t *BtcTxTrie) Loggable() map[string]interface{} {
+ return map[string]interface{}{
+ "type": "bitcoin_tx_tree",
+ }
+}
+
+func (t *BtcTxTrie) Resolve(path []string) (interface{}, []string, error) {
+ if len(path) == 0 {
+ return nil, nil, fmt.Errorf("zero length path")
+ }
+
+ switch path[0] {
+ case "0":
+ return t.Left, path[1:], nil
+ case "1":
+ return t.Right, path[1:], nil
+ default:
+ return nil, nil, fmt.Errorf("no such link")
+ }
+}
+
+func (t *BtcTxTrie) Copy() node.Node {
+ nt := *t
+ return &nt
+}
+
+func (t *BtcTxTrie) ResolveLink(path []string) (*node.Link, []string, error) {
+ out, rest, err := t.Resolve(path)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ lnk, ok := out.(*node.Link)
+ if ok {
+ return lnk, rest, nil
+ }
+
+ return nil, nil, fmt.Errorf("path did not lead to link")
+}
+
+func (t *BtcTxTrie) Size() (uint64, error) {
+ return uint64(len(t.RawData())), nil
+}
+
+func (t *BtcTxTrie) Stat() (*node.NodeStat, error) {
+ return &node.NodeStat{}, nil
+}
+
+func (t *BtcTxTrie) String() string {
+ return fmt.Sprintf("[bitcoin transaction tree]")
+}
+
+func (t *BtcTxTrie) Tree(p string, depth int) []string {
+ return []string{"0", "1"}
+}
diff --git a/pkg/ipfs/ipld/eth_block.go b/pkg/ipfs/ipld/eth_block.go
new file mode 100644
index 00000000..3e68cfee
--- /dev/null
+++ b/pkg/ipfs/ipld/eth_block.go
@@ -0,0 +1,97 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ mh "github.com/multiformats/go-multihash"
+)
+
+// FromBlock takes a block and processes it
+// to return it a set of IPLD nodes for further processing.
+func FromBlock(block *types.Block, receipts []*types.Receipt) (*EthHeader, []*EthTx, []*EthTxTrie, []*EthReceipt, []*EthRctTrie, error) {
+ // process the eth-header object
+ headerRawData := getRLP(block.Header())
+ cid, err := RawdataToCid(MEthHeader, headerRawData, mh.KECCAK_256)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+ ethHeader := &EthHeader{
+ Header: block.Header(),
+ cid: cid,
+ rawdata: headerRawData,
+ }
+
+ // Process the found eth-tx objects
+ ethTxNodes, ethTxTrieNodes, err := processTransactions(block.Transactions(),
+ block.Header().TxHash[:])
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+ // process the eth-rct objects
+ ethRctNodes, ethRctTrieNodes, err := processReceipts(receipts,
+ block.Header().ReceiptHash[:])
+
+ return ethHeader, ethTxNodes, ethTxTrieNodes, ethRctNodes, ethRctTrieNodes, nil
+}
+
+// processTransactions will take the found transactions in a parsed block body
+// to return IPLD node slices for eth-tx and eth-tx-trie
+func processTransactions(txs []*types.Transaction, expectedTxRoot []byte) ([]*EthTx, []*EthTxTrie, error) {
+ var ethTxNodes []*EthTx
+ transactionTrie := newTxTrie()
+
+ for idx, tx := range txs {
+ ethTx, err := NewEthTx(tx)
+ if err != nil {
+ return nil, nil, err
+ }
+ ethTxNodes = append(ethTxNodes, ethTx)
+ transactionTrie.add(idx, ethTx.RawData())
+ }
+
+ if !bytes.Equal(transactionTrie.rootHash(), expectedTxRoot) {
+ return nil, nil, fmt.Errorf("wrong transaction hash computed")
+ }
+
+ return ethTxNodes, transactionTrie.getNodes(), nil
+}
+
+// processReceipts will take in receipts
+// to return IPLD node slices for eth-rct and eth-rct-trie
+func processReceipts(rcts []*types.Receipt, expectedRctRoot []byte) ([]*EthReceipt, []*EthRctTrie, error) {
+ var ethRctNodes []*EthReceipt
+ receiptTrie := newRctTrie()
+
+ for idx, rct := range rcts {
+ ethRct, err := NewReceipt(rct)
+ if err != nil {
+ return nil, nil, err
+ }
+ ethRctNodes = append(ethRctNodes, ethRct)
+ receiptTrie.add(idx, ethRct.RawData())
+ }
+
+ if !bytes.Equal(receiptTrie.rootHash(), expectedRctRoot) {
+ return nil, nil, fmt.Errorf("wrong receipt hash computed")
+ }
+
+ return ethRctNodes, receiptTrie.getNodes(), nil
+}
diff --git a/pkg/ipfs/ipld/eth_header.go b/pkg/ipfs/ipld/eth_header.go
index 55b8bc34..c33931d4 100644
--- a/pkg/ipfs/ipld/eth_header.go
+++ b/pkg/ipfs/ipld/eth_header.go
@@ -20,7 +20,6 @@ import (
"encoding/json"
"fmt"
- "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ipfs/go-cid"
@@ -60,6 +59,24 @@ func NewEthHeader(header *types.Header) (*EthHeader, error) {
}, nil
}
+/*
+ OUTPUT
+*/
+
+// DecodeEthHeader takes a cid and its raw binary data
+// from IPFS and returns an EthTx object for further processing.
+func DecodeEthHeader(c cid.Cid, b []byte) (*EthHeader, error) {
+ var h *types.Header
+ if err := rlp.DecodeBytes(b, h); err != nil {
+ return nil, err
+ }
+ return &EthHeader{
+ Header: h,
+ cid: c,
+ rawdata: b,
+ }, nil
+}
+
/*
Block INTERFACE
*/
@@ -237,38 +254,3 @@ func (b *EthHeader) MarshalJSON() ([]byte, error) {
}
return json.Marshal(out)
}
-
-// objJSONBlock defines the output of the JSON RPC API for either
-// "eth_BlockByHash" or "eth_BlockByHeader".
-type objJSONBlock struct {
- Result objJSONBlockResult `json:"result"`
-}
-
-// objJSONBLockResult is the nested struct that takes
-// the contents of the JSON field "result".
-type objJSONBlockResult struct {
- types.Header // Use its fields and unmarshaler
- *objJSONBlockResultExt // Add these fields to the parsing
-}
-
-// objJSONBLockResultExt facilitates the composition
-// of the field "result", adding to the
-// `types.Header` fields, both ommers (their hashes) and transactions.
-type objJSONBlockResultExt struct {
- OmmerHashes []common.Hash `json:"uncles"`
- Transactions []*types.Transaction `json:"transactions"`
-}
-
-// UnmarshalJSON overrides the function types.Header.UnmarshalJSON, allowing us
-// to parse the fields of Header, plus ommer hashes and transactions.
-// (yes, ommer hashes. You will need to "eth_getUncleCountByBlockHash" per each ommer)
-func (o *objJSONBlockResult) UnmarshalJSON(input []byte) error {
- err := o.Header.UnmarshalJSON(input)
- if err != nil {
- return err
- }
-
- o.objJSONBlockResultExt = &objJSONBlockResultExt{}
- err = json.Unmarshal(input, o.objJSONBlockResultExt)
- return err
-}
diff --git a/pkg/ipfs/ipld/eth_receipt.go b/pkg/ipfs/ipld/eth_receipt.go
index 95f92f47..cfa46b36 100644
--- a/pkg/ipfs/ipld/eth_receipt.go
+++ b/pkg/ipfs/ipld/eth_receipt.go
@@ -59,6 +59,24 @@ func NewReceipt(receipt *types.Receipt) (*EthReceipt, error) {
}, nil
}
+/*
+ OUTPUT
+*/
+
+// DecodeEthReceipt takes a cid and its raw binary data
+// from IPFS and returns an EthTx object for further processing.
+func DecodeEthReceipt(c cid.Cid, b []byte) (*EthReceipt, error) {
+ var r *types.Receipt
+ if err := rlp.DecodeBytes(b, r); err != nil {
+ return nil, err
+ }
+ return &EthReceipt{
+ Receipt: r,
+ cid: c,
+ rawdata: b,
+ }, nil
+}
+
/*
Block INTERFACE
*/
diff --git a/pkg/ipfs/ipld/eth_receipt_trie.go b/pkg/ipfs/ipld/eth_receipt_trie.go
new file mode 100644
index 00000000..6a1b7e40
--- /dev/null
+++ b/pkg/ipfs/ipld/eth_receipt_trie.go
@@ -0,0 +1,152 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "fmt"
+
+ "github.com/ipfs/go-cid"
+ node "github.com/ipfs/go-ipld-format"
+ "github.com/multiformats/go-multihash"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// EthRctTrie (eth-tx-trie codec 0x92) represents
+// a node from the transaction trie in ethereum.
+type EthRctTrie struct {
+ *TrieNode
+}
+
+// Static (compile time) check that EthRctTrie satisfies the node.Node interface.
+var _ node.Node = (*EthRctTrie)(nil)
+
+/*
+ INPUT
+*/
+
+// To create a proper trie of the eth-tx-trie objects, it is required
+// to input all transactions belonging to a forest in a single step.
+// We are adding the transactions, and creating its trie on
+// block body parsing time.
+
+/*
+ OUTPUT
+*/
+
+// DecodeEthRctTrie returns an EthRctTrie object from its cid and rawdata.
+func DecodeEthRctTrie(c cid.Cid, b []byte) (*EthRctTrie, error) {
+ tn, err := decodeTrieNode(c, b, decodeEthRctTrieLeaf)
+ if err != nil {
+ return nil, err
+ }
+ return &EthRctTrie{TrieNode: tn}, nil
+}
+
+// decodeEthRctTrieLeaf parses a eth-rct-trie leaf
+//from decoded RLP elements
+func decodeEthRctTrieLeaf(i []interface{}) ([]interface{}, error) {
+ var r types.Receipt
+ err := rlp.DecodeBytes(i[1].([]byte), &r)
+ if err != nil {
+ return nil, err
+ }
+ c, err := RawdataToCid(MEthTxReceipt, i[1].([]byte), multihash.KECCAK_256)
+ if err != nil {
+ return nil, err
+ }
+ return []interface{}{
+ i[0].([]byte),
+ &EthReceipt{
+ Receipt: &r,
+ cid: c,
+ rawdata: i[1].([]byte),
+ },
+ }, nil
+}
+
+/*
+ Block INTERFACE
+*/
+
+// RawData returns the binary of the RLP encode of the transaction.
+func (t *EthRctTrie) RawData() []byte {
+ return t.rawdata
+}
+
+// Cid returns the cid of the transaction.
+func (t *EthRctTrie) Cid() cid.Cid {
+ return t.cid
+}
+
+// String is a helper for output
+func (t *EthRctTrie) String() string {
+ return fmt.Sprintf("", t.cid)
+}
+
+// Loggable returns in a map the type of IPLD Link.
+func (t *EthRctTrie) Loggable() map[string]interface{} {
+ return map[string]interface{}{
+ "type": "eth-rct-trie",
+ }
+}
+
+/*
+ EthRctTrie functions
+*/
+
+// rctTrie wraps a localTrie for use on the receipt trie.
+type rctTrie struct {
+ *localTrie
+}
+
+// newRctTrie initializes and returns a rctTrie.
+func newRctTrie() *rctTrie {
+ return &rctTrie{
+ localTrie: newLocalTrie(),
+ }
+}
+
+// getNodes invokes the localTrie, which computes the root hash of the
+// transaction trie and returns its database keys, to return a slice
+// of EthRctTrie nodes.
+func (rt *rctTrie) getNodes() []*EthRctTrie {
+ keys := rt.getKeys()
+ var out []*EthRctTrie
+ it := rt.trie.NodeIterator([]byte{})
+ for it.Next(true) {
+
+ }
+ for _, k := range keys {
+ rawdata, err := rt.db.Get(k)
+ if err != nil {
+ panic(err)
+ }
+ c, err := RawdataToCid(MEthTxReceiptTrie, rawdata, multihash.KECCAK_256)
+ if err != nil {
+ return nil
+ }
+ tn := &TrieNode{
+ cid: c,
+ rawdata: rawdata,
+ }
+ out = append(out, &EthRctTrie{TrieNode: tn})
+ }
+
+ return out
+}
diff --git a/pkg/ipfs/ipld/eth_state.go b/pkg/ipfs/ipld/eth_state.go
index cf8f9a6c..a127f956 100644
--- a/pkg/ipfs/ipld/eth_state.go
+++ b/pkg/ipfs/ipld/eth_state.go
@@ -21,14 +21,15 @@ import (
"github.com/ipfs/go-cid"
node "github.com/ipfs/go-ipld-format"
- mh "github.com/multiformats/go-multihash"
+ "github.com/multiformats/go-multihash"
+
+ "github.com/ethereum/go-ethereum/rlp"
)
// EthStateTrie (eth-state-trie, codec 0x96), represents
-// a node from the state trie in ethereum.
+// a node from the satte trie in ethereum.
type EthStateTrie struct {
- cid cid.Cid
- rawdata []byte
+ *TrieNode
}
// Static (compile time) check that EthStateTrie satisfies the node.Node interface.
@@ -38,16 +39,51 @@ var _ node.Node = (*EthStateTrie)(nil)
INPUT
*/
-// FromStateTrieRLP takes the RLP bytes of an ethereum
+// FromStateTrieRLP takes the RLP representation of an ethereum
// state trie node to return it as an IPLD node for further processing.
-func FromStateTrieRLP(stateNodeRLP []byte) (*EthStateTrie, error) {
- c, err := RawdataToCid(MEthStateTrie, stateNodeRLP, mh.KECCAK_256)
+func FromStateTrieRLP(raw []byte) (*EthStateTrie, error) {
+ c, err := RawdataToCid(MEthStateTrie, raw, multihash.KECCAK_256)
if err != nil {
return nil, err
}
- return &EthStateTrie{
- cid: c,
- rawdata: stateNodeRLP,
+ // Let's run the whole mile and process the nodeKind and
+ // its elements, in case somebody would need this function
+ // to parse an RLP element from the filesystem
+ return DecodeEthStateTrie(c, raw)
+}
+
+/*
+ OUTPUT
+*/
+
+// DecodeEthStateTrie returns an EthStateTrie object from its cid and rawdata.
+func DecodeEthStateTrie(c cid.Cid, b []byte) (*EthStateTrie, error) {
+ tn, err := decodeTrieNode(c, b, decodeEthStateTrieLeaf)
+ if err != nil {
+ return nil, err
+ }
+ return &EthStateTrie{TrieNode: tn}, nil
+}
+
+// decodeEthStateTrieLeaf parses a eth-tx-trie leaf
+// from decoded RLP elements
+func decodeEthStateTrieLeaf(i []interface{}) ([]interface{}, error) {
+ var account EthAccount
+ err := rlp.DecodeBytes(i[1].([]byte), &account)
+ if err != nil {
+ return nil, err
+ }
+ c, err := RawdataToCid(MEthAccountSnapshot, i[1].([]byte), multihash.KECCAK_256)
+ if err != nil {
+ return nil, err
+ }
+ return []interface{}{
+ i[0].([]byte),
+ &EthAccountSnapshot{
+ EthAccount: &account,
+ cid: c,
+ rawdata: i[1].([]byte),
+ },
}, nil
}
@@ -70,35 +106,6 @@ func (st *EthStateTrie) String() string {
return fmt.Sprintf("", st.cid)
}
-// Copy will go away. It is here to comply with the Node interface.
-func (*EthStateTrie) Copy() node.Node {
- panic("implement me")
-}
-
-func (*EthStateTrie) Links() []*node.Link {
- panic("implement me")
-}
-
-func (*EthStateTrie) Resolve(path []string) (interface{}, []string, error) {
- panic("implement me")
-}
-
-func (*EthStateTrie) ResolveLink(path []string) (*node.Link, []string, error) {
- panic("implement me")
-}
-
-func (*EthStateTrie) Size() (uint64, error) {
- panic("implement me")
-}
-
-func (*EthStateTrie) Stat() (*node.NodeStat, error) {
- panic("implement me")
-}
-
-func (*EthStateTrie) Tree(path string, depth int) []string {
- panic("implement me")
-}
-
// Loggable returns in a map the type of IPLD Link.
func (st *EthStateTrie) Loggable() map[string]interface{} {
return map[string]interface{}{
diff --git a/pkg/ipfs/ipld/eth_storage.go b/pkg/ipfs/ipld/eth_storage.go
index b0d3af79..779cad4d 100644
--- a/pkg/ipfs/ipld/eth_storage.go
+++ b/pkg/ipfs/ipld/eth_storage.go
@@ -21,14 +21,13 @@ import (
"github.com/ipfs/go-cid"
node "github.com/ipfs/go-ipld-format"
- mh "github.com/multiformats/go-multihash"
+ "github.com/multiformats/go-multihash"
)
// EthStorageTrie (eth-storage-trie, codec 0x98), represents
// a node from the storage trie in ethereum.
type EthStorageTrie struct {
- cid cid.Cid
- rawdata []byte
+ *TrieNode
}
// Static (compile time) check that EthStorageTrie satisfies the node.Node interface.
@@ -38,16 +37,39 @@ var _ node.Node = (*EthStorageTrie)(nil)
INPUT
*/
-// FromStorageTrieRLP takes the RLP bytes of an ethereum
+// FromStorageTrieRLP takes the RLP representation of an ethereum
// storage trie node to return it as an IPLD node for further processing.
-func FromStorageTrieRLP(storageNodeRLP []byte) (*EthStorageTrie, error) {
- c, err := RawdataToCid(MEthStorageTrie, storageNodeRLP, mh.KECCAK_256)
+func FromStorageTrieRLP(raw []byte) (*EthStorageTrie, error) {
+ c, err := RawdataToCid(MEthStorageTrie, raw, multihash.KECCAK_256)
if err != nil {
return nil, err
}
- return &EthStorageTrie{
- cid: c,
- rawdata: storageNodeRLP,
+
+ // Let's run the whole mile and process the nodeKind and
+ // its elements, in case somebody would need this function
+ // to parse an RLP element from the filesystem
+ return DecodeEthStorageTrie(c, raw)
+}
+
+/*
+ OUTPUT
+*/
+
+// DecodeEthStorageTrie returns an EthStorageTrie object from its cid and rawdata.
+func DecodeEthStorageTrie(c cid.Cid, b []byte) (*EthStorageTrie, error) {
+ tn, err := decodeTrieNode(c, b, decodeEthStorageTrieLeaf)
+ if err != nil {
+ return nil, err
+ }
+ return &EthStorageTrie{TrieNode: tn}, nil
+}
+
+// decodeEthStorageTrieLeaf parses a eth-tx-trie leaf
+// from decoded RLP elements
+func decodeEthStorageTrieLeaf(i []interface{}) ([]interface{}, error) {
+ return []interface{}{
+ i[0].([]byte),
+ i[1].([]byte),
}, nil
}
@@ -70,35 +92,6 @@ func (st *EthStorageTrie) String() string {
return fmt.Sprintf("", st.cid)
}
-// Copy will go away. It is here to comply with the Node interface.
-func (*EthStorageTrie) Copy() node.Node {
- panic("implement me")
-}
-
-func (*EthStorageTrie) Links() []*node.Link {
- panic("implement me")
-}
-
-func (*EthStorageTrie) Resolve(path []string) (interface{}, []string, error) {
- panic("implement me")
-}
-
-func (*EthStorageTrie) ResolveLink(path []string) (*node.Link, []string, error) {
- panic("implement me")
-}
-
-func (*EthStorageTrie) Size() (uint64, error) {
- panic("implement me")
-}
-
-func (*EthStorageTrie) Stat() (*node.NodeStat, error) {
- panic("implement me")
-}
-
-func (*EthStorageTrie) Tree(path string, depth int) []string {
- panic("implement me")
-}
-
// Loggable returns in a map the type of IPLD Link.
func (st *EthStorageTrie) Loggable() map[string]interface{} {
return map[string]interface{}{
diff --git a/pkg/ipfs/ipld/eth_tx_trie.go b/pkg/ipfs/ipld/eth_tx_trie.go
new file mode 100644
index 00000000..6f106f6d
--- /dev/null
+++ b/pkg/ipfs/ipld/eth_tx_trie.go
@@ -0,0 +1,152 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "fmt"
+
+ "github.com/ipfs/go-cid"
+ node "github.com/ipfs/go-ipld-format"
+ "github.com/multiformats/go-multihash"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// EthTxTrie (eth-tx-trie codec 0x92) represents
+// a node from the transaction trie in ethereum.
+type EthTxTrie struct {
+ *TrieNode
+}
+
+// Static (compile time) check that EthTxTrie satisfies the node.Node interface.
+var _ node.Node = (*EthTxTrie)(nil)
+
+/*
+ INPUT
+*/
+
+// To create a proper trie of the eth-tx-trie objects, it is required
+// to input all transactions belonging to a forest in a single step.
+// We are adding the transactions, and creating its trie on
+// block body parsing time.
+
+/*
+ OUTPUT
+*/
+
+// DecodeEthTxTrie returns an EthTxTrie object from its cid and rawdata.
+func DecodeEthTxTrie(c cid.Cid, b []byte) (*EthTxTrie, error) {
+ tn, err := decodeTrieNode(c, b, decodeEthTxTrieLeaf)
+ if err != nil {
+ return nil, err
+ }
+ return &EthTxTrie{TrieNode: tn}, nil
+}
+
+// decodeEthTxTrieLeaf parses a eth-tx-trie leaf
+//from decoded RLP elements
+func decodeEthTxTrieLeaf(i []interface{}) ([]interface{}, error) {
+ var t types.Transaction
+ err := rlp.DecodeBytes(i[1].([]byte), &t)
+ if err != nil {
+ return nil, err
+ }
+ c, err := RawdataToCid(MEthTx, i[1].([]byte), multihash.KECCAK_256)
+ if err != nil {
+ return nil, err
+ }
+ return []interface{}{
+ i[0].([]byte),
+ &EthTx{
+ Transaction: &t,
+ cid: c,
+ rawdata: i[1].([]byte),
+ },
+ }, nil
+}
+
+/*
+ Block INTERFACE
+*/
+
+// RawData returns the binary of the RLP encode of the transaction.
+func (t *EthTxTrie) RawData() []byte {
+ return t.rawdata
+}
+
+// Cid returns the cid of the transaction.
+func (t *EthTxTrie) Cid() cid.Cid {
+ return t.cid
+}
+
+// String is a helper for output
+func (t *EthTxTrie) String() string {
+ return fmt.Sprintf("", t.cid)
+}
+
+// Loggable returns in a map the type of IPLD Link.
+func (t *EthTxTrie) Loggable() map[string]interface{} {
+ return map[string]interface{}{
+ "type": "eth-tx-trie",
+ }
+}
+
+/*
+ EthTxTrie functions
+*/
+
+// txTrie wraps a localTrie for use on the transaction trie.
+type txTrie struct {
+ *localTrie
+}
+
+// newTxTrie initializes and returns a txTrie.
+func newTxTrie() *txTrie {
+ return &txTrie{
+ localTrie: newLocalTrie(),
+ }
+}
+
+// getNodes invokes the localTrie, which computes the root hash of the
+// transaction trie and returns its database keys, to return a slice
+// of EthTxTrie nodes.
+func (tt *txTrie) getNodes() []*EthTxTrie {
+ keys := tt.getKeys()
+ var out []*EthTxTrie
+ it := tt.trie.NodeIterator([]byte{})
+ for it.Next(true) {
+
+ }
+ for _, k := range keys {
+ rawdata, err := tt.db.Get(k)
+ if err != nil {
+ panic(err)
+ }
+ c, err := RawdataToCid(MEthTxTrie, rawdata, multihash.KECCAK_256)
+ if err != nil {
+ return nil
+ }
+ tn := &TrieNode{
+ cid: c,
+ rawdata: rawdata,
+ }
+ out = append(out, &EthTxTrie{TrieNode: tn})
+ }
+
+ return out
+}
diff --git a/pkg/ipfs/ipld/shared.go b/pkg/ipfs/ipld/shared.go
index 802d944c..fe6293f7 100644
--- a/pkg/ipfs/ipld/shared.go
+++ b/pkg/ipfs/ipld/shared.go
@@ -17,7 +17,13 @@
package ipld
import (
+ "bytes"
+
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
"github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"
)
@@ -87,3 +93,56 @@ func sha256ToCid(codec uint64, h []byte) cid.Cid {
return cid.NewCidV1(codec, hash)
}
+
+// getRLP encodes the given object to RLP returning its bytes.
+func getRLP(object interface{}) []byte {
+ buf := new(bytes.Buffer)
+ if err := rlp.Encode(buf, object); err != nil {
+ panic(err)
+ }
+
+ return buf.Bytes()
+}
+
+// localTrie wraps a go-ethereum trie and its underlying memory db.
+// It contributes to the creation of the trie node objects.
+type localTrie struct {
+ keys [][]byte
+ db ethdb.Database
+ trie *trie.Trie
+}
+
+// newLocalTrie initializes and returns a localTrie object
+func newLocalTrie() *localTrie {
+ var err error
+ lt := &localTrie{}
+ lt.db = rawdb.NewMemoryDatabase()
+ lt.trie, err = trie.New(common.Hash{}, trie.NewDatabase(lt.db))
+ if err != nil {
+ panic(err)
+ }
+ return lt
+}
+
+// add receives the index of an object and its rawdata value
+// and includes it into the localTrie
+func (lt *localTrie) add(idx int, rawdata []byte) {
+ key, err := rlp.EncodeToBytes(uint(idx))
+ if err != nil {
+ panic(err)
+ }
+ lt.keys = append(lt.keys, key)
+ lt.trie.Update(key, rawdata)
+}
+
+// rootHash returns the computed trie root.
+// Useful for sanity checks on parsed data.
+func (lt *localTrie) rootHash() []byte {
+ return lt.trie.Hash().Bytes()
+}
+
+// getKeys returns the stored keys of the memory database
+// of the localTrie for further processing.
+func (lt *localTrie) getKeys() [][]byte {
+ return lt.keys
+}
diff --git a/pkg/ipfs/ipld/trie_node.go b/pkg/ipfs/ipld/trie_node.go
new file mode 100644
index 00000000..4534138e
--- /dev/null
+++ b/pkg/ipfs/ipld/trie_node.go
@@ -0,0 +1,440 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ipld
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ipfs/go-cid"
+ node "github.com/ipfs/go-ipld-format"
+)
+
+// TrieNode is the general abstraction for
+//ethereum IPLD trie nodes.
+type TrieNode struct {
+ // leaf, extension or branch
+ nodeKind string
+
+ // If leaf or extension: [0] is key, [1] is val.
+ // If branch: [0] - [16] are children.
+ elements []interface{}
+
+ // IPLD block information
+ cid cid.Cid
+ rawdata []byte
+}
+
+/*
+ OUTPUT
+*/
+
+type trieNodeLeafDecoder func([]interface{}) ([]interface{}, error)
+
+// decodeTrieNode returns a TrieNode object from an IPLD block's
+// cid and rawdata.
+func decodeTrieNode(c cid.Cid, b []byte,
+ leafDecoder trieNodeLeafDecoder) (*TrieNode, error) {
+ var (
+ i, decoded, elements []interface{}
+ nodeKind string
+ err error
+ )
+
+ err = rlp.DecodeBytes(b, &i)
+ if err != nil {
+ return nil, err
+ }
+
+ codec := c.Type()
+ switch len(i) {
+ case 2:
+ nodeKind, decoded, err = decodeCompactKey(i)
+ if err != nil {
+ return nil, err
+ }
+
+ if nodeKind == "extension" {
+ elements, err = parseTrieNodeExtension(decoded, codec)
+ }
+ if nodeKind == "leaf" {
+ elements, err = leafDecoder(decoded)
+ }
+ if nodeKind != "extension" && nodeKind != "leaf" {
+ return nil, fmt.Errorf("unexpected nodeKind returned from decoder")
+ }
+ case 17:
+ nodeKind = "branch"
+ elements, err = parseTrieNodeBranch(i, codec)
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, fmt.Errorf("unknown trie node type")
+ }
+
+ return &TrieNode{
+ nodeKind: nodeKind,
+ elements: elements,
+ rawdata: b,
+ cid: c,
+ }, nil
+}
+
+// decodeCompactKey takes a compact key, and returns its nodeKind and value.
+func decodeCompactKey(i []interface{}) (string, []interface{}, error) {
+ first := i[0].([]byte)
+ last := i[1].([]byte)
+
+ switch first[0] / 16 {
+ case '\x00':
+ return "extension", []interface{}{
+ nibbleToByte(first)[2:],
+ last,
+ }, nil
+ case '\x01':
+ return "extension", []interface{}{
+ nibbleToByte(first)[1:],
+ last,
+ }, nil
+ case '\x02':
+ return "leaf", []interface{}{
+ nibbleToByte(first)[2:],
+ last,
+ }, nil
+ case '\x03':
+ return "leaf", []interface{}{
+ nibbleToByte(first)[1:],
+ last,
+ }, nil
+ default:
+ return "", nil, fmt.Errorf("unknown hex prefix")
+ }
+}
+
+// parseTrieNodeExtension helper improves readability
+func parseTrieNodeExtension(i []interface{}, codec uint64) ([]interface{}, error) {
+ return []interface{}{
+ i[0].([]byte),
+ keccak256ToCid(codec, i[1].([]byte)),
+ }, nil
+}
+
+// parseTrieNodeBranch helper improves readability
+func parseTrieNodeBranch(i []interface{}, codec uint64) ([]interface{}, error) {
+ var out []interface{}
+
+ for _, vi := range i {
+ v := vi.([]byte)
+
+ switch len(v) {
+ case 0:
+ out = append(out, nil)
+ case 32:
+ out = append(out, keccak256ToCid(codec, v))
+ default:
+ return nil, fmt.Errorf("unrecognized object: %v", v)
+ }
+ }
+
+ return out, nil
+}
+
+/*
+ Node INTERFACE
+*/
+
+// Resolve resolves a path through this node, stopping at any link boundary
+// and returning the object found as well as the remaining path to traverse
+func (t *TrieNode) Resolve(p []string) (interface{}, []string, error) {
+ switch t.nodeKind {
+ case "extension":
+ return t.resolveTrieNodeExtension(p)
+ case "leaf":
+ return t.resolveTrieNodeLeaf(p)
+ case "branch":
+ return t.resolveTrieNodeBranch(p)
+ default:
+ return nil, nil, fmt.Errorf("nodeKind case not implemented")
+ }
+}
+
+// Tree lists all paths within the object under 'path', and up to the given depth.
+// To list the entire object (similar to `find .`) pass "" and -1
+func (t *TrieNode) Tree(p string, depth int) []string {
+ if p != "" || depth == 0 {
+ return nil
+ }
+
+ var out []string
+
+ switch t.nodeKind {
+ case "extension":
+ var val string
+ for _, e := range t.elements[0].([]byte) {
+ val += fmt.Sprintf("%x", e)
+ }
+ return []string{val}
+ case "branch":
+ for i, elem := range t.elements {
+ if _, ok := elem.(*cid.Cid); ok {
+ out = append(out, fmt.Sprintf("%x", i))
+ }
+ }
+ return out
+
+ default:
+ return nil
+ }
+}
+
+// ResolveLink is a helper function that calls resolve and asserts the
+// output is a link
+func (t *TrieNode) ResolveLink(p []string) (*node.Link, []string, error) {
+ obj, rest, err := t.Resolve(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ lnk, ok := obj.(*node.Link)
+ if !ok {
+ return nil, nil, fmt.Errorf("was not a link")
+ }
+
+ return lnk, rest, nil
+}
+
+// Copy will go away. It is here to comply with the interface.
+func (t *TrieNode) Copy() node.Node {
+ panic("dont use this yet")
+}
+
+// Links is a helper function that returns all links within this object
+func (t *TrieNode) Links() []*node.Link {
+ var out []*node.Link
+
+ for _, i := range t.elements {
+ c, ok := i.(cid.Cid)
+ if ok {
+ out = append(out, &node.Link{Cid: c})
+ }
+ }
+
+ return out
+}
+
+// Stat will go away. It is here to comply with the interface.
+func (t *TrieNode) Stat() (*node.NodeStat, error) {
+ return &node.NodeStat{}, nil
+}
+
+// Size will go away. It is here to comply with the interface.
+func (t *TrieNode) Size() (uint64, error) {
+ return 0, nil
+}
+
+/*
+ TrieNode functions
+*/
+
+// MarshalJSON processes the transaction trie into readable JSON format.
+func (t *TrieNode) MarshalJSON() ([]byte, error) {
+ var out map[string]interface{}
+
+ switch t.nodeKind {
+ case "extension":
+ fallthrough
+ case "leaf":
+ var hexPrefix string
+ for _, e := range t.elements[0].([]byte) {
+ hexPrefix += fmt.Sprintf("%x", e)
+ }
+
+ // if we got a byte we need to do this casting otherwise
+ // it will be marshaled to a base64 encoded value
+ if _, ok := t.elements[1].([]byte); ok {
+ var hexVal string
+ for _, e := range t.elements[1].([]byte) {
+ hexVal += fmt.Sprintf("%x", e)
+ }
+
+ t.elements[1] = hexVal
+ }
+
+ out = map[string]interface{}{
+ "type": t.nodeKind,
+ hexPrefix: t.elements[1],
+ }
+
+ case "branch":
+ out = map[string]interface{}{
+ "type": "branch",
+ "0": t.elements[0],
+ "1": t.elements[1],
+ "2": t.elements[2],
+ "3": t.elements[3],
+ "4": t.elements[4],
+ "5": t.elements[5],
+ "6": t.elements[6],
+ "7": t.elements[7],
+ "8": t.elements[8],
+ "9": t.elements[9],
+ "a": t.elements[10],
+ "b": t.elements[11],
+ "c": t.elements[12],
+ "d": t.elements[13],
+ "e": t.elements[14],
+ "f": t.elements[15],
+ }
+ default:
+ return nil, fmt.Errorf("nodeKind %s not supported", t.nodeKind)
+ }
+
+ return json.Marshal(out)
+}
+
+// nibbleToByte expands the nibbles of a byte slice into their own bytes.
+func nibbleToByte(k []byte) []byte {
+ var out []byte
+
+ for _, b := range k {
+ out = append(out, b/16)
+ out = append(out, b%16)
+ }
+
+ return out
+}
+
+// Resolve reading conveniences
+func (t *TrieNode) resolveTrieNodeExtension(p []string) (interface{}, []string, error) {
+ nibbles := t.elements[0].([]byte)
+ idx, rest := shiftFromPath(p, len(nibbles))
+ if len(idx) < len(nibbles) {
+ return nil, nil, fmt.Errorf("not enough nibbles to traverse this extension")
+ }
+
+ for _, i := range idx {
+ if getHexIndex(string(i)) == -1 {
+ return nil, nil, fmt.Errorf("invalid path element")
+ }
+ }
+
+ for i, n := range nibbles {
+ if string(idx[i]) != fmt.Sprintf("%x", n) {
+ return nil, nil, fmt.Errorf("no such link in this extension")
+ }
+ }
+
+ return &node.Link{Cid: t.elements[1].(cid.Cid)}, rest, nil
+}
+
+func (t *TrieNode) resolveTrieNodeLeaf(p []string) (interface{}, []string, error) {
+ nibbles := t.elements[0].([]byte)
+
+ if len(nibbles) != 0 {
+ idx, rest := shiftFromPath(p, len(nibbles))
+ if len(idx) < len(nibbles) {
+ return nil, nil, fmt.Errorf("not enough nibbles to traverse this leaf")
+ }
+
+ for _, i := range idx {
+ if getHexIndex(string(i)) == -1 {
+ return nil, nil, fmt.Errorf("invalid path element")
+ }
+ }
+
+ for i, n := range nibbles {
+ if string(idx[i]) != fmt.Sprintf("%x", n) {
+ return nil, nil, fmt.Errorf("no such link in this extension")
+ }
+ }
+
+ p = rest
+ }
+
+ link, ok := t.elements[1].(node.Node)
+ if !ok {
+ return nil, nil, fmt.Errorf("leaf children is not an IPLD node")
+ }
+
+ return link.Resolve(p)
+}
+
+func (t *TrieNode) resolveTrieNodeBranch(p []string) (interface{}, []string, error) {
+ idx, rest := shiftFromPath(p, 1)
+ hidx := getHexIndex(idx)
+ if hidx == -1 {
+ return nil, nil, fmt.Errorf("incorrect path")
+ }
+
+ child := t.elements[hidx]
+ if child != nil {
+ return &node.Link{Cid: child.(cid.Cid)}, rest, nil
+ }
+ return nil, nil, fmt.Errorf("no such link in this branch")
+}
+
+// shiftFromPath extracts from a given path (as a slice of strings)
+// the given number of elements as a single string, returning whatever
+// it has not taken.
+//
+// Examples:
+// ["0", "a", "something"] and 1 -> "0" and ["a", "something"]
+// ["ab", "c", "d", "1"] and 2 -> "ab" and ["c", "d", "1"]
+// ["abc", "d", "1"] and 2 -> "ab" and ["c", "d", "1"]
+func shiftFromPath(p []string, i int) (string, []string) {
+ var (
+ out string
+ rest []string
+ )
+
+ for _, pe := range p {
+ re := ""
+ for _, c := range pe {
+ if len(out) < i {
+ out += string(c)
+ } else {
+ re += string(c)
+ }
+ }
+
+ if len(out) == i && re != "" {
+ rest = append(rest, re)
+ }
+ }
+
+ return out, rest
+}
+
+// getHexIndex returns to you the integer 0 - 15 equivalent to your
+// string character if applicable, or -1 otherwise.
+func getHexIndex(s string) int {
+ if len(s) != 1 {
+ return -1
+ }
+
+ c := byte(s[0])
+ switch {
+ case '0' <= c && c <= '9':
+ return int(c - '0')
+ case 'a' <= c && c <= 'f':
+ return int(c - 'a' + 10)
+ }
+
+ return -1
+}