V1.10.2 statediff 0.0.20 #68
@ -141,7 +141,7 @@ func (as *EthAccountSnapshot) ResolveLink(p []string) (*node.Link, []string, err
|
|||||||
|
|
||||||
// Copy will go away. It is here to comply with the interface.
|
// Copy will go away. It is here to comply with the interface.
|
||||||
func (as *EthAccountSnapshot) Copy() node.Node {
|
func (as *EthAccountSnapshot) Copy() node.Node {
|
||||||
panic("dont use this yet")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links is a helper function that returns all links within this object
|
// Links is a helper function that returns all links within this object
|
||||||
|
292
statediff/indexer/ipfs/ipld/eth_account_test.go
Normal file
292
statediff/indexer/ipfs/ipld/eth_account_test.go
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Block INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestAccountSnapshotBlockElements(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", eas.RawData())[:10] != "f84e808a03" {
|
||||||
|
t.Fatal("Wrong Data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if eas.Cid().String() !=
|
||||||
|
"baglqcgzasckx2alxk43cksshnztjvhfyvbbh6bkp376gtcndm5cg4fkrkhsa" {
|
||||||
|
t.Fatal("Wrong Cid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotString(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
if eas.String() !=
|
||||||
|
"<EthereumAccountSnapshot baglqcgzasckx2alxk43cksshnztjvhfyvbbh6bkp376gtcndm5cg4fkrkhsa>" {
|
||||||
|
t.Fatalf("Wrong String()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotLoggable(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
l := eas.Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-account-snapshot" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-account-snapshot", l["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Node INTERFACE
|
||||||
|
*/
|
||||||
|
func TestAccountSnapshotResolve(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
// Empty path
|
||||||
|
obj, rest, err := eas.Resolve([]string{})
|
||||||
|
reas, ok := obj.(*EthAccountSnapshot)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Wrong type of returned object\r\nexpected %T\r\ngot %T", &EthAccountSnapshot{}, reas)
|
||||||
|
}
|
||||||
|
if reas.Cid() != eas.Cid() {
|
||||||
|
t.Fatalf("wrong returned CID\r\nexpected %s\r\ngot %s", eas.Cid().String(), reas.Cid().String())
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("rest should be nil")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// len(p) > 1
|
||||||
|
badCases := [][]string{
|
||||||
|
{"two", "elements"},
|
||||||
|
{"here", "three", "elements"},
|
||||||
|
{"and", "here", "four", "elements"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bc := range badCases {
|
||||||
|
obj, rest, err = eas.Resolve(bc)
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("obj should be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("rest should be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != fmt.Sprintf("unexpected path elements past %s", bc[0]) {
|
||||||
|
t.Fatal("wrong error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moreBadCases := []string{
|
||||||
|
"i",
|
||||||
|
"am",
|
||||||
|
"not",
|
||||||
|
"an",
|
||||||
|
"account",
|
||||||
|
"field",
|
||||||
|
}
|
||||||
|
for _, mbc := range moreBadCases {
|
||||||
|
obj, rest, err = eas.Resolve([]string{mbc})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("obj should be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("rest should be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != fmt.Sprintf("no such link") {
|
||||||
|
t.Fatal("wrong error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goodCases := []string{
|
||||||
|
"balance",
|
||||||
|
"codeHash",
|
||||||
|
"nonce",
|
||||||
|
"root",
|
||||||
|
}
|
||||||
|
for _, gc := range goodCases {
|
||||||
|
_, _, err = eas.Resolve([]string{gc})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error should be nil %v", gc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotTree(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
// Bad cases
|
||||||
|
tree := eas.Tree("non-empty-string", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = eas.Tree("non-empty-string", 1)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = eas.Tree("", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Good cases
|
||||||
|
tree = eas.Tree("", 1)
|
||||||
|
lookupElements := map[string]interface{}{
|
||||||
|
"balance": nil,
|
||||||
|
"codeHash": nil,
|
||||||
|
"nonce": nil,
|
||||||
|
"root": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tree) != len(lookupElements) {
|
||||||
|
t.Fatalf("Wrong number of elements\r\nexpected %d\r\ngot %d", len(lookupElements), len(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, te := range tree {
|
||||||
|
if _, ok := lookupElements[te]; !ok {
|
||||||
|
t.Fatalf("Unexpected Element: %v", te)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotResolveLink(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
// bad case
|
||||||
|
obj, rest, err := eas.ResolveLink([]string{"supercalifragilist"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "no such link" {
|
||||||
|
t.Fatal("Wrong error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// good case
|
||||||
|
obj, rest, err = eas.ResolveLink([]string{"nonce"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "resolved item was not a link" {
|
||||||
|
t.Fatal("Wrong error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotCopy(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
t.Fatal("Expected panic")
|
||||||
|
}
|
||||||
|
if r != "implement me" {
|
||||||
|
t.Fatalf("Wrong panic message\r\n expected %s\r\ngot %s", "'implement me'", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = eas.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotLinks(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
if eas.Links() != nil {
|
||||||
|
t.Fatal("Links() expected to return nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotStat(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
obj, err := eas.Stat()
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatal("Expected a not null object node.NodeStat")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountSnapshotSize(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
size, err := eas.Size()
|
||||||
|
if size != uint64(0) {
|
||||||
|
t.Fatalf("Wrong size\r\nexpected %d\r\ngot %d", 0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
EthAccountSnapshot functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestAccountSnapshotMarshalJSON(t *testing.T) {
|
||||||
|
eas := prepareEthAccountSnapshot(t)
|
||||||
|
|
||||||
|
jsonOutput, err := eas.MarshalJSON()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.Unmarshal(jsonOutput, &data)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
balanceExpression := regexp.MustCompile(`{"balance":16011846000000000000000,`)
|
||||||
|
if !balanceExpression.MatchString(string(jsonOutput)) {
|
||||||
|
t.Fatal("Balance expression not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
code, _ := data["codeHash"].(map[string]interface{})
|
||||||
|
if fmt.Sprintf("%s", code["/"]) !=
|
||||||
|
"bafkrwigf2jdadbxxem6je7t5wlomoa6a4ualmu6kqittw6723acf3bneoa" {
|
||||||
|
t.Fatalf("Wrong Marshaled Value\r\nexpected %s\r\ngot %s", "bafkrwigf2jdadbxxem6je7t5wlomoa6a4ualmu6kqittw6723acf3bneoa", fmt.Sprintf("%s", code["/"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%v", data["nonce"]) != "0" {
|
||||||
|
t.Fatalf("Wrong Marshaled Value\r\nexpected %s\r\ngot %s", "0", fmt.Sprintf("%v", data["nonce"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
root, _ := data["root"].(map[string]interface{})
|
||||||
|
if fmt.Sprintf("%s", root["/"]) !=
|
||||||
|
"bagmacgzak3ub6fy3zrk2n74dixtjfqhynznurya3tfwk3qabmix3ly3dwqqq" {
|
||||||
|
t.Fatalf("Wrong Marshaled Value\r\nexpected %s\r\ngot %s", "bagmacgzak3ub6fy3zrk2n74dixtjfqhynznurya3tfwk3qabmix3ly3dwqqq", fmt.Sprintf("%s", root["/"]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUXILIARS
|
||||||
|
*/
|
||||||
|
func prepareEthAccountSnapshot(t *testing.T) *EthAccountSnapshot {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-c9070d")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
return output.elements[1].(*EthAccountSnapshot)
|
||||||
|
}
|
@ -20,6 +20,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
@ -66,7 +68,7 @@ func NewEthHeader(header *types.Header) (*EthHeader, error) {
|
|||||||
// DecodeEthHeader takes a cid and its raw binary data
|
// DecodeEthHeader takes a cid and its raw binary data
|
||||||
// from IPFS and returns an EthTx object for further processing.
|
// from IPFS and returns an EthTx object for further processing.
|
||||||
func DecodeEthHeader(c cid.Cid, b []byte) (*EthHeader, error) {
|
func DecodeEthHeader(c cid.Cid, b []byte) (*EthHeader, error) {
|
||||||
var h *types.Header
|
h := new(types.Header)
|
||||||
if err := rlp.DecodeBytes(b, h); err != nil {
|
if err := rlp.DecodeBytes(b, h); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -99,7 +101,7 @@ func (b *EthHeader) String() string {
|
|||||||
// Loggable returns a map the type of IPLD Link.
|
// Loggable returns a map the type of IPLD Link.
|
||||||
func (b *EthHeader) Loggable() map[string]interface{} {
|
func (b *EthHeader) Loggable() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"type": "eth-block",
|
"type": "eth-header",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,3 +256,38 @@ func (b *EthHeader) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return json.Marshal(out)
|
return json.Marshal(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// objJSONHeader defines the output of the JSON RPC API for either
|
||||||
|
// "eth_BlockByHash" or "eth_BlockByHeader".
|
||||||
|
type objJSONHeader struct {
|
||||||
|
Result objJSONHeaderResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// objJSONBLockResult is the nested struct that takes
|
||||||
|
// the contents of the JSON field "result".
|
||||||
|
type objJSONHeaderResult struct {
|
||||||
|
types.Header // Use its fields and unmarshaler
|
||||||
|
*objJSONHeaderResultExt // 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 objJSONHeaderResultExt 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 *objJSONHeaderResult) UnmarshalJSON(input []byte) error {
|
||||||
|
err := o.Header.UnmarshalJSON(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.objJSONHeaderResultExt = &objJSONHeaderResultExt{}
|
||||||
|
err = json.Unmarshal(input, o.objJSONHeaderResultExt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
585
statediff/indexer/ipfs/ipld/eth_header_test.go
Normal file
585
statediff/indexer/ipfs/ipld/eth_header_test.go
Normal file
@ -0,0 +1,585 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
block "github.com/ipfs/go-block-format"
|
||||||
|
node "github.com/ipfs/go-ipld-format"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBlockBodyRlpParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-rlp-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, _, _, err := FromBlockRLP(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
testEthBlockFields(output, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockHeaderRlpParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-header-rlp-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, _, _, err := FromBlockRLP(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
testEthBlockFields(output, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockBodyJsonParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-json-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, _, _, err := FromBlockJSON(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
testEthBlockFields(output, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockProcessTransactionsError(t *testing.T) {
|
||||||
|
// Let's just change one byte in a field of one of these transactions.
|
||||||
|
fi, err := os.Open("test_data/error-tx-eth-block-body-json-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, _, _, err = FromBlockJSON(fi)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDecodeBlockHeader should work for both inputs (block header and block body)
|
||||||
|
// as what we are storing is just the block header
|
||||||
|
func TestDecodeBlockHeader(t *testing.T) {
|
||||||
|
storedEthBlock := prepareStoredEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
ethBlock, err := DecodeEthHeader(storedEthBlock.Cid(), storedEthBlock.RawData())
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
testEthBlockFields(ethBlock, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockString(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
if ethBlock.String() != "<EthHeader bagiacgzawt5236hkiuvrhfyy4jya3qitlt6icfcqgheew6vsptlraokppm4a>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthHeader bagiacgzawt5236hkiuvrhfyy4jya3qitlt6icfcqgheew6vsptlraokppm4a>", ethBlock.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockLoggable(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
l := ethBlock.Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-header" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-header", l["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockJSONMarshal(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
jsonOutput, err := ethBlock.MarshalJSON()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.Unmarshal(jsonOutput, &data)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// Testing all fields is boring, but can help us to avoid
|
||||||
|
// that dreaded regression
|
||||||
|
if data["bloom"].(string)[:10] != "0x00000000" {
|
||||||
|
t.Fatalf("Wrong Bloom\r\nexpected %s\r\ngot %s", "0x00000000", data["bloom"].(string)[:10])
|
||||||
|
t.Fatal("Wrong Bloom")
|
||||||
|
}
|
||||||
|
if data["coinbase"] != "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5" {
|
||||||
|
t.Fatalf("Wrong coinbase\r\nexpected %s\r\ngot %s", "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5", data["coinbase"])
|
||||||
|
}
|
||||||
|
if parseFloat(data["difficulty"]) != "12555463106190" {
|
||||||
|
t.Fatalf("Wrong Difficulty\r\nexpected %s\r\ngot %s", "12555463106190", parseFloat(data["difficulty"]))
|
||||||
|
}
|
||||||
|
if data["extra"] != "0xd783010303844765746887676f312e342e32856c696e7578" {
|
||||||
|
t.Fatalf("Wrong Extra\r\nexpected %s\r\ngot %s", "0xd783010303844765746887676f312e342e32856c696e7578", data["extra"])
|
||||||
|
}
|
||||||
|
if parseFloat(data["gaslimit"]) != "3141592" {
|
||||||
|
t.Fatalf("Wrong Gas limit\r\nexpected %s\r\ngot %s", "3141592", parseFloat(data["gaslimit"]))
|
||||||
|
}
|
||||||
|
if parseFloat(data["gasused"]) != "231000" {
|
||||||
|
t.Fatalf("Wrong Gas used\r\nexpected %s\r\ngot %s", "231000", parseFloat(data["gasused"]))
|
||||||
|
}
|
||||||
|
if data["mixdigest"] != "0x5b10f4a08a6c209d426f6158bd24b574f4f7b7aa0099c67c14a1f693b4dd04d0" {
|
||||||
|
t.Fatalf("Wrong Mix digest\r\nexpected %s\r\ngot %s", "0x5b10f4a08a6c209d426f6158bd24b574f4f7b7aa0099c67c14a1f693b4dd04d0", data["mixdigest"])
|
||||||
|
}
|
||||||
|
if data["nonce"] != "0xf491f46b60fe04b3" {
|
||||||
|
t.Fatalf("Wrong nonce\r\nexpected %s\r\ngot %s", "0xf491f46b60fe04b3", data["nonce"])
|
||||||
|
}
|
||||||
|
if parseFloat(data["number"]) != "999999" {
|
||||||
|
t.Fatalf("Wrong block number\r\nexpected %s\r\ngot %s", "999999", parseFloat(data["number"]))
|
||||||
|
}
|
||||||
|
if parseMapElement(data["parent"]) != "bagiacgza2m6j3xu774hlvjxhd2fsnuv5ufom6ei4ply3mm3jrleeozt7b62a" {
|
||||||
|
t.Fatalf("Wrong Parent cid\r\nexpected %s\r\ngot %s", "bagiacgza2m6j3xu774hlvjxhd2fsnuv5ufom6ei4ply3mm3jrleeozt7b62a", parseMapElement(data["parent"]))
|
||||||
|
}
|
||||||
|
if parseMapElement(data["receipts"]) != "bagkacgzap6qpnsrkagbdecgybaa63ljx4pr2aa5vlsetdg2f5mpzpbrk2iuq" {
|
||||||
|
t.Fatalf("Wrong Receipt root cid\r\nexpected %s\r\ngot %s", "bagkacgzap6qpnsrkagbdecgybaa63ljx4pr2aa5vlsetdg2f5mpzpbrk2iuq", parseMapElement(data["receipts"]))
|
||||||
|
}
|
||||||
|
if parseMapElement(data["root"]) != "baglacgza5wmkus23dhec7m2tmtyikcfobjw6yzs7uv3ghxfjjroxavkm3yia" {
|
||||||
|
t.Fatalf("Wrong root hash cid\r\nexpected %s\r\ngot %s", "baglacgza5wmkus23dhec7m2tmtyikcfobjw6yzs7uv3ghxfjjroxavkm3yia", parseMapElement(data["root"]))
|
||||||
|
}
|
||||||
|
if parseFloat(data["time"]) != "1455404037" {
|
||||||
|
t.Fatalf("Wrong Time\r\nexpected %s\r\ngot %s", "1455404037", parseFloat(data["time"]))
|
||||||
|
}
|
||||||
|
if parseMapElement(data["tx"]) != "bagjacgzair6l3dci6smknejlccbrzx7vtr737s56onoksked2t5anxgxvzka" {
|
||||||
|
t.Fatalf("Wrong Tx root cid\r\nexpected %s\r\ngot %s", "bagjacgzair6l3dci6smknejlccbrzx7vtr737s56onoksked2t5anxgxvzka", parseMapElement(data["tx"]))
|
||||||
|
}
|
||||||
|
if parseMapElement(data["uncles"]) != "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq" {
|
||||||
|
t.Fatalf("Wrong Uncle hash cid\r\nexpected %s\r\ngot %s", "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq", parseMapElement(data["uncles"]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockLinks(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
links := ethBlock.Links()
|
||||||
|
if links[0].Cid.String() != "bagiacgza2m6j3xu774hlvjxhd2fsnuv5ufom6ei4ply3mm3jrleeozt7b62a" {
|
||||||
|
t.Fatalf("Wrong cid for parent link\r\nexpected: %s\r\ngot %s", "bagiacgza2m6j3xu774hlvjxhd2fsnuv5ufom6ei4ply3mm3jrleeozt7b62a", links[0].Cid.String())
|
||||||
|
}
|
||||||
|
if links[1].Cid.String() != "bagkacgzap6qpnsrkagbdecgybaa63ljx4pr2aa5vlsetdg2f5mpzpbrk2iuq" {
|
||||||
|
t.Fatalf("Wrong cid for receipt root link\r\nexpected: %s\r\ngot %s", "bagkacgzap6qpnsrkagbdecgybaa63ljx4pr2aa5vlsetdg2f5mpzpbrk2iuq", links[1].Cid.String())
|
||||||
|
}
|
||||||
|
if links[2].Cid.String() != "baglacgza5wmkus23dhec7m2tmtyikcfobjw6yzs7uv3ghxfjjroxavkm3yia" {
|
||||||
|
t.Fatalf("Wrong cid for state root link\r\nexpected: %s\r\ngot %s", "baglacgza5wmkus23dhec7m2tmtyikcfobjw6yzs7uv3ghxfjjroxavkm3yia", links[2].Cid.String())
|
||||||
|
}
|
||||||
|
if links[3].Cid.String() != "bagjacgzair6l3dci6smknejlccbrzx7vtr737s56onoksked2t5anxgxvzka" {
|
||||||
|
t.Fatalf("Wrong cid for tx root link\r\nexpected: %s\r\ngot %s", "bagjacgzair6l3dci6smknejlccbrzx7vtr737s56onoksked2t5anxgxvzka", links[3].Cid.String())
|
||||||
|
}
|
||||||
|
if links[4].Cid.String() != "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq" {
|
||||||
|
t.Fatalf("Wrong cid for uncles root link\r\nexpected: %s\r\ngot %s", "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq", links[4].Cid.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveEmptyPath(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{})
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if ethBlock != obj.(*EthHeader) {
|
||||||
|
t.Fatal("Should have returned the same eth-block object")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rest) != 0 {
|
||||||
|
t.Fatalf("Wrong len of rest of the path returned\r\nexpected %d\r\ngot %d", 0, len(rest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveNoSuchLink(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
_, _, err := ethBlock.Resolve([]string{"wewonthavethisfieldever"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Should have failed with unknown field")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != "no such link" {
|
||||||
|
t.Fatalf("Wrong error message\r\nexpected %s\r\ngot %s", "no such link", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveBloom(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{"bloom"})
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// The marshaler of types.Bloom should output it as 0x
|
||||||
|
bloomInText := fmt.Sprintf("%x", obj.(types.Bloom))
|
||||||
|
if bloomInText[:10] != "0000000000" {
|
||||||
|
t.Fatalf("Wrong Bloom\r\nexpected %s\r\ngot %s", "0000000000", bloomInText[:10])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rest) != 0 {
|
||||||
|
t.Fatalf("Wrong len of rest of the path returned\r\nexpected %d\r\ngot %d", 0, len(rest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveBloomExtraPathElements(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{"bloom", "unexpected", "extra", "elements"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("Returned obj should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Returned rest should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != "unexpected path elements past bloom" {
|
||||||
|
t.Fatalf("Wrong error\r\nexpected %s\r\ngot %s", "unexpected path elements past bloom", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveNonLinkFields(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
testCases := map[string][]string{
|
||||||
|
"coinbase": {"%x", "52bc44d5378309ee2abf1539bf71de1b7d7be3b5"},
|
||||||
|
"difficulty": {"%s", "12555463106190"},
|
||||||
|
"extra": {"%s", "0xd783010303844765746887676f312e342e32856c696e7578"},
|
||||||
|
"gaslimit": {"%d", "3141592"},
|
||||||
|
"gasused": {"%d", "231000"},
|
||||||
|
"mixdigest": {"%x", "5b10f4a08a6c209d426f6158bd24b574f4f7b7aa0099c67c14a1f693b4dd04d0"},
|
||||||
|
"nonce": {"%x", "f491f46b60fe04b3"},
|
||||||
|
"number": {"%s", "999999"},
|
||||||
|
"time": {"%d", "1455404037"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for field, value := range testCases {
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{field})
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
format := value[0]
|
||||||
|
result := value[1]
|
||||||
|
if fmt.Sprintf(format, obj) != result {
|
||||||
|
t.Fatalf("Wrong %v\r\nexpected %v\r\ngot %s", field, result, fmt.Sprintf(format, obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rest) != 0 {
|
||||||
|
t.Fatalf("Wrong len of rest of the path returned\r\nexpected %d\r\ngot %d", 0, len(rest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveNonLinkFieldsExtraPathElements(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
testCases := []string{
|
||||||
|
"coinbase",
|
||||||
|
"difficulty",
|
||||||
|
"extra",
|
||||||
|
"gaslimit",
|
||||||
|
"gasused",
|
||||||
|
"mixdigest",
|
||||||
|
"nonce",
|
||||||
|
"number",
|
||||||
|
"time",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range testCases {
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{field, "unexpected", "extra", "elements"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("Returned obj should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Returned rest should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != "unexpected path elements past "+field {
|
||||||
|
t.Fatalf("Wrong error\r\nexpected %s\r\ngot %s", "unexpected path elements past "+field, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveLinkFields(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
testCases := map[string]string{
|
||||||
|
"parent": "bagiacgza2m6j3xu774hlvjxhd2fsnuv5ufom6ei4ply3mm3jrleeozt7b62a",
|
||||||
|
"receipts": "bagkacgzap6qpnsrkagbdecgybaa63ljx4pr2aa5vlsetdg2f5mpzpbrk2iuq",
|
||||||
|
"root": "baglacgza5wmkus23dhec7m2tmtyikcfobjw6yzs7uv3ghxfjjroxavkm3yia",
|
||||||
|
"tx": "bagjacgzair6l3dci6smknejlccbrzx7vtr737s56onoksked2t5anxgxvzka",
|
||||||
|
"uncles": "bagiqcgzadxge32g6y5oxvk4fwvt3ntgudljreri3ssfhie7qufbp2qgusndq",
|
||||||
|
}
|
||||||
|
|
||||||
|
for field, result := range testCases {
|
||||||
|
obj, rest, err := ethBlock.Resolve([]string{field, "anything", "goes", "here"})
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
lnk, ok := obj.(*node.Link)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Returned object is not a link")
|
||||||
|
}
|
||||||
|
|
||||||
|
if lnk.Cid.String() != result {
|
||||||
|
t.Fatalf("Wrong %s cid\r\nexpected %v\r\ngot %v", field, result, lnk.Cid.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range []string{"anything", "goes", "here"} {
|
||||||
|
if rest[i] != p {
|
||||||
|
t.Fatalf("Wrong rest of the path returned\r\nexpected %s\r\ngot %s", p, rest[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockTreeBadParams(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
tree := ethBlock.Tree("non-empty-string", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = ethBlock.Tree("non-empty-string", 1)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = ethBlock.Tree("", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEThBlockTree(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
tree := ethBlock.Tree("", 1)
|
||||||
|
lookupElements := map[string]interface{}{
|
||||||
|
"bloom": nil,
|
||||||
|
"coinbase": nil,
|
||||||
|
"difficulty": nil,
|
||||||
|
"extra": nil,
|
||||||
|
"gaslimit": nil,
|
||||||
|
"gasused": nil,
|
||||||
|
"mixdigest": nil,
|
||||||
|
"nonce": nil,
|
||||||
|
"number": nil,
|
||||||
|
"parent": nil,
|
||||||
|
"receipts": nil,
|
||||||
|
"root": nil,
|
||||||
|
"time": nil,
|
||||||
|
"tx": nil,
|
||||||
|
"uncles": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tree) != len(lookupElements) {
|
||||||
|
t.Fatalf("Wrong number of elements\r\nexpected %d\r\ngot %d", len(lookupElements), len(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, te := range tree {
|
||||||
|
if _, ok := lookupElements[te]; !ok {
|
||||||
|
t.Fatalf("Unexpected Element: %v", te)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The two functions above: TestEthBlockResolveNonLinkFields and
|
||||||
|
TestEthBlockResolveLinkFields did all the heavy lifting. Then, we will
|
||||||
|
just test two use cases.
|
||||||
|
*/
|
||||||
|
func TestEthBlockResolveLinksBadLink(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, rest, err := ethBlock.ResolveLink([]string{"supercalifragilist"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "no such link" {
|
||||||
|
t.Fatalf("Expected error\r\nexpected %s\r\ngot %s", "no such link", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockResolveLinksGoodLink(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, rest, err := ethBlock.ResolveLink([]string{"tx", "0", "0", "0"})
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("Expected valid *node.Link obj to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest == nil {
|
||||||
|
t.Fatal("Expected rest to be returned")
|
||||||
|
}
|
||||||
|
for i, p := range []string{"0", "0", "0"} {
|
||||||
|
if rest[i] != p {
|
||||||
|
t.Fatalf("Wrong rest of the path returned\r\nexpected %s\r\ngot %s", p, rest[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Non error expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
These functions below should go away
|
||||||
|
We are working on test coverage anyways...
|
||||||
|
*/
|
||||||
|
func TestEthBlockCopy(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
t.Fatal("Expected panic")
|
||||||
|
}
|
||||||
|
if r != "implement me" {
|
||||||
|
t.Fatalf("Wrong panic message\r\nexpected %s\r\ngot %s", "'implement me'", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = ethBlock.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockStat(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
obj, err := ethBlock.Stat()
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatal("Expected a not null object node.NodeStat")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthBlockSize(t *testing.T) {
|
||||||
|
ethBlock := prepareDecodedEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
|
||||||
|
size, err := ethBlock.Size()
|
||||||
|
if size != 0 {
|
||||||
|
t.Fatalf("Wrong size\r\nexpected %d\r\ngot %d", 0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUXILIARS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// checkError makes 3 lines into 1.
|
||||||
|
func checkError(err error, t *testing.T) {
|
||||||
|
if err != nil {
|
||||||
|
_, fn, line, _ := runtime.Caller(1)
|
||||||
|
t.Fatalf("[%v:%v] %v", fn, line, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFloat is a convenience function to test json output
|
||||||
|
func parseFloat(v interface{}) string {
|
||||||
|
return strconv.FormatFloat(v.(float64), 'f', 0, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMapElement is a convenience function to tets json output
|
||||||
|
func parseMapElement(v interface{}) string {
|
||||||
|
return v.(map[string]interface{})["/"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareStoredEthBlock reads the block from a file source to get its rawdata
|
||||||
|
// and computes its cid, for then, feeding it into a new IPLD block function.
|
||||||
|
// So we can pretend that we got this block from the datastore
|
||||||
|
func prepareStoredEthBlock(filepath string, t *testing.T) *block.BasicBlock {
|
||||||
|
// Prepare the "fetched block". This one is supposed to be in the datastore
|
||||||
|
// and given away by github.com/ipfs/go-ipfs/merkledag
|
||||||
|
fi, err := os.Open(filepath)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
c, err := RawdataToCid(MEthHeader, b, multihash.KECCAK_256)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// It's good to clarify that this one below is an IPLD block
|
||||||
|
storedEthBlock, err := block.NewBlockWithCid(b, c)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
return storedEthBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareDecodedEthBlock is more complex than function above, as it stores a
|
||||||
|
// basic block and RLP-decodes it
|
||||||
|
func prepareDecodedEthBlock(filepath string, t *testing.T) *EthHeader {
|
||||||
|
// Get the block from the datastore and decode it.
|
||||||
|
storedEthBlock := prepareStoredEthBlock("test_data/eth-block-header-rlp-999999", t)
|
||||||
|
ethBlock, err := DecodeEthHeader(storedEthBlock.Cid(), storedEthBlock.RawData())
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
return ethBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// testEthBlockFields checks the fields of EthBlock one by one.
|
||||||
|
func testEthBlockFields(ethBlock *EthHeader, t *testing.T) {
|
||||||
|
// Was the cid calculated?
|
||||||
|
if ethBlock.Cid().String() != "bagiacgzawt5236hkiuvrhfyy4jya3qitlt6icfcqgheew6vsptlraokppm4a" {
|
||||||
|
t.Fatalf("Wrong cid\r\nexpected %s\r\ngot %s", "bagiacgzawt5236hkiuvrhfyy4jya3qitlt6icfcqgheew6vsptlraokppm4a", ethBlock.Cid().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have the rawdata available?
|
||||||
|
if fmt.Sprintf("%x", ethBlock.RawData()[:10]) != "f90218a0d33c9dde9fff" {
|
||||||
|
t.Fatalf("Wrong Rawdata\r\nexpected %s\r\ngot %s", "f90218a0d33c9dde9fff", fmt.Sprintf("%x", ethBlock.RawData()[:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proper Fields of types.Header
|
||||||
|
if fmt.Sprintf("%x", ethBlock.ParentHash) != "d33c9dde9fff0ebaa6e71e8b26d2bda15ccf111c7af1b633698ac847667f0fb4" {
|
||||||
|
t.Fatalf("Wrong ParentHash\r\nexpected %s\r\ngot %s", "d33c9dde9fff0ebaa6e71e8b26d2bda15ccf111c7af1b633698ac847667f0fb4", fmt.Sprintf("%x", ethBlock.ParentHash))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.UncleHash) != "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" {
|
||||||
|
t.Fatalf("Wrong UncleHash field\r\nexpected %s\r\ngot %s", "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", fmt.Sprintf("%x", ethBlock.UncleHash))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.Coinbase) != "52bc44d5378309ee2abf1539bf71de1b7d7be3b5" {
|
||||||
|
t.Fatalf("Wrong Coinbase\r\nexpected %s\r\ngot %s", "52bc44d5378309ee2abf1539bf71de1b7d7be3b5", fmt.Sprintf("%x", ethBlock.Coinbase))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.Root) != "ed98aa4b5b19c82fb35364f08508ae0a6dec665fa57663dca94c5d70554cde10" {
|
||||||
|
t.Fatalf("Wrong Root\r\nexpected %s\r\ngot %s", "ed98aa4b5b19c82fb35364f08508ae0a6dec665fa57663dca94c5d70554cde10", fmt.Sprintf("%x", ethBlock.Root))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.TxHash) != "447cbd8c48f498a6912b10831cdff59c7fbfcbbe735ca92883d4fa06dcd7ae54" {
|
||||||
|
t.Fatalf("Wrong TxHash\r\nexpected %s\r\ngot %s", "447cbd8c48f498a6912b10831cdff59c7fbfcbbe735ca92883d4fa06dcd7ae54", fmt.Sprintf("%x", ethBlock.TxHash))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.ReceiptHash) != "7fa0f6ca2a01823208d80801edad37e3e3a003b55c89319b45eb1f97862ad229" {
|
||||||
|
t.Fatalf("Wrong ReceiptHash\r\nexpected %s\r\ngot %s", "7fa0f6ca2a01823208d80801edad37e3e3a003b55c89319b45eb1f97862ad229", fmt.Sprintf("%x", ethBlock.ReceiptHash))
|
||||||
|
}
|
||||||
|
if len(ethBlock.Bloom) != 256 {
|
||||||
|
t.Fatalf("Wrong Bloom Length\r\nexpected %d\r\ngot %d", 256, len(ethBlock.Bloom))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.Bloom[71:76]) != "0000000000" { // You wouldn't want me to print out the whole bloom field?
|
||||||
|
t.Fatalf("Wrong Bloom\r\nexpected %s\r\ngot %s", "0000000000", fmt.Sprintf("%x", ethBlock.Bloom[71:76]))
|
||||||
|
}
|
||||||
|
if ethBlock.Difficulty.String() != "12555463106190" {
|
||||||
|
t.Fatalf("Wrong Difficulty\r\nexpected %s\r\ngot %s", "12555463106190", ethBlock.Difficulty.String())
|
||||||
|
}
|
||||||
|
if ethBlock.Number.String() != "999999" {
|
||||||
|
t.Fatalf("Wrong Block Number\r\nexpected %s\r\ngot %s", "999999", ethBlock.Number.String())
|
||||||
|
}
|
||||||
|
if ethBlock.GasLimit != uint64(3141592) {
|
||||||
|
t.Fatalf("Wrong Gas Limit\r\nexpected %d\r\ngot %d", 3141592, ethBlock.GasLimit)
|
||||||
|
}
|
||||||
|
if ethBlock.GasUsed != uint64(231000) {
|
||||||
|
t.Fatalf("Wrong Gas Used\r\nexpected %d\r\ngot %d", 231000, ethBlock.GasUsed)
|
||||||
|
}
|
||||||
|
if ethBlock.Time != uint64(1455404037) {
|
||||||
|
t.Fatalf("Wrong Time\r\nexpected %d\r\ngot %d", 1455404037, ethBlock.Time)
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.Extra) != "d783010303844765746887676f312e342e32856c696e7578" {
|
||||||
|
t.Fatalf("Wrong Extra\r\nexpected %s\r\ngot %s", "d783010303844765746887676f312e342e32856c696e7578", fmt.Sprintf("%x", ethBlock.Extra))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.Nonce) != "f491f46b60fe04b3" {
|
||||||
|
t.Fatalf("Wrong Nonce\r\nexpected %s\r\ngot %s", "f491f46b60fe04b3", fmt.Sprintf("%x", ethBlock.Nonce))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%x", ethBlock.MixDigest) != "5b10f4a08a6c209d426f6158bd24b574f4f7b7aa0099c67c14a1f693b4dd04d0" {
|
||||||
|
t.Fatalf("Wrong MixDigest\r\nexpected %s\r\ngot %s", "5b10f4a08a6c209d426f6158bd24b574f4f7b7aa0099c67c14a1f693b4dd04d0", fmt.Sprintf("%x", ethBlock.MixDigest))
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,108 @@ package ipld
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FromBlockRLP takes an RLP message representing
|
||||||
|
// an ethereum block header or body (header, ommers and txs)
|
||||||
|
// to return it as a set of IPLD nodes for further processing.
|
||||||
|
func FromBlockRLP(r io.Reader) (*EthHeader, []*EthTx, []*EthTxTrie, error) {
|
||||||
|
// We may want to use this stream several times
|
||||||
|
rawdata, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's try to decode the received element as a block body
|
||||||
|
var decodedBlock types.Block
|
||||||
|
err = rlp.Decode(bytes.NewBuffer(rawdata), &decodedBlock)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error()[:41] != "rlp: expected input list for types.Header" {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe it is just a header... (body sans ommers and txs)
|
||||||
|
var decodedHeader types.Header
|
||||||
|
err := rlp.Decode(bytes.NewBuffer(rawdata), &decodedHeader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := RawdataToCid(MEthHeader, rawdata, multihash.KECCAK_256)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
// It was a header
|
||||||
|
return &EthHeader{
|
||||||
|
Header: &decodedHeader,
|
||||||
|
cid: c,
|
||||||
|
rawdata: rawdata,
|
||||||
|
}, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a block body (header + ommers + txs)
|
||||||
|
// We'll extract the header bits here
|
||||||
|
headerRawData := getRLP(decodedBlock.Header())
|
||||||
|
c, err := RawdataToCid(MEthHeader, headerRawData, multihash.KECCAK_256)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
ethBlock := &EthHeader{
|
||||||
|
Header: decodedBlock.Header(),
|
||||||
|
cid: c,
|
||||||
|
rawdata: headerRawData,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the found eth-tx objects
|
||||||
|
ethTxNodes, ethTxTrieNodes, err := processTransactions(decodedBlock.Transactions(),
|
||||||
|
decodedBlock.Header().TxHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethBlock, ethTxNodes, ethTxTrieNodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBlockJSON takes the output of an ethereum client JSON API
|
||||||
|
// (i.e. parity or geth) and returns a set of IPLD nodes.
|
||||||
|
func FromBlockJSON(r io.Reader) (*EthHeader, []*EthTx, []*EthTxTrie, error) {
|
||||||
|
var obj objJSONHeader
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
err := dec.Decode(&obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
headerRawData := getRLP(obj.Result.Header)
|
||||||
|
c, err := RawdataToCid(MEthHeader, headerRawData, multihash.KECCAK_256)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
ethBlock := &EthHeader{
|
||||||
|
Header: &obj.Result.Header,
|
||||||
|
cid: c,
|
||||||
|
rawdata: headerRawData,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the found eth-tx objects
|
||||||
|
ethTxNodes, ethTxTrieNodes, err := processTransactions(obj.Result.Transactions,
|
||||||
|
obj.Result.Header.TxHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethBlock, ethTxNodes, ethTxTrieNodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FromBlockAndReceipts takes a block and processes it
|
// FromBlockAndReceipts takes a block and processes it
|
||||||
// to return it a set of IPLD nodes for further processing.
|
// to return it a set of IPLD nodes for further processing.
|
||||||
func FromBlockAndReceipts(block *types.Block, receipts []*types.Receipt) (*EthHeader, []*EthHeader, []*EthTx, []*EthTxTrie, []*EthReceipt, []*EthRctTrie, error) {
|
func FromBlockAndReceipts(block *types.Block, receipts []*types.Receipt) (*EthHeader, []*EthHeader, []*EthTx, []*EthTxTrie, []*EthReceipt, []*EthRctTrie, error) {
|
||||||
@ -64,14 +161,16 @@ func processTransactions(txs []*types.Transaction, expectedTxRoot []byte) ([]*Et
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
ethTxNodes = append(ethTxNodes, ethTx)
|
ethTxNodes = append(ethTxNodes, ethTx)
|
||||||
transactionTrie.add(idx, ethTx.RawData())
|
if err := transactionTrie.add(idx, ethTx.RawData()); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(transactionTrie.rootHash(), expectedTxRoot) {
|
if !bytes.Equal(transactionTrie.rootHash(), expectedTxRoot) {
|
||||||
return nil, nil, fmt.Errorf("wrong transaction hash computed")
|
return nil, nil, fmt.Errorf("wrong transaction hash computed")
|
||||||
}
|
}
|
||||||
|
txTrieNodes, err := transactionTrie.getNodes()
|
||||||
return ethTxNodes, transactionTrie.getNodes(), nil
|
return ethTxNodes, txTrieNodes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// processReceipts will take in receipts
|
// processReceipts will take in receipts
|
||||||
@ -86,12 +185,14 @@ func processReceipts(rcts []*types.Receipt, expectedRctRoot []byte) ([]*EthRecei
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
ethRctNodes = append(ethRctNodes, ethRct)
|
ethRctNodes = append(ethRctNodes, ethRct)
|
||||||
receiptTrie.add(idx, ethRct.RawData())
|
if err := receiptTrie.add(idx, ethRct.RawData()); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(receiptTrie.rootHash(), expectedRctRoot) {
|
if !bytes.Equal(receiptTrie.rootHash(), expectedRctRoot) {
|
||||||
return nil, nil, fmt.Errorf("wrong receipt hash computed")
|
return nil, nil, fmt.Errorf("wrong receipt hash computed")
|
||||||
}
|
}
|
||||||
|
rctTrieNodes, err := receiptTrie.getNodes()
|
||||||
return ethRctNodes, receiptTrie.getNodes(), nil
|
return ethRctNodes, rctTrieNodes, err
|
||||||
}
|
}
|
||||||
|
@ -125,13 +125,13 @@ func newRctTrie() *rctTrie {
|
|||||||
// getNodes invokes the localTrie, which computes the root hash of the
|
// getNodes invokes the localTrie, which computes the root hash of the
|
||||||
// transaction trie and returns its database keys, to return a slice
|
// transaction trie and returns its database keys, to return a slice
|
||||||
// of EthRctTrie nodes.
|
// of EthRctTrie nodes.
|
||||||
func (rt *rctTrie) getNodes() []*EthRctTrie {
|
func (rt *rctTrie) getNodes() ([]*EthRctTrie, error) {
|
||||||
keys := rt.getKeys()
|
keys, err := rt.getKeys()
|
||||||
var out []*EthRctTrie
|
if err != nil {
|
||||||
it := rt.trie.NodeIterator([]byte{})
|
return nil, err
|
||||||
for it.Next(true) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
var out []*EthRctTrie
|
||||||
|
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
rawdata, err := rt.db.Get(k)
|
rawdata, err := rt.db.Get(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -139,7 +139,7 @@ func (rt *rctTrie) getNodes() []*EthRctTrie {
|
|||||||
}
|
}
|
||||||
c, err := RawdataToCid(MEthTxReceiptTrie, rawdata, multihash.KECCAK_256)
|
c, err := RawdataToCid(MEthTxReceiptTrie, rawdata, multihash.KECCAK_256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
tn := &TrieNode{
|
tn := &TrieNode{
|
||||||
cid: c,
|
cid: c,
|
||||||
@ -148,5 +148,5 @@ func (rt *rctTrie) getNodes() []*EthRctTrie {
|
|||||||
out = append(out, &EthRctTrie{TrieNode: tn})
|
out = append(out, &EthRctTrie{TrieNode: tn})
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out, nil
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package ipld
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
node "github.com/ipfs/go-ipld-format"
|
node "github.com/ipfs/go-ipld-format"
|
||||||
@ -39,6 +41,16 @@ var _ node.Node = (*EthStateTrie)(nil)
|
|||||||
INPUT
|
INPUT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// FromStateTrieRLPFile takes the RLP representation of an ethereum
|
||||||
|
// state trie node to return it as an IPLD node for further processing.
|
||||||
|
func FromStateTrieRLPFile(r io.Reader) (*EthStateTrie, error) {
|
||||||
|
raw, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FromStateTrieRLP(raw)
|
||||||
|
}
|
||||||
|
|
||||||
// FromStateTrieRLP takes the RLP representation of an ethereum
|
// FromStateTrieRLP takes the RLP representation of an ethereum
|
||||||
// state trie node to return it as an IPLD node for further processing.
|
// state trie node to return it as an IPLD node for further processing.
|
||||||
func FromStateTrieRLP(raw []byte) (*EthStateTrie, error) {
|
func FromStateTrieRLP(raw []byte) (*EthStateTrie, error) {
|
||||||
|
326
statediff/indexer/ipfs/ipld/eth_state_test.go
Normal file
326
statediff/indexer/ipfs/ipld/eth_state_test.go
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
node "github.com/ipfs/go-ipld-format"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
INPUT
|
||||||
|
OUTPUT
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestStateTrieNodeEvenExtensionParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-eb2f5f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "extension" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "extension", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.elements[0]) != "0d08" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "0d08", fmt.Sprintf("%x", output.elements[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.elements[1].(cid.Cid).String() !=
|
||||||
|
"baglacgzalnzmhhnxudxtga6t3do2rctb6ycgyj6mjnycoamlnc733nnbkd6q" {
|
||||||
|
t.Fatalf("Wrong CID\r\nexpected %s\r\ngot %s", "baglacgzalnzmhhnxudxtga6t3do2rctb6ycgyj6mjnycoamlnc733nnbkd6q", output.elements[1].(cid.Cid).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieNodeOddExtensionParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-56864f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "extension" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "extension", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.elements[0]) != "02" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "02", fmt.Sprintf("%x", output.elements[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.elements[1].(cid.Cid).String() !=
|
||||||
|
"baglacgzaizf2czb7wztoox4lu23qkwkbfamqsdzcmejzr3rsszrvkaktpfeq" {
|
||||||
|
t.Fatalf("Wrong CID\r\nexpected %s\r\ngot %s", "baglacgzaizf2czb7wztoox4lu23qkwkbfamqsdzcmejzr3rsszrvkaktpfeq", output.elements[1].(cid.Cid).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieNodeEvenLeafParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-0e8b34")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "leaf" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "leaf", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
// bd66f60e5b954e1af93ded1b02cb575ff0ed6d9241797eff7576b0bf0637
|
||||||
|
if fmt.Sprintf("%x", output.elements[0].([]byte)[0:10]) != "0b0d06060f06000e050b" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "0b0d06060f06000e050b", fmt.Sprintf("%x", output.elements[0].([]byte)[0:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.elements[1].(*EthAccountSnapshot).String() !=
|
||||||
|
"<EthereumAccountSnapshot baglqcgzaf5tapdf2fwb6mo4ijtovqpoi4n3f4jv2yx6avvz6sjypp6vytfva>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumAccountSnapshot baglqcgzaf5tapdf2fwb6mo4ijtovqpoi4n3f4jv2yx6avvz6sjypp6vytfva>", output.elements[1].(*EthAccountSnapshot).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieNodeOddLeafParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-c9070d")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "leaf" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "leaf", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6c9db9bb545a03425e300f3ee72bae098110336dd3eaf48c20a2e5b6865fc
|
||||||
|
if fmt.Sprintf("%x", output.elements[0].([]byte)[0:10]) != "060c090d0b090b0b0504" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "060c090d0b090b0b0504", fmt.Sprintf("%x", output.elements[0].([]byte)[0:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.elements[1].(*EthAccountSnapshot).String() !=
|
||||||
|
"<EthereumAccountSnapshot baglqcgzasckx2alxk43cksshnztjvhfyvbbh6bkp376gtcndm5cg4fkrkhsa>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumAccountSnapshot baglqcgzasckx2alxk43cksshnztjvhfyvbbh6bkp376gtcndm5cg4fkrkhsa>", output.elements[1].(*EthAccountSnapshot).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Block INTERFACE
|
||||||
|
*/
|
||||||
|
func TestStateTrieBlockElements(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-d7f897")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.RawData())[:10] != "f90211a090" {
|
||||||
|
t.Fatalf("Wrong Data\r\nexpected %s\r\ngot %s", "f90211a090", fmt.Sprintf("%x", output.RawData())[:10])
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.Cid().String() !=
|
||||||
|
"baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca", output.Cid().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieString(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-d7f897")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.String() !=
|
||||||
|
"<EthereumStateTrie baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumStateTrie baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca>", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieLoggable(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-d7f897")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
l := output.Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-state-trie" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-state-trie", l["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TRIE NODE (Through EthStateTrie)
|
||||||
|
Node INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTraverseStateTrieWithResolve(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
stMap := prepareStateTrieMap(t)
|
||||||
|
|
||||||
|
// This is the cid of the root of the block 0
|
||||||
|
// baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca
|
||||||
|
currentNode := stMap["baglacgza274jot5vvr4ntlajtonnkaml5xbm4cts3liye6qxbhndawapavca"]
|
||||||
|
|
||||||
|
// This is the path we want to traverse
|
||||||
|
// The eth address is 0x5abfec25f74cd88437631a7731906932776356f9
|
||||||
|
// Its keccak-256 is cdd3e25edec0a536a05f5e5ab90a5603624c0ed77453b2e8f955cf8b43d4d0fb
|
||||||
|
// We use the keccak-256(addr) to traverse the state trie in ethereum.
|
||||||
|
var traversePath []string
|
||||||
|
for _, s := range "cdd3e25edec0a536a05f5e5ab90a5603624c0ed77453b2e8f955cf8b43d4d0fb" {
|
||||||
|
traversePath = append(traversePath, string(s))
|
||||||
|
}
|
||||||
|
traversePath = append(traversePath, "balance")
|
||||||
|
|
||||||
|
var obj interface{}
|
||||||
|
for {
|
||||||
|
obj, traversePath, err = currentNode.Resolve(traversePath)
|
||||||
|
link, ok := obj.(*node.Link)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = stMap[link.Cid.String()]
|
||||||
|
if currentNode == nil {
|
||||||
|
t.Fatal("state trie node not found in memory map")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%v", obj) != "11901484239480000000000000" {
|
||||||
|
t.Fatalf("Wrong balance value\r\nexpected %s\r\ngot %s", "11901484239480000000000000", fmt.Sprintf("%v", obj))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieResolveLinks(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-eb2f5f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
stNode, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// bad case
|
||||||
|
obj, rest, err := stNode.ResolveLink([]string{"supercalifragilist"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "invalid path element" {
|
||||||
|
t.Fatalf("Wrong error\r\nexpected %s\r\ngot %s", "invalid path element", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// good case
|
||||||
|
obj, rest, err = stNode.ResolveLink([]string{"d8"})
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("Expected a not nil obj to be returned")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected error to be nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieCopy(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-eb2f5f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
stNode, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
t.Fatal("Expected panic")
|
||||||
|
}
|
||||||
|
if r != "implement me" {
|
||||||
|
t.Fatalf("Wrong panic message\r\nexpected %s\r\ngot %s", "'implement me'", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = stNode.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieStat(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-eb2f5f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
stNode, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
obj, err := stNode.Stat()
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatal("Expected a not null object node.NodeStat")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieSize(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-state-trie-rlp-eb2f5f")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
stNode, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
size, err := stNode.Size()
|
||||||
|
if size != uint64(0) {
|
||||||
|
t.Fatalf("Wrong size\r\nexpected %d\r\ngot %d", 0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareStateTrieMap(t *testing.T) map[string]*EthStateTrie {
|
||||||
|
filepaths := []string{
|
||||||
|
"test_data/eth-state-trie-rlp-0e8b34",
|
||||||
|
"test_data/eth-state-trie-rlp-56864f",
|
||||||
|
"test_data/eth-state-trie-rlp-6fc2d7",
|
||||||
|
"test_data/eth-state-trie-rlp-727994",
|
||||||
|
"test_data/eth-state-trie-rlp-c9070d",
|
||||||
|
"test_data/eth-state-trie-rlp-d5be90",
|
||||||
|
"test_data/eth-state-trie-rlp-d7f897",
|
||||||
|
"test_data/eth-state-trie-rlp-eb2f5f",
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]*EthStateTrie)
|
||||||
|
|
||||||
|
for _, fp := range filepaths {
|
||||||
|
fi, err := os.Open(fp)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
stateTrieNode, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
out[stateTrieNode.Cid().String()] = stateTrieNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
@ -18,6 +18,8 @@ package ipld
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
node "github.com/ipfs/go-ipld-format"
|
node "github.com/ipfs/go-ipld-format"
|
||||||
@ -37,6 +39,16 @@ var _ node.Node = (*EthStorageTrie)(nil)
|
|||||||
INPUT
|
INPUT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// FromStorageTrieRLPFile takes the RLP representation of an ethereum
|
||||||
|
// storage trie node to return it as an IPLD node for further processing.
|
||||||
|
func FromStorageTrieRLPFile(r io.Reader) (*EthStorageTrie, error) {
|
||||||
|
raw, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FromStorageTrieRLP(raw)
|
||||||
|
}
|
||||||
|
|
||||||
// FromStorageTrieRLP takes the RLP representation of an ethereum
|
// FromStorageTrieRLP takes the RLP representation of an ethereum
|
||||||
// storage trie node to return it as an IPLD node for further processing.
|
// storage trie node to return it as an IPLD node for further processing.
|
||||||
func FromStorageTrieRLP(raw []byte) (*EthStorageTrie, error) {
|
func FromStorageTrieRLP(raw []byte) (*EthStorageTrie, error) {
|
||||||
|
140
statediff/indexer/ipfs/ipld/eth_storage_test.go
Normal file
140
statediff/indexer/ipfs/ipld/eth_storage_test.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
INPUT
|
||||||
|
OUTPUT
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestStorageTrieNodeExtensionParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-113049")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "extension" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "extension", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.elements[0]) != "0a" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "0a", fmt.Sprintf("%x", output.elements[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.elements[1].(cid.Cid).String() !=
|
||||||
|
"baglacgzautxeutufae7owyrezfvwpan2vusocmxgzwqhzrhjbwprp2texgsq" {
|
||||||
|
t.Fatalf("Wrong CID\r\nexpected %s\r\ngot %s", "baglacgzautxeutufae7owyrezfvwpan2vusocmxgzwqhzrhjbwprp2texgsq", output.elements[1].(cid.Cid).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieNodeLeafParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-ffbcad")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStorageTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "leaf" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "leaf", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an leaf node\r\nexpected %d\r\ngot %d", 2, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2ee1ae9c502e48e0ed528b7b39ac569cef69d7844b5606841a7f3fe898a2
|
||||||
|
if fmt.Sprintf("%x", output.elements[0].([]byte)[:10]) != "020e0e010a0e090c0500" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "020e0e010a0e090c0500", fmt.Sprintf("%x", output.elements[0].([]byte)[:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.elements[1]) != "89056c31f304b2530000" {
|
||||||
|
t.Fatalf("Wrong Value\r\nexpected %s\r\ngot %s", "89056c31f304b2530000", fmt.Sprintf("%x", output.elements[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateTrieNodeBranchParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-ffc25c")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStateTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.nodeKind != "branch" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "branch", output.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output.elements) != 17 {
|
||||||
|
t.Fatalf("Wrong number of elements for an branch node\r\nexpected %d\r\ngot %d", 17, len(output.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%s", output.elements[4]) !=
|
||||||
|
"baglacgzadqhbmlxrxtw5hplcq5jn74p4dceryzw664w3237ra52dnghbjpva" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "baglacgzadqhbmlxrxtw5hplcq5jn74p4dceryzw664w3237ra52dnghbjpva", fmt.Sprintf("%s", output.elements[4]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%s", output.elements[10]) !=
|
||||||
|
"baglacgza77d37i2v6uhtzeeq4vngragjbgbwq3lylpoc3lihenvzimybzxmq" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "baglacgza77d37i2v6uhtzeeq4vngragjbgbwq3lylpoc3lihenvzimybzxmq", fmt.Sprintf("%s", output.elements[10]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Block INTERFACE
|
||||||
|
*/
|
||||||
|
func TestStorageTrieBlockElements(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-ffbcad")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStorageTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", output.RawData())[:10] != "eb9f202ee1" {
|
||||||
|
t.Fatalf("Wrong Data\r\nexpected %s\r\ngot %s", "eb9f202ee1", fmt.Sprintf("%x", output.RawData())[:10])
|
||||||
|
}
|
||||||
|
|
||||||
|
if output.Cid().String() !=
|
||||||
|
"bagmacgza766k3oprj2qxn36eycw55pogmu3dwtfay6zdh6ajrhvw3b2nqg5a" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "bagmacgza766k3oprj2qxn36eycw55pogmu3dwtfay6zdh6ajrhvw3b2nqg5a", output.Cid().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorageTrieString(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-ffbcad")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStorageTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if output.String() !=
|
||||||
|
"<EthereumStorageTrie bagmacgza766k3oprj2qxn36eycw55pogmu3dwtfay6zdh6ajrhvw3b2nqg5a>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumStorageTrie bagmacgza766k3oprj2qxn36eycw55pogmu3dwtfay6zdh6ajrhvw3b2nqg5a>", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorageTrieLoggable(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-storage-trie-rlp-ffbcad")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
output, err := FromStorageTrieRLPFile(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
l := output.Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-storage-trie" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-storage-trie", l["type"])
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -186,9 +187,30 @@ func (t *EthTx) Stat() (*node.NodeStat, error) {
|
|||||||
return &node.NodeStat{}, nil
|
return &node.NodeStat{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size will go away. It is here to comply with the interface.
|
// Size will go away. It is here to comply with the interface. It returns the byte size for the transaction
|
||||||
func (t *EthTx) Size() (uint64, error) {
|
func (t *EthTx) Size() (uint64, error) {
|
||||||
return strconv.ParseUint(t.Transaction.Size().String(), 10, 64)
|
spl := strings.Split(t.Transaction.Size().String(), " ")
|
||||||
|
size, units := spl[0], spl[1]
|
||||||
|
floatSize, err := strconv.ParseFloat(size, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var byteSize uint64
|
||||||
|
switch units {
|
||||||
|
case "B":
|
||||||
|
byteSize = uint64(floatSize)
|
||||||
|
case "KB":
|
||||||
|
byteSize = uint64(floatSize * 1000)
|
||||||
|
case "MB":
|
||||||
|
byteSize = uint64(floatSize * 1000000)
|
||||||
|
case "GB":
|
||||||
|
byteSize = uint64(floatSize * 1000000000)
|
||||||
|
case "TB":
|
||||||
|
byteSize = uint64(floatSize * 1000000000000)
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("unreconginized units %s", units)
|
||||||
|
}
|
||||||
|
return byteSize, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
410
statediff/indexer/ipfs/ipld/eth_tx_test.go
Normal file
410
statediff/indexer/ipfs/ipld/eth_tx_test.go
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
block "github.com/ipfs/go-block-format"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
EthBlock
|
||||||
|
INPUT
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTxInBlockBodyRlpParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-rlp-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, output, _, err := FromBlockRLP(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if len(output) != 11 {
|
||||||
|
t.Fatalf("Wrong number of parsed txs\r\nexpected %d\r\ngot %d", 11, len(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oh, let's just grab the last element and one from the middle
|
||||||
|
testTx05Fields(output[5], t)
|
||||||
|
testTx10Fields(output[10], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxInBlockHeaderRlpParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-header-rlp-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, output, _, err := FromBlockRLP(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if len(output) != 0 {
|
||||||
|
t.Fatalf("Wrong number of txs\r\nexpected %d\r\ngot %d", 0, len(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxInBlockBodyJsonParsing(t *testing.T) {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-json-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, output, _, err := FromBlockJSON(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if len(output) != 11 {
|
||||||
|
t.Fatalf("Wrong number of parsed txs\r\nexpected %d\r\ngot %d", 11, len(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
testTx05Fields(output[5], t)
|
||||||
|
testTx10Fields(output[10], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestDecodeTransaction(t *testing.T) {
|
||||||
|
// Prepare the "fetched transaction".
|
||||||
|
// This one is supposed to be in the datastore already,
|
||||||
|
// and given away by github.com/ipfs/go-ipfs/merkledag
|
||||||
|
rawTransactionString :=
|
||||||
|
"f86c34850df84758008252089432be343b94f860124dc4fee278fdcbd38c102d88880f25" +
|
||||||
|
"8512af0d4000801ba0e9a25c929c26d1a95232ba75aef419a91b470651eb77614695e16c" +
|
||||||
|
"5ba023e383a0679fb2fc0d0b0f3549967c0894ee7d947f07d238a83ef745bc3ced5143a4af36"
|
||||||
|
rawTransaction, err := hex.DecodeString(rawTransactionString)
|
||||||
|
checkError(err, t)
|
||||||
|
c, err := RawdataToCid(MEthTx, rawTransaction, multihash.KECCAK_256)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// Just to clarify: This `block` is an IPFS block
|
||||||
|
storedTransaction, err := block.NewBlockWithCid(rawTransaction, c)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
// Now the proper test
|
||||||
|
ethTransaction, err := DecodeEthTx(storedTransaction.Cid(), storedTransaction.RawData())
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
testTx05Fields(ethTransaction, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Block INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestEthTxLoggable(t *testing.T) {
|
||||||
|
txs := prepareParsedTxs(t)
|
||||||
|
|
||||||
|
l := txs[0].Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-tx" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-tx", l["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Node INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestEthTxResolve(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
// Empty path
|
||||||
|
obj, rest, err := tx.Resolve([]string{})
|
||||||
|
rtx, ok := obj.(*EthTx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Wrong type of returned object")
|
||||||
|
}
|
||||||
|
if rtx.Cid() != tx.Cid() {
|
||||||
|
t.Fatalf("Wrong CID\r\nexpected %s\r\ngot %s", tx.Cid().String(), rtx.Cid().String())
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("est should be nil")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// len(p) > 1
|
||||||
|
badCases := [][]string{
|
||||||
|
{"two", "elements"},
|
||||||
|
{"here", "three", "elements"},
|
||||||
|
{"and", "here", "four", "elements"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bc := range badCases {
|
||||||
|
obj, rest, err = tx.Resolve(bc)
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("obj should be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("rest should be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != fmt.Sprintf("unexpected path elements past %s", bc[0]) {
|
||||||
|
t.Fatalf("wrong error\r\nexpected %s\r\ngot %s", fmt.Sprintf("unexpected path elements past %s", bc[0]), err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moreBadCases := []string{
|
||||||
|
"i",
|
||||||
|
"am",
|
||||||
|
"not",
|
||||||
|
"a",
|
||||||
|
"tx",
|
||||||
|
"field",
|
||||||
|
}
|
||||||
|
for _, mbc := range moreBadCases {
|
||||||
|
obj, rest, err = tx.Resolve([]string{mbc})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatal("obj should be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("rest should be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "no such link" {
|
||||||
|
t.Fatalf("wrong error\r\nexpected %s\r\ngot %s", "no such link", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goodCases := []string{
|
||||||
|
"gas",
|
||||||
|
"gasPrice",
|
||||||
|
"input",
|
||||||
|
"nonce",
|
||||||
|
"r",
|
||||||
|
"s",
|
||||||
|
"toAddress",
|
||||||
|
"v",
|
||||||
|
"value",
|
||||||
|
}
|
||||||
|
for _, gc := range goodCases {
|
||||||
|
_, _, err = tx.Resolve([]string{gc})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error should be nil %v", gc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxTree(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
_ = tx
|
||||||
|
|
||||||
|
// Bad cases
|
||||||
|
tree := tx.Tree("non-empty-string", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = tx.Tree("non-empty-string", 1)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = tx.Tree("", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Good cases
|
||||||
|
tree = tx.Tree("", 1)
|
||||||
|
lookupElements := map[string]interface{}{
|
||||||
|
"gas": nil,
|
||||||
|
"gasPrice": nil,
|
||||||
|
"input": nil,
|
||||||
|
"nonce": nil,
|
||||||
|
"r": nil,
|
||||||
|
"s": nil,
|
||||||
|
"toAddress": nil,
|
||||||
|
"v": nil,
|
||||||
|
"value": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tree) != len(lookupElements) {
|
||||||
|
t.Fatalf("Wrong number of elements\r\nexpected %d\r\ngot %d", len(lookupElements), len(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, te := range tree {
|
||||||
|
if _, ok := lookupElements[te]; !ok {
|
||||||
|
t.Fatalf("Unexpected Element: %v", te)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxResolveLink(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
// bad case
|
||||||
|
obj, rest, err := tx.ResolveLink([]string{"supercalifragilist"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "no such link" {
|
||||||
|
t.Fatalf("Wrong error\r\nexpected %s\r\ngot %s", "no such link", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// good case
|
||||||
|
obj, rest, err = tx.ResolveLink([]string{"nonce"})
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Expected obj to be nil")
|
||||||
|
}
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatal("Expected rest to be nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "resolved item was not a link" {
|
||||||
|
t.Fatalf("Wrong error\r\nexpected %s\r\ngot %s", "resolved item was not a link", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxCopy(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
t.Fatal("Expected panic")
|
||||||
|
}
|
||||||
|
if r != "implement me" {
|
||||||
|
t.Fatalf("Wrong panic message\r\nexpected %s\r\ngot %s", "'implement me'", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = tx.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxLinks(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
if tx.Links() != nil {
|
||||||
|
t.Fatal("Links() expected to return nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxStat(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
obj, err := tx.Stat()
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatal("Expected a not null object node.NodeStat")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected a nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxSize(t *testing.T) {
|
||||||
|
tx := prepareParsedTxs(t)[0]
|
||||||
|
|
||||||
|
size, err := tx.Size()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
spl := strings.Split(tx.Transaction.Size().String(), " ")
|
||||||
|
expectedSize, units := spl[0], spl[1]
|
||||||
|
floatSize, err := strconv.ParseFloat(expectedSize, 64)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var byteSize uint64
|
||||||
|
switch units {
|
||||||
|
case "B":
|
||||||
|
byteSize = uint64(floatSize)
|
||||||
|
case "KB":
|
||||||
|
byteSize = uint64(floatSize * 1000)
|
||||||
|
case "MB":
|
||||||
|
byteSize = uint64(floatSize * 1000000)
|
||||||
|
case "GB":
|
||||||
|
byteSize = uint64(floatSize * 1000000000)
|
||||||
|
case "TB":
|
||||||
|
byteSize = uint64(floatSize * 1000000000000)
|
||||||
|
default:
|
||||||
|
t.Fatal("Unexpected size units")
|
||||||
|
}
|
||||||
|
if size != byteSize {
|
||||||
|
t.Fatalf("Wrong size\r\nexpected %d\r\ngot %d", byteSize, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUXILIARS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// prepareParsedTxs is a convenienve method
|
||||||
|
func prepareParsedTxs(t *testing.T) []*EthTx {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-rlp-999999")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, output, _, err := FromBlockRLP(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTx05Fields(ethTx *EthTx, t *testing.T) {
|
||||||
|
// Was the cid calculated?
|
||||||
|
if ethTx.Cid().String() != "bagjqcgzawhfnvdnpmpcfoug7d3tz53k2ht3cidr45pnw3y7snpd46azbpp2a" {
|
||||||
|
t.Fatalf("Wrong cid\r\nexpected %s\r\ngot %s\r\n", "bagjqcgzawhfnvdnpmpcfoug7d3tz53k2ht3cidr45pnw3y7snpd46azbpp2a", ethTx.Cid().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have the rawdata available?
|
||||||
|
if fmt.Sprintf("%x", ethTx.RawData()[:10]) != "f86c34850df847580082" {
|
||||||
|
t.Fatalf("Wrong Rawdata\r\nexpected %s\r\ngot %s", "f86c34850df847580082", fmt.Sprintf("%x", ethTx.RawData()[:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proper Fields of types.Transaction
|
||||||
|
if fmt.Sprintf("%x", ethTx.To()) != "32be343b94f860124dc4fee278fdcbd38c102d88" {
|
||||||
|
t.Fatalf("Wrong Recipient\r\nexpected %s\r\ngot %s", "32be343b94f860124dc4fee278fdcbd38c102d88", fmt.Sprintf("%x", ethTx.To()))
|
||||||
|
}
|
||||||
|
if len(ethTx.Data()) != 0 {
|
||||||
|
t.Fatalf("Wrong len of Data\r\nexpected %d\r\ngot %d", 0, len(ethTx.Data()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Gas()) != "21000" {
|
||||||
|
t.Fatalf("Wrong Gas\r\nexpected %s\r\ngot %s", "21000", fmt.Sprintf("%v", ethTx.Gas()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Value()) != "1091424800000000000" {
|
||||||
|
t.Fatalf("Wrong Value\r\nexpected %s\r\ngot %s", "1091424800000000000", fmt.Sprintf("%v", ethTx.Value()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Nonce()) != "52" {
|
||||||
|
t.Fatalf("Wrong Nonce\r\nexpected %s\r\ngot %s", "52", fmt.Sprintf("%v", ethTx.Nonce()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.GasPrice()) != "60000000000" {
|
||||||
|
t.Fatalf("Wrong Gas Price\r\nexpected %s\r\ngot %s", "60000000000", fmt.Sprintf("%v", ethTx.GasPrice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTx10Fields(ethTx *EthTx, t *testing.T) {
|
||||||
|
// Was the cid calculated?
|
||||||
|
if ethTx.Cid().String() != "bagjqcgzaykakwayoec6j55zmq62cbvmplgf5u5j67affge3ksi4ermgitjoa" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "bagjqcgzaykakwayoec6j55zmq62cbvmplgf5u5j67affge3ksi4ermgitjoa", ethTx.Cid().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have the rawdata available?
|
||||||
|
if fmt.Sprintf("%x", ethTx.RawData()[:10]) != "f8708302a120850ba43b" {
|
||||||
|
t.Fatalf("Wrong Rawdata\r\nexpected %s\r\ngot %s", "f8708302a120850ba43b", fmt.Sprintf("%x", ethTx.RawData()[:10]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proper Fields of types.Transaction
|
||||||
|
if fmt.Sprintf("%x", ethTx.To()) != "1c51bf013add0857c5d9cf2f71a7f15ca93d4816" {
|
||||||
|
t.Fatalf("Wrong Recipient\r\nexpected %s\r\ngot %s", "1c51bf013add0857c5d9cf2f71a7f15ca93d4816", fmt.Sprintf("%x", ethTx.To()))
|
||||||
|
}
|
||||||
|
if len(ethTx.Data()) != 0 {
|
||||||
|
t.Fatalf("Wrong len of Data\r\nexpected %d\r\ngot %d", 0, len(ethTx.Data()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Gas()) != "90000" {
|
||||||
|
t.Fatalf("Wrong Gas\r\nexpected %s\r\ngot %s", "90000", fmt.Sprintf("%v", ethTx.Gas()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Value()) != "1049756850000000000" {
|
||||||
|
t.Fatalf("Wrong Value\r\nexpected %s\r\ngot %s", "1049756850000000000", fmt.Sprintf("%v", ethTx.Value()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.Nonce()) != "172320" {
|
||||||
|
t.Fatalf("Wrong Nonce\r\nexpected %s\r\ngot %s", "172320", fmt.Sprintf("%v", ethTx.Nonce()))
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%v", ethTx.GasPrice()) != "50000000000" {
|
||||||
|
t.Fatalf("Wrong Gas Price\r\nexpected %s\r\ngot %s", "50000000000", fmt.Sprintf("%v", ethTx.GasPrice()))
|
||||||
|
}
|
||||||
|
}
|
@ -125,13 +125,13 @@ func newTxTrie() *txTrie {
|
|||||||
// getNodes invokes the localTrie, which computes the root hash of the
|
// getNodes invokes the localTrie, which computes the root hash of the
|
||||||
// transaction trie and returns its database keys, to return a slice
|
// transaction trie and returns its database keys, to return a slice
|
||||||
// of EthTxTrie nodes.
|
// of EthTxTrie nodes.
|
||||||
func (tt *txTrie) getNodes() []*EthTxTrie {
|
func (tt *txTrie) getNodes() ([]*EthTxTrie, error) {
|
||||||
keys := tt.getKeys()
|
keys, err := tt.getKeys()
|
||||||
var out []*EthTxTrie
|
if err != nil {
|
||||||
it := tt.trie.NodeIterator([]byte{})
|
return nil, err
|
||||||
for it.Next(true) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
var out []*EthTxTrie
|
||||||
|
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
rawdata, err := tt.db.Get(k)
|
rawdata, err := tt.db.Get(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -139,7 +139,7 @@ func (tt *txTrie) getNodes() []*EthTxTrie {
|
|||||||
}
|
}
|
||||||
c, err := RawdataToCid(MEthTxTrie, rawdata, multihash.KECCAK_256)
|
c, err := RawdataToCid(MEthTxTrie, rawdata, multihash.KECCAK_256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
tn := &TrieNode{
|
tn := &TrieNode{
|
||||||
cid: c,
|
cid: c,
|
||||||
@ -148,5 +148,5 @@ func (tt *txTrie) getNodes() []*EthTxTrie {
|
|||||||
out = append(out, &EthTxTrie{TrieNode: tn})
|
out = append(out, &EthTxTrie{TrieNode: tn})
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out, nil
|
||||||
}
|
}
|
||||||
|
505
statediff/indexer/ipfs/ipld/eth_tx_trie_test.go
Normal file
505
statediff/indexer/ipfs/ipld/eth_tx_trie_test.go
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
package ipld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
block "github.com/ipfs/go-block-format"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
node "github.com/ipfs/go-ipld-format"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
EthBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTxTriesInBlockBodyJSONParsing(t *testing.T) {
|
||||||
|
// HINT: 306 txs
|
||||||
|
// cat test_data/eth-block-body-json-4139497 | jsontool | grep transactionIndex | wc -l
|
||||||
|
// or, https://etherscan.io/block/4139497
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-json-4139497")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, _, output, err := FromBlockJSON(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
if len(output) != 331 {
|
||||||
|
t.Fatalf("Wrong number of obtained tx trie nodes\r\nexpected %d\r\n got %d", 331, len(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTxTrieDecodeExtension(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
if ethTxTrie.nodeKind != "extension" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "extension", ethTxTrie.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ethTxTrie.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for an extension node\r\nexpected %d\r\ngot %d", 2, len(ethTxTrie.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", ethTxTrie.elements[0].([]byte)) != "0001" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "0001", fmt.Sprintf("%x", ethTxTrie.elements[0].([]byte)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ethTxTrie.elements[1].(cid.Cid).String() !=
|
||||||
|
"bagjacgzak6wdjvshdtb7lrvlteweyd7f5qjr3dmzmh7g2xpi4xrwoujsio2a" {
|
||||||
|
t.Fatalf("Wrong CID\r\nexpected %s\r\ngot %s", "bagjacgzak6wdjvshdtb7lrvlteweyd7f5qjr3dmzmh7g2xpi4xrwoujsio2a", ethTxTrie.elements[1].(cid.Cid).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieDecodeLeaf(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieLeaf(t)
|
||||||
|
|
||||||
|
if ethTxTrie.nodeKind != "leaf" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "leaf", ethTxTrie.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ethTxTrie.elements) != 2 {
|
||||||
|
t.Fatalf("Wrong number of elements for a leaf node\r\nexpected %d\r\ngot %d", 2, len(ethTxTrie.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", ethTxTrie.elements[0].([]byte)) != "" {
|
||||||
|
t.Fatalf("Wrong key\r\nexpected %s\r\ngot %s", "", fmt.Sprintf("%x", ethTxTrie.elements[0].([]byte)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := ethTxTrie.elements[1].(*EthTx); !ok {
|
||||||
|
t.Fatal("Expected element to be an EthTx")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ethTxTrie.elements[1].(*EthTx).String() !=
|
||||||
|
"<EthereumTx bagjqcgzaqsbvff5xrqh5lobxmhuharvkqdc4jmsqfalsu2xs4pbyix7dvfzq>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumTx bagjqcgzaqsbvff5xrqh5lobxmhuharvkqdc4jmsqfalsu2xs4pbyix7dvfzq>", ethTxTrie.elements[1].(*EthTx).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieDecodeBranch(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
if ethTxTrie.nodeKind != "branch" {
|
||||||
|
t.Fatalf("Wrong nodeKind\r\nexpected %s\r\ngot %s", "branch", ethTxTrie.nodeKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ethTxTrie.elements) != 17 {
|
||||||
|
t.Fatalf("Wrong number of elements for a branch node\r\nexpected %d\r\ngot %d", 17, len(ethTxTrie.elements))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, element := range ethTxTrie.elements {
|
||||||
|
switch {
|
||||||
|
case i < 9:
|
||||||
|
if _, ok := element.(cid.Cid); !ok {
|
||||||
|
t.Fatal("Expected element to be a cid")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
if element != nil {
|
||||||
|
t.Fatal("Expected element to be a nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Block INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestEthTxTrieBlockElements(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
if fmt.Sprintf("%x", ethTxTrie.RawData())[:10] != "e4820001a0" {
|
||||||
|
t.Fatalf("Wrong Data\r\nexpected %s\r\ngot %s", "e4820001a0", fmt.Sprintf("%x", ethTxTrie.RawData())[:10])
|
||||||
|
}
|
||||||
|
|
||||||
|
if ethTxTrie.Cid().String() !=
|
||||||
|
"bagjacgzaw6ccgrfc3qnrl6joodbjjiet4haufnt2xww725luwgfhijnmg36q" {
|
||||||
|
t.Fatalf("Wrong Cid\r\nexpected %s\r\ngot %s", "bagjacgzaw6ccgrfc3qnrl6joodbjjiet4haufnt2xww725luwgfhijnmg36q", ethTxTrie.Cid().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxTrieString(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
if ethTxTrie.String() != "<EthereumTxTrie bagjacgzaw6ccgrfc3qnrl6joodbjjiet4haufnt2xww725luwgfhijnmg36q>" {
|
||||||
|
t.Fatalf("Wrong String()\r\nexpected %s\r\ngot %s", "<EthereumTxTrie bagjacgzaw6ccgrfc3qnrl6joodbjjiet4haufnt2xww725luwgfhijnmg36q>", ethTxTrie.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthTxTrieLoggable(t *testing.T) {
|
||||||
|
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
l := ethTxTrie.Loggable()
|
||||||
|
if _, ok := l["type"]; !ok {
|
||||||
|
t.Fatal("Loggable map expected the field 'type'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l["type"] != "eth-tx-trie" {
|
||||||
|
t.Fatalf("Wrong Loggable 'type' value\r\nexpected %s\r\ngot %s", "eth-tx-trie", l["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Node INTERFACE
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTxTrieResolveExtension(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
_ = ethTxTrie
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieResolveLeaf(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieLeaf(t)
|
||||||
|
|
||||||
|
_ = ethTxTrie
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieResolveBranch(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
indexes := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}
|
||||||
|
|
||||||
|
for j, index := range indexes {
|
||||||
|
obj, rest, err := ethTxTrie.Resolve([]string{index, "nonce"})
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case j < 9:
|
||||||
|
_, ok := obj.(*node.Link)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Returned object is not a link (index: %d)", j)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest[0] != "nonce" {
|
||||||
|
t.Fatalf("Wrong rest of the path returned\r\nexpected %s\r\ngot %s", "nonce", rest[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if obj != nil {
|
||||||
|
t.Fatalf("Returned object should have been nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest != nil {
|
||||||
|
t.Fatalf("Rest of the path returned should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != "no such link in this branch" {
|
||||||
|
t.Fatalf("Wrong error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
otherSuccessCases := [][]string{
|
||||||
|
{"0", "1", "banana"},
|
||||||
|
{"1", "banana"},
|
||||||
|
{"7bc", "def"},
|
||||||
|
{"bc", "def"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(otherSuccessCases); i = i + 2 {
|
||||||
|
osc := otherSuccessCases[i]
|
||||||
|
expectedRest := otherSuccessCases[i+1]
|
||||||
|
|
||||||
|
obj, rest, err := ethTxTrie.Resolve(osc)
|
||||||
|
_, ok := obj.(*node.Link)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Returned object is not a link")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, _ := range expectedRest {
|
||||||
|
if rest[j] != expectedRest[j] {
|
||||||
|
t.Fatalf("Wrong rest of the path returned\r\nexpected %s\r\ngot %s", expectedRest[j], rest[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseTxTrieWithResolve(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
txMap := prepareTxTrieMap(t)
|
||||||
|
|
||||||
|
// This is the cid of the tx root at the block 4,139,497
|
||||||
|
currentNode := txMap["bagjacgzaqolvvlyflkdiylijcu4ts6myxczkb2y3ewxmln5oyrsrkfc4v7ua"]
|
||||||
|
|
||||||
|
// This is the path we want to traverse
|
||||||
|
// the transaction id 256, which is RLP encoded to 820100
|
||||||
|
var traversePath []string
|
||||||
|
for _, s := range "820100" {
|
||||||
|
traversePath = append(traversePath, string(s))
|
||||||
|
}
|
||||||
|
traversePath = append(traversePath, "value")
|
||||||
|
|
||||||
|
var obj interface{}
|
||||||
|
for {
|
||||||
|
obj, traversePath, err = currentNode.Resolve(traversePath)
|
||||||
|
link, ok := obj.(*node.Link)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error should be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = txMap[link.Cid.String()]
|
||||||
|
if currentNode == nil {
|
||||||
|
t.Fatal("transaction trie node not found in memory map")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%v", obj) != "0xc495a958603400" {
|
||||||
|
t.Fatalf("Wrong value\r\nexpected %s\r\ngot %s", "0xc495a958603400", fmt.Sprintf("%v", obj))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieTreeBadParams(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
tree := ethTxTrie.Tree("non-empty-string", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = ethTxTrie.Tree("non-empty-string", 1)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = ethTxTrie.Tree("", 0)
|
||||||
|
if tree != nil {
|
||||||
|
t.Fatal("Expected nil to be returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieTreeExtension(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
tree := ethTxTrie.Tree("", -1)
|
||||||
|
|
||||||
|
if len(tree) != 1 {
|
||||||
|
t.Fatalf("An extension should have one element")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tree[0] != "01" {
|
||||||
|
t.Fatalf("Wrong trie element\r\nexpected %s\r\ngot %s", "01", tree[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieTreeBranch(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
tree := ethTxTrie.Tree("", -1)
|
||||||
|
|
||||||
|
lookupElements := map[string]interface{}{
|
||||||
|
"0": nil,
|
||||||
|
"1": nil,
|
||||||
|
"2": nil,
|
||||||
|
"3": nil,
|
||||||
|
"4": nil,
|
||||||
|
"5": nil,
|
||||||
|
"6": nil,
|
||||||
|
"7": nil,
|
||||||
|
"8": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tree) != len(lookupElements) {
|
||||||
|
t.Fatalf("Wrong number of elements\r\nexpected %d\r\ngot %d", len(lookupElements), len(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, te := range tree {
|
||||||
|
if _, ok := lookupElements[te]; !ok {
|
||||||
|
t.Fatalf("Unexpected Element: %v", te)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieLinksBranch(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
desiredValues := []string{
|
||||||
|
"bagjacgzakhtcfpja453ydiaqxgidqmxhh7jwmxujib663deebwfs3m2n3hoa",
|
||||||
|
"bagjacgza2p2fuqh4vumknq6x5w7i47usvtu5ixqins6qjjtcks4zge3vx3qq",
|
||||||
|
"bagjacgza4fkhn7et3ra66yjkzbtvbxjefuketda6jctlut6it7gfahxhywga",
|
||||||
|
"bagjacgzacnryeybs52xryrka5uxi4eg4hi2mh66esaghu7cetzu6fsukrynq",
|
||||||
|
"bagjacgzastu5tc7lwz4ap3gznjwkyyepswquub7gvhags5mgdyfynnwbi43a",
|
||||||
|
"bagjacgza5qgp76ovvorkydni2lchew6ieu5wb55w6hdliiu6vft7zlxtdhjq",
|
||||||
|
"bagjacgzafnssc4yvln6zxmks5roskw4ckngta5n4yfy2skhlu435ve4b575a",
|
||||||
|
"bagjacgzagkuei7qxfxefufme2d3xizxokkq4ad3rzl2x4dq2uao6dcr4va2a",
|
||||||
|
"bagjacgzaxpaehtananrdxjghwukh2wwkkzcqwveppf6xclkrtd26rm27kqwq",
|
||||||
|
}
|
||||||
|
|
||||||
|
links := ethTxTrie.Links()
|
||||||
|
|
||||||
|
for i, v := range desiredValues {
|
||||||
|
if links[i].Cid.String() != v {
|
||||||
|
t.Fatalf("Wrong cid for link %d\r\nexpected %s\r\ngot %s", i, v, links[i].Cid.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
EthTxTrie Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestTxTrieJSONMarshalExtension(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieExtension(t)
|
||||||
|
|
||||||
|
jsonOutput, err := ethTxTrie.MarshalJSON()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.Unmarshal(jsonOutput, &data)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if parseMapElement(data["01"]) !=
|
||||||
|
"bagjacgzak6wdjvshdtb7lrvlteweyd7f5qjr3dmzmh7g2xpi4xrwoujsio2a" {
|
||||||
|
t.Fatalf("Wrong Marshaled Value\r\nexpected %s\r\ngot %s", "bagjacgzak6wdjvshdtb7lrvlteweyd7f5qjr3dmzmh7g2xpi4xrwoujsio2a", parseMapElement(data["01"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if data["type"] != "extension" {
|
||||||
|
t.Fatalf("Wrong node type\r\nexpected %s\r\ngot %s", "extension", data["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieJSONMarshalLeaf(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieLeaf(t)
|
||||||
|
|
||||||
|
jsonOutput, err := ethTxTrie.MarshalJSON()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.Unmarshal(jsonOutput, &data)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
if data["type"] != "leaf" {
|
||||||
|
t.Fatalf("Wrong node type\r\nexpected %s\r\ngot %s", "leaf", data["type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%v", data[""].(map[string]interface{})["nonce"]) !=
|
||||||
|
"40243" {
|
||||||
|
t.Fatalf("Wrong nonce value\r\nexepcted %s\r\ngot %s", "40243", fmt.Sprintf("%v", data[""].(map[string]interface{})["nonce"]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxTrieJSONMarshalBranch(t *testing.T) {
|
||||||
|
ethTxTrie := prepareDecodedEthTxTrieBranch(t)
|
||||||
|
|
||||||
|
jsonOutput, err := ethTxTrie.MarshalJSON()
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = json.Unmarshal(jsonOutput, &data)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
desiredValues := map[string]string{
|
||||||
|
"0": "bagjacgzakhtcfpja453ydiaqxgidqmxhh7jwmxujib663deebwfs3m2n3hoa",
|
||||||
|
"1": "bagjacgza2p2fuqh4vumknq6x5w7i47usvtu5ixqins6qjjtcks4zge3vx3qq",
|
||||||
|
"2": "bagjacgza4fkhn7et3ra66yjkzbtvbxjefuketda6jctlut6it7gfahxhywga",
|
||||||
|
"3": "bagjacgzacnryeybs52xryrka5uxi4eg4hi2mh66esaghu7cetzu6fsukrynq",
|
||||||
|
"4": "bagjacgzastu5tc7lwz4ap3gznjwkyyepswquub7gvhags5mgdyfynnwbi43a",
|
||||||
|
"5": "bagjacgza5qgp76ovvorkydni2lchew6ieu5wb55w6hdliiu6vft7zlxtdhjq",
|
||||||
|
"6": "bagjacgzafnssc4yvln6zxmks5roskw4ckngta5n4yfy2skhlu435ve4b575a",
|
||||||
|
"7": "bagjacgzagkuei7qxfxefufme2d3xizxokkq4ad3rzl2x4dq2uao6dcr4va2a",
|
||||||
|
"8": "bagjacgzaxpaehtananrdxjghwukh2wwkkzcqwveppf6xclkrtd26rm27kqwq",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range desiredValues {
|
||||||
|
if parseMapElement(data[k]) != v {
|
||||||
|
t.Fatalf("Wrong Marshaled Value %s\r\nexpected %s\r\ngot %s", k, v, parseMapElement(data[k]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range []string{"a", "b", "c", "d", "e", "f"} {
|
||||||
|
if data[v] != nil {
|
||||||
|
t.Fatal("Expected value to be nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data["type"] != "branch" {
|
||||||
|
t.Fatalf("Wrong node type\r\nexpected %s\r\ngot %s", "branch", data["type"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AUXILIARS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// prepareDecodedEthTxTrie simulates an IPLD block available in the datastore,
|
||||||
|
// checks the source RLP and tests for the absence of errors during the decoding fase.
|
||||||
|
func prepareDecodedEthTxTrie(branchDataRLP string, t *testing.T) *EthTxTrie {
|
||||||
|
b, err := hex.DecodeString(branchDataRLP)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
c, err := RawdataToCid(MEthTxTrie, b, multihash.KECCAK_256)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
storedEthTxTrie, err := block.NewBlockWithCid(b, c)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
ethTxTrie, err := DecodeEthTxTrie(storedEthTxTrie.Cid(), storedEthTxTrie.RawData())
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
return ethTxTrie
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDecodedEthTxTrieExtension(t *testing.T) *EthTxTrie {
|
||||||
|
extensionDataRLP :=
|
||||||
|
"e4820001a057ac34d6471cc3f5c6ab992c4c0fe5ec131d8d9961fe6d5de8e5e367513243b4"
|
||||||
|
return prepareDecodedEthTxTrie(extensionDataRLP, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDecodedEthTxTrieLeaf(t *testing.T) *EthTxTrie {
|
||||||
|
leafDataRLP :=
|
||||||
|
"f87220b86ff86d829d3384ee6b280083015f9094e0e6c781b8cba08bc840" +
|
||||||
|
"7eac0101b668d1fa6f4987c495a9586034008026a0981b6223c9d3c31971" +
|
||||||
|
"6da3cf057da84acf0fef897f4003d8a362d7bda42247dba066be134c4bc4" +
|
||||||
|
"32125209b5056ef274b7423bcac7cc398cf60b83aaff7b95469f"
|
||||||
|
return prepareDecodedEthTxTrie(leafDataRLP, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDecodedEthTxTrieBranch(t *testing.T) *EthTxTrie {
|
||||||
|
branchDataRLP :=
|
||||||
|
"f90131a051e622bd20e77781a010b9903832e73fd3665e89407ded8c840d8b2db34dd9" +
|
||||||
|
"dca0d3f45a40fcad18a6c3d7edbe8e7e92ace9d45e086cbd04a66254b9931375bee1a0" +
|
||||||
|
"e15476fc93dc41ef612ac86750dd242d14498c1e48a6ba4fc89fcc501ee7c58ca01363" +
|
||||||
|
"826032eeaf1c4540ed2e8e10dc3a34c3fbc4900c7a7c449e69e2ca8a8e1ba094e9d98b" +
|
||||||
|
"ebb67807ecd96a6cac608f95a14a07e6a9c06975861e0b86b6c14736a0ec0cfff9d5ab" +
|
||||||
|
"a2ac0da8d2c4725bc8253b60f7b6f1c6b4229ea967fcaef319d3a02b652173155b7d9b" +
|
||||||
|
"b152ec5d255b82534d3075bcc171a928eba737da9381effaa032a8447e172dc85a1584" +
|
||||||
|
"d0f77466ee52a1c00f71caf57e0e1aa01de18a3ca834a0bbc043cc0d03623ba4c7b514" +
|
||||||
|
"7d5aca56450b548f797d712d5198f5e8b35f542d8080808080808080"
|
||||||
|
return prepareDecodedEthTxTrie(branchDataRLP, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareTxTrieMap(t *testing.T) map[string]*EthTxTrie {
|
||||||
|
fi, err := os.Open("test_data/eth-block-body-json-4139497")
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
_, _, txTrieNodes, err := FromBlockJSON(fi)
|
||||||
|
checkError(err, t)
|
||||||
|
|
||||||
|
out := make(map[string]*EthTxTrie)
|
||||||
|
|
||||||
|
for _, txTrieNode := range txTrieNodes {
|
||||||
|
decodedNode, err := DecodeEthTxTrie(txTrieNode.Cid(), txTrieNode.RawData())
|
||||||
|
checkError(err, t)
|
||||||
|
out[txTrieNode.Cid().String()] = decodedNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
@ -17,13 +17,16 @@
|
|||||||
package ipld
|
package ipld
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
mh "github.com/multiformats/go-multihash"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ipfs/go-cid"
|
|
||||||
mh "github.com/multiformats/go-multihash"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPLD Codecs for Ethereum
|
// IPLD Codecs for Ethereum
|
||||||
@ -42,6 +45,10 @@ const (
|
|||||||
MEthStorageTrie = 0x98
|
MEthStorageTrie = 0x98
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
|
)
|
||||||
|
|
||||||
// RawdataToCid takes the desired codec and a slice of bytes
|
// RawdataToCid takes the desired codec and a slice of bytes
|
||||||
// and returns the proper cid of the object.
|
// and returns the proper cid of the object.
|
||||||
func RawdataToCid(codec uint64, rawdata []byte, multiHash uint64) (cid.Cid, error) {
|
func RawdataToCid(codec uint64, rawdata []byte, multiHash uint64) (cid.Cid, error) {
|
||||||
@ -82,9 +89,9 @@ func commonHashToCid(codec uint64, h common.Hash) cid.Cid {
|
|||||||
// localTrie wraps a go-ethereum trie and its underlying memory db.
|
// localTrie wraps a go-ethereum trie and its underlying memory db.
|
||||||
// It contributes to the creation of the trie node objects.
|
// It contributes to the creation of the trie node objects.
|
||||||
type localTrie struct {
|
type localTrie struct {
|
||||||
keys [][]byte
|
db ethdb.Database
|
||||||
db ethdb.Database
|
trieDB *trie.Database
|
||||||
trie *trie.Trie
|
trie *trie.Trie
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLocalTrie initializes and returns a localTrie object
|
// newLocalTrie initializes and returns a localTrie object
|
||||||
@ -92,7 +99,8 @@ func newLocalTrie() *localTrie {
|
|||||||
var err error
|
var err error
|
||||||
lt := &localTrie{}
|
lt := &localTrie{}
|
||||||
lt.db = rawdb.NewMemoryDatabase()
|
lt.db = rawdb.NewMemoryDatabase()
|
||||||
lt.trie, err = trie.New(common.Hash{}, trie.NewDatabase(lt.db))
|
lt.trieDB = trie.NewDatabase(lt.db)
|
||||||
|
lt.trie, err = trie.New(common.Hash{}, lt.trieDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -101,16 +109,12 @@ func newLocalTrie() *localTrie {
|
|||||||
|
|
||||||
// add receives the index of an object and its rawdata value
|
// add receives the index of an object and its rawdata value
|
||||||
// and includes it into the localTrie
|
// and includes it into the localTrie
|
||||||
func (lt *localTrie) add(idx int, rawdata []byte) {
|
func (lt *localTrie) add(idx int, rawdata []byte) error {
|
||||||
key, err := rlp.EncodeToBytes(uint(idx))
|
key, err := rlp.EncodeToBytes(uint(idx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
lt.keys = append(lt.keys, key)
|
return lt.trie.TryUpdate(key, rawdata)
|
||||||
if err := lt.db.Put(key, rawdata); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
lt.trie.Update(key, rawdata)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rootHash returns the computed trie root.
|
// rootHash returns the computed trie root.
|
||||||
@ -121,6 +125,35 @@ func (lt *localTrie) rootHash() []byte {
|
|||||||
|
|
||||||
// getKeys returns the stored keys of the memory database
|
// getKeys returns the stored keys of the memory database
|
||||||
// of the localTrie for further processing.
|
// of the localTrie for further processing.
|
||||||
func (lt *localTrie) getKeys() [][]byte {
|
func (lt *localTrie) getKeys() ([][]byte, error) {
|
||||||
return lt.keys
|
// commit trie nodes to trieDB
|
||||||
|
var err error
|
||||||
|
_, err = lt.trie.Commit(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// commit trieDB to the underlying ethdb.Database
|
||||||
|
if err := lt.trieDB.Commit(lt.trie.Hash(), false, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// collect all of the node keys
|
||||||
|
it := lt.trie.NodeIterator([]byte{})
|
||||||
|
keyBytes := make([][]byte, 0)
|
||||||
|
for it.Next(true) {
|
||||||
|
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keyBytes = append(keyBytes, it.Hash().Bytes())
|
||||||
|
}
|
||||||
|
return keyBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","id":1,"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x400000000","extraData":"0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa","gasLimit":"0x1388","gasUsed":"0x0","hash":"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000042"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x21c","stateRoot":"0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544","timestamp":"0x0","totalDifficulty":"0x400000000","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"author":"0x4bb96091ee9d802ed039c4d1a5f6216f90f81b01","difficulty":"0xae22b2113ed","extraData":"0xd783010400844765746887676f312e352e31856c696e7578","gasLimit":"0x2fefd8","gasUsed":"0x5208","hash":"0x79851e1adb52a8c5490da2df5d8c060b1cc44a3b6eeaada2e20edba5a8e84523","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x4bb96091ee9d802ed039c4d1a5f6216f90f81b01","mixHash":"0x2565992ba4dbd7ab3bb08d1da34051ae1d90c79bc637a21aa2f51f6380bf5f6a","nonce":"0xf7a14147c2320b2d","number":"0xf3892","parentHash":"0x8ad6d5cbe7ec75ed71d5153dd58f2fd413b17c398ad2a7d9309459ce884e6c9b","receiptsRoot":"0xa73a95d90de29c66220c8b8da825cf34ae969efc7f9a878d8ed893565e4b4676","sealFields":["0xa02565992ba4dbd7ab3bb08d1da34051ae1d90c79bc637a21aa2f51f6380bf5f6a","0x88f7a14147c2320b2d"],"sha3Uncles":"0x08793b633d0b21b980107f3e3277c6693f2f3739e0c676a238cbe24d9ae6e252","size":"0x6c0","stateRoot":"0x11e5ea49ecbee25a9b8f267492a5d296ac09cf6179b43bc334242d052bac5963","timestamp":"0x56bf10c5","totalDifficulty":"0x629a0a89232bcd5b","transactions":[{"blockHash":"0x79851e1adb52a8c5490da2df5d8c060b1cc44a3b6eeaada2e20edba5a8e84523","blockNumber":"0xf3892","condition":null,"creates":null,"from":"0x4bb96091ee9d802ed039c4d1a5f6216f90f81b01","gas":"0x15f90","gasPrice":"0xa","hash":"0xd0fc6b051f16468862c462c672532427efef537ea3737b25b10716949d0e2228","input":"0x","networkId":null,"nonce":"0x7c37","publicKey":"0xa9177f27b99a4ad938359d77e0dca4b64e7ce3722c835d8087d4eecb27c8a54d59e2917e6b31ec12e44b1064d102d35815f9707af9571f15e92d1b6fbcd207e9","r":"0x76933e91718154f18db2e993bc96e82abd9a0fac2bae284875341cbecafa837b","raw":"0xf86a827c370a83015f909404a6c6a293340fc3f2244d097b0cfd84d5317ba58844b1eec616322c1c801ba076933e91718154f18db2e993bc96e82abd9a0fac2bae284875341cbecafa837ba02f165c2c4b5f4b786a95e106c48bccc7e065647af5a1942025b6fbfafeabbbf6","s":"0x2f165c2c4b5f4b786a95e106c48bccc7e065647af5a1942025b6fbfafeabbbf6","standardV":"0x0","to":"0x04a6c6a293340fc3f2244d097b0cfd84d5317ba5","transactionIndex":"0x0","v":"0x1b","value":"0x44b1eec616322c1c"}],"transactionsRoot":"0x7ab22cfcf6db5d1628ac888c25e6bc49aba2faaa200fc880f800f1db1e8bd3cc","uncles":["0x319e0dc9a53711579c4ba88062c927a0045443cca57625903ef471d760506a94","0x0324272e484e509c3c9e9e75ad8b48c7d34556e6b269dd72331033fd5cdc1b2a"]},"id":1}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
statediff/indexer/ipfs/ipld/test_data/eth-block-body-rlp-997522
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-block-body-rlp-997522
Normal file
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-block-body-rlp-999999
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-block-body-rlp-999999
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-0e8b34
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-0e8b34
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
â FKˇd?¶fç_‹¦·YA( "a<13>î2–cUSyI
|
@ -0,0 +1,5 @@
|
|||||||
|
ù Úä<C39A>[G“(»o½Uå,ÔrBÇõSsµ^²€^âä©) 7ó7ì€.Ytâç5_ñ¤ƒ+9¸FÙÃYzFv Ú<C2A0>b{¸ûî³à¨äõ(Û1Y¶«-í¤©÷Ê<C3B7>*µ —f&HÕ‚•ÐЪK€UX<55> v•Â R€%IÙJ/ ÌÇïä³A?Ö¦lŸ@éU¯wFI¨Ùý!-jZ9Ý»g Öͳ.+Ö5î/ž¼”ݽ ±À<>fbŽfzìW [‰ =É@æúpìNÐIÓ¥º ¨ùÀRRVíIŸ ¸B'Ô<>öŠìÇr“šY¯©á¤«W<C2AB>{i‹Û‰â›`DfŽ ý™ p¹JÎW䌿e¡j§pÆEùõﺇ»å<C2BB>
|
||||||
|
) áj|ΦtŠé
é/Šï;=ÂH¥W¹¬N)i41?$÷üí_ B7<ô 0ÙMé
|
||||||
|
#¸óŒík|¸¸’_î<5F>*(¢Z _‰ÒAÿBˆd÷ˆ˜fHLïb-å:Fç•ßÞÃ61Ÿ u— fE&ÈǕ΢{‹rE\IeqàEURÛÀhź1 Õ¾<C395>‰/Ú,XZ–˜Ž¥ïÍ:˜Ž
|
||||||
|
†‚ iK7Å ÷°5.8ò١MQºêMÞáw tÈâ 5R3ÃÈ<C383>În I¿n<C2BF>ð¬¯Ðïømïî³VŽDÕ-"5Ï4
|
||||||
|
á\`4â²A€
|
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-727994
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-727994
Normal file
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-c9070d
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-c9070d
Normal file
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-d5be90
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-d5be90
Normal file
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-d7f897
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-d7f897
Normal file
Binary file not shown.
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-eb2f5f
Normal file
BIN
statediff/indexer/ipfs/ipld/test_data/eth-state-trie-rlp-eb2f5f
Normal file
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
β ¤ξJN…>λb$Ιkg<6B>Ί$α2ζΝ |Δι
<0A>κdΉ¥
|
@ -0,0 +1 @@
|
|||||||
|
<EFBFBD>
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
<EFBFBD>Q<EFBFBD><EFBFBD><EFBFBD><EFBFBD> .ّ<>ٍس<D98D>b<EFBFBD>R<EFBFBD>ّ<EFBFBD><18>f<><66>-<2D>oّt6<74>فKي<4B><D98A><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>ا؟<D8A7>U<EFBFBD><<3C><>مZh<5A>ة <09>hmx[<5B>-#k<>3حع<D8AD><D8B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"author":"0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da","difficulty":"0xae387bd92cc","extraData":"0xd783010400844765746887676f312e352e31856c696e7578","gasLimit":"0x2fefd8","gasUsed":"0x0","hash":"0x319e0dc9a53711579c4ba88062c927a0045443cca57625903ef471d760506a94","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da","mixHash":"0x2d4fd3be43fd50f5c0ba7f1c86c8f468b5c14f75b6143da927a2994383f26640","nonce":"0x0aaaa7fe9d7cf7f4","number":"0xf388f","parentHash":"0xac74216bbdb0ebec6612ad5f26301ab50e588aabe75a804bc2068f83980eefc6","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa02d4fd3be43fd50f5c0ba7f1c86c8f468b5c14f75b6143da927a2994383f26640","0x880aaaa7fe9d7cf7f4"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":null,"stateRoot":"0xf9309492322aab44243f8c38240874b37dd0c563bac85f1a816941acc945b21d","timestamp":"0x56bf1097","totalDifficulty":"0x6299e9e3fdb6eb4d","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"id":1}
|
@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"author":"0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da","difficulty":"0xae22b4c9b9a","extraData":"0xd783010400844765746887676f312e352e31856c696e7578","gasLimit":"0x2fefd8","gasUsed":"0xf618","hash":"0x0324272e484e509c3c9e9e75ad8b48c7d34556e6b269dd72331033fd5cdc1b2a","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x68795c4aa09d6f4ed3e5deddf8c2ad3049a601da","mixHash":"0x0f3bdea5170d6af74b70fcf0df81969f6bb1b740f4a6c78df1d354f172865594","nonce":"0x4c691de262b2b3d9","number":"0xf3890","parentHash":"0xcb9efe9bc3c59be7fb673576d661aff9ca75b1522f58fd38d03d3d49b32bddb3","receiptsRoot":"0x5cf73738487f67f1c0a1c2d1083ae014f38e1aab5eb26a8929a511c48b07ea03","sealFields":["0xa00f3bdea5170d6af74b70fcf0df81969f6bb1b740f4a6c78df1d354f172865594","0x884c691de262b2b3d9"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":null,"stateRoot":"0x968e8d8d099572ac783f4511724ec646f59bb33f7395edf858f98b37c8c3b265","timestamp":"0x56bf10b1","totalDifficulty":"0x6299f4c6290386e7","transactions":[],"transactionsRoot":"0x9cea6a59a5df69111ead7406a431c764b2357120e5b61425388df62f87cbcbc3","uncles":[]},"id":1}
|
@ -208,7 +208,7 @@ func (t *TrieNode) Tree(p string, depth int) []string {
|
|||||||
return []string{val}
|
return []string{val}
|
||||||
case branch:
|
case branch:
|
||||||
for i, elem := range t.elements {
|
for i, elem := range t.elements {
|
||||||
if _, ok := elem.(*cid.Cid); ok {
|
if _, ok := elem.(cid.Cid); ok {
|
||||||
out = append(out, fmt.Sprintf("%x", i))
|
out = append(out, fmt.Sprintf("%x", i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,7 +237,7 @@ func (t *TrieNode) ResolveLink(p []string) (*node.Link, []string, error) {
|
|||||||
|
|
||||||
// Copy will go away. It is here to comply with the interface.
|
// Copy will go away. It is here to comply with the interface.
|
||||||
func (t *TrieNode) Copy() node.Node {
|
func (t *TrieNode) Copy() node.Node {
|
||||||
panic("dont use this yet")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links is a helper function that returns all links within this object
|
// Links is a helper function that returns all links within this object
|
||||||
|
Loading…
Reference in New Issue
Block a user