diff --git a/cmd/pics/state.go b/cmd/pics/state.go
index 149a84d34cbf594457239c8a84eb7d7307799820..d59f66ffe0ad3adbba6d4db30e614de79dffd09a 100644
--- a/cmd/pics/state.go
+++ b/cmd/pics/state.go
@@ -584,7 +584,7 @@ func initialState1() error {
 		return err
 	}
 	var witness bytes.Buffer
-	if err = bwb.WriteTo(&witness); err != nil {
+	if _, err = bwb.WriteTo(&witness); err != nil {
 		return err
 	}
 	var witnessTrie *trie.Trie
diff --git a/cmd/state/state.go b/cmd/state/state.go
index 005ed3f8065c0f9d0c2bfc20f76663d1a98ac7de..6cf4d1d720fd444586a22e6261f218e5d89374f3 100644
--- a/cmd/state/state.go
+++ b/cmd/state/state.go
@@ -58,6 +58,7 @@ var preroot = flag.Bool("preroot", false, "Attempt to compute hash of the trie w
 var snapshotInterval = flag.Uint64("snapshotInterval", 0, "how often to take snapshots (0 - never, 1 - every block, 1000 - every 1000th block, etc)")
 var snapshotFrom = flag.Uint64("snapshotFrom", 0, "from which block to start snapshots")
 var witnessInterval = flag.Uint64("witnessInterval", 1, "after which block to extract witness (put a large number like 10000000 to disable)")
+var statsfile = flag.String("statsfile", "stateless.csv", "path where to write the stats file")
 
 func check(e error) {
 	if e != nil {
@@ -1681,26 +1682,11 @@ func main() {
 			return ethdb.NewBoltDatabase(path)
 		}
 
-		stateless(*chaindata, *statefile, *triesize, *preroot, *snapshotInterval, *snapshotFrom, *witnessInterval, createDb)
+		stateless(*chaindata, *statefile, *triesize, *preroot, *snapshotInterval, *snapshotFrom, *witnessInterval, *statsfile, createDb)
 	}
 	if *action == "stateless_chart" {
-		stateless_chart_key_values("/Users/alexeyakhunov/mygit/go-ethereum/st_1/stateless.csv", []int{21, 20, 19, 18}, "breakdown.png", 2800000, 1)
-	}
-	//stateless_chart_key_values("stateless1.csv", []int{17}, "total.png", 1, 0)
-	//stateless_chart_key_values("stateless1_256.csv", []int{17}, "total256.png", 1, 0)
-	//stateless_chart_key_values([]int{17}, "total_2675000.png", 2675000, 0)
-	//stateless_chart_key_values("stateless1.csv", []int{12, 15, 16}, "breakdown.png", 1, 1)
-	//stateless_chart_key_values("stateless1_256.csv", []int{12, 15, 16}, "breakdown256.png", 1, 1)
-	//stateless_chart_key_values([]int{12, 15, 16}, "breakdown_2675000.png", 2675000, 1)
-	//stateless_chart_key_values("stateless1.csv", []int{13,14}, "key_vals.png", 1, 4)
-	//stateless_chart_key_values("stateless1_256.csv", []int{13,14}, "key_vals256.png", 1, 4)
-	//stateless_chart_key_values("stateless1.csv", []int{10,11}, "c_key_vals.png", 1, 4)
-	//stateless_chart_key_values("stateless1_256.csv", []int{10,11}, "c_key_vals256.png", 1, 4)
-	//stateless_chart_key_values("stateless1.csv", []int{6, 7}, "mask_hash.png", 1, 4)
-	//stateless_chart_key_values("stateless1_256.csv", []int{6, 7}, "mask_hash256.png", 1, 4)
-	//stateless_chart_key_values("stateless1.csv", []int{1, 2}, "c_mask_hash.png", 1, 4)
-	//stateless_chart_key_values("stateless1_256.csv", []int{1, 2}, "c_mask_hash256.png", 1, 4)
-	//stateless_chart_key_values([]int{12}, "codes_28m.png", 2800000)
+		statelessDoKVChart(*statsfile, []int{21, 20, 19, 18}, fmt.Sprintf("%s-chart.png", *statsfile), 2800000, 1)
+	}
 	if *action == "stateSnapshot" {
 		if err := stateSnapshot(); err != nil {
 			fmt.Printf("Error: %v\n", err)
diff --git a/cmd/state/stateless.go b/cmd/state/stateless.go
index 90d1458b148d5a249a1bccc9fbe02871569c88c6..00a2dcf5a0fc15dedab9a2357b4e90746f5e9b0b 100644
--- a/cmd/state/stateless.go
+++ b/cmd/state/stateless.go
@@ -1,12 +1,8 @@
 package main
 
 import (
-	"bufio"
-	"bytes"
 	"context"
-	"encoding/csv"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/signal"
 	"syscall"
@@ -24,6 +20,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
+	"github.com/ledgerwatch/turbo-geth/trie"
 )
 
 var chartColors = []drawing.Color{
@@ -71,41 +68,6 @@ func runBlock(dbstate *state.Stateless, chainConfig *params.ChainConfig,
 	return nil
 }
 
-/*
-func writeStats(w io.Writer, blockNum uint64, blockProof trie.BlockProof) {
-	var totalCShorts, totalCValues, totalCodes, totalShorts, totalValues int
-	for _, short := range blockProof.CShortKeys {
-		l := len(short)
-		if short[l-1] == 16 {
-			l -= 1
-		}
-		l = l/2 + 1
-		totalCShorts += l
-	}
-	for _, value := range blockProof.CValues {
-		totalCValues += len(value)
-	}
-	for _, code := range blockProof.Codes {
-		totalCodes += len(code)
-	}
-	for _, short := range blockProof.ShortKeys {
-		l := len(short)
-		if short[l-1] == 16 {
-			l -= 1
-		}
-		l = l/2 + 1
-		totalShorts += l
-	}
-	for _, value := range blockProof.Values {
-		totalValues += len(value)
-	}
-	fmt.Fprintf(w, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
-		blockNum, len(blockProof.Contracts), len(blockProof.CMasks), len(blockProof.CHashes), len(blockProof.CShortKeys), len(blockProof.CValues), len(blockProof.Codes),
-		len(blockProof.Masks), len(blockProof.Hashes), len(blockProof.ShortKeys), len(blockProof.Values), totalCShorts, totalCValues, totalCodes, totalShorts, totalValues,
-	)
-}
-*/
-
 type CreateDbFunc func(string) (ethdb.Database, error)
 
 func stateless(chaindata string,
@@ -115,6 +77,7 @@ func stateless(chaindata string,
 	interval uint64,
 	ignoreOlderThan uint64,
 	witnessThreshold uint64,
+	statsfile string,
 	createDb CreateDbFunc) {
 
 	state.MaxTrieCacheGen = uint32(triesize)
@@ -132,11 +95,11 @@ func stateless(chaindata string,
 	check(err)
 	defer ethDb.Close()
 	chainConfig := params.MainnetChainConfig
-	slFile, err := os.OpenFile("stateless.csv", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
+
+	stats, err := NewStatsFile(statsfile)
 	check(err)
-	defer slFile.Close()
-	w := bufio.NewWriter(slFile)
-	defer w.Flush()
+	defer stats.Close()
+
 	vmConfig := vm.Config{}
 	engine := ethash.NewFullFaker()
 	bcb, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil)
@@ -223,11 +186,15 @@ func stateless(chaindata string,
 		witness = nil
 		if blockNum >= witnessThreshold {
 			// Witness has to be extracted before the state trie is modified
-			witness, err = tds.ExtractWitness(trace)
+			var tapeStats trie.WitnessTapeStats
+			witness, tapeStats, err = tds.ExtractWitness(trace)
 			if err != nil {
 				fmt.Printf("error extracting witness for block %d: %v\n", blockNum, err)
 				return
 			}
+
+			err = stats.AddRow(blockNum, witness, tapeStats)
+			check(err)
 		}
 		finalRootFail := false
 		if blockNum >= witnessThreshold && witness != nil { // witness == nil means the extraction fails
@@ -336,178 +303,3 @@ func stateless(chaindata string,
 	fmt.Printf("Next time specify -block %d\n", blockNum)
 	fmt.Printf("Stateless client analysis took %s\n", time.Since(startTime))
 }
-
-func stateless_chart_key_values(filename string, right []int, chartFileName string, start int, startColor int) {
-	file, err := os.Open(filename)
-	check(err)
-	defer file.Close()
-	reader := csv.NewReader(bufio.NewReader(file))
-	var blocks []float64
-	var vals [22][]float64
-	count := 0
-	for records, _ := reader.Read(); len(records) == 16; records, _ = reader.Read() {
-		count++
-		if count < start {
-			continue
-		}
-		blocks = append(blocks, parseFloat64(records[0])/1000000.0)
-		for i := 0; i < 22; i++ {
-			cProofs := 4.0*parseFloat64(records[2]) + 32.0*parseFloat64(records[3]) + parseFloat64(records[11]) + parseFloat64(records[12])
-			proofs := 4.0*parseFloat64(records[7]) + 32.0*parseFloat64(records[8]) + parseFloat64(records[14]) + parseFloat64(records[15])
-			switch i {
-			case 1, 6:
-				vals[i] = append(vals[i], 4.0*parseFloat64(records[i+1]))
-			case 2, 7:
-				vals[i] = append(vals[i], 32.0*parseFloat64(records[i+1]))
-			case 15:
-				vals[i] = append(vals[i], cProofs)
-			case 16:
-				vals[i] = append(vals[i], proofs)
-			case 17:
-				vals[i] = append(vals[i], cProofs+proofs+parseFloat64(records[13]))
-			case 18:
-				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7]))
-			case 19:
-				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
-					parseFloat64(records[3])+parseFloat64(records[4])+parseFloat64(records[10])+parseFloat64(records[11]))
-			case 20:
-				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
-					parseFloat64(records[4])+parseFloat64(records[5])+parseFloat64(records[10])+parseFloat64(records[11])+
-					32.0*parseFloat64(records[3])+32.0*parseFloat64(records[8]))
-			case 21:
-				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
-					parseFloat64(records[4])+parseFloat64(records[5])+parseFloat64(records[10])+parseFloat64(records[11])+
-					32.0*parseFloat64(records[3])+32.0*parseFloat64(records[8])+parseFloat64(records[13]))
-			default:
-				vals[i] = append(vals[i], parseFloat64(records[i+1]))
-			}
-		}
-	}
-	var windowSums [22]float64
-	var window int = 1024
-	var movingAvgs [22][]float64
-	for i := 0; i < 22; i++ {
-		movingAvgs[i] = make([]float64, len(blocks)-(window-1))
-	}
-	for j := 0; j < len(blocks); j++ {
-		for i := 0; i < 22; i++ {
-			windowSums[i] += vals[i][j]
-		}
-		if j >= window {
-			for i := 0; i < 22; i++ {
-				windowSums[i] -= vals[i][j-window]
-			}
-		}
-		if j >= window-1 {
-			for i := 0; i < 22; i++ {
-				movingAvgs[i][j-window+1] = windowSums[i] / float64(window)
-			}
-		}
-	}
-	movingBlock := blocks[window-1:]
-	seriesNames := [22]string{
-		"Number of contracts",
-		"Contract masks",
-		"Contract hashes",
-		"Number of contract leaf keys",
-		"Number of contract leaf vals",
-		"Number of contract codes",
-		"Masks",
-		"Hashes",
-		"Number of leaf keys",
-		"Number of leaf values",
-		"Total size of contract leaf keys",
-		"Total size of contract leaf vals",
-		"Total size of codes",
-		"Total size of leaf keys",
-		"Total size of leaf vals",
-		"Block proofs (contracts only)",
-		"Block proofs (without contracts)",
-		"Block proofs (total)",
-		"Structure (total)",
-		"Leaves (total)",
-		"Hashes (total)",
-		"Code (total)",
-	}
-	var currentColor int = startColor
-	var series []chart.Series
-	for _, r := range right {
-		s := &chart.ContinuousSeries{
-			Name: seriesNames[r],
-			Style: chart.Style{
-				Show:        true,
-				StrokeWidth: 0.0,
-				StrokeColor: chartColors[currentColor],
-				FillColor:   chartColors[currentColor],
-				//FillColor:   chartColors[currentColor].WithAlpha(100),
-			},
-			XValues: movingBlock,
-			YValues: movingAvgs[r],
-		}
-		currentColor++
-		series = append(series, s)
-	}
-
-	graph1 := chart.Chart{
-		Width:  1280,
-		Height: 720,
-		Background: chart.Style{
-			Padding: chart.Box{
-				Top: 50,
-			},
-		},
-		YAxis: chart.YAxis{
-			Name:      "kBytes",
-			NameStyle: chart.StyleShow(),
-			Style:     chart.StyleShow(),
-			TickStyle: chart.Style{
-				TextRotationDegrees: 45.0,
-			},
-			ValueFormatter: func(v interface{}) string {
-				return fmt.Sprintf("%d kB", int(v.(float64)/1024.0))
-			},
-			GridMajorStyle: chart.Style{
-				Show:        true,
-				StrokeColor: chart.ColorBlack,
-				StrokeWidth: 1.0,
-			},
-			//GridLines: days(),
-		},
-		/*
-			YAxisSecondary: chart.YAxis{
-				NameStyle: chart.StyleShow(),
-				Style: chart.StyleShow(),
-				TickStyle: chart.Style{
-					TextRotationDegrees: 45.0,
-				},
-				ValueFormatter: func(v interface{}) string {
-					return fmt.Sprintf("%d", int(v.(float64)))
-				},
-			},
-		*/
-		XAxis: chart.XAxis{
-			Name: "Blocks, million",
-			Style: chart.Style{
-				Show: true,
-			},
-			ValueFormatter: func(v interface{}) string {
-				return fmt.Sprintf("%.3fm", v.(float64))
-			},
-			GridMajorStyle: chart.Style{
-				Show:        true,
-				StrokeColor: chart.ColorAlternateGray,
-				StrokeWidth: 1.0,
-			},
-			//GridLines: blockMillions(),
-		},
-		Series: series,
-	}
-
-	graph1.Elements = []chart.Renderable{chart.LegendThin(&graph1)}
-
-	buffer := bytes.NewBuffer([]byte{})
-	err = graph1.Render(chart.PNG, buffer)
-	check(err)
-	err = ioutil.WriteFile(chartFileName, buffer.Bytes(), 0644)
-	check(err)
-}
diff --git a/cmd/state/stateless_chart.go b/cmd/state/stateless_chart.go
new file mode 100644
index 0000000000000000000000000000000000000000..b4a942e255b3c209b40e3d9c11b0ec8df08b67bb
--- /dev/null
+++ b/cmd/state/stateless_chart.go
@@ -0,0 +1,187 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/csv"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	chart "github.com/wcharczuk/go-chart"
+)
+
+func statelessDoKVChart(filename string, right []int, chartFileName string, start int, startColor int) {
+	file, err := os.Open(filename)
+	check(err)
+	defer file.Close()
+	reader := csv.NewReader(bufio.NewReader(file))
+	var blocks []float64
+	var vals [22][]float64
+	count := 0
+	for records, _ := reader.Read(); len(records) == 16; records, _ = reader.Read() {
+		count++
+		if count < start {
+			continue
+		}
+		blocks = append(blocks, parseFloat64(records[0])/1000000.0)
+		for i := 0; i < 22; i++ {
+			cProofs := 4.0*parseFloat64(records[2]) + 32.0*parseFloat64(records[3]) + parseFloat64(records[11]) + parseFloat64(records[12])
+			proofs := 4.0*parseFloat64(records[7]) + 32.0*parseFloat64(records[8]) + parseFloat64(records[14]) + parseFloat64(records[15])
+			switch i {
+			case 1, 6:
+				vals[i] = append(vals[i], 4.0*parseFloat64(records[i+1]))
+			case 2, 7:
+				vals[i] = append(vals[i], 32.0*parseFloat64(records[i+1]))
+			case 15:
+				vals[i] = append(vals[i], cProofs)
+			case 16:
+				vals[i] = append(vals[i], proofs)
+			case 17:
+				vals[i] = append(vals[i], cProofs+proofs+parseFloat64(records[13]))
+			case 18:
+				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7]))
+			case 19:
+				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
+					parseFloat64(records[3])+parseFloat64(records[4])+parseFloat64(records[10])+parseFloat64(records[11]))
+			case 20:
+				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
+					parseFloat64(records[4])+parseFloat64(records[5])+parseFloat64(records[10])+parseFloat64(records[11])+
+					32.0*parseFloat64(records[3])+32.0*parseFloat64(records[8]))
+			case 21:
+				vals[i] = append(vals[i], 4.0*parseFloat64(records[2])+4.0*parseFloat64(records[7])+
+					parseFloat64(records[4])+parseFloat64(records[5])+parseFloat64(records[10])+parseFloat64(records[11])+
+					32.0*parseFloat64(records[3])+32.0*parseFloat64(records[8])+parseFloat64(records[13]))
+			default:
+				vals[i] = append(vals[i], parseFloat64(records[i+1]))
+			}
+		}
+	}
+	var windowSums [22]float64
+	window := 1024
+	var movingAvgs [22][]float64
+	for i := 0; i < 22; i++ {
+		movingAvgs[i] = make([]float64, len(blocks)-(window-1))
+	}
+	for j := 0; j < len(blocks); j++ {
+		for i := 0; i < 22; i++ {
+			windowSums[i] += vals[i][j]
+		}
+		if j >= window {
+			for i := 0; i < 22; i++ {
+				windowSums[i] -= vals[i][j-window]
+			}
+		}
+		if j >= window-1 {
+			for i := 0; i < 22; i++ {
+				movingAvgs[i][j-window+1] = windowSums[i] / float64(window)
+			}
+		}
+	}
+	movingBlock := blocks[window-1:]
+	seriesNames := [22]string{
+		"Number of contracts",
+		"Contract masks",
+		"Contract hashes",
+		"Number of contract leaf keys",
+		"Number of contract leaf vals",
+		"Number of contract codes",
+		"Masks",
+		"Hashes",
+		"Number of leaf keys",
+		"Number of leaf values",
+		"Total size of contract leaf keys",
+		"Total size of contract leaf vals",
+		"Total size of codes",
+		"Total size of leaf keys",
+		"Total size of leaf vals",
+		"Block proofs (contracts only)",
+		"Block proofs (without contracts)",
+		"Block proofs (total)",
+		"Structure (total)",
+		"Leaves (total)",
+		"Hashes (total)",
+		"Code (total)",
+	}
+	currentColor := startColor
+	series := make([]chart.Series, len(right))
+	for i, r := range right {
+		s := &chart.ContinuousSeries{
+			Name: seriesNames[r],
+			Style: chart.Style{
+				Show:        true,
+				StrokeWidth: 0.0,
+				StrokeColor: chartColors[currentColor],
+				FillColor:   chartColors[currentColor],
+				//FillColor:   chartColors[currentColor].WithAlpha(100),
+			},
+			XValues: movingBlock,
+			YValues: movingAvgs[r],
+		}
+		currentColor++
+		series[i] = s
+	}
+
+	graph1 := chart.Chart{
+		Width:  1280,
+		Height: 720,
+		Background: chart.Style{
+			Padding: chart.Box{
+				Top: 50,
+			},
+		},
+		YAxis: chart.YAxis{
+			Name:      "kBytes",
+			NameStyle: chart.StyleShow(),
+			Style:     chart.StyleShow(),
+			TickStyle: chart.Style{
+				TextRotationDegrees: 45.0,
+			},
+			ValueFormatter: func(v interface{}) string {
+				return fmt.Sprintf("%d kB", int(v.(float64)/1024.0))
+			},
+			GridMajorStyle: chart.Style{
+				Show:        true,
+				StrokeColor: chart.ColorBlack,
+				StrokeWidth: 1.0,
+			},
+			//GridLines: days(),
+		},
+		/*
+			YAxisSecondary: chart.YAxis{
+				NameStyle: chart.StyleShow(),
+				Style: chart.StyleShow(),
+				TickStyle: chart.Style{
+					TextRotationDegrees: 45.0,
+				},
+				ValueFormatter: func(v interface{}) string {
+					return fmt.Sprintf("%d", int(v.(float64)))
+				},
+			},
+		*/
+		XAxis: chart.XAxis{
+			Name: "Blocks, million",
+			Style: chart.Style{
+				Show: true,
+			},
+			ValueFormatter: func(v interface{}) string {
+				return fmt.Sprintf("%.3fm", v.(float64))
+			},
+			GridMajorStyle: chart.Style{
+				Show:        true,
+				StrokeColor: chart.ColorAlternateGray,
+				StrokeWidth: 1.0,
+			},
+			//GridLines: blockMillions(),
+		},
+		Series: series,
+	}
+
+	graph1.Elements = []chart.Renderable{chart.LegendThin(&graph1)}
+
+	buffer := bytes.NewBuffer([]byte{})
+	err = graph1.Render(chart.PNG, buffer)
+	check(err)
+	err = ioutil.WriteFile(chartFileName, buffer.Bytes(), 0644)
+	check(err)
+}
diff --git a/cmd/state/stateless_stats.go b/cmd/state/stateless_stats.go
new file mode 100644
index 0000000000000000000000000000000000000000..92e049850fa35a44dfa679dad93e30a14b9c83fd
--- /dev/null
+++ b/cmd/state/stateless_stats.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+	"encoding/csv"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/ledgerwatch/turbo-geth/trie"
+)
+
+var keys = []string{"balances", "codes", "hashes", "keys", "nonces", "structure", "values"}
+
+type StatsFile struct {
+	file      io.WriteCloser
+	buffer    *csv.Writer
+	hasHeader bool
+}
+
+func NewStatsFile(path string) (*StatsFile, error) {
+	_, err := os.Stat(path)
+	appending := err == nil || !os.IsNotExist(err)
+
+	w, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
+	if err != nil {
+		return nil, err
+	}
+
+	return &StatsFile{file: w, buffer: csv.NewWriter(w), hasHeader: appending}, nil
+}
+
+func (s *StatsFile) writeHeader() error {
+	return s.buffer.Write(
+		append([]string{"block-number", "witness"}, keys...),
+	)
+}
+
+func (s *StatsFile) AddRow(blockNum uint64, witness []byte, tapeStats trie.WitnessTapeStats) error {
+	if !s.hasHeader {
+		fmt.Println("writing header")
+		if err := s.writeHeader(); err != nil {
+			return err
+		}
+		s.hasHeader = true
+	}
+	fields := make([]string, 2+len(keys))
+	fieldIndex := 0
+
+	fields[fieldIndex] = stringify(blockNum)
+	fieldIndex++
+
+	fields[fieldIndex] = stringify(uint64(len(witness)))
+	fieldIndex++
+
+	for _, key := range keys {
+		fields[fieldIndex] = stringify(uint64(tapeStats.GetOrZero(key)))
+		fieldIndex++
+	}
+
+	return s.buffer.Write(fields)
+}
+
+func (s *StatsFile) Close() error {
+	s.buffer.Flush()
+	if err := s.buffer.Error(); err != nil {
+		return err
+	}
+	return s.file.Close()
+}
+
+func stringify(v uint64) string {
+	return fmt.Sprintf("%d", v)
+}
diff --git a/core/state/database.go b/core/state/database.go
index f2e9d010a3f80ac8ab3142cb130d2a89db8411f1..3a647da622ce8a48610549319e6884fd28bf56dc 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -1131,7 +1131,7 @@ func (dsw *DbStateWriter) WriteAccountStorage(ctx context.Context, address commo
 }
 
 // ExtractWitness produces block witness for the block just been processed, in a serialised form
-func (tds *TrieDbState) ExtractWitness(trace bool) ([]byte, error) {
+func (tds *TrieDbState) ExtractWitness(trace bool) ([]byte, trie.WitnessTapeStats, error) {
 	bwb := trie.NewBlockWitnessBuilder(trace)
 	rs := trie.NewResolveSet(0)
 	touches, storageTouches := tds.pg.ExtractTouches()
@@ -1143,13 +1143,17 @@ func (tds *TrieDbState) ExtractWitness(trace bool) ([]byte, error) {
 	}
 	codeMap := tds.pg.ExtractCodeMap()
 	if err := bwb.MakeBlockWitness(tds.t, rs, codeMap); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 	var b bytes.Buffer
-	if err := bwb.WriteTo(&b); err != nil {
-		return nil, err
+
+	stats, err := bwb.WriteTo(&b)
+
+	if err != nil {
+		return nil, nil, err
 	}
-	return b.Bytes(), nil
+
+	return b.Bytes(), stats, nil
 }
 
 func (tsw *TrieStateWriter) CreateContract(address common.Address) error {
diff --git a/trie/proof_generator.go b/trie/proof_generator.go
index 095479bd04e7f0c3ec1b109b4aba8320cff6123b..d6116519d83b0f756bd6e8fe171b49d4e6aaf3c4 100644
--- a/trie/proof_generator.go
+++ b/trie/proof_generator.go
@@ -28,6 +28,12 @@ import (
 	"github.com/ugorji/go/codec"
 )
 
+type WitnessTapeStats map[string]int
+
+func (s WitnessTapeStats) GetOrZero(key string) int {
+	return s[key]
+}
+
 // TapeBuilder stores the sequence of values that is getting serialised using CBOR into a byte buffer
 type TapeBuilder struct {
 	buffer  bytes.Buffer     // Byte buffer where the CBOR-encoded values end up being written
@@ -445,7 +451,8 @@ func (bwb *BlockWitnessBuilder) makeBlockWitness(
 
 // WriteTo creates serialised representation of the block witness
 // and writes it into the given writer
-func (bwb *BlockWitnessBuilder) WriteTo(w io.Writer) error {
+// returns stats (tape lengths) and stuff
+func (bwb *BlockWitnessBuilder) WriteTo(w io.Writer) (WitnessTapeStats, error) {
 	// Calculate the lengths of all the tapes and write them as an array
 	var lens = map[string]int{
 		KeyTape:       bwb.Keys.buffer.Len(),
@@ -460,30 +467,30 @@ func (bwb *BlockWitnessBuilder) WriteTo(w io.Writer) error {
 	handle.EncodeOptions.Canonical = true
 	encoder := codec.NewEncoder(w, &handle)
 	if err := encoder.Encode(&lens); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Keys.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Values.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Nonces.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Balances.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Hashes.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Codes.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
 	if _, err := bwb.Structure.buffer.WriteTo(w); err != nil {
-		return err
+		return nil, err
 	}
-	return nil
+	return WitnessTapeStats(lens), nil
 }
 
 // CborBytesTape implements BytesTape and takes values from CBOR-encoded slice of bytes
diff --git a/trie/proof_generator_test.go b/trie/proof_generator_test.go
index ee84b838d7bcc051708c293339a92922bd617ad8..427f4a6ffc972b40c0a1421a138adfe443d5d941 100644
--- a/trie/proof_generator_test.go
+++ b/trie/proof_generator_test.go
@@ -179,7 +179,7 @@ func TestSerialiseBlockWitness(t *testing.T) {
 		t.Errorf("Could not make block witness: %v", err)
 	}
 	var b bytes.Buffer
-	if err := bwb.WriteTo(&b); err != nil {
+	if _, err := bwb.WriteTo(&b); err != nil {
 		t.Errorf("Could not make block witness: %v", err)
 	}