blockchain v0.1.7 Blockchain.Block

This module effective encodes a Block, the heart of the blockchain. A chain is formed when blocks point to previous blocks, either as a parent or an ommer (uncle). For more information, see Section 4.4 of the Yellow Paper.

Link to this section Summary

Functions

Attaches an ommer to a block. We do no validation at this stage

Adds the rewards to miners (including for ommers) to a block. This is defined in Section 11.3, Eq.(147), Eq.(148) and Eq.(149) of the Yellow Paper

For a given block, this will add the given transactions to its list of transaction and update the header state accordingly. That is, we will execute each transaction and update the state root, transaction receipts, etc. We effectively implement Eq.(2), Eq.(3) and Eq.(4) of the Yellow Paper, referred to as Π

Decodes a block from an RLP encoding. Effectively inverts L_B defined in Eq.(33)

Creates a new block from a parent block. This will handle setting the block number, the difficulty and will keep the gas_limit the same as the parent’s block unless specified in opts

Creates a genesis block for a given chain

Returns a given block from the database, if the hash exists in the database

Returns a given block from the database, if the hash exists in the database

Returns the cumulative gas used by a block based on the listed transactions. This is defined in largely in the note after Eq.(66) referenced as l(B_R)_u, or the last receipt’s cumulative gas

Gets an ommer for a given block, based on the ommers_hash

Returns the parent node for a given block, if it exists

Returns a given receipt from a block. This is based on the receipts root where all receipts are stored for the given block

Returns a trie rooted at the state_root of a given block

Returns a given transaction from a block. This is based on the transactions root where all transactions are stored for the given block

Returns the total number of transactions included in a block. This is based on the transaction list for a given block

Computes hash of a block, which is simply the hash of the serialized block after applying RLP encoding

Checks the validity of a block, including the validity of the header and the transactions. This should verify that we should accept the authenticity of a block

Returns whether or not a block is a genesis block, based on block number

Determines whether or not a block is valid. This is defined in Eq.(29) of the Yellow Paper

Stores a given block in the database and returns the block hash

Sets a given block header field as a shortcut when we want to change a single field

Updates a block by adding a receipt to the list of receipts at position i

Updates a block by adding a transaction to the list of transactions and updating the transactions_root in the header at position i, which should be equilvant to the current number of transactions

Encodes a block such that it can be represented in RLP encoding. This is defined as L_B Eq.(33) in the Yellow Paper

Set the difficulty of a new block based on Eq.(39), better defined in Block.Header`

Sets the gas limit of a given block, or raises if the block limit is not acceptable. The validity check is defined in Eq.(45), Eq.(46) and Eq.(47) of the Yellow Paper

Calculates the number for a new block. This implements Eq.(38) from the Yellow Paper

Sets the state_root of a given block from a trie

Link to this section Types

Link to this type t()
t() :: %Blockchain.Block{block_hash: EVM.hash | nil, header: Block.Header.t, ommers: [Block.Header.t], transactions: [Blockchain.Transaction.t]}

Link to this section Functions

Link to this function add_ommers_to_block(block, ommers)
add_ommers_to_block(t, [Block.Header.t]) :: t

Attaches an ommer to a block. We do no validation at this stage.

Examples

iex> Blockchain.Block.add_ommers_to_block(%Blockchain.Block{}, [%Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<7::256>>, nonce: <<8::64>>}])
%Blockchain.Block{
  ommers: [
    %Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<7::256>>, nonce: <<8::64>>}
  ],
  header: %Block.Header{
    ommers_hash: <<59, 196, 156, 242, 196, 38, 21, 97, 112, 6, 73, 111, 12, 88, 35, 155, 72, 175, 82, 0, 163, 128, 115, 236, 45, 99, 88, 62, 88, 80, 122, 96>>
  }
}
Link to this function add_rewards_to_block(block, db, reward_wei \\ 5000000000000000000)
add_rewards_to_block(t, MerklePatriciaTree.DB.db, EVM.Wei.t) :: t

Adds the rewards to miners (including for ommers) to a block. This is defined in Section 11.3, Eq.(147), Eq.(148) and Eq.(149) of the Yellow Paper.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> miner = <<0x05::160>>
iex> state = MerklePatriciaTree.Trie.new(db)
...>         |> Blockchain.Account.put_account(miner, %Blockchain.Account{balance: 400_000})
iex> block = %Blockchain.Block{header: %Block.Header{number: 0, state_root: state.root_hash, beneficiary: miner}}
iex> block
...> |> Blockchain.Block.add_rewards_to_block(db)
...> |> Blockchain.Block.get_state(db)
...> |> Blockchain.Account.get_accounts([miner])
[%Blockchain.Account{balance: 400_000}]

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> miner = <<0x05::160>>
iex> state = MerklePatriciaTree.Trie.new(db)
...>         |> Blockchain.Account.put_account(miner, %Blockchain.Account{balance: 400_000})
iex> block = %Blockchain.Block{header: %Block.Header{state_root: state.root_hash, beneficiary: miner}}
iex> block
...> |> Blockchain.Block.add_rewards_to_block(db)
...> |> Blockchain.Block.get_state(db)
...> |> Blockchain.Account.get_accounts([miner])
[%Blockchain.Account{balance: 5000000000000400000}]

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> miner = <<0x05::160>>
iex> state = MerklePatriciaTree.Trie.new(db)
...>         |> Blockchain.Account.put_account(miner, %Blockchain.Account{balance: 400_000})
iex> block = %Blockchain.Block{header: %Block.Header{state_root: state.root_hash, beneficiary: miner}}
iex> block
...> |> Blockchain.Block.add_rewards_to_block(db, 100)
...> |> Blockchain.Block.get_state(db)
...> |> Blockchain.Account.get_accounts([miner])
[%Blockchain.Account{balance: 400100}]
Link to this function add_transactions_to_block(block, transactions, db)
add_transactions_to_block(t, [Blockchain.Transaction.t], MerklePatriciaTree.DB.db) :: t

For a given block, this will add the given transactions to its list of transaction and update the header state accordingly. That is, we will execute each transaction and update the state root, transaction receipts, etc. We effectively implement Eq.(2), Eq.(3) and Eq.(4) of the Yellow Paper, referred to as Π.

The trie db refers to where we expect our trie to exist, e.g. in :ets or :leveldb. See MerklePatriciaTree.DB.

TODO: Add a rich set of test cases in block_test.exs

Examples

# Create a contract
iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> beneficiary = <<0x05::160>>
iex> private_key = <<1::256>>
iex> sender = <<126, 95, 69, 82, 9, 26, 105, 18, 93, 93, 252, 183, 184, 194, 101, 144, 41, 57, 91, 223>> # based on simple private key
iex> contract_address = Blockchain.Contract.new_contract_address(sender, 6)
iex> machine_code = EVM.MachineCode.compile([:push1, 3, :push1, 5, :add, :push1, 0x00, :mstore, :push1, 32, :push1, 0, :return])
iex> trx = %Blockchain.Transaction{nonce: 5, gas_price: 3, gas_limit: 100_000, to: <<>>, value: 5, init: machine_code}
...>           |> Blockchain.Transaction.Signature.sign_transaction(private_key)
iex> state = MerklePatriciaTree.Trie.new(db)
...>           |> Blockchain.Account.put_account(sender, %Blockchain.Account{balance: 400_000, nonce: 5})
iex> block = %Blockchain.Block{header: %Block.Header{state_root: state.root_hash, beneficiary: beneficiary}, transactions: []}
...>           |> Blockchain.Block.add_transactions_to_block([trx], db)
iex> Enum.count(block.transactions)
1
iex> Blockchain.Block.get_receipt(block, 0, db)
%Blockchain.Transaction.Receipt{bloom_filter: "", cumulative_gas: 53780, logs: "", state: block.header.state_root}
iex> Blockchain.Block.get_transaction(block, 0, db)
%Blockchain.Transaction{data: "", gas_limit: 100000, gas_price: 3, init: <<96, 3, 96, 5, 1, 96, 0, 82, 96, 32, 96, 0, 243>>, nonce: 5, r: 107081699003708865501096995082166450904153826331883689397382301082384794234940, s: 15578885506929783846367818105804923093083001199223955674477534036059482186127, to: "", v: 27, value: 5}
iex> Blockchain.Block.get_state(block, db)
...> |> Blockchain.Account.get_accounts([sender, beneficiary, contract_address])
[%Blockchain.Account{balance: 238655, nonce: 6}, %Blockchain.Account{balance: 161340}, %Blockchain.Account{balance: 5, code_hash: <<243, 247, 169, 254, 54, 79, 170, 185, 59, 33, 109, 165, 10, 50, 20, 21, 79, 34, 160, 162, 180, 21, 178, 58, 132, 200, 22, 158, 139, 99, 110, 227>>}]
Link to this function deserialize(rlp)
deserialize(ExRLP.t) :: t

Decodes a block from an RLP encoding. Effectively inverts L_B defined in Eq.(33).

Examples

iex> Blockchain.Block.deserialize([
...>   [<<1::256>>, <<2::256>>, <<3::160>>, <<4::256>>, <<5::256>>, <<6::256>>, <<>>, <<5>>, <<1>>, <<5>>, <<3>>, <<6>>, "Hi mom", <<7::256>>, <<8::64>>],
...>   [[<<5>>, <<6>>, <<7>>, <<1::160>>, <<8>>, "hi", <<27>>, <<9>>, <<10>>]],
...>   [[<<11::256>>, <<12::256>>, <<13::160>>, <<14::256>>, <<15::256>>, <<16::256>>, <<>>, <<5>>, <<1>>, <<5>>, <<3>>, <<6>>, "Hi mom", <<17::256>>, <<18::64>>]]
...> ])
%Blockchain.Block{
  header: %Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<7::256>>, nonce: <<8::64>>},
  transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
  ommers: [%Block.Header{parent_hash: <<11::256>>, ommers_hash: <<12::256>>, beneficiary: <<13::160>>, state_root: <<14::256>>, transactions_root: <<15::256>>, receipts_root: <<16::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<17::256>>, nonce: <<18::64>>}]
}
Link to this function gen_child_block(parent_block, chain, opts \\ [])
gen_child_block(t, Chain.t, [timestamp: EVM.timestamp, gas_limit: EVM.val, beneficiary: EVM.address, extra_data: binary, state_root: EVM.hash]) :: t

Creates a new block from a parent block. This will handle setting the block number, the difficulty and will keep the gas_limit the same as the parent’s block unless specified in opts.

A timestamp is required for difficulty calculation. If it’s not specified, it will default to the current system time.

This function is not directly addressed in the Yellow Paper.

Examples

iex> %Blockchain.Block{header: %Block.Header{state_root: <<1::256>>, number: 100_000, difficulty: 15_500_0000, timestamp: 5_000_000, gas_limit: 500_000}}
...> |> Blockchain.Block.gen_child_block(Blockchain.Test.ropsten_chain(), timestamp: 5010000, extra_data: "hi", beneficiary: <<5::160>>)
%Blockchain.Block{
  header: %Block.Header{
    state_root: <<1::256>>,
    beneficiary: <<5::160>>,
    number: 100_001,
    difficulty: 147_507_383,
    timestamp: 5_010_000,
    gas_limit: 500_000,
    extra_data: "hi"
  }
}

iex> %Blockchain.Block{header: %Block.Header{state_root: <<1::256>>, number: 100_000, difficulty: 1_500_0000, timestamp: 5000, gas_limit: 500_000}}
...> |> Blockchain.Block.gen_child_block(Blockchain.Test.ropsten_chain(), state_root: <<2::256>>, timestamp: 6010, extra_data: "hi", beneficiary: <<5::160>>)
%Blockchain.Block{
  header: %Block.Header{
    state_root: <<2::256>>,
    beneficiary: <<5::160>>,
    number: 100_001,
    difficulty: 142_74_924,
    timestamp: 6010,
    gas_limit: 500_000,
    extra_data: "hi"
  }
}
Link to this function gen_genesis_block(chain, db)
gen_genesis_block(Chain.t, MerklePatriciaTree.DB.db) :: t

Creates a genesis block for a given chain.

The genesis block is specified by parameters in the chain itself. Thus, this function takes no additional parameters.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Chain.load_chain(:ropsten)
iex> Blockchain.Block.gen_genesis_block(chain, db)
%Blockchain.Block{
  header: %Block.Header{
    number: 0,
    timestamp: 0,
    beneficiary: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>,
    difficulty: 1048576,
    extra_data: "55555555555555555555555555555555",
    gas_limit: 16777216,
    parent_hash: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>,
    state_root: <<33, 123, 11, 188, 251, 114, 226, 213, 126, 40, 243, 60, 179, 97, 185, 152, 53, 19, 23, 119, 85, 220, 63, 51, 206, 62, 112, 34, 237, 98, 183, 123>>,
    transactions_root: <<86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33>>,
    receipts_root: <<86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33>>,
    ommers_hash: <<29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71>>,
  },
  ommers: [],
  transactions: []
}

# TODO: Add test case with initial storage
Link to this function get_block(block_hash, db)
get_block(EVM.hash, MerklePatriciaTree.DB.db) ::
  {:ok, t} |
  :not_found

Returns a given block from the database, if the hash exists in the database.

See Blockchain.Block.put_block/2 for details.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> Blockchain.Block.get_block(<<1, 2, 3>>, db)
:not_found

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> Blockchain.Block.put_block(block, db)
iex> Blockchain.Block.get_block(block |> Blockchain.Block.hash, db)
{:ok, %Blockchain.Block{
  transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
  header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
}}
Link to this function get_block_hash_by_steps(curr_block_hash, steps, db)
get_block_hash_by_steps(EVM.hash, non_neg_integer, MerklePatriciaTree.DB.db) ::
  {:ok, EVM.hash} |
  :not_found
get_block_hash_by_steps(EVM.hash, non_neg_integer, MerklePatriciaTree.DB.db) ::
  {:ok, EVM.hash} |
  :not_found

Returns a given block from the database, if the hash exists in the database.

See Blockchain.Block.put_block/2 for details.

Examples

# Legit, current block
iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> Blockchain.Block.put_block(block, db)
iex> Blockchain.Block.get_block_hash_by_steps(block |> Blockchain.Block.hash, 0, db)
{:ok, <<78, 28, 127, 10, 192, 253, 127, 239, 254, 179, 39, 34, 245, 44,
       152, 98, 128, 71, 238, 155, 100, 161, 199, 71, 243, 223, 172, 191,
       74, 99, 128, 63>>}

# Bad, in the future
iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> Blockchain.Block.put_block(block, db)
iex> Blockchain.Block.get_block_hash_by_steps(block |> Blockchain.Block.hash, -1, db)
:not_found

# Bad, before the dawn of time
iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> Blockchain.Block.put_block(block, db)
iex> Blockchain.Block.get_block_hash_by_steps(block |> Blockchain.Block.hash, 6, db)
:not_found

iex> Blockchain.Block.get_block_hash_by_steps(<<1, 2, 3>>, 0, nil)
{:ok, <<1, 2, 3>>}

# Legit, back zero and one
iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block_1 = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> {:ok, block_1_hash} = Blockchain.Block.put_block(block_1, db)
iex> block_2 = %Blockchain.Block{
...>   transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: "hi"}],
...>   header: %Block.Header{number: 6, parent_hash: block_1_hash, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}
...> }
iex> Blockchain.Block.put_block(block_2, db)
iex> Blockchain.Block.get_block_hash_by_steps(block_2 |> Blockchain.Block.hash, 0, db)
{:ok, <<203, 210, 109, 46, 207, 246, 94, 33, 247, 97, 60, 56, 65, 134,
        203, 120, 62, 64, 59, 64, 101, 190, 181, 7, 242, 215, 247, 212,
        107, 12, 92, 9>>}
iex> Blockchain.Block.get_block_hash_by_steps(block_2 |> Blockchain.Block.hash, 1, db)
{:ok, <<78, 28, 127, 10, 192, 253, 127, 239, 254, 179, 39, 34, 245, 44,
        152, 98, 128, 71, 238, 155, 100, 161, 199, 71, 243, 223, 172, 191,
        74, 99, 128, 63>>}
Link to this function get_cumulative_gas(block, db)
get_cumulative_gas(t, atom) :: EVM.Gas.t

Returns the cumulative gas used by a block based on the listed transactions. This is defined in largely in the note after Eq.(66) referenced as l(B_R)_u, or the last receipt’s cumulative gas.

The receipts aren’t directly included in the block, so we’ll need to pull it from the receipts root.

Note: this will case if we do not have a receipt for the most recent transaction.

Examples

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{transactions: [1,2,3,4,5,6,7]}
...> |> Blockchain.Block.put_receipt(6, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
...> |> Blockchain.Block.put_receipt(7, %Blockchain.Transaction.Receipt{state: <<4, 5, 6>>, cumulative_gas: 11, bloom_filter: <<5, 6, 7>>, logs: "hi dad"}, trie.db)
...> |> Blockchain.Block.get_cumulative_gas(trie.db)
11

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{transactions: [1,2,3,4,5,6]}
...> |> Blockchain.Block.put_receipt(6, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
...> |> Blockchain.Block.put_receipt(7, %Blockchain.Transaction.Receipt{state: <<4, 5, 6>>, cumulative_gas: 11, bloom_filter: <<5, 6, 7>>, logs: "hi dad"}, trie.db)
...> |> Blockchain.Block.get_cumulative_gas(trie.db)
10

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.get_cumulative_gas(trie.db)
0

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{transactions: [1,2,3,4,5,6,7,8]}
...> |> Blockchain.Block.put_receipt(6, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
...> |> Blockchain.Block.put_receipt(7, %Blockchain.Transaction.Receipt{state: <<4, 5, 6>>, cumulative_gas: 11, bloom_filter: <<5, 6, 7>>, logs: "hi dad"}, trie.db)
...> |> Blockchain.Block.get_cumulative_gas(trie.db)
** (RuntimeError) cannot find receipt
Link to this function get_ommer(block, i)
get_ommer(t, integer) :: Block.Header.t

Gets an ommer for a given block, based on the ommers_hash.

Examples

iex> %Blockchain.Block{}
...> |> Blockchain.Block.add_ommers_to_block([%Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<7::256>>, nonce: <<8::64>>}])
...> |> Blockchain.Block.get_ommer(0)
%Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: "Hi mom", mix_hash: <<7::256>>, nonce: <<8::64>>}
Link to this function get_parent_block(block, db)
get_parent_block(t, MerklePatriciaTree.DB.db) ::
  {:ok, t} |
  :genesis |
  :not_found

Returns the parent node for a given block, if it exists.

We assume a block is a genesis block if it does not have a valid parent_hash set.

Examples

iex> Blockchain.Block.get_parent_block(%Blockchain.Block{header: %Block.Header{number: 0}}, nil)
:genesis

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
iex> Blockchain.Block.put_block(block, db)
iex> Blockchain.Block.get_parent_block(%Blockchain.Block{header: %Block.Header{parent_hash: block |> Blockchain.Block.hash}}, db)
{:ok, %Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}}

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
iex> Blockchain.Block.get_parent_block(%Blockchain.Block{header: %Block.Header{parent_hash: block |> Blockchain.Block.hash}}, db)
:not_found
Link to this function get_receipt(block, i, db)

Returns a given receipt from a block. This is based on the receipts root where all receipts are stored for the given block.

Examples

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_receipt(6, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
...> |> Blockchain.Block.put_receipt(7, %Blockchain.Transaction.Receipt{state: <<4, 5, 6>>, cumulative_gas: 11, bloom_filter: <<5, 6, 7>>, logs: "hi dad"}, trie.db)
...> |> Blockchain.Block.get_receipt(6, trie.db)
%Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_receipt(6, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
...> |> Blockchain.Block.get_receipt(7, trie.db)
nil

Returns a trie rooted at the state_root of a given block.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db(:get_state)
iex> Blockchain.Block.get_state(%Blockchain.Block{header: %Block.Header{state_root: <<5::256>>}}, db)
%MerklePatriciaTree.Trie{root_hash: <<5::256>>, db: {MerklePatriciaTree.DB.ETS, :get_state}}
Link to this function get_transaction(block, i, db)
get_transaction(t, integer, MerklePatriciaTree.DB.db) ::
  Blockchain.Transaction.t |
  nil

Returns a given transaction from a block. This is based on the transactions root where all transactions are stored for the given block.

Examples

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_transaction(6, %Blockchain.Transaction{nonce: 1, v: 1, r: 2, s: 3}, trie.db)
...> |> Blockchain.Block.put_transaction(7, %Blockchain.Transaction{nonce: 2, v: 1, r: 2, s: 3}, trie.db)
...> |> Blockchain.Block.get_transaction(6, trie.db)
%Blockchain.Transaction{nonce: 1, v: 1, r: 2, s: 3}

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_transaction(6, %Blockchain.Transaction{data: "", gas_limit: 100000, gas_price: 3, init: <<96, 3, 96, 5, 1, 96, 0, 82, 96, 0, 96, 32, 243>>, nonce: 5, r: 110274197540583527170567040609004947678532096020311055824363076718114581104395, s: 15165203061950746568488278734700551064641299899120962819352765267479743108366, to: "", v: 27, value: 5}, trie.db)
...> |> Blockchain.Block.get_transaction(6, trie.db)
%Blockchain.Transaction{data: "", gas_limit: 100000, gas_price: 3, init: <<96, 3, 96, 5, 1, 96, 0, 82, 96, 0, 96, 32, 243>>, nonce: 5, r: 110274197540583527170567040609004947678532096020311055824363076718114581104395, s: 15165203061950746568488278734700551064641299899120962819352765267479743108366, to: "", v: 27, value: 5}

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_transaction(6, %Blockchain.Transaction{nonce: 1, v: 1, r: 2, s: 3}, trie.db)
...> |> Blockchain.Block.get_transaction(7, trie.db)
nil
Link to this function get_transaction_count(block)
get_transaction_count(t) :: integer

Returns the total number of transactions included in a block. This is based on the transaction list for a given block.

Examples

iex> Blockchain.Block.get_transaction_count(%Blockchain.Block{transactions: [%Blockchain.Transaction{}, %Blockchain.Transaction{}]})
2
Link to this function hash(block)
hash(t) :: EVM.hash

Computes hash of a block, which is simply the hash of the serialized block after applying RLP encoding.

This is defined in Eq.(37) of the Yellow Paper.

Examples

iex> %Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
...> |> Blockchain.Block.hash()
<<78, 28, 127, 10, 192, 253, 127, 239, 254, 179, 39, 34, 245, 44, 152, 98, 128, 71, 238, 155, 100, 161, 199, 71, 243, 223, 172, 191, 74, 99, 128, 63>>

iex> %Blockchain.Block{header: %Block.Header{number: 0, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
...> |> Blockchain.Block.hash()
<<218, 225, 46, 241, 196, 160, 136, 96, 109, 216, 73, 167, 92, 174, 91, 228, 85, 112, 234, 129, 99, 200, 158, 61, 223, 166, 165, 132, 187, 24, 142, 193>>
Link to this function identity(block)
identity(t) :: t
Link to this function is_fully_valid?(block, chain, parent_block, db)
is_fully_valid?(t, Chain.t, t, MerklePatriciaTree.DB.db) ::
  :valid |
  {:invalid, [atom]}

Checks the validity of a block, including the validity of the header and the transactions. This should verify that we should accept the authenticity of a block.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Test.ropsten_chain()
iex> Blockchain.Block.gen_genesis_block(chain, db)
...> |> Blockchain.Block.add_rewards_to_block(db)
...> |> Blockchain.Block.is_fully_valid?(chain, nil, db)
:valid

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Test.ropsten_chain()
iex> parent = Blockchain.Block.gen_genesis_block(chain, db)
...> child = Blockchain.Block.gen_child_block(parent, chain)
...> Blockchain.Block.is_fully_valid?(child, chain, nil, db)
{:errors, [:non_genesis_block_requires_parent]}

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Test.ropsten_chain()
iex> parent = Blockchain.Block.gen_genesis_block(chain, db)
iex> child = Blockchain.Block.gen_child_block(parent, chain)
...>         |> Blockchain.Block.put_header(:beneficiary, <<0x05::160>>)
...>         |> Blockchain.Block.add_rewards_to_block(db)
iex> Blockchain.Block.is_fully_valid?(child, chain, parent, db)
:valid
Link to this function is_genesis?(block)
is_genesis?(t) :: boolean

Returns whether or not a block is a genesis block, based on block number.

Examples

iex> Blockchain.Block.is_genesis?(%Blockchain.Block{header: %Block.Header{number: 0}})
true

iex> Blockchain.Block.is_genesis?(%Blockchain.Block{header: %Block.Header{number: 1}})
false
Link to this function is_holistic_valid?(block, chain, parent_block, db)
is_holistic_valid?(t, Chain.t, t | nil, MerklePatriciaTree.DB.db) ::
  :valid |
  {:invalid, [atom]}

Determines whether or not a block is valid. This is defined in Eq.(29) of the Yellow Paper.

Note, this is a serious intensive operation, and not faint of heart (since we need to run all transaction in the block to validate the block).

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Test.ropsten_chain()
iex> beneficiary = <<0x05::160>>
iex> private_key = <<1::256>>
iex> sender = <<126, 95, 69, 82, 9, 26, 105, 18, 93, 93, 252, 183, 184, 194, 101, 144, 41, 57, 91, 223>> # based on simple private key
iex> machine_code = EVM.MachineCode.compile([:push1, 3, :push1, 5, :add, :push1, 0x00, :mstore, :push1, 0, :push1, 32, :return])
iex> trx = %Blockchain.Transaction{nonce: 5, gas_price: 3, gas_limit: 100_000, to: <<>>, value: 5, init: machine_code}
...>       |> Blockchain.Transaction.Signature.sign_transaction(private_key)
iex> state = MerklePatriciaTree.Trie.new(db)
...>         |> Blockchain.Account.put_account(sender, %Blockchain.Account{balance: 400_000, nonce: 5})
iex> parent_block = %Blockchain.Block{header: %Block.Header{number: 50, state_root: state.root_hash, difficulty: 50_000, timestamp: 9999, gas_limit: 125_001}}
iex> block = Blockchain.Block.gen_child_block(parent_block, chain, beneficiary: beneficiary, timestamp: 10000, gas_limit: 125_001)
...>         |> Blockchain.Block.add_transactions_to_block([trx], db)
...>         |> Blockchain.Block.add_rewards_to_block(db)
iex> Blockchain.Block.is_holistic_valid?(block, chain, parent_block, db)
:valid

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> chain = Blockchain.Test.ropsten_chain()
iex> beneficiary = <<0x05::160>>
iex> private_key = <<1::256>>
iex> sender = <<126, 95, 69, 82, 9, 26, 105, 18, 93, 93, 252, 183, 184, 194, 101, 144, 41, 57, 91, 223>> # based on simple private key
iex> machine_code = EVM.MachineCode.compile([:push1, 3, :push1, 5, :add, :push1, 0x00, :mstore, :push1, 0, :push1, 32, :return])
iex> trx = %Blockchain.Transaction{nonce: 5, gas_price: 3, gas_limit: 100_000, to: <<>>, value: 5, init: machine_code}
...>       |> Blockchain.Transaction.Signature.sign_transaction(private_key)
iex> state = MerklePatriciaTree.Trie.new(db)
...>         |> Blockchain.Account.put_account(sender, %Blockchain.Account{balance: 400_000, nonce: 5})
iex> parent_block = %Blockchain.Block{header: %Block.Header{number: 50, state_root: state.root_hash, difficulty: 50_000, timestamp: 9999, gas_limit: 125_001}}
iex> block = Blockchain.Block.gen_child_block(parent_block, chain, beneficiary: beneficiary, timestamp: 10000, gas_limit: 125_001)
...>         |> Blockchain.Block.add_transactions_to_block([trx], db)
iex> %{block | header: %{block.header | state_root: <<1,2,3>>, ommers_hash: <<2,3,4>>, transactions_root: <<3,4,5>>, receipts_root: <<4,5,6>>}}
...> |> Blockchain.Block.is_holistic_valid?(chain, parent_block, db)
{:invalid, [:state_root_mismatch, :ommers_hash_mismatch, :transactions_root_mismatch, :receipts_root_mismatch]}
Link to this function put_block(block, db)
put_block(t, MerklePatriciaTree.DB.db) :: {:ok, EVM.hash}

Stores a given block in the database and returns the block hash.

This should be used if we ever want to retrieve that block in the future.

Note: Blocks are identified by a hash of the block header,

  thus we will only get the same block back if the header
  matches what we stored.

Examples

iex> db = MerklePatriciaTree.Test.random_ets_db()
iex> block = %Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
iex> Blockchain.Block.put_block(block, db)
{:ok, <<78, 28, 127, 10, 192, 253, 127, 239, 254, 179, 39, 34, 245, 44, 152, 98, 128, 71, 238, 155, 100, 161, 199, 71, 243, 223, 172, 191, 74, 99, 128, 63>>}
iex> {:ok, serialized_block} = MerklePatriciaTree.DB.get(db, block |> Blockchain.Block.hash)
iex> serialized_block |> ExRLP.decode |> Blockchain.Block.deserialize()
%Blockchain.Block{header: %Block.Header{number: 5, parent_hash: <<1, 2, 3>>, beneficiary: <<2, 3, 4>>, difficulty: 100, timestamp: 11, mix_hash: <<1>>, nonce: <<2>>}}
Link to this function put_header(block, key, value)
put_header(t, any, any) :: t

Sets a given block header field as a shortcut when we want to change a single field.

Examples

iex> %Blockchain.Block{}
...> |> Blockchain.Block.put_header(:number, 5)
%Blockchain.Block{
  header: %Block.Header{
    number: 5
  }
}
Link to this function put_receipt(block, i, receipt, db)

Updates a block by adding a receipt to the list of receipts at position i.

Examples

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> block = Blockchain.Block.put_receipt(%Blockchain.Block{}, 5, %Blockchain.Transaction.Receipt{state: <<1, 2, 3>>, cumulative_gas: 10, bloom_filter: <<2, 3, 4>>, logs: "hi mom"}, trie.db)
iex> MerklePatriciaTree.Trie.into(block.header.receipts_root, trie)
...> |> MerklePatriciaTree.Trie.Inspector.all_values()
[{<<5>>, <<208, 131, 1, 2, 3, 10, 131, 2, 3, 4, 134, 104, 105, 32, 109, 111, 109>>}]
Link to this function put_transaction(block, i, trx, db)
put_transaction(t, integer, Blockchain.Transaction.t, MerklePatriciaTree.DB.db) :: t

Updates a block by adding a transaction to the list of transactions and updating the transactions_root in the header at position i, which should be equilvant to the current number of transactions.

Examples

iex> trie = MerklePatriciaTree.Trie.new(MerklePatriciaTree.Test.random_ets_db())
iex> block = Blockchain.Block.put_transaction(%Blockchain.Block{}, 0, %Blockchain.Transaction{nonce: 1, v: 2, r: 3, s: 4}, trie.db)
iex> block.transactions
[%Blockchain.Transaction{nonce: 1, v: 2, r: 3, s: 4}]
iex> MerklePatriciaTree.Trie.into(block.header.transactions_root, trie)
...> |> MerklePatriciaTree.Trie.Inspector.all_values()
[{<<0x80>>, <<201, 1, 128, 128, 128, 128, 128, 2, 3, 4>>}]
Link to this function serialize(block)
serialize(t) :: ExRLP.t

Encodes a block such that it can be represented in RLP encoding. This is defined as L_B Eq.(33) in the Yellow Paper.

Examples

iex> Blockchain.Block.serialize(%Blockchain.Block{ …> header: %Block.Header{parent_hash: <<1::256>>, ommers_hash: <<2::256>>, beneficiary: <<3::160>>, state_root: <<4::256>>, transactions_root: <<5::256>>, receipts_root: <<6::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: “Hi mom”, mix_hash: <<7::256>>, nonce: <<8::64>>}, …> transactions: [%Blockchain.Transaction{nonce: 5, gas_price: 6, gas_limit: 7, to: <<1::160>>, value: 8, v: 27, r: 9, s: 10, data: “hi”}], …> ommers: [%Block.Header{parent_hash: <<11::256>>, ommers_hash: <<12::256>>, beneficiary: <<13::160>>, state_root: <<14::256>>, transactions_root: <<15::256>>, receipts_root: <<16::256>>, logs_bloom: <<>>, difficulty: 5, number: 1, gas_limit: 5, gas_used: 3, timestamp: 6, extra_data: “Hi mom”, mix_hash: <<17::256>>, nonce: <<18::64>>}] …> }) [

[<<1::256>>, <<2::256>>, <<3::160>>, <<4::256>>, <<5::256>>, <<6::256>>, <<>>, 5, 1, 5, 3, 6, "Hi mom", <<7::256>>, <<8::64>>],
[[<<5>>, <<6>>, <<7>>, <<1::160>>, <<8>>, "hi", <<27>>, <<9>>, <<10>>]],
[[<<11::256>>, <<12::256>>, <<13::160>>, <<14::256>>, <<15::256>>, <<16::256>>, <<>>, 5, 1, 5, 3, 6, "Hi mom", <<17::256>>, <<18::64>>]]

]

iex> Blockchain.Block.serialize(%Blockchain.Block{}) [

[
  nil,
  <<29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71>>,
  nil,
  <<86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33>>,
  <<86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33>>,
  <<86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33>>,
  <<0::2048>>,
  nil,
  nil,
  0,
  0,
  nil,
  "",
  nil,
  nil
],
[],
[]

]

Link to this function set_block_difficulty(block, chain, parent_block)
set_block_difficulty(t, Chain.t, t) :: t

Set the difficulty of a new block based on Eq.(39), better defined in Block.Header`. # TODO: Validate these results ## Examples iex> Blockchain.Block.set_block_difficulty( …> %Blockchain.Block{header: %Block.Header{number: 0, timestamp: 0}}, …> Blockchain.Test.ropsten_chain(), …> nil …> ) %Blockchain.Block{header: %Block.Header{number: 0, timestamp: 0, difficulty: 1_048_576}} iex> Blockchain.Block.set_block_difficulty( …> %Blockchain.Block{header: %Block.Header{number: 1, timestamp: 1_479_642_530}}, …> Blockchain.Test.ropsten_chain(), …> %Blockchain.Block{header: %Block.Header{number: 0, timestamp: 0, difficulty: 1_048_576}} …> ) %Blockchain.Block{header: %Block.Header{number: 1, timestamp: 1_479_642_530, difficulty: 997_888}}

Link to this function set_block_gas_limit(block, chain, parent_block, gas_limit)
set_block_gas_limit(t, Chain.t, t, EVM.Gas.t) :: t

Sets the gas limit of a given block, or raises if the block limit is not acceptable. The validity check is defined in Eq.(45), Eq.(46) and Eq.(47) of the Yellow Paper.

Examples

iex> Blockchain.Block.set_block_gas_limit(
...>   %Blockchain.Block{header: %Block.Header{}},
...>   Blockchain.Test.ropsten_chain(),
...>   %Blockchain.Block{header: %Block.Header{gas_limit: 1_000_000}},
...>   1_000_500
...> )
%Blockchain.Block{header: %Block.Header{gas_limit: 1_000_500}}

iex> Blockchain.Block.set_block_gas_limit(
...>   %Blockchain.Block{header: %Block.Header{}},
...>   Blockchain.Test.ropsten_chain(),
...>   %Blockchain.Block{header: %Block.Header{gas_limit: 1_000_000}},
...>   2_000_000
...> )
** (RuntimeError) Block gas limit not valid
Link to this function set_block_number(block, parent_block)
set_block_number(t, t) :: t

Calculates the number for a new block. This implements Eq.(38) from the Yellow Paper.

Examples

iex> Blockchain.Block.set_block_number(%Blockchain.Block{header: %Block.Header{extra_data: "hello"}}, %Blockchain.Block{header: %Block.Header{number: 32}})
%Blockchain.Block{header: %Block.Header{number: 33, extra_data: "hello"}}
Link to this function set_state(block, trie)
set_state(t, MerklePatriciaTree.Trie.t) :: t

Sets the state_root of a given block from a trie.

Examples

iex> trie = %MerklePatriciaTree.Trie{root_hash: <<5::256>>, db: {MerklePatriciaTree.DB.ETS, :get_state}}
iex> Blockchain.Block.set_state(%Blockchain.Block{}, trie)
%Blockchain.Block{header: %Block.Header{state_root: <<5::256>>}}