diff --git a/core/rlp_test.go b/core/rlp_test.go
new file mode 100644
index 0000000000..5171981aad
--- /dev/null
+++ b/core/rlp_test.go
@@ -0,0 +1,194 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+package core
+
+import (
+ "fmt"
+ "golang.org/x/crypto/sha3"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func getBlock(transactions int, uncles int, dataSize int) *types.Block {
+ var (
+ aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
+ // Generate a canonical chain to act as the main dataset
+ engine = ethash.NewFaker()
+ db = rawdb.NewMemoryDatabase()
+ // A sender who makes transactions, has some funds
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ funds = big.NewInt(1000000000)
+ gspec = &Genesis{
+ Config: params.TestChainConfig,
+ Alloc: GenesisAlloc{address: {Balance: funds}},
+ }
+ genesis = gspec.MustCommit(db)
+ )
+
+ // We need to generate as many blocks +1 as uncles
+ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, uncles+1,
+ func(n int, b *BlockGen) {
+ if n == uncles {
+ // Add transactions and stuff on the last block
+ for i := 0; i < transactions; i++ {
+ tx, _ := types.SignTx(types.NewTransaction(uint64(i), aa,
+ big.NewInt(0), 50000, big.NewInt(1), make([]byte, dataSize)), types.HomesteadSigner{}, key)
+ b.AddTx(tx)
+ }
+ for i := 0; i < uncles; i++ {
+ b.AddUncle(&types.Header{ParentHash: b.PrevBlock(n - 1 - i).Hash(), Number: big.NewInt(int64(n - i))})
+ }
+ }
+ })
+ block := blocks[len(blocks)-1]
+ return block
+}
+
+// TestRlpIterator tests that individual transactions can be picked out
+// from blocks without full unmarshalling/marshalling
+func TestRlpIterator(t *testing.T) {
+ for _, tt := range []struct {
+ txs int
+ uncles int
+ datasize int
+ }{
+ {0, 0, 0},
+ {0, 2, 0},
+ {10, 0, 0},
+ {10, 2, 0},
+ {10, 2, 50},
+ } {
+ testRlpIterator(t, tt.txs, tt.uncles, tt.datasize)
+ }
+}
+
+func testRlpIterator(t *testing.T, txs, uncles, datasize int) {
+ desc := fmt.Sprintf("%d txs [%d datasize] and %d uncles", txs, datasize, uncles)
+ bodyRlp, _ := rlp.EncodeToBytes(getBlock(txs, uncles, datasize).Body())
+ it, err := rlp.NewListIterator(bodyRlp)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Check that txs exist
+ if !it.Next() {
+ t.Fatal("expected two elems, got zero")
+ }
+ txdata := it.Value()
+ // Check that uncles exist
+ if !it.Next() {
+ t.Fatal("expected two elems, got one")
+ }
+ // No more after that
+ if it.Next() {
+ t.Fatal("expected only two elems, got more")
+ }
+ txIt, err := rlp.NewListIterator(txdata)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var gotHashes []common.Hash
+ var expHashes []common.Hash
+ for txIt.Next() {
+ gotHashes = append(gotHashes, crypto.Keccak256Hash(txIt.Value()))
+ }
+
+ var expBody types.Body
+ err = rlp.DecodeBytes(bodyRlp, &expBody)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, tx := range expBody.Transactions {
+ expHashes = append(expHashes, tx.Hash())
+ }
+ if gotLen, expLen := len(gotHashes), len(expHashes); gotLen != expLen {
+ t.Fatalf("testcase %v: length wrong, got %d exp %d", desc, gotLen, expLen)
+ }
+ // also sanity check against input
+ if gotLen := len(gotHashes); gotLen != txs {
+ t.Fatalf("testcase %v: length wrong, got %d exp %d", desc, gotLen, txs)
+ }
+ for i, got := range gotHashes {
+ if exp := expHashes[i]; got != exp {
+ t.Errorf("testcase %v: hash wrong, got %x, exp %x", desc, got, exp)
+ }
+ }
+}
+
+// BenchmarkHashing compares the speeds of hashing a rlp raw data directly
+// without the unmarshalling/marshalling step
+func BenchmarkHashing(b *testing.B) {
+ // Make a pretty fat block
+ var (
+ bodyRlp []byte
+ blockRlp []byte
+ )
+ {
+ block := getBlock(200, 2, 50)
+ bodyRlp, _ = rlp.EncodeToBytes(block.Body())
+ blockRlp, _ = rlp.EncodeToBytes(block)
+ }
+ var got common.Hash
+ var hasher = sha3.NewLegacyKeccak256()
+ b.Run("iteratorhashing", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var hash common.Hash
+ it, err := rlp.NewListIterator(bodyRlp)
+ it.Next()
+ txs := it.Value()
+ txIt, err := rlp.NewListIterator(txs)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for txIt.Next() {
+ hasher.Reset()
+ hasher.Write(txIt.Value())
+ hasher.Sum(hash[:0])
+ got = hash
+ }
+ }
+ })
+ var exp common.Hash
+ b.Run("fullbodyhashing", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var body types.Body
+ rlp.DecodeBytes(bodyRlp, &body)
+ for _, tx := range body.Transactions {
+ exp = tx.Hash()
+ }
+ }
+ })
+ b.Run("fullblockhashing", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ var block types.Block
+ rlp.DecodeBytes(blockRlp, &block)
+ for _, tx := range block.Transactions() {
+ tx.Hash()
+ }
+ }
+ })
+ if got != exp {
+ b.Fatalf("hash wrong, got %x exp %x", got, exp)
+ }
+}
diff --git a/rlp/iterator.go b/rlp/iterator.go
new file mode 100644
index 0000000000..c28866dbc1
--- /dev/null
+++ b/rlp/iterator.go
@@ -0,0 +1,60 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rlp
+
+type listIterator struct {
+ data []byte
+ next []byte
+ err error
+}
+
+// NewListIterator creates an iterator for the (list) represented by data
+func NewListIterator(data RawValue) (*listIterator, error) {
+ k, t, c, err := readKind(data)
+ if err != nil {
+ return nil, err
+ }
+ if k != List {
+ return nil, ErrExpectedList
+ }
+ it := &listIterator{
+ data: data[t : t+c],
+ }
+ return it, nil
+
+}
+
+// Next forwards the iterator one step, returns true if it was not at end yet
+func (it *listIterator) Next() bool {
+ if len(it.data) == 0 {
+ return false
+ }
+ _, t, c, err := readKind(it.data)
+ it.next = it.data[:t+c]
+ it.data = it.data[t+c:]
+ it.err = err
+ return true
+}
+
+// Value returns the current value
+func (it *listIterator) Value() []byte {
+ return it.next
+}
+
+func (it *listIterator) Err() error {
+ return it.err
+}
diff --git a/rlp/iterator_test.go b/rlp/iterator_test.go
new file mode 100644
index 0000000000..53c3819186
--- /dev/null
+++ b/rlp/iterator_test.go
@@ -0,0 +1,59 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rlp
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+// TestIterator tests some basic things about the ListIterator. A more
+// comprehensive test can be found in core/rlp_test.go, where we can
+// use both types and rlp without dependency cycles
+func TestIterator(t *testing.T) {
+ bodyRlpHex := "0xf902cbf8d6f869800182c35094000000000000000000000000000000000000aaaa808a000000000000000000001ba01025c66fad28b4ce3370222624d952c35529e602af7cbe04f667371f61b0e3b3a00ab8813514d1217059748fd903288ace1b4001a4bc5fbde2790debdc8167de2ff869010182c35094000000000000000000000000000000000000aaaa808a000000000000000000001ca05ac4cf1d19be06f3742c21df6c49a7e929ceb3dbaf6a09f3cfb56ff6828bd9a7a06875970133a35e63ac06d360aa166d228cc013e9b96e0a2cae7f55b22e1ee2e8f901f0f901eda0c75448377c0e426b8017b23c5f77379ecf69abc1d5c224284ad3ba1c46c59adaa00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
+ bodyRlp := hexutil.MustDecode(bodyRlpHex)
+
+ it, err := NewListIterator(bodyRlp)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Check that txs exist
+ if !it.Next() {
+ t.Fatal("expected two elems, got zero")
+ }
+ txs := it.Value()
+ // Check that uncles exist
+ if !it.Next() {
+ t.Fatal("expected two elems, got one")
+ }
+ txit, err := NewListIterator(txs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var i = 0
+ for txit.Next() {
+ if txit.err != nil {
+ t.Fatal(txit.err)
+ }
+ i++
+ }
+ if exp := 2; i != exp {
+ t.Errorf("count wrong, expected %d got %d", i, exp)
+ }
+}