V1.10.2 statediff 0.0.20 #68
+82
-19
@@ -41,6 +41,9 @@ var (
|
||||
// This error is returned when a typed receipt is decoded, but the string is empty.
|
||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
||||
|
||||
// This error is returned when a typed receipt has an unsupported type
|
||||
var errRctTypeNotSupported = errors.New("receipt type not supported")
|
||||
|
||||
const (
|
||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||
ReceiptStatusFailed = uint64(0)
|
||||
@@ -136,6 +139,9 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
|
||||
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
|
||||
// into an RLP stream. If no post state is present, byzantium fork is assumed.
|
||||
// For a legacy Receipt this returns RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
|
||||
// For a EIP-2718 Receipt this returns RLP(TxType || ReceiptPayload)
|
||||
// For a EIP-2930 Receipt, TxType == 0x01 and ReceiptPayload == RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
|
||||
func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
if r.Type == LegacyTxType {
|
||||
@@ -148,13 +154,34 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf.WriteByte(r.Type)
|
||||
if err := rlp.Encode(buf, data); err != nil {
|
||||
if err := r.encodeTyped(data, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
// encodeTyped writes the canonical encoding of a typed receipt to w.
|
||||
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
|
||||
w.WriteByte(r.Type)
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns the canonical consensus encoding of the receipt.
|
||||
// For a legacy Receipt this returns RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
|
||||
// For a EIP-2718 Receipt this returns TxType || ReceiptPayload
|
||||
// For a EIP-2930, TxType == 0x01 and ReceiptPayload == RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs])
|
||||
func (r *Receipt) MarshalBinary() ([]byte, error) {
|
||||
if r.Type == LegacyTxType {
|
||||
return rlp.EncodeToBytes(r)
|
||||
}
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
err := r.encodeTyped(data, buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||
// from an RLP stream.
|
||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
@@ -193,6 +220,42 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the canonical encoding of receipts.
|
||||
// It supports legacy RLP receipts and EIP-2718 typed receipts.
|
||||
func (r *Receipt) UnmarshalBinary(b []byte) error {
|
||||
if len(b) > 0 && b[0] > 0x7f {
|
||||
// It's a legacy receipt decode the RLP
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(data)
|
||||
}
|
||||
// It's an EIP2718 typed transaction envelope.
|
||||
return r.decodeTyped(b)
|
||||
}
|
||||
|
||||
// decodeTyped decodes a typed receipt from the canonical format.
|
||||
func (r *Receipt) decodeTyped(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b[1:], &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = AccessListTxType
|
||||
return r.setFromRLP(data)
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Receipt) setFromRLP(data receiptRLP) error {
|
||||
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
|
||||
return r.setStatus(data.PostStateOrStatus)
|
||||
@@ -355,42 +418,42 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
||||
|
||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||
// data and contextual infos like containing block and transactions.
|
||||
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
||||
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(r) {
|
||||
if len(txs) != len(rs) {
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
for i := 0; i < len(r); i++ {
|
||||
for i := 0; i < len(rs); i++ {
|
||||
// The transaction type and hash can be retrieved from the transaction itself
|
||||
r[i].Type = txs[i].Type()
|
||||
r[i].TxHash = txs[i].Hash()
|
||||
rs[i].Type = txs[i].Type()
|
||||
rs[i].TxHash = txs[i].Hash()
|
||||
|
||||
// block location fields
|
||||
r[i].BlockHash = hash
|
||||
r[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
r[i].TransactionIndex = uint(i)
|
||||
rs[i].BlockHash = hash
|
||||
rs[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
rs[i].TransactionIndex = uint(i)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if txs[i].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
from, _ := Sender(signer, txs[i])
|
||||
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||
}
|
||||
// The used gas can be calculated based on previous r
|
||||
if i == 0 {
|
||||
r[i].GasUsed = r[i].CumulativeGasUsed
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed
|
||||
} else {
|
||||
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
|
||||
}
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
for j := 0; j < len(r[i].Logs); j++ {
|
||||
r[i].Logs[j].BlockNumber = number
|
||||
r[i].Logs[j].BlockHash = hash
|
||||
r[i].Logs[j].TxHash = r[i].TxHash
|
||||
r[i].Logs[j].TxIndex = uint(i)
|
||||
r[i].Logs[j].Index = logIndex
|
||||
for j := 0; j < len(rs[i].Logs); j++ {
|
||||
rs[i].Logs[j].BlockNumber = number
|
||||
rs[i].Logs[j].BlockHash = hash
|
||||
rs[i].Logs[j].TxHash = rs[i].TxHash
|
||||
rs[i].Logs[j].TxIndex = uint(i)
|
||||
rs[i].Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,42 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var (
|
||||
legacyReceipt = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
}
|
||||
accessListReceipt = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
Type: AccessListTxType,
|
||||
}
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
@@ -117,6 +153,76 @@ func TestLegacyReceiptDecoding(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiptMarshalBinary(t *testing.T) {
|
||||
// Legacy Receipt
|
||||
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
|
||||
have, err := legacyReceipt.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("marshal binary error: %v", err)
|
||||
}
|
||||
legacyReceipts := Receipts{legacyReceipt}
|
||||
buf := new(bytes.Buffer)
|
||||
legacyReceipts.EncodeIndex(0, buf)
|
||||
haveEncodeIndex := buf.Bytes()
|
||||
if !bytes.Equal(have, haveEncodeIndex) {
|
||||
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
|
||||
}
|
||||
buf.Reset()
|
||||
if err := legacyReceipt.EncodeRLP(buf); err != nil {
|
||||
t.Fatalf("encode rlp error: %v", err)
|
||||
}
|
||||
haveRLPEncode := buf.Bytes()
|
||||
if !bytes.Equal(have, haveRLPEncode) {
|
||||
t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode)
|
||||
}
|
||||
legacyWant := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
if !bytes.Equal(have, legacyWant) {
|
||||
t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant)
|
||||
}
|
||||
|
||||
// 2930 Receipt
|
||||
buf.Reset()
|
||||
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
|
||||
have, err = accessListReceipt.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("marshal binary error: %v", err)
|
||||
}
|
||||
accessListReceipts := Receipts{accessListReceipt}
|
||||
accessListReceipts.EncodeIndex(0, buf)
|
||||
haveEncodeIndex = buf.Bytes()
|
||||
if !bytes.Equal(have, haveEncodeIndex) {
|
||||
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
|
||||
}
|
||||
accessListWant := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
if !bytes.Equal(have, accessListWant) {
|
||||
t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiptUnmarshalBinary(t *testing.T) {
|
||||
// Legacy Receipt
|
||||
legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
gotLegacyReceipt := new(Receipt)
|
||||
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
|
||||
t.Fatalf("unmarshal binary error: %v", err)
|
||||
}
|
||||
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
|
||||
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
|
||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
|
||||
}
|
||||
|
||||
// 2930 Receipt
|
||||
accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
gotAccessListReceipt := new(Receipt)
|
||||
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
|
||||
t.Fatalf("unmarshal binary error: %v", err)
|
||||
}
|
||||
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
|
||||
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
|
||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeAsStoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &storedReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
|
||||
@@ -83,6 +83,9 @@ type TxData interface {
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
// For a legacy Transaction this returns RLP([AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, V, R, S])
|
||||
// For a EIP-2718 Transaction this returns RLP(TxType || TxPayload)
|
||||
// For a EIP-2930 Transaction, TxType == 0x01 and TxPayload == RLP([ChainID, AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, AccessList, V, R, S]
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.Encode(w, tx.inner)
|
||||
@@ -103,9 +106,10 @@ func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
|
||||
return rlp.Encode(w, tx.inner)
|
||||
}
|
||||
|
||||
// MarshalBinary returns the canonical encoding of the transaction.
|
||||
// For legacy transactions, it returns the RLP encoding. For EIP-2718 typed
|
||||
// transactions, it returns the type and payload.
|
||||
// MarshalBinary returns the canonical consensus encoding of the transaction.
|
||||
// For a legacy Transaction this returns RLP([AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, V, R, S])
|
||||
// For a EIP-2718 Transaction this returns TxType || TxPayload
|
||||
// For a EIP-2930 Transaction, TxType == 0x01 and TxPayload == RLP([ChainID, AccountNonce, GasPrice, GasLimit, Recipient, Amount, Data, AccessList, V, R, S]
|
||||
func (tx *Transaction) MarshalBinary() ([]byte, error) {
|
||||
if tx.Type() == LegacyTxType {
|
||||
return rlp.EncodeToBytes(tx.inner)
|
||||
|
||||
@@ -33,6 +33,7 @@ require (
|
||||
github.com/holiman/uint256 v1.1.1
|
||||
github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88
|
||||
github.com/influxdata/influxdb v1.8.3
|
||||
github.com/ipfs/go-block-format v0.0.2
|
||||
github.com/ipfs/go-cid v0.0.7
|
||||
github.com/ipfs/go-ipfs-blockstore v1.0.1
|
||||
github.com/ipfs/go-ipfs-ds-help v1.0.0
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 10 // Minor version component of the current release
|
||||
VersionPatch = 2 // Patch version component of the current release
|
||||
VersionMeta = "statediff-0.0.19" // Version metadata to append to the version string
|
||||
VersionMeta = "statediff-0.0.20" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
// Version holds the textual version string.
|
||||
|
||||
@@ -9,6 +9,7 @@ CREATE TABLE eth.transaction_cids (
|
||||
dst VARCHAR(66) NOT NULL,
|
||||
src VARCHAR(66) NOT NULL,
|
||||
tx_data BYTEA,
|
||||
tx_type BYTEA,
|
||||
UNIQUE (header_id, tx_hash)
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.access_list_element (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tx_id INTEGER NOT NULL REFERENCES eth.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
index INTEGER NOT NULL,
|
||||
address VARCHAR(66),
|
||||
storage_keys VARCHAR(66)[],
|
||||
UNIQUE (tx_id, index)
|
||||
);
|
||||
|
||||
CREATE INDEX accesss_list_element_address_index ON eth.access_list_element USING btree (address);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX eth.accesss_list_element_address_index;
|
||||
DROP TABLE eth.access_list_element;
|
||||
+164
-32
@@ -103,6 +103,33 @@ end;
|
||||
$_$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: canonical_header(bigint); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.canonical_header(height bigint) RETURNS integer
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
current_weight INT;
|
||||
heaviest_weight INT DEFAULT 0;
|
||||
heaviest_id INT;
|
||||
r eth.header_cids%ROWTYPE;
|
||||
BEGIN
|
||||
FOR r IN SELECT * FROM eth.header_cids
|
||||
WHERE block_number = height
|
||||
LOOP
|
||||
SELECT INTO current_weight * FROM header_weight(r.block_hash);
|
||||
IF current_weight > heaviest_weight THEN
|
||||
heaviest_weight := current_weight;
|
||||
heaviest_id := r.id;
|
||||
END IF;
|
||||
END LOOP;
|
||||
RETURN heaviest_id;
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: canonical_header_from_array(eth.header_cids[]); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
@@ -111,7 +138,7 @@ CREATE FUNCTION public.canonical_header_from_array(headers eth.header_cids[]) RE
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
canonical_header eth.header_cids;
|
||||
canonical_header eth.header_cids;
|
||||
canonical_child eth.header_cids;
|
||||
header eth.header_cids;
|
||||
current_child_result child_result;
|
||||
@@ -130,25 +157,25 @@ BEGIN
|
||||
current_header_with_child = header;
|
||||
-- and add the children to the growing set of child headers
|
||||
child_headers = array_cat(child_headers, current_child_result.children);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END IF;
|
||||
END LOOP;
|
||||
-- if none of the headers had children, none is more canonical than the other
|
||||
IF has_children_count = 0 THEN
|
||||
-- return the first one selected
|
||||
SELECT * INTO canonical_header FROM unnest(headers) LIMIT 1;
|
||||
-- if only one header had children, it can be considered the heaviest/canonical header of the set
|
||||
ELSIF has_children_count = 1 THEN
|
||||
SELECT * INTO canonical_header FROM unnest(headers) LIMIT 1;
|
||||
-- if only one header had children, it can be considered the heaviest/canonical header of the set
|
||||
ELSIF has_children_count = 1 THEN
|
||||
-- return the only header with a child
|
||||
canonical_header = current_header_with_child;
|
||||
-- if there are multiple headers with children
|
||||
ELSE
|
||||
ELSE
|
||||
-- find the canonical header from the child set
|
||||
canonical_child = canonical_header_from_array(child_headers);
|
||||
-- the header that is parent to this header, is the canonical header at this level
|
||||
SELECT * INTO canonical_header FROM unnest(headers)
|
||||
WHERE block_hash = canonical_child.parent_hash;
|
||||
END IF;
|
||||
RETURN canonical_header;
|
||||
SELECT * INTO canonical_header FROM unnest(headers)
|
||||
WHERE block_hash = canonical_child.parent_hash;
|
||||
END IF;
|
||||
RETURN canonical_header;
|
||||
END
|
||||
$$;
|
||||
|
||||
@@ -161,17 +188,17 @@ CREATE FUNCTION public.canonical_header_id(height bigint) RETURNS integer
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
canonical_header eth.header_cids;
|
||||
canonical_header eth.header_cids;
|
||||
headers eth.header_cids[];
|
||||
header_count INT;
|
||||
temp_header eth.header_cids;
|
||||
BEGIN
|
||||
-- collect all headers at this height
|
||||
FOR temp_header IN
|
||||
SELECT * FROM eth.header_cids WHERE block_number = height
|
||||
LOOP
|
||||
FOR temp_header IN
|
||||
SELECT * FROM eth.header_cids WHERE block_number = height
|
||||
LOOP
|
||||
headers = array_append(headers, temp_header);
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
-- count the number of headers collected
|
||||
header_count = array_length(headers, 1);
|
||||
-- if we have less than 1 header, return NULL
|
||||
@@ -181,14 +208,25 @@ END LOOP;
|
||||
ELSIF header_count = 1 THEN
|
||||
RETURN headers[1].id;
|
||||
-- if we have multiple headers we need to determine which one is canonical
|
||||
ELSE
|
||||
ELSE
|
||||
canonical_header = canonical_header_from_array(headers);
|
||||
RETURN canonical_header.id;
|
||||
END IF;
|
||||
RETURN canonical_header.id;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: ethHeaderCidByBlockNumber(bigint); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE FUNCTION public."ethHeaderCidByBlockNumber"(n bigint) RETURNS SETOF eth.header_cids
|
||||
LANGUAGE sql STABLE
|
||||
AS $_$
|
||||
SELECT * FROM eth.header_cids WHERE block_number=$1 ORDER BY id
|
||||
$_$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: has_child(character varying, bigint); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
@@ -197,31 +235,53 @@ CREATE FUNCTION public.has_child(hash character varying, height bigint) RETURNS
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
child_height INT;
|
||||
child_height INT;
|
||||
temp_child eth.header_cids;
|
||||
new_child_result child_result;
|
||||
BEGIN
|
||||
child_height = height + 1;
|
||||
-- short circuit if there are no children
|
||||
SELECT exists(SELECT 1
|
||||
SELECT exists(SELECT 1
|
||||
FROM eth.header_cids
|
||||
WHERE parent_hash = hash
|
||||
AND block_number = child_height
|
||||
LIMIT 1)
|
||||
INTO new_child_result.has_child;
|
||||
-- collect all the children for this header
|
||||
IF new_child_result.has_child THEN
|
||||
INTO new_child_result.has_child;
|
||||
-- collect all the children for this header
|
||||
IF new_child_result.has_child THEN
|
||||
FOR temp_child IN
|
||||
SELECT * FROM eth.header_cids WHERE parent_hash = hash AND block_number = child_height
|
||||
SELECT * FROM eth.header_cids WHERE parent_hash = hash AND block_number = child_height
|
||||
LOOP
|
||||
new_child_result.children = array_append(new_child_result.children, temp_child);
|
||||
END LOOP;
|
||||
END IF;
|
||||
RETURN new_child_result;
|
||||
END LOOP;
|
||||
END IF;
|
||||
RETURN new_child_result;
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: header_weight(character varying); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.header_weight(hash character varying) RETURNS bigint
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
WITH RECURSIVE validator AS (
|
||||
SELECT block_hash, parent_hash, block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash
|
||||
UNION
|
||||
SELECT eth.header_cids.block_hash, eth.header_cids.parent_hash, eth.header_cids.block_number
|
||||
FROM eth.header_cids
|
||||
INNER JOIN validator
|
||||
ON eth.header_cids.parent_hash = validator.block_hash
|
||||
AND eth.header_cids.block_number = validator.block_number + 1
|
||||
)
|
||||
SELECT COUNT(*) FROM validator;
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: was_state_removed(bytea, bigint, character varying); Type: FUNCTION; Schema: public; Owner: -
|
||||
--
|
||||
@@ -231,7 +291,7 @@ CREATE FUNCTION public.was_state_removed(path bytea, height bigint, hash charact
|
||||
AS $$
|
||||
SELECT exists(SELECT 1
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE state_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
@@ -251,8 +311,8 @@ CREATE FUNCTION public.was_storage_removed(path bytea, height bigint, hash chara
|
||||
AS $$
|
||||
SELECT exists(SELECT 1
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE storage_path = path
|
||||
AND block_number > height
|
||||
AND block_number <= (SELECT block_number
|
||||
@@ -263,6 +323,39 @@ SELECT exists(SELECT 1
|
||||
$$;
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry; Type: TABLE; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE eth.access_list_entry (
|
||||
id integer NOT NULL,
|
||||
index integer NOT NULL,
|
||||
tx_id integer NOT NULL,
|
||||
address character varying(66),
|
||||
storage_keys character varying(66)[]
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry_id_seq; Type: SEQUENCE; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE eth.access_list_entry_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry_id_seq; Type: SEQUENCE OWNED BY; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE eth.access_list_entry_id_seq OWNED BY eth.access_list_entry.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: header_cids_id_seq; Type: SEQUENCE; Schema: eth; Owner: -
|
||||
--
|
||||
@@ -441,7 +534,8 @@ CREATE TABLE eth.transaction_cids (
|
||||
mh_key text NOT NULL,
|
||||
dst character varying(66) NOT NULL,
|
||||
src character varying(66) NOT NULL,
|
||||
tx_data bytea
|
||||
tx_data bytea,
|
||||
tx_type bytea
|
||||
);
|
||||
|
||||
|
||||
@@ -597,6 +691,13 @@ CREATE SEQUENCE public.nodes_id_seq
|
||||
ALTER SEQUENCE public.nodes_id_seq OWNED BY public.nodes.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry id; Type: DEFAULT; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY eth.access_list_entry ALTER COLUMN id SET DEFAULT nextval('eth.access_list_entry_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: header_cids id; Type: DEFAULT; Schema: eth; Owner: -
|
||||
--
|
||||
@@ -660,6 +761,22 @@ ALTER TABLE ONLY public.goose_db_version ALTER COLUMN id SET DEFAULT nextval('pu
|
||||
ALTER TABLE ONLY public.nodes ALTER COLUMN id SET DEFAULT nextval('public.nodes_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry access_list_entry_pkey; Type: CONSTRAINT; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY eth.access_list_entry
|
||||
ADD CONSTRAINT access_list_entry_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry access_list_entry_tx_id_index_key; Type: CONSTRAINT; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY eth.access_list_entry
|
||||
ADD CONSTRAINT access_list_entry_tx_id_index_key UNIQUE (tx_id, index);
|
||||
|
||||
|
||||
--
|
||||
-- Name: header_cids header_cids_block_number_block_hash_key; Type: CONSTRAINT; Schema: eth; Owner: -
|
||||
--
|
||||
@@ -804,6 +921,13 @@ ALTER TABLE ONLY public.nodes
|
||||
ADD CONSTRAINT nodes_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: accesss_list_address_index; Type: INDEX; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX accesss_list_address_index ON eth.access_list_entry USING btree (address);
|
||||
|
||||
|
||||
--
|
||||
-- Name: account_state_id_index; Type: INDEX; Schema: eth; Owner: -
|
||||
--
|
||||
@@ -1091,6 +1215,14 @@ CREATE TRIGGER transaction_cids_ai AFTER INSERT ON eth.transaction_cids FOR EACH
|
||||
CREATE TRIGGER uncle_cids_ai AFTER INSERT ON eth.uncle_cids FOR EACH ROW EXECUTE FUNCTION eth.graphql_subscription('uncle_cids', 'id');
|
||||
|
||||
|
||||
--
|
||||
-- Name: access_list_entry access_list_entry_tx_id_fkey; Type: FK CONSTRAINT; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY eth.access_list_entry
|
||||
ADD CONSTRAINT access_list_entry_tx_id_fkey FOREIGN KEY (tx_id) REFERENCES eth.transaction_cids(id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
|
||||
--
|
||||
-- Name: header_cids header_cids_mh_key_fkey; Type: FK CONSTRAINT; Schema: eth; Owner: -
|
||||
--
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -163,7 +165,8 @@ func (sdi *StateDiffIndexer) PushBlock(block *types.Block, receipts types.Receip
|
||||
t = time.Now()
|
||||
|
||||
// Publish and index header, collect headerID
|
||||
headerID, err := sdi.processHeader(tx, block.Header(), headerNode, reward, totalDifficulty)
|
||||
var headerID int64
|
||||
headerID, err = sdi.processHeader(tx, block.Header(), headerNode, reward, totalDifficulty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -322,10 +325,38 @@ func (sdi *StateDiffIndexer) processReceiptsAndTxs(tx *sqlx.Tx, args processArgs
|
||||
CID: txNode.Cid().String(),
|
||||
MhKey: shared.MultihashKeyFromCID(txNode.Cid()),
|
||||
}
|
||||
txType := trx.Type()
|
||||
if txType != types.LegacyTxType {
|
||||
txModel.Type = &txType
|
||||
}
|
||||
txID, err := sdi.dbWriter.upsertTransactionCID(tx, txModel, args.headerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// AccessListEntryModel is the db model for eth.access_list_entry
|
||||
type AccessListElementModel struct {
|
||||
ID int64 `db:"id"`
|
||||
Index int64 `db:"index"`
|
||||
TxID int64 `db:"tx_id"`
|
||||
Address string `db:"address"`
|
||||
StorageKeys pq.StringArray `db:"storage_keys"`
|
||||
}
|
||||
// index access list if this is one
|
||||
for j, accessListElement := range trx.AccessList() {
|
||||
storageKeys := make([]string, len(accessListElement.StorageKeys))
|
||||
for k, storageKey := range accessListElement.StorageKeys {
|
||||
storageKeys[k] = storageKey.Hex()
|
||||
}
|
||||
accessListElementModel := models.AccessListElementModel{
|
||||
Index: int64(j),
|
||||
Address: accessListElement.Address.Hex(),
|
||||
StorageKeys: storageKeys,
|
||||
}
|
||||
if err := sdi.dbWriter.upsertAccessListElement(tx, accessListElementModel, txID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// index the receipt
|
||||
rctModel := models.ReceiptModel{
|
||||
Topic0s: topicSets[0],
|
||||
|
||||
@@ -43,11 +43,11 @@ var (
|
||||
ind *indexer.StateDiffIndexer
|
||||
ipfsPgGet = `SELECT data FROM public.blocks
|
||||
WHERE key = $1`
|
||||
tx1, tx2, tx3, rct1, rct2, rct3 []byte
|
||||
mockBlock *types.Block
|
||||
headerCID, trx1CID, trx2CID, trx3CID cid.Cid
|
||||
rct1CID, rct2CID, rct3CID cid.Cid
|
||||
state1CID, state2CID, storageCID cid.Cid
|
||||
tx1, tx2, tx3, tx4, rct1, rct2, rct3, rct4 []byte
|
||||
mockBlock *types.Block
|
||||
headerCID, trx1CID, trx2CID, trx3CID, trx4CID cid.Cid
|
||||
rct1CID, rct2CID, rct3CID, rct4CID cid.Cid
|
||||
state1CID, state2CID, storageCID cid.Cid
|
||||
)
|
||||
|
||||
func expectTrue(t *testing.T, value bool) {
|
||||
@@ -76,6 +76,11 @@ func init() {
|
||||
copy(tx3, buf.Bytes())
|
||||
buf.Reset()
|
||||
|
||||
txs.EncodeIndex(3, buf)
|
||||
tx4 = make([]byte, buf.Len())
|
||||
copy(tx4, buf.Bytes())
|
||||
buf.Reset()
|
||||
|
||||
rcts.EncodeIndex(0, buf)
|
||||
rct1 = make([]byte, buf.Len())
|
||||
copy(rct1, buf.Bytes())
|
||||
@@ -91,13 +96,20 @@ func init() {
|
||||
copy(rct3, buf.Bytes())
|
||||
buf.Reset()
|
||||
|
||||
rcts.EncodeIndex(3, buf)
|
||||
rct4 = make([]byte, buf.Len())
|
||||
copy(rct4, buf.Bytes())
|
||||
buf.Reset()
|
||||
|
||||
headerCID, _ = ipld.RawdataToCid(ipld.MEthHeader, mocks.MockHeaderRlp, multihash.KECCAK_256)
|
||||
trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx1, multihash.KECCAK_256)
|
||||
trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx2, multihash.KECCAK_256)
|
||||
trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx3, multihash.KECCAK_256)
|
||||
trx4CID, _ = ipld.RawdataToCid(ipld.MEthTx, tx4, multihash.KECCAK_256)
|
||||
rct1CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct1, multihash.KECCAK_256)
|
||||
rct2CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct2, multihash.KECCAK_256)
|
||||
rct3CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct3, multihash.KECCAK_256)
|
||||
rct4CID, _ = ipld.RawdataToCid(ipld.MEthTxReceipt, rct4, multihash.KECCAK_256)
|
||||
state1CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.ContractLeafNode, multihash.KECCAK_256)
|
||||
state2CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, mocks.AccountLeafNode, multihash.KECCAK_256)
|
||||
storageCID, _ = ipld.RawdataToCid(ipld.MEthStorageTrie, mocks.StorageLeafNode, multihash.KECCAK_256)
|
||||
@@ -147,13 +159,13 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
ID int
|
||||
}
|
||||
header := new(res)
|
||||
err = db.QueryRowx(pgStr, 1).StructScan(header)
|
||||
err = db.QueryRowx(pgStr, mocks.BlockNumber.Uint64()).StructScan(header)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shared.ExpectEqual(t, header.CID, headerCID.String())
|
||||
shared.ExpectEqual(t, header.TD, mocks.MockBlock.Difficulty().String())
|
||||
shared.ExpectEqual(t, header.Reward, "5000000000000011250")
|
||||
shared.ExpectEqual(t, header.Reward, "2000000000000021250")
|
||||
dc, err := cid.Decode(header.CID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -175,14 +187,15 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
trxs := make([]string, 0)
|
||||
pgStr := `SELECT transaction_cids.cid FROM eth.transaction_cids INNER JOIN eth.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
WHERE header_cids.block_number = $1`
|
||||
err = db.Select(&trxs, pgStr, 1)
|
||||
err = db.Select(&trxs, pgStr, mocks.BlockNumber.Uint64())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shared.ExpectEqual(t, len(trxs), 3)
|
||||
shared.ExpectEqual(t, len(trxs), 4)
|
||||
expectTrue(t, shared.ListContainsString(trxs, trx1CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(trxs, trx2CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(trxs, trx3CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(trxs, trx4CID.String()))
|
||||
// and published
|
||||
for _, c := range trxs {
|
||||
dc, err := cid.Decode(c)
|
||||
@@ -199,10 +212,68 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
switch c {
|
||||
case trx1CID.String():
|
||||
shared.ExpectEqual(t, data, tx1)
|
||||
var txType *uint8
|
||||
pgStr = `SELECT tx_type FROM eth.transaction_cids WHERE cid = $1`
|
||||
err = db.Get(&txType, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if txType != nil {
|
||||
t.Fatalf("expected nil tx_type, got %d", *txType)
|
||||
}
|
||||
case trx2CID.String():
|
||||
shared.ExpectEqual(t, data, tx2)
|
||||
var txType *uint8
|
||||
pgStr = `SELECT tx_type FROM eth.transaction_cids WHERE cid = $1`
|
||||
err = db.Get(&txType, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if txType != nil {
|
||||
t.Fatalf("expected nil tx_type, got %d", *txType)
|
||||
}
|
||||
case trx3CID.String():
|
||||
shared.ExpectEqual(t, data, tx3)
|
||||
var txType *uint8
|
||||
pgStr = `SELECT tx_type FROM eth.transaction_cids WHERE cid = $1`
|
||||
err = db.Get(&txType, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if txType != nil {
|
||||
t.Fatalf("expected nil tx_type, got %d", *txType)
|
||||
}
|
||||
case trx4CID.String():
|
||||
shared.ExpectEqual(t, data, tx4)
|
||||
var txType *uint8
|
||||
pgStr = `SELECT tx_type FROM eth.transaction_cids WHERE cid = $1`
|
||||
err = db.Get(&txType, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *txType != types.AccessListTxType {
|
||||
t.Fatalf("expected AccessListTxType (1), got %d", *txType)
|
||||
}
|
||||
accessListElementModels := make([]models.AccessListElementModel, 0)
|
||||
pgStr = `SELECT access_list_element.* FROM eth.access_list_element INNER JOIN eth.transaction_cids ON (tx_id = transaction_cids.id) WHERE cid = $1 ORDER BY access_list_element.index ASC`
|
||||
err = db.Select(&accessListElementModels, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(accessListElementModels) != 2 {
|
||||
t.Fatalf("expected two access list entries, got %d", len(accessListElementModels))
|
||||
}
|
||||
model1 := models.AccessListElementModel{
|
||||
Index: accessListElementModels[0].Index,
|
||||
Address: accessListElementModels[0].Address,
|
||||
}
|
||||
model2 := models.AccessListElementModel{
|
||||
Index: accessListElementModels[1].Index,
|
||||
Address: accessListElementModels[1].Address,
|
||||
StorageKeys: accessListElementModels[1].StorageKeys,
|
||||
}
|
||||
shared.ExpectEqual(t, model1, mocks.AccessListEntry1Model)
|
||||
shared.ExpectEqual(t, model2, mocks.AccessListEntry2Model)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -216,14 +287,15 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
WHERE receipt_cids.tx_id = transaction_cids.id
|
||||
AND transaction_cids.header_id = header_cids.id
|
||||
AND header_cids.block_number = $1`
|
||||
err = db.Select(&rcts, pgStr, 1)
|
||||
err = db.Select(&rcts, pgStr, mocks.BlockNumber.Uint64())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shared.ExpectEqual(t, len(rcts), 3)
|
||||
shared.ExpectEqual(t, len(rcts), 4)
|
||||
expectTrue(t, shared.ListContainsString(rcts, rct1CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(rcts, rct2CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(rcts, rct3CID.String()))
|
||||
expectTrue(t, shared.ListContainsString(rcts, rct4CID.String()))
|
||||
// and published
|
||||
for _, c := range rcts {
|
||||
dc, err := cid.Decode(c)
|
||||
@@ -265,6 +337,15 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shared.ExpectEqual(t, postState, mocks.ExpectedPostState2)
|
||||
case rct4CID.String():
|
||||
shared.ExpectEqual(t, data, rct4)
|
||||
var postState string
|
||||
pgStr = `SELECT post_state FROM eth.receipt_cids WHERE cid = $1`
|
||||
err = db.Get(&postState, pgStr, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shared.ExpectEqual(t, postState, mocks.ExpectedPostState3)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -277,7 +358,7 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
pgStr := `SELECT state_cids.id, state_cids.cid, state_cids.state_leaf_key, state_cids.node_type, state_cids.state_path, state_cids.header_id
|
||||
FROM eth.state_cids INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE header_cids.block_number = $1`
|
||||
err = db.Select(&stateNodes, pgStr, 1)
|
||||
err = db.Select(&stateNodes, pgStr, mocks.BlockNumber.Uint64())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -341,7 +422,7 @@ func TestPublishAndIndexer(t *testing.T) {
|
||||
WHERE storage_cids.state_id = state_cids.id
|
||||
AND state_cids.header_id = header_cids.id
|
||||
AND header_cids.block_number = $1`
|
||||
err = db.Select(&storageNodes, pgStr, 1)
|
||||
err = db.Select(&storageNodes, pgStr, mocks.BlockNumber.Uint64())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
@@ -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"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ipfs/go-cid"
|
||||
@@ -66,7 +68,7 @@ func NewEthHeader(header *types.Header) (*EthHeader, error) {
|
||||
// DecodeEthHeader takes a cid and its raw binary data
|
||||
// from IPFS and returns an EthTx object for further processing.
|
||||
func DecodeEthHeader(c cid.Cid, b []byte) (*EthHeader, error) {
|
||||
var h *types.Header
|
||||
h := new(types.Header)
|
||||
if err := rlp.DecodeBytes(b, h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,7 +101,7 @@ func (b *EthHeader) String() string {
|
||||
// Loggable returns a map the type of IPLD Link.
|
||||
func (b *EthHeader) Loggable() 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/multiformats/go-multihash"
|
||||
|
||||
"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
|
||||
// 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) {
|
||||
@@ -64,14 +161,16 @@ func processTransactions(txs []*types.Transaction, expectedTxRoot []byte) ([]*Et
|
||||
return nil, nil, err
|
||||
}
|
||||
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) {
|
||||
return nil, nil, fmt.Errorf("wrong transaction hash computed")
|
||||
}
|
||||
|
||||
return ethTxNodes, transactionTrie.getNodes(), nil
|
||||
txTrieNodes, err := transactionTrie.getNodes()
|
||||
return ethTxNodes, txTrieNodes, err
|
||||
}
|
||||
|
||||
// processReceipts will take in receipts
|
||||
@@ -86,12 +185,14 @@ func processReceipts(rcts []*types.Receipt, expectedRctRoot []byte) ([]*EthRecei
|
||||
return nil, nil, err
|
||||
}
|
||||
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) {
|
||||
return nil, nil, fmt.Errorf("wrong receipt hash computed")
|
||||
}
|
||||
|
||||
return ethRctNodes, receiptTrie.getNodes(), nil
|
||||
rctTrieNodes, err := receiptTrie.getNodes()
|
||||
return ethRctNodes, rctTrieNodes, err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
@@ -44,18 +43,18 @@ var _ node.Node = (*EthReceipt)(nil)
|
||||
|
||||
// NewReceipt converts a types.ReceiptForStorage to an EthReceipt IPLD node
|
||||
func NewReceipt(receipt *types.Receipt) (*EthReceipt, error) {
|
||||
receiptRLP, err := rlp.EncodeToBytes(receipt)
|
||||
rctRaw, err := receipt.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := RawdataToCid(MEthTxReceipt, receiptRLP, mh.KECCAK_256)
|
||||
c, err := RawdataToCid(MEthTxReceipt, rctRaw, mh.KECCAK_256)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EthReceipt{
|
||||
Receipt: receipt,
|
||||
cid: c,
|
||||
rawdata: receiptRLP,
|
||||
rawdata: rctRaw,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -66,8 +65,8 @@ func NewReceipt(receipt *types.Receipt) (*EthReceipt, error) {
|
||||
// DecodeEthReceipt takes a cid and its raw binary data
|
||||
// from IPFS and returns an EthTx object for further processing.
|
||||
func DecodeEthReceipt(c cid.Cid, b []byte) (*EthReceipt, error) {
|
||||
var r *types.Receipt
|
||||
if err := rlp.DecodeBytes(b, r); err != nil {
|
||||
r := new(types.Receipt)
|
||||
if err := r.UnmarshalBinary(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EthReceipt{
|
||||
|
||||
@@ -125,13 +125,13 @@ func newRctTrie() *rctTrie {
|
||||
// getNodes invokes the localTrie, which computes the root hash of the
|
||||
// transaction trie and returns its database keys, to return a slice
|
||||
// of EthRctTrie nodes.
|
||||
func (rt *rctTrie) getNodes() []*EthRctTrie {
|
||||
keys := rt.getKeys()
|
||||
var out []*EthRctTrie
|
||||
it := rt.trie.NodeIterator([]byte{})
|
||||
for it.Next(true) {
|
||||
|
||||
func (rt *rctTrie) getNodes() ([]*EthRctTrie, error) {
|
||||
keys, err := rt.getKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []*EthRctTrie
|
||||
|
||||
for _, k := range keys {
|
||||
rawdata, err := rt.db.Get(k)
|
||||
if err != nil {
|
||||
@@ -139,7 +139,7 @@ func (rt *rctTrie) getNodes() []*EthRctTrie {
|
||||
}
|
||||
c, err := RawdataToCid(MEthTxReceiptTrie, rawdata, multihash.KECCAK_256)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
tn := &TrieNode{
|
||||
cid: c,
|
||||
@@ -148,5 +148,5 @@ func (rt *rctTrie) getNodes() []*EthRctTrie {
|
||||
out = append(out, &EthRctTrie{TrieNode: tn})
|
||||
}
|
||||
|
||||
return out
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package ipld
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
@@ -39,6 +41,16 @@ var _ node.Node = (*EthStateTrie)(nil)
|
||||
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
|
||||
// state trie node to return it as an IPLD node for further processing.
|
||||
func FromStateTrieRLP(raw []byte) (*EthStateTrie, error) {
|
||||
|
||||
@@ -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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
@@ -37,6 +39,16 @@ var _ node.Node = (*EthStorageTrie)(nil)
|
||||
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
|
||||
// storage trie node to return it as an IPLD node for further processing.
|
||||
func FromStorageTrieRLP(raw []byte) (*EthStorageTrie, error) {
|
||||
|
||||
@@ -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,10 +20,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
@@ -46,18 +46,18 @@ var _ node.Node = (*EthTx)(nil)
|
||||
|
||||
// NewEthTx converts a *types.Transaction to an EthTx IPLD node
|
||||
func NewEthTx(tx *types.Transaction) (*EthTx, error) {
|
||||
txRLP, err := rlp.EncodeToBytes(tx)
|
||||
txRaw, err := tx.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := RawdataToCid(MEthTx, txRLP, mh.KECCAK_256)
|
||||
c, err := RawdataToCid(MEthTx, txRaw, mh.KECCAK_256)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EthTx{
|
||||
Transaction: tx,
|
||||
cid: c,
|
||||
rawdata: txRLP,
|
||||
rawdata: txRaw,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ func NewEthTx(tx *types.Transaction) (*EthTx, error) {
|
||||
// DecodeEthTx takes a cid and its raw binary data
|
||||
// from IPFS and returns an EthTx object for further processing.
|
||||
func DecodeEthTx(c cid.Cid, b []byte) (*EthTx, error) {
|
||||
var t *types.Transaction
|
||||
if err := rlp.DecodeBytes(b, t); err != nil {
|
||||
t := new(types.Transaction)
|
||||
if err := t.UnmarshalBinary(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EthTx{
|
||||
@@ -187,9 +187,30 @@ func (t *EthTx) Stat() (*node.NodeStat, error) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
// transaction trie and returns its database keys, to return a slice
|
||||
// of EthTxTrie nodes.
|
||||
func (tt *txTrie) getNodes() []*EthTxTrie {
|
||||
keys := tt.getKeys()
|
||||
var out []*EthTxTrie
|
||||
it := tt.trie.NodeIterator([]byte{})
|
||||
for it.Next(true) {
|
||||
|
||||
func (tt *txTrie) getNodes() ([]*EthTxTrie, error) {
|
||||
keys, err := tt.getKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []*EthTxTrie
|
||||
|
||||
for _, k := range keys {
|
||||
rawdata, err := tt.db.Get(k)
|
||||
if err != nil {
|
||||
@@ -139,7 +139,7 @@ func (tt *txTrie) getNodes() []*EthTxTrie {
|
||||
}
|
||||
c, err := RawdataToCid(MEthTxTrie, rawdata, multihash.KECCAK_256)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
tn := &TrieNode{
|
||||
cid: c,
|
||||
@@ -148,5 +148,5 @@ func (tt *txTrie) getNodes() []*EthTxTrie {
|
||||
out = append(out, &EthTxTrie{TrieNode: tn})
|
||||
}
|
||||
|
||||
return out
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ipfs/go-cid"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
// IPLD Codecs for Ethereum
|
||||
@@ -42,6 +45,10 @@ const (
|
||||
MEthStorageTrie = 0x98
|
||||
)
|
||||
|
||||
var (
|
||||
nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
)
|
||||
|
||||
// RawdataToCid takes the desired codec and a slice of bytes
|
||||
// and returns the proper cid of the object.
|
||||
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.
|
||||
// It contributes to the creation of the trie node objects.
|
||||
type localTrie struct {
|
||||
keys [][]byte
|
||||
db ethdb.Database
|
||||
trie *trie.Trie
|
||||
db ethdb.Database
|
||||
trieDB *trie.Database
|
||||
trie *trie.Trie
|
||||
}
|
||||
|
||||
// newLocalTrie initializes and returns a localTrie object
|
||||
@@ -92,7 +99,8 @@ func newLocalTrie() *localTrie {
|
||||
var err error
|
||||
lt := &localTrie{}
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
@@ -101,16 +109,12 @@ func newLocalTrie() *localTrie {
|
||||
|
||||
// add receives the index of an object and its rawdata value
|
||||
// and includes it into the localTrie
|
||||
func (lt *localTrie) add(idx int, rawdata []byte) {
|
||||
func (lt *localTrie) add(idx int, rawdata []byte) error {
|
||||
key, err := rlp.EncodeToBytes(uint(idx))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lt.keys = append(lt.keys, key)
|
||||
if err := lt.db.Put(key, rawdata); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lt.trie.Update(key, rawdata)
|
||||
return lt.trie.TryUpdate(key, rawdata)
|
||||
}
|
||||
|
||||
// rootHash returns the computed trie root.
|
||||
@@ -121,6 +125,35 @@ func (lt *localTrie) rootHash() []byte {
|
||||
|
||||
// getKeys returns the stored keys of the memory database
|
||||
// of the localTrie for further processing.
|
||||
func (lt *localTrie) getKeys() [][]byte {
|
||||
return lt.keys
|
||||
func (lt *localTrie) getKeys() ([][]byte, error) {
|
||||
// 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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
â FKˇd?¶fç_‹¦·YA( "a�î2–cUSyI
|
||||
@@ -0,0 +1,5 @@
|
||||
ù Úä�[G“(»o½Uå,ÔrBÇõSsµ^²€^âä©) 7ó7ì€.Ytâç5_ñ¤ƒ+9¸FÙÃYzFv Ú�b{¸ûî³à¨äõ(Û1Y¶«-í¤©÷Ê�*µ —f&HÕ‚•ÐЪK€UX� v• R€%IÙJ/ ÌÇïä³A?Ö¦lŸ@éU¯wFI¨Ùý!-jZ9Ý»g Öͳ.+Ö5î/ž¼”ݽ ±À�fbŽfzìW [‰ =É@æúpìNÐIÓ¥º ¨ùÀRRVíIŸ ¸B'Ô�öŠìÇr“šY¯©á¤«W�{i‹Û‰â›`DfŽ ý™ p¹JÎW䌿e¡j§pÆEùõﺇ»å�
|
||||
) áj|ΦtŠé␍é/Šï;=ÂH¥W¹¬N)i41?$÷üí_ B7<ô 0ÙMé
|
||||
#¸óŒík|¸¸’_î�*(¢Z _‰ÒAÿBˆd÷ˆ˜fHLïb-å:Fç•ßÞÃ61Ÿ u— fE&ÈǕ΢{‹rE\IeqàEURÛÀhź1 Õ¾�‰/Ú,XZ–˜Ž¥ïÍ:˜Ž
|
||||
†‚ iK7Å ÷°5.8ò١MQºêMÞáw tÈâ 5R3ÃÈ�În I¿n�ð¬¯Ðïømïî³VŽDÕ-"5Ï4
|
||||
á\`4â²A€
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
β ¤ξJN…>λb$Ιkg�Ί$α2ζΝ |Δι␍�κdΉ¥
|
||||
@@ -0,0 +1 @@
|
||||
�
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
�Q���� .ّ�ٍس�b�R�ّ��f��-�oّt6�فKي����� �ا؟�U�<��مZh�ة �hmx[�-#k�3حع������
|
||||
@@ -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}
|
||||
case branch:
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
@@ -31,6 +31,8 @@ type indexerMetricsHandles struct {
|
||||
transactions metrics.Counter
|
||||
// The total number of processed receipts
|
||||
receipts metrics.Counter
|
||||
// The total number of access list entries processed
|
||||
accessListEntries metrics.Counter
|
||||
// Time spent waiting for free postgres tx
|
||||
tFreePostgres metrics.Timer
|
||||
// Postgres transaction commit duration
|
||||
@@ -50,6 +52,7 @@ func RegisterIndexerMetrics(reg metrics.Registry) indexerMetricsHandles {
|
||||
blocks: metrics.NewCounter(),
|
||||
transactions: metrics.NewCounter(),
|
||||
receipts: metrics.NewCounter(),
|
||||
accessListEntries: metrics.NewCounter(),
|
||||
tFreePostgres: metrics.NewTimer(),
|
||||
tPostgresCommit: metrics.NewTimer(),
|
||||
tHeaderProcessing: metrics.NewTimer(),
|
||||
@@ -61,6 +64,7 @@ func RegisterIndexerMetrics(reg metrics.Registry) indexerMetricsHandles {
|
||||
reg.Register(metricName(subsys, "blocks"), ctx.blocks)
|
||||
reg.Register(metricName(subsys, "transactions"), ctx.transactions)
|
||||
reg.Register(metricName(subsys, "receipts"), ctx.receipts)
|
||||
reg.Register(metricName(subsys, "access_list_entries"), ctx.accessListEntries)
|
||||
reg.Register(metricName(subsys, "t_free_postgres"), ctx.tFreePostgres)
|
||||
reg.Register(metricName(subsys, "t_postgres_commit"), ctx.tPostgresCommit)
|
||||
reg.Register(metricName(subsys, "t_header_processing"), ctx.tHeaderProcessing)
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -38,7 +40,7 @@ import (
|
||||
// Test variables
|
||||
var (
|
||||
// block data
|
||||
BlockNumber = big.NewInt(1)
|
||||
BlockNumber = big.NewInt(12244001)
|
||||
MockHeader = types.Header{
|
||||
Time: 0,
|
||||
Number: new(big.Int).Set(BlockNumber),
|
||||
@@ -62,6 +64,7 @@ var (
|
||||
ExpectedPostStatus uint64 = 1
|
||||
ExpectedPostState1 = common.Bytes2Hex(common.HexToHash("0x1").Bytes())
|
||||
ExpectedPostState2 = common.Bytes2Hex(common.HexToHash("0x2").Bytes())
|
||||
ExpectedPostState3 = common.Bytes2Hex(common.HexToHash("0x3").Bytes())
|
||||
MockLog1 = &types.Log{
|
||||
Address: Address,
|
||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||
@@ -73,12 +76,32 @@ var (
|
||||
Data: []byte{},
|
||||
}
|
||||
|
||||
// access list entries
|
||||
AccessListEntry1 = types.AccessTuple{
|
||||
Address: Address,
|
||||
}
|
||||
AccessListEntry2 = types.AccessTuple{
|
||||
Address: AnotherAddress,
|
||||
StorageKeys: []common.Hash{common.BytesToHash(StorageLeafKey), common.BytesToHash(MockStorageLeafKey)},
|
||||
}
|
||||
AccessListEntry1Model = models.AccessListElementModel{
|
||||
Index: 0,
|
||||
Address: Address.Hex(),
|
||||
}
|
||||
AccessListEntry2Model = models.AccessListElementModel{
|
||||
Index: 1,
|
||||
Address: AnotherAddress.Hex(),
|
||||
StorageKeys: []string{common.BytesToHash(StorageLeafKey).Hex(), common.BytesToHash(MockStorageLeafKey).Hex()},
|
||||
}
|
||||
|
||||
// statediff data
|
||||
storageLocation = common.HexToHash("0")
|
||||
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
|
||||
StorageValue = common.Hex2Bytes("01")
|
||||
StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||
StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{
|
||||
storageLocation = common.HexToHash("0")
|
||||
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
|
||||
mockStorageLocation = common.HexToHash("1")
|
||||
MockStorageLeafKey = crypto.Keccak256Hash(mockStorageLocation[:]).Bytes()
|
||||
StorageValue = common.Hex2Bytes("01")
|
||||
StoragePartialPath = common.Hex2Bytes("20290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||
StorageLeafNode, _ = rlp.EncodeToBytes([]interface{}{
|
||||
StoragePartialPath,
|
||||
StorageValue,
|
||||
})
|
||||
@@ -140,13 +163,43 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
// AccessListTx is the data of EIP-2930 access list transactions.
|
||||
type AccessListTx struct {
|
||||
ChainID *big.Int // destination chain ID
|
||||
Nonce uint64 // nonce of sender account
|
||||
GasPrice *big.Int // wei per gas
|
||||
Gas uint64 // gas limit
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int // wei amount
|
||||
Data []byte // contract invocation input data
|
||||
AccessList AccessList // EIP-2930 access list
|
||||
V, R, S *big.Int // signature values
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// createTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
|
||||
func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common.Address) {
|
||||
// make transactions
|
||||
trx1 := types.NewTransaction(0, Address, big.NewInt(1000), 50, big.NewInt(100), []byte{})
|
||||
trx2 := types.NewTransaction(1, AnotherAddress, big.NewInt(2000), 100, big.NewInt(200), []byte{})
|
||||
trx3 := types.NewContractCreation(2, big.NewInt(1500), 75, big.NewInt(150), MockContractByteCode)
|
||||
transactionSigner := types.MakeSigner(params.MainnetChainConfig, new(big.Int).Set(BlockNumber))
|
||||
trx4 := types.NewTx(&types.AccessListTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
GasPrice: big.NewInt(100),
|
||||
Gas: 50,
|
||||
To: &AnotherAddress,
|
||||
Value: big.NewInt(1000),
|
||||
Data: []byte{},
|
||||
AccessList: types.AccessList{
|
||||
AccessListEntry1,
|
||||
AccessListEntry2,
|
||||
},
|
||||
})
|
||||
|
||||
transactionSigner := types.NewEIP2930Signer(params.MainnetChainConfig.ChainID)
|
||||
mockCurve := elliptic.P256()
|
||||
mockPrvKey, err := ecdsa.GenerateKey(mockCurve, rand.Reader)
|
||||
if err != nil {
|
||||
@@ -164,7 +217,12 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
||||
if err != nil {
|
||||
log.Crit(err.Error())
|
||||
}
|
||||
SenderAddr, err := types.Sender(transactionSigner, signedTrx1) // same for both trx
|
||||
signedTrx4, err := types.SignTx(trx4, transactionSigner, mockPrvKey)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
log.Crit(err.Error())
|
||||
}
|
||||
senderAddr, err := types.Sender(transactionSigner, signedTrx1) // same for both trx
|
||||
if err != nil {
|
||||
log.Crit(err.Error())
|
||||
}
|
||||
@@ -178,6 +236,14 @@ func createTransactionsAndReceipts() (types.Transactions, types.Receipts, common
|
||||
mockReceipt3 := types.NewReceipt(common.HexToHash("0x2").Bytes(), false, 75)
|
||||
mockReceipt3.Logs = []*types.Log{}
|
||||
mockReceipt3.TxHash = signedTrx3.Hash()
|
||||
mockReceipt4 := &types.Receipt{
|
||||
Type: types.AccessListTxType,
|
||||
PostState: common.HexToHash("0x3").Bytes(),
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
CumulativeGasUsed: 175,
|
||||
Logs: []*types.Log{},
|
||||
TxHash: signedTrx4.Hash(),
|
||||
}
|
||||
|
||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3}, SenderAddr
|
||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3, signedTrx4}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3, mockReceipt4}, senderAddr
|
||||
}
|
||||
|
||||
@@ -60,6 +60,16 @@ type TxModel struct {
|
||||
Dst string `db:"dst"`
|
||||
Src string `db:"src"`
|
||||
Data []byte `db:"tx_data"`
|
||||
Type *uint8 `db:"tx_type"`
|
||||
}
|
||||
|
||||
// AccessListEntryModel is the db model for eth.access_list_entry
|
||||
type AccessListElementModel struct {
|
||||
ID int64 `db:"id"`
|
||||
Index int64 `db:"index"`
|
||||
TxID int64 `db:"tx_id"`
|
||||
Address string `db:"address"`
|
||||
StorageKeys pq.StringArray `db:"storage_keys"`
|
||||
}
|
||||
|
||||
// ReceiptModel is the db model for eth.receipt_cids
|
||||
|
||||
@@ -69,10 +69,10 @@ func (in *PostgresCIDWriter) upsertUncleCID(tx *sqlx.Tx, uncle models.UncleModel
|
||||
|
||||
func (in *PostgresCIDWriter) upsertTransactionCID(tx *sqlx.Tx, transaction models.TxModel, headerID int64) (int64, error) {
|
||||
var txID int64
|
||||
err := tx.QueryRowx(`INSERT INTO eth.transaction_cids (header_id, tx_hash, cid, dst, src, index, mh_key, tx_data) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (header_id, tx_hash) DO UPDATE SET (cid, dst, src, index, mh_key, tx_data) = ($3, $4, $5, $6, $7, $8)
|
||||
err := tx.QueryRowx(`INSERT INTO eth.transaction_cids (header_id, tx_hash, cid, dst, src, index, mh_key, tx_data, tx_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
ON CONFLICT (header_id, tx_hash) DO UPDATE SET (cid, dst, src, index, mh_key, tx_data, tx_type) = ($3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING id`,
|
||||
headerID, transaction.TxHash, transaction.CID, transaction.Dst, transaction.Src, transaction.Index, transaction.MhKey, transaction.Data).Scan(&txID)
|
||||
headerID, transaction.TxHash, transaction.CID, transaction.Dst, transaction.Src, transaction.Index, transaction.MhKey, transaction.Data, transaction.Type).Scan(&txID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error upserting transaction_cids entry: %v", err)
|
||||
}
|
||||
@@ -80,6 +80,17 @@ func (in *PostgresCIDWriter) upsertTransactionCID(tx *sqlx.Tx, transaction model
|
||||
return txID, nil
|
||||
}
|
||||
|
||||
func (in *PostgresCIDWriter) upsertAccessListElement(tx *sqlx.Tx, accessListElement models.AccessListElementModel, txID int64) error {
|
||||
_, err := tx.Exec(`INSERT INTO eth.access_list_element (tx_id, index, address, storage_keys) VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (tx_id, index) DO UPDATE SET (address, storage_keys) = ($3, $4)`,
|
||||
txID, accessListElement.Index, accessListElement.Address, accessListElement.StorageKeys)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error upserting access_list_element entry: %v", err)
|
||||
}
|
||||
indexerMetrics.accessListEntries.Inc(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (in *PostgresCIDWriter) upsertReceiptCID(tx *sqlx.Tx, rct models.ReceiptModel, txID int64) error {
|
||||
_, err := tx.Exec(`INSERT INTO eth.receipt_cids (tx_id, cid, contract, contract_hash, topic0s, topic1s, topic2s, topic3s, log_contracts, mh_key, post_state, post_status) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
ON CONFLICT (tx_id) DO UPDATE SET (cid, contract, contract_hash, topic0s, topic1s, topic2s, topic3s, log_contracts, mh_key, post_state, post_status) = ($2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
|
||||
|
||||
Reference in New Issue
Block a user