blockchain v0.1.5 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
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
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>>
}
}
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}]
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>>}]
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>>}]
}
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"
}
}
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
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>>}
}}
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>>}
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
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>>}
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
get_receipt(t, integer, MerklePatriciaTree.DB.db) :: Blockchain.Transaction.Receipt.t | nil
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
get_state(t, MerklePatriciaTree.DB.db) :: MerklePatriciaTree.Trie.t
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}}
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
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
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>>
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
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
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]}
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>>}}
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
}
}
put_receipt(t, integer, Blockchain.Transaction.Receipt.t, MerklePatriciaTree.DB.db) :: t
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>>}]
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>>}]
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
],
[],
[]
]
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}}
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
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"}}