// 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 <http://www.gnu.org/licenses/>.

package statediff_test

import (
	"encoding/hex"
	"testing"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/holiman/uint256"

	statediff "github.com/cerc-io/plugeth-statediff"
	"github.com/cerc-io/plugeth-statediff/indexer/ipld"
	"github.com/cerc-io/plugeth-statediff/indexer/shared"
	"github.com/cerc-io/plugeth-statediff/test_helpers"
	sdtypes "github.com/cerc-io/plugeth-statediff/types"
	"github.com/cerc-io/plugeth-statediff/utils"
)

var (
	contractLeafKey                                        []byte
	emptyDiffs                                             []sdtypes.StateLeafNode
	emptyStorage                                           []sdtypes.StorageLeafNode
	block0, block1, block2, block3, block4, block5, block6 *types.Block
	builder                                                statediff.Builder
	minerAddress                                           = common.HexToAddress("0x0")
	minerLeafKey                                           = test_helpers.AddressToLeafKey(minerAddress)

	slot0 = common.HexToHash("0")
	slot1 = common.HexToHash("1")
	slot2 = common.HexToHash("2")
	slot3 = common.HexToHash("3")

	slot0StorageKey = crypto.Keccak256Hash(slot0[:])
	slot1StorageKey = crypto.Keccak256Hash(slot1[:])
	slot2StorageKey = crypto.Keccak256Hash(slot2[:])
	slot3StorageKey = crypto.Keccak256Hash(slot3[:])

	slot0StorageValue, _ = hex.DecodeString("94703c4b2bd70c169f5717101caee543299fc946c7") // prefixed AccountAddr1
	slot1StorageValue, _ = hex.DecodeString("01")
	slot2StorageValue, _ = hex.DecodeString("09")
	slot3StorageValue, _ = hex.DecodeString("03")

	slot0StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
		slot0StorageValue,
	})
	slot1StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("310e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"),
		slot1StorageValue,
	})
	slot2StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"),
		slot2StorageValue,
	})
	slot3StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("32575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"),
		slot3StorageValue,
	})
	contractAccountAtBlock2 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(),
		Root:     crypto.Keccak256Hash(block2StorageBranchRootNode),
	}
	contractAccountAtBlock2RLP, _      = rlp.EncodeToBytes(contractAccountAtBlock2)
	contractAccountAtBlock2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45"),
		contractAccountAtBlock2RLP,
	})
	contractAccountAtBlock3 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(),
		Root:     crypto.Keccak256Hash(block3StorageBranchRootNode),
	}
	contractAccountAtBlock3RLP, _      = rlp.EncodeToBytes(contractAccountAtBlock3)
	contractAccountAtBlock3LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45"),
		contractAccountAtBlock3RLP,
	})
	contractAccountAtBlock4 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(),
		Root:     crypto.Keccak256Hash(block4StorageBranchRootNode),
	}
	contractAccountAtBlock4RLP, _      = rlp.EncodeToBytes(contractAccountAtBlock4)
	contractAccountAtBlock4LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45"),
		contractAccountAtBlock4RLP,
	})
	contractAccountAtBlock5 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(),
		Root:     crypto.Keccak256Hash(block5StorageBranchRootNode),
	}
	contractAccountAtBlock5RLP, _      = rlp.EncodeToBytes(contractAccountAtBlock5)
	contractAccountAtBlock5LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45"),
		contractAccountAtBlock5RLP,
	})
	minerAccountAtBlock1 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(2000002625000000000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	minerAccountAtBlock1RLP, _      = rlp.EncodeToBytes(minerAccountAtBlock1)
	minerAccountAtBlock1LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"),
		minerAccountAtBlock1RLP,
	})
	minerAccountAtBlock2 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(4000111203461610525),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	minerAccountAtBlock2RLP, _      = rlp.EncodeToBytes(minerAccountAtBlock2)
	minerAccountAtBlock2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"),
		minerAccountAtBlock2RLP,
	})

	account1AtBlock1 = &types.StateAccount{
		Nonce:    0,
		Balance:  test_helpers.Block1Account1Balance,
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock1RLP, _      = rlp.EncodeToBytes(account1AtBlock1)
	account1AtBlock1LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock1RLP,
	})
	account1AtBlock2 = &types.StateAccount{
		Nonce:    2,
		Balance:  uint256.NewInt(999555797000009000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock2RLP, _      = rlp.EncodeToBytes(account1AtBlock2)
	account1AtBlock2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock2RLP,
	})
	account1AtBlock5 = &types.StateAccount{
		Nonce:    2,
		Balance:  uint256.NewInt(2999586469962854280),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock5RLP, _      = rlp.EncodeToBytes(account1AtBlock5)
	account1AtBlock5LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock5RLP,
	})
	account1AtBlock6 = &types.StateAccount{
		Nonce:    3,
		Balance:  uint256.NewInt(2999557977962854280),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock6RLP, _      = rlp.EncodeToBytes(account1AtBlock6)
	account1AtBlock6LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock6RLP,
	})
	account2AtBlock2 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(1000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account2AtBlock2RLP, _      = rlp.EncodeToBytes(account2AtBlock2)
	account2AtBlock2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45"),
		account2AtBlock2RLP,
	})
	account2AtBlock3 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(2000013574009435976),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account2AtBlock3RLP, _      = rlp.EncodeToBytes(account2AtBlock3)
	account2AtBlock3LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45"),
		account2AtBlock3RLP,
	})
	account2AtBlock4 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(4000048088163070348),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account2AtBlock4RLP, _      = rlp.EncodeToBytes(account2AtBlock4)
	account2AtBlock4LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45"),
		account2AtBlock4RLP,
	})
	account2AtBlock6 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(6000063258066544204),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account2AtBlock6RLP, _      = rlp.EncodeToBytes(account2AtBlock6)
	account2AtBlock6LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45"),
		account2AtBlock6RLP,
	})
	bankAccountAtBlock0 = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.MustFromBig(test_helpers.TestBankFunds),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock0RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock0)
	bankAccountAtBlock0LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("2000bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock0RLP,
	})

	block1BankBalance   = test_helpers.TestBankFunds.Int64() - test_helpers.BalanceChange10000 - test_helpers.GasFees
	bankAccountAtBlock1 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(uint64(block1BankBalance)),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock1RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock1)
	bankAccountAtBlock1LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock1RLP,
	})

	block2BankBalance   = block1BankBalance - test_helpers.BalanceChange1Ether - test_helpers.GasFees
	bankAccountAtBlock2 = &types.StateAccount{
		Nonce:    2,
		Balance:  uint256.NewInt(uint64(block2BankBalance)),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock2RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock2)
	bankAccountAtBlock2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock2RLP,
	})
	bankAccountAtBlock3 = &types.StateAccount{
		Nonce:    3,
		Balance:  uint256.NewInt(999914255999990000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock3RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock3)
	bankAccountAtBlock3LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock3RLP,
	})
	bankAccountAtBlock4 = &types.StateAccount{
		Nonce:    6,
		Balance:  uint256.NewInt(999826859999990000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock4RLP, _      = rlp.EncodeToBytes(&bankAccountAtBlock4)
	bankAccountAtBlock4LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock4RLP,
	})
	bankAccountAtBlock5 = &types.StateAccount{
		Nonce:    8,
		Balance:  uint256.NewInt(999761283999990000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock5RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock5)
	bankAccountAtBlock5LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock5RLP,
	})

	block1BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock1LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock1LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account1AtBlock1LeafNode),
		[]byte{},
		[]byte{},
	})
	block2BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock2LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2LeafNode),
		crypto.Keccak256(contractAccountAtBlock2LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account2AtBlock2LeafNode),
		[]byte{},
		crypto.Keccak256(account1AtBlock2LeafNode),
		[]byte{},
		[]byte{},
	})
	block3BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock3LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2LeafNode),
		crypto.Keccak256(contractAccountAtBlock3LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account2AtBlock3LeafNode),
		[]byte{},
		crypto.Keccak256(account1AtBlock2LeafNode),
		[]byte{},
		[]byte{},
	})
	block4BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock4LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2LeafNode),
		crypto.Keccak256(contractAccountAtBlock4LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account2AtBlock4LeafNode),
		[]byte{},
		crypto.Keccak256(account1AtBlock2LeafNode),
		[]byte{},
		[]byte{},
	})
	block5BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock5LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2LeafNode),
		crypto.Keccak256(contractAccountAtBlock5LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account2AtBlock4LeafNode),
		[]byte{},
		crypto.Keccak256(account1AtBlock5LeafNode),
		[]byte{},
		[]byte{},
	})
	block6BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock5LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account2AtBlock6LeafNode),
		[]byte{},
		crypto.Keccak256(account1AtBlock6LeafNode),
		[]byte{},
		[]byte{},
	})

	block2StorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot0StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot1StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})
	block3StorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot0StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot1StorageLeafNode),
		crypto.Keccak256(slot3StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})
	block4StorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot0StorageLeafNode),
		[]byte{},
		crypto.Keccak256(slot2StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})
	block5StorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot0StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot3StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})
)

func TestBuilder(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(3, test_helpers.Genesis, test_helpers.TestChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block0 = test_helpers.Genesis
	block1 = blocks[0]
	block2 = blocks[1]
	block3 = blocks[2]
	params := statediff.Params{}

	var tests = []test_helpers.DiffTestCase{
		{
			"testEmptyDiff",
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes:       emptyDiffs,
			},
		},
		{
			"testBlock0",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: test_helpers.NullHash,
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock0,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock0LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock0LeafNode)).String(),
						Content: bankAccountAtBlock0LeafNode,
					},
				},
			},
		},
		{
			"testBlock1",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block1.Root(),
				BlockNumber:  block1.Number(),
				BlockHash:    block1.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block1.Number(),
				BlockHash:   block1.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock1,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock1LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: minerAccountAtBlock1,
							LeafKey: minerLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock1LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock1,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block1BranchRootNode)).String(),
						Content: block1BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock1LeafNode)).String(),
						Content: bankAccountAtBlock1LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock1LeafNode)).String(),
						Content: minerAccountAtBlock1LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1LeafNode)).String(),
						Content: account1AtBlock1LeafNode,
					},
				},
			},
		},
		{
			"testBlock2",
			// 1000 transferred from testBankAddress to account1Addr
			// 1000 transferred from account1Addr to account2Addr
			// account1addr creates a new contract
			statediff.Args{
				OldStateRoot: block1.Root(),
				NewStateRoot: block2.Root(),
				BlockNumber:  block2.Number(),
				BlockHash:    block2.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block2.Number(),
				BlockHash:   block2.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock2,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock2LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: minerAccountAtBlock2,
							LeafKey: minerLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock2LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock2,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock2,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot0StorageValue,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
							},
							{
								Removed: false,
								Value:   slot1StorageValue,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock2,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock2LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHash.Bytes()).String(),
						Content: test_helpers.ByteCodeAfterDeployment,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block2BranchRootNode)).String(),
						Content: block2BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock2LeafNode)).String(),
						Content: bankAccountAtBlock2LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock2LeafNode)).String(),
						Content: minerAccountAtBlock2LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2LeafNode)).String(),
						Content: account1AtBlock2LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2LeafNode)).String(),
						Content: contractAccountAtBlock2LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block2StorageBranchRootNode)).String(),
						Content: block2StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
						Content: slot0StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
						Content: slot1StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock2LeafNode)).String(),
						Content: account2AtBlock2LeafNode,
					},
				},
			},
		},
		{
			"testBlock3",
			//the contract's storage is changed
			//and the block is mined by account 2
			statediff.Args{
				OldStateRoot: block2.Root(),
				NewStateRoot: block3.Root(),
				BlockNumber:  block3.Number(),
				BlockHash:    block3.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block3.Number(),
				BlockHash:   block3.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock3,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock3LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock3,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot3StorageValue,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock3,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock3LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block3BranchRootNode)).String(),
						Content: block3BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock3LeafNode)).String(),
						Content: bankAccountAtBlock3LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3LeafNode)).String(),
						Content: contractAccountAtBlock3LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3StorageBranchRootNode)).String(),
						Content: block3StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
						Content: slot3StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock3LeafNode)).String(),
						Content: account2AtBlock3LeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block0: bankAccountAtBlock0LeafNode,
		block1: block1BranchRootNode,
		block2: block2BranchRootNode,
		block3: block3BranchRootNode,
	}.Check(t)
}

func TestBuilderWithWatchedAddressList(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(3, test_helpers.Genesis, test_helpers.TestChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block0 = test_helpers.Genesis
	block1 = blocks[0]
	block2 = blocks[1]
	block3 = blocks[2]
	params := statediff.Params{
		WatchedAddresses: []common.Address{test_helpers.Account1Addr, test_helpers.ContractAddr},
	}
	params.ComputeWatchedAddressesLeafPaths()

	var tests = []test_helpers.DiffTestCase{
		{
			"testEmptyDiff",
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes:       emptyDiffs,
			},
		},
		{
			"testBlock0",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: test_helpers.NullHash,
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes:       emptyDiffs,
			},
		},
		{
			"testBlock1",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block1.Root(),
				BlockNumber:  block1.Number(),
				BlockHash:    block1.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block1.Number(),
				BlockHash:   block1.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock1,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block1BranchRootNode)).String(),
						Content: block1BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1LeafNode)).String(),
						Content: account1AtBlock1LeafNode,
					},
				},
			},
		},
		{
			"testBlock2",
			//1000 transferred from testBankAddress to account1Addr
			//1000 transferred from account1Addr to account2Addr
			statediff.Args{
				OldStateRoot: block1.Root(),
				NewStateRoot: block2.Root(),
				BlockNumber:  block2.Number(),
				BlockHash:    block2.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block2.Number(),
				BlockHash:   block2.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock2,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot0StorageValue,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
							},
							{
								Removed: false,
								Value:   slot1StorageValue,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock2,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHash.Bytes()).String(),
						Content: test_helpers.ByteCodeAfterDeployment,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block2BranchRootNode)).String(),
						Content: block2BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2LeafNode)).String(),
						Content: contractAccountAtBlock2LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block2StorageBranchRootNode)).String(),
						Content: block2StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
						Content: slot0StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
						Content: slot1StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2LeafNode)).String(),
						Content: account1AtBlock2LeafNode,
					},
				},
			},
		},
		{
			"testBlock3",
			//the contract's storage is changed
			//and the block is mined by account 2
			statediff.Args{
				OldStateRoot: block2.Root(),
				NewStateRoot: block3.Root(),
				BlockNumber:  block3.Number(),
				BlockHash:    block3.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block3.Number(),
				BlockHash:   block3.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock3,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot3StorageValue,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block3BranchRootNode)).String(),
						Content: block3BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3LeafNode)).String(),
						Content: contractAccountAtBlock3LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3StorageBranchRootNode)).String(),
						Content: block3StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
						Content: slot3StorageLeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block0: bankAccountAtBlock0LeafNode,
		block1: block1BranchRootNode,
		block2: block2BranchRootNode,
		block3: block3BranchRootNode,
	}.Check(t)
}

func TestBuilderWithRemovedAccountAndStorage(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(6, test_helpers.Genesis, test_helpers.TestChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block3 = blocks[2]
	block4 = blocks[3]
	block5 = blocks[4]
	block6 = blocks[5]
	params := statediff.Params{}

	var tests = []test_helpers.DiffTestCase{
		// blocks 0-3 are the same as in TestBuilderWithIntermediateNodes
		{
			"testBlock4",
			statediff.Args{
				OldStateRoot: block3.Root(),
				NewStateRoot: block4.Root(),
				BlockNumber:  block4.Number(),
				BlockHash:    block4.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block4.Number(),
				BlockHash:   block4.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock4,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock4LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock4,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock4LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot2StorageValue,
								LeafKey: slot2StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot2StorageLeafNode)).String(),
							},
							{
								Removed: true,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
							{
								Removed: true,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock4,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock4LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block4BranchRootNode)).String(),
						Content: block4BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock4LeafNode)).String(),
						Content: bankAccountAtBlock4LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock4LeafNode)).String(),
						Content: contractAccountAtBlock4LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block4StorageBranchRootNode)).String(),
						Content: block4StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot2StorageLeafNode)).String(),
						Content: slot2StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock4LeafNode)).String(),
						Content: account2AtBlock4LeafNode,
					},
				},
			},
		},
		{
			"testBlock5",
			statediff.Args{
				OldStateRoot: block4.Root(),
				NewStateRoot: block5.Root(),
				BlockNumber:  block5.Number(),
				BlockHash:    block5.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block5.Number(),
				BlockHash:   block5.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock5,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock5LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock5,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock5LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot3StorageValue,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
							},
							{
								Removed: true,
								LeafKey: slot2StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock5,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block5BranchRootNode)).String(),
						Content: block5BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock5LeafNode)).String(),
						Content: bankAccountAtBlock5LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock5LeafNode)).String(),
						Content: contractAccountAtBlock5LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block5StorageBranchRootNode)).String(),
						Content: block5StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
						Content: slot3StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String(),
						Content: account1AtBlock5LeafNode,
					},
				},
			},
		},
		{
			"testBlock6",
			statediff.Args{
				OldStateRoot: block5.Root(),
				NewStateRoot: block6.Root(),
				BlockNumber:  block6.Number(),
				BlockHash:    block6.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block6.Number(),
				BlockHash:   block6.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: true,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: nil,
							LeafKey: contractLeafKey,
							CID:     shared.RemovedNodeStateCID},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: true,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
							{
								Removed: true,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock6,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock6LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock6,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block6BranchRootNode)).String(),
						Content: block6BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock6LeafNode)).String(),
						Content: account2AtBlock6LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String(),
						Content: account1AtBlock6LeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block4: block4BranchRootNode,
		block5: block5BranchRootNode,
		block6: block6BranchRootNode,
	}.Check(t)
}

func TestBuilderWithRemovedNonWatchedAccount(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(6, test_helpers.Genesis, test_helpers.TestChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block3 = blocks[2]
	block4 = blocks[3]
	block5 = blocks[4]
	block6 = blocks[5]
	params := statediff.Params{
		WatchedAddresses: []common.Address{test_helpers.Account1Addr, test_helpers.Account2Addr},
	}
	params.ComputeWatchedAddressesLeafPaths()

	var tests = []test_helpers.DiffTestCase{
		{
			"testBlock4",
			statediff.Args{
				OldStateRoot: block3.Root(),
				NewStateRoot: block4.Root(),
				BlockNumber:  block4.Number(),
				BlockHash:    block4.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block4.Number(),
				BlockHash:   block4.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock4,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock4LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block4BranchRootNode)).String(),
						Content: block4BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock4LeafNode)).String(),
						Content: account2AtBlock4LeafNode,
					},
				},
			},
		},
		{
			"testBlock5",
			statediff.Args{
				OldStateRoot: block4.Root(),
				NewStateRoot: block5.Root(),
				BlockNumber:  block5.Number(),
				BlockHash:    block5.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block5.Number(),
				BlockHash:   block5.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock5,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block5BranchRootNode)).String(),
						Content: block5BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String(),
						Content: account1AtBlock5LeafNode,
					},
				},
			},
		},
		{
			"testBlock6",
			statediff.Args{
				OldStateRoot: block5.Root(),
				NewStateRoot: block6.Root(),
				BlockNumber:  block6.Number(),
				BlockHash:    block6.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block6.Number(),
				BlockHash:   block6.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account2AtBlock6,
							LeafKey: test_helpers.Account2LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock6LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock6,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block6BranchRootNode)).String(),
						Content: block6BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account2AtBlock6LeafNode)).String(),
						Content: account2AtBlock6LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String(),
						Content: account1AtBlock6LeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block4: block4BranchRootNode,
		block5: block5BranchRootNode,
		block6: block6BranchRootNode,
	}.Check(t)
}

func TestBuilderWithRemovedWatchedAccount(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(6, test_helpers.Genesis, test_helpers.TestChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block3 = blocks[2]
	block4 = blocks[3]
	block5 = blocks[4]
	block6 = blocks[5]
	params := statediff.Params{
		WatchedAddresses: []common.Address{test_helpers.Account1Addr, test_helpers.ContractAddr},
	}
	params.ComputeWatchedAddressesLeafPaths()

	var tests = []test_helpers.DiffTestCase{
		{
			"testBlock4",
			statediff.Args{
				OldStateRoot: block3.Root(),
				NewStateRoot: block4.Root(),
				BlockNumber:  block4.Number(),
				BlockHash:    block4.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block4.Number(),
				BlockHash:   block4.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock4,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock4LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								LeafKey: slot2StorageKey.Bytes(),
								Value:   slot2StorageValue,
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot2StorageLeafNode)).String(),
							},
							{
								Removed: true,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
							{
								Removed: true,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block4BranchRootNode)).String(),
						Content: block4BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock4LeafNode)).String(),
						Content: contractAccountAtBlock4LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block4StorageBranchRootNode)).String(),
						Content: block4StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot2StorageLeafNode)).String(),
						Content: slot2StorageLeafNode,
					},
				},
			},
		},
		{
			"testBlock5",
			statediff.Args{
				OldStateRoot: block4.Root(),
				NewStateRoot: block5.Root(),
				BlockNumber:  block5.Number(),
				BlockHash:    block5.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block5.Number(),
				BlockHash:   block5.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock5,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock5LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								LeafKey: slot3StorageKey.Bytes(),
								Value:   slot3StorageValue,
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
							},
							{
								Removed: true,
								LeafKey: slot2StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock5,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block5BranchRootNode)).String(),
						Content: block5BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock5LeafNode)).String(),
						Content: contractAccountAtBlock5LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block5StorageBranchRootNode)).String(),
						Content: block5StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot3StorageLeafNode)).String(),
						Content: slot3StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock5LeafNode)).String(),
						Content: account1AtBlock5LeafNode,
					},
				},
			},
		},
		{
			"testBlock6",
			statediff.Args{
				OldStateRoot: block5.Root(),
				NewStateRoot: block6.Root(),
				BlockNumber:  block6.Number(),
				BlockHash:    block6.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block6.Number(),
				BlockHash:   block6.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: true,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: nil,
							LeafKey: contractLeafKey,
							CID:     shared.RemovedNodeStateCID},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: true,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
							{
								Removed: true,
								LeafKey: slot3StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock6,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block6BranchRootNode)).String(),
						Content: block6BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock6LeafNode)).String(),
						Content: account1AtBlock6LeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block4: block4BranchRootNode,
		block5: block5BranchRootNode,
		block6: block6BranchRootNode,
	}.Check(t)
}

var (
	slot00StorageValue = utils.Hex2Bytes("9471562b71999873db5b286df957af199ec94617f7") // prefixed TestBankAddress

	slot00StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
		slot00StorageValue,
	})

	contractAccountAtBlock01 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(),
		Root:     crypto.Keccak256Hash(block01StorageBranchRootNode),
	}
	contractAccountAtBlock01RLP, _      = rlp.EncodeToBytes(contractAccountAtBlock01)
	contractAccountAtBlock01LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3cb2583748c26e89ef19c2a8529b05a270f735553b4d44b6f2a1894987a71c8b"),
		contractAccountAtBlock01RLP,
	})

	bankAccountAtBlock01 = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(3999629697375000000),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock01RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock01)
	bankAccountAtBlock01LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock01RLP,
	})
	bankAccountAtBlock02 = &types.StateAccount{
		Nonce:    2,
		Balance:  uint256.NewInt(5999607323457344852),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock02RLP, _      = rlp.EncodeToBytes(bankAccountAtBlock02)
	bankAccountAtBlock02LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("2000bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock02RLP,
	})

	block01BranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256Hash(bankAccountAtBlock01LeafNode),
		crypto.Keccak256Hash(contractAccountAtBlock01LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})

	block01StorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot00StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot1StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
	})
)

func TestBuilderWithMovedAccount(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(2, test_helpers.Genesis, test_helpers.TestSelfDestructChainGen)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block0 = test_helpers.Genesis
	block1 = blocks[0]
	block2 = blocks[1]
	params := statediff.Params{}

	var tests = []test_helpers.DiffTestCase{
		{
			"testBlock1",
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block1.Root(),
				BlockNumber:  block1.Number(),
				BlockHash:    block1.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block1.Number(),
				BlockHash:   block1.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock01,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock01LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock01,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock01LeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								LeafKey: slot0StorageKey.Bytes(),
								Value:   slot00StorageValue,
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot00StorageLeafNode)).String(),
							},
							{
								Removed: false,
								LeafKey: slot1StorageKey.Bytes(),
								Value:   slot1StorageValue,
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHash.Bytes()).String(),
						Content: test_helpers.ByteCodeAfterDeployment,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block01BranchRootNode)).String(),
						Content: block01BranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock01LeafNode)).String(),
						Content: bankAccountAtBlock01LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock01LeafNode)).String(),
						Content: contractAccountAtBlock01LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block01StorageBranchRootNode)).String(),
						Content: block01StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot00StorageLeafNode)).String(),
						Content: slot00StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
						Content: slot1StorageLeafNode,
					},
				},
			},
		},
		{
			"testBlock2",
			statediff.Args{
				OldStateRoot: block1.Root(),
				NewStateRoot: block2.Root(),
				BlockNumber:  block2.Number(),
				BlockHash:    block2.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block2.Number(),
				BlockHash:   block2.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock02,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock02LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: true,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: nil,
							LeafKey: contractLeafKey,
							CID:     shared.RemovedNodeStateCID},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: true,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
							{
								Removed: true,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     shared.RemovedNodeStorageCID,
								Value:   []byte{},
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock02LeafNode)).String(),
						Content: bankAccountAtBlock02LeafNode,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block1: block01BranchRootNode,
		block2: bankAccountAtBlock02LeafNode,
	}.Check(t)
}

/*
pragma solidity ^0.5.10;

contract test {
    address payable owner;

    modifier onlyOwner {
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        _;
    }

    uint256[100] data;

	constructor() public {
	    owner = msg.sender;
		data = [1];
	}

    function Put(uint256 addr, uint256 value) public {
        data[addr] = value;
    }

    function close() public onlyOwner { //onlyOwner is custom modifier
        selfdestruct(owner);  // `owner` is the owners address
    }
}
*/

var (
	b                    = uint256.NewInt(0).Sub(uint256.MustFromBig(test_helpers.TestBIGBankFunds), test_helpers.BalanceChangeBIG)
	block1BankBigBalance = uint256.NewInt(0).Sub(b, uint256.NewInt(uint64(test_helpers.GasFees2)))
	bankAccountAtBlock1b = &types.StateAccount{
		Nonce:    1,
		Balance:  block1BankBigBalance,
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock1bRLP, _      = rlp.EncodeToBytes(bankAccountAtBlock1b)
	bankAccountAtBlock1bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock1bRLP,
	})

	account1AtBlock1b = &types.StateAccount{
		Nonce:    0,
		Balance:  test_helpers.Block1bAccount1Balance,
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock1bRLP, _      = rlp.EncodeToBytes(account1AtBlock1b)
	account1AtBlock1bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock1bRLP,
	})

	account1AtBlock2bBalance = uint256.MustFromDecimal("1999999999999999999999999761539571000000000")
	account1AtBlock2b        = &types.StateAccount{
		Nonce:    1,
		Balance:  account1AtBlock2bBalance,
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	account1AtBlock2bRLP, _      = rlp.EncodeToBytes(account1AtBlock2b)
	account1AtBlock2bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
		account1AtBlock2bRLP,
	})

	minerAccountAtBlock2b = &types.StateAccount{
		Nonce:    0,
		Balance:  uint256.NewInt(4055891787808414571),
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	minerAccountAtBlock2bRLP, _      = rlp.EncodeToBytes(minerAccountAtBlock2b)
	minerAccountAtBlock2bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"),
		minerAccountAtBlock2bRLP,
	})

	contractAccountAtBlock2b = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: test_helpers.CodeHashForInternalizedLeafNode.Bytes(),
		Root:     crypto.Keccak256Hash(block2StorageBranchRootNode),
	}
	contractAccountAtBlock2bRLP, _      = rlp.EncodeToBytes(contractAccountAtBlock2b)
	contractAccountAtBlock2bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3d7e14f1723fa19b5d6d9f8b86b49acefbc9c400bf4ed686c10d6b6467fc5b3a"),
		contractAccountAtBlock2bRLP,
	})

	bankAccountAtBlock3bBalance = uint256.MustFromDecimal("18000000000000000000000001999920365757724976")
	bankAccountAtBlock3b        = &types.StateAccount{
		Nonce:    3,
		Balance:  bankAccountAtBlock3bBalance,
		CodeHash: test_helpers.NullCodeHash.Bytes(),
		Root:     test_helpers.EmptyContractRoot,
	}
	bankAccountAtBlock3bRLP, _      = rlp.EncodeToBytes(bankAccountAtBlock3b)
	bankAccountAtBlock3bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
		bankAccountAtBlock3bRLP,
	})

	contractAccountAtBlock3b = &types.StateAccount{
		Nonce:    1,
		Balance:  uint256.NewInt(0),
		CodeHash: test_helpers.CodeHashForInternalizedLeafNode.Bytes(),
		Root:     crypto.Keccak256Hash(block3bStorageBranchRootNode),
	}
	contractAccountAtBlock3bRLP, _      = rlp.EncodeToBytes(contractAccountAtBlock3b)
	contractAccountAtBlock3bLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("3d7e14f1723fa19b5d6d9f8b86b49acefbc9c400bf4ed686c10d6b6467fc5b3a"),
		contractAccountAtBlock3bRLP,
	})

	slot40364  = common.BytesToHash(uint256.NewInt(40364).Bytes())
	slot105566 = common.BytesToHash(uint256.NewInt(105566).Bytes())

	slot40364StorageValue  = utils.Hex2Bytes("01")
	slot105566StorageValue = utils.Hex2Bytes("02")

	slot40364StorageKey  = crypto.Keccak256Hash(slot40364[:])
	slot105566StorageKey = crypto.Keccak256Hash(slot105566[:])

	slot40364StorageInternalLeafNode = []interface{}{
		utils.Hex2Bytes("3077bbc951a04529defc15da8c06e427cde0d7a1499c50975bbe8aab"),
		slot40364StorageValue,
	}
	slot105566StorageInternalLeafNode = []interface{}{
		utils.Hex2Bytes("3c62586c18bf1ecfda161ced374b7a894630e2db426814c24e5d42af"),
		slot105566StorageValue,
	}

	block3bStorageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot0StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(slot1StorageLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(block3bStorageExtensionNode),
		[]byte{},
	})

	block3bStorageExtensionNode, _ = rlp.EncodeToBytes(&[]interface{}{
		utils.Hex2Bytes("1291631c"),
		crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves),
	})

	block3bStorageBranchNodeWithInternalLeaves, _ = rlp.EncodeToBytes(&[]interface{}{
		slot105566StorageInternalLeafNode,
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		slot40364StorageInternalLeafNode,
		[]byte{},
		[]byte{},
		[]byte{},
	})

	block1bBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock1bLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock1LeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account1AtBlock1bLeafNode),
		[]byte{},
		[]byte{},
	})

	block2bBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock1bLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2bLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account1AtBlock2bLeafNode),
		crypto.Keccak256(contractAccountAtBlock2bLeafNode),
		[]byte{},
	})

	block3bBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
		crypto.Keccak256(bankAccountAtBlock3bLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(minerAccountAtBlock2bLeafNode),
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		[]byte{},
		crypto.Keccak256(account1AtBlock2bLeafNode),
		crypto.Keccak256(contractAccountAtBlock3bLeafNode),
		[]byte{},
	})
)

func TestBuilderWithInternalizedLeafNode(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(3, test_helpers.GenesisForInternalLeafNodeTest, test_helpers.TestChainGenWithInternalLeafNode)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block0 = test_helpers.Genesis
	block1 = blocks[0]
	block2 = blocks[1]
	block3 = blocks[2]
	params := statediff.Params{}

	var tests = []test_helpers.DiffTestCase{
		{
			"testEmptyDiff",
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes:       emptyDiffs,
			},
		},
		{
			"testBlock0",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: test_helpers.NullHash,
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock0,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock0LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock0LeafNode)).String(),
						Content: bankAccountAtBlock0LeafNode,
					},
				},
			},
		},
		{
			"testBlock1",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block1.Root(),
				BlockNumber:  block1.Number(),
				BlockHash:    block1.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block1.Number(),
				BlockHash:   block1.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock1b,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock1bLeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: minerAccountAtBlock1,
							LeafKey: minerLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock1LeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock1b,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1bLeafNode)).String()},
						StorageDiff: emptyStorage,
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block1bBranchRootNode)).String(),
						Content: block1bBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock1bLeafNode)).String(),
						Content: bankAccountAtBlock1bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock1LeafNode)).String(),
						Content: minerAccountAtBlock1LeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock1bLeafNode)).String(),
						Content: account1AtBlock1bLeafNode,
					},
				},
			},
		},
		{
			"testBlock2",
			// 1000 transferred from testBankAddress to account1Addr
			// 1000 transferred from account1Addr to account2Addr
			// account1addr creates a new contract
			statediff.Args{
				OldStateRoot: block1.Root(),
				NewStateRoot: block2.Root(),
				BlockNumber:  block2.Number(),
				BlockHash:    block2.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block2.Number(),
				BlockHash:   block2.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: minerAccountAtBlock2b,
							LeafKey: minerLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock2bLeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: account1AtBlock2b,
							LeafKey: test_helpers.Account1LeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2bLeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock2b,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2bLeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot0StorageValue,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
							},
							{
								Removed: false,
								Value:   slot1StorageValue,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHashForInternalizedLeafNode.Bytes()).String(),
						Content: test_helpers.ByteCodeAfterDeploymentForInternalLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block2bBranchRootNode)).String(),
						Content: block2bBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(minerAccountAtBlock2bLeafNode)).String(),
						Content: minerAccountAtBlock2bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(account1AtBlock2bLeafNode)).String(),
						Content: account1AtBlock2bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2bLeafNode)).String(),
						Content: contractAccountAtBlock2bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block2StorageBranchRootNode)).String(),
						Content: block2StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
						Content: slot0StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
						Content: slot1StorageLeafNode,
					},
				},
			},
		},
		{
			"testBlock3",
			//the contract's storage is changed
			//and the block is mined by account 2
			statediff.Args{
				OldStateRoot: block2.Root(),
				NewStateRoot: block3.Root(),
				BlockNumber:  block3.Number(),
				BlockHash:    block3.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block3.Number(),
				BlockHash:   block3.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: bankAccountAtBlock3b,
							LeafKey: test_helpers.BankLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock3bLeafNode)).String()},
						StorageDiff: emptyStorage,
					},
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock3b,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3bLeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot105566StorageValue,
								LeafKey: slot105566StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
							},
							{
								Removed: false,
								Value:   slot40364StorageValue,
								LeafKey: slot40364StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block3bBranchRootNode)).String(),
						Content: block3bBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(bankAccountAtBlock3bLeafNode)).String(),
						Content: bankAccountAtBlock3bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3bLeafNode)).String(),
						Content: contractAccountAtBlock3bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchRootNode)).String(),
						Content: block3bStorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageExtensionNode)).String(),
						Content: block3bStorageExtensionNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
						Content: block3bStorageBranchNodeWithInternalLeaves,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block1: block1bBranchRootNode,
		block2: block2bBranchRootNode,
		block3: block3bBranchRootNode,
	}.Check(t)
}

func TestBuilderWithInternalizedLeafNodeAndWatchedAddress(t *testing.T) {
	blocks, chain := test_helpers.MakeChain(3, test_helpers.GenesisForInternalLeafNodeTest, test_helpers.TestChainGenWithInternalLeafNode)
	contractLeafKey = test_helpers.AddressToLeafKey(test_helpers.ContractAddr)
	defer chain.Stop()
	block0 = test_helpers.Genesis
	block1 = blocks[0]
	block2 = blocks[1]
	block3 = blocks[2]
	params := statediff.Params{
		WatchedAddresses: []common.Address{
			test_helpers.ContractAddr,
		},
	}
	params.ComputeWatchedAddressesLeafPaths()

	var tests = []test_helpers.DiffTestCase{
		{
			"testEmptyDiff",
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				Nodes:       emptyDiffs,
			},
		},
		{
			"testBlock0",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: test_helpers.NullHash,
				NewStateRoot: block0.Root(),
				BlockNumber:  block0.Number(),
				BlockHash:    block0.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block0.Number(),
				BlockHash:   block0.Hash(),
				// there's some kind of weird behavior where if our root node is a leaf node even
				// though it is along the path to the watched leaf (necessarily, as it is the root)
				// it doesn't get included. unconsequential, but kinda odd.
			},
		},
		{
			"testBlock1",
			//10000 transferred from testBankAddress to account1Addr
			statediff.Args{
				OldStateRoot: block0.Root(),
				NewStateRoot: block1.Root(),
				BlockNumber:  block1.Number(),
				BlockHash:    block1.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block1.Number(),
				BlockHash:   block1.Hash(),
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block1bBranchRootNode)).String(),
						Content: block1bBranchRootNode,
					},
				},
			},
		},
		{
			"testBlock2",
			// 1000 transferred from testBankAddress to account1Addr
			// 1000 transferred from account1Addr to account2Addr
			// account1addr creates a new contract
			statediff.Args{
				OldStateRoot: block1.Root(),
				NewStateRoot: block2.Root(),
				BlockNumber:  block2.Number(),
				BlockHash:    block2.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block2.Number(),
				BlockHash:   block2.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock2b,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2bLeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot0StorageValue,
								LeafKey: slot0StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
							},
							{
								Removed: false,
								Value:   slot1StorageValue,
								LeafKey: slot1StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHashForInternalizedLeafNode.Bytes()).String(),
						Content: test_helpers.ByteCodeAfterDeploymentForInternalLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block2bBranchRootNode)).String(),
						Content: block2bBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock2bLeafNode)).String(),
						Content: contractAccountAtBlock2bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block2StorageBranchRootNode)).String(),
						Content: block2StorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot0StorageLeafNode)).String(),
						Content: slot0StorageLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(slot1StorageLeafNode)).String(),
						Content: slot1StorageLeafNode,
					},
				},
			},
		},
		{
			"testBlock3",
			//the contract's storage is changed
			//and the block is mined by account 2
			statediff.Args{
				OldStateRoot: block2.Root(),
				NewStateRoot: block3.Root(),
				BlockNumber:  block3.Number(),
				BlockHash:    block3.Hash(),
			},
			&sdtypes.StateObject{
				BlockNumber: block3.Number(),
				BlockHash:   block3.Hash(),
				Nodes: []sdtypes.StateLeafNode{
					{
						Removed: false,
						AccountWrapper: sdtypes.AccountWrapper{
							Account: contractAccountAtBlock3b,
							LeafKey: contractLeafKey,
							CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3bLeafNode)).String()},
						StorageDiff: []sdtypes.StorageLeafNode{
							{
								Removed: false,
								Value:   slot105566StorageValue,
								LeafKey: slot105566StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
							},
							{
								Removed: false,
								Value:   slot40364StorageValue,
								LeafKey: slot40364StorageKey.Bytes(),
								CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
							},
						},
					},
				},
				IPLDs: []sdtypes.IPLD{
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block3bBranchRootNode)).String(),
						Content: block3bBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(contractAccountAtBlock3bLeafNode)).String(),
						Content: contractAccountAtBlock3bLeafNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchRootNode)).String(),
						Content: block3bStorageBranchRootNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageExtensionNode)).String(),
						Content: block3bStorageExtensionNode,
					},
					{
						CID:     ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block3bStorageBranchNodeWithInternalLeaves)).String(),
						Content: block3bStorageBranchNodeWithInternalLeaves,
					},
				},
			},
		},
	}

	test_helpers.RunBuildStateDiff(t, chain.StateCache(), tests, params)
	test_helpers.CheckedRoots{
		block1: block1bBranchRootNode,
		block2: block2bBranchRootNode,
		block3: block3bBranchRootNode,
	}.Check(t)
}

/*
pragma solidity ^0.5.10;

contract test {
    address payable owner;

    modifier onlyOwner {
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        _;
    }

    uint256[105566] data;

	constructor() public {
	    owner = msg.sender;
		data = [1];
	}

    function Put(uint256 addr, uint256 value) public {
        data[addr] = value;
    }

    function close() public onlyOwner { //onlyOwner is custom modifier
        selfdestruct(owner);  // `owner` is the owners address
    }
}
*/