Browse Source

Verification and state updating.

cl-refactor
Gav Wood 11 years ago
parent
commit
b679d9d829
  1. 6
      eth/main.cpp
  2. 65
      libethereum/BlockChain.cpp
  3. 25
      libethereum/BlockChain.h
  4. 43
      libethereum/BlockInfo.cpp
  5. 11
      libethereum/BlockInfo.h
  6. 61
      libethereum/Instruction.h
  7. 13
      libethereum/RLP.cpp
  8. 2
      libethereum/RLP.h
  9. 61
      libethereum/State.cpp
  10. 25
      libethereum/State.h

6
eth/main.cpp

@ -37,7 +37,8 @@ int main()
// s.restore(); // TODO: Implement - key optimisation.
// Synchronise the state according to the block chain - i.e. replay all transactions, in order. Will take a while if the state isn't restored.
s.sync(bc, tq);
s.sync(bc);
s.sync(tq);
PeerNetwork net; // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required.
while (true)
@ -55,7 +56,8 @@ int main()
// This might mean reverting to an earlier state and replaying some blocks, or, (worst-case:
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
s.sync(bc, tq); // Resynchronise state with block chain & trans
s.sync(bc); // Resynchronise state with block chain & trans
s.sync(tq);
// Mine for a while.
if (s.mine(100))

65
libethereum/BlockChain.cpp

@ -24,6 +24,7 @@
#include "Exceptions.h"
#include "Dagger.h"
#include "BlockInfo.h"
#include "State.h"
#include "BlockChain.h"
using namespace std;
using namespace eth;
@ -47,9 +48,9 @@ u256s BlockChain::blockChain(u256Set const& _earlyExit) const
// TODO: return the current valid block chain from most recent to genesis.
// TODO: arguments for specifying a set of early-ends
u256s ret;
ret.reserve(m_numberAndParent[m_lastBlockHash].first + 1);
ret.reserve(m_details[m_lastBlockHash].number + 1);
auto i = m_lastBlockHash;
for (; i != m_genesisHash && !_earlyExit.count(i); i = m_numberAndParent[i].second)
for (; i != m_genesisHash && !_earlyExit.count(i); i = m_details[i].parent)
ret.push_back(i);
ret.push_back(i);
return ret;
@ -57,52 +58,62 @@ u256s BlockChain::blockChain(u256Set const& _earlyExit) const
void BlockChain::import(bytes const& _block)
{
BlockInfo bi;
try
{
// VERIFY: populates from the block and checks the block is internally coherent.
bi.populate(&_block);
BlockInfo bi(&_block);
bi.verifyInternals(&_block);
auto newHash = eth::sha3(_block);
// Check block doesn't already exist first!
if (m_numberAndParent.count(newHash))
if (m_details.count(newHash))
return;
// Work out its number as the parent's number + 1
auto it = m_numberAndParent.find(bi.parentHash);
if (it == m_numberAndParent.end())
auto it = m_details.find(bi.parentHash);
if (it == m_details.end())
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
return;
bi.number = it->second.first + 1;
// Check family:
BlockInfo bip(block(bi.parentHash));
bi.verifyParent(bip);
// TODO: check transactions are valid and that they result in a state equivalent to our state_root.
// Check transactions are valid and that they result in a state equivalent to our state_root.
// this saves us from an embarrassing exit later.
State s(bi.coinbaseAddress);
s.sync(*this, bi.parentHash);
for (auto const& i: RLP(_block)[1])
s.execute(i.data());
if (s.rootHash() != bi.stateRoot)
throw InvalidStateRoot();
// Initalise total difficulty calculation.
u256 td = it->second.totalDifficulty + bi.difficulty;
// Check uncles.
for (auto const& i: RLP(_block)[2])
{
auto it = m_numberAndParent.find(i.toInt<u256>());
if (it == m_numberAndParent.end())
return; // Don't (yet) have the uncle in our list.
if (it->second.second != bi.parentHash)
BlockInfo uncle(i.data());
if (it->second.parent != bi.parentHash)
throw InvalidUncle();
uncle.verifyParent(bip);
td += uncle.difficulty;
}
// Insert into DB
m_numberAndParent[newHash] = make_pair((uint)bi.number, bi.parentHash);
m_children.insert(make_pair(bi.parentHash, newHash));
ldb::WriteOptions o;
m_db->Put(o, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block));
// All ok - insert into DB
m_details[newHash] = BlockDetails{(uint)it->second.number + 1, bi.parentHash, td};
// This might be the new last block; count back through ancestors to common shared ancestor and compare to current.
// TODO: Use GHOST algorithm.
m_children.insert(make_pair(bi.parentHash, newHash));
m_db->Put(m_writeOptions, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block));
// This might be the new last block...
if (td > m_details[m_lastBlockHash].totalDifficulty)
m_lastBlockHash = newHash;
}
catch (...)
{
@ -115,13 +126,15 @@ bytesConstRef BlockChain::block(u256 _hash) const
{
if (_hash == m_genesisHash)
return &m_genesisBlock;
return &m_genesisBlock;
m_db->Get(m_readOptions, ldb::Slice(toBigEndianString(_hash)), &m_cache[_hash]);
return bytesConstRef(&m_cache[_hash]);
}
u256 BlockChain::lastBlockNumber() const
BlockDetails const& BlockChain::details(u256 _h) const
{
if (m_lastBlockHash == m_genesisHash)
return 0;
return m_numberAndParent[m_lastBlockHash].first;
auto it = m_details.find(_h);
if (it == m_details.end())
return NullBlockDetails;
return it->second;
}

25
libethereum/BlockChain.h

@ -28,6 +28,15 @@ namespace ldb = leveldb;
namespace eth
{
struct BlockDetails
{
uint number;
u256 totalDifficulty;
u256 parent;
};
static const BlockDetails NullBlockDetails({0, 0, 0});
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
*/
@ -47,28 +56,34 @@ public:
/// Import block into disk-backed DB
void import(bytes const& _block);
/// Get the last block of the longest chain.
bytesConstRef lastBlock() const { return block(m_lastBlockHash); }
/// Get the full block chain, according to the GHOST algo and the blocks available in the db.
u256s blockChain(u256Set const& _earlyExit) const;
/// Get the number of the last block of the longest chain.
u256 lastBlockNumber() const;
BlockDetails const& details(u256 _hash) const;
/// Get a given block (RLP format).
bytesConstRef block(u256 _hash) const;
/// Get a given block (RLP format).
u256 currentHash() const { return m_lastBlockHash; }
private:
/// Get fully populated from disk DB.
mutable std::map<u256, std::pair<uint, u256>> m_numberAndParent;
mutable std::map<u256, BlockDetails> m_details;
mutable std::multimap<u256, u256> m_children;
mutable std::map<u256, std::string> m_cache;
ldb::DB* m_db;
/// Hash of the last (valid) block on the longest chain.
u256 m_lastBlockHash;
u256 m_genesisHash;
bytes m_genesisBlock;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
};
}

43
libethereum/BlockInfo.cpp

@ -29,14 +29,13 @@ using namespace eth;
BlockInfo* BlockInfo::s_genesis = nullptr;
BlockInfo::BlockInfo()
BlockInfo::BlockInfo(): timestamp(Invalid256)
{
number = Invalid256;
}
BlockInfo::BlockInfo(bytesConstRef _block, u256 _number)
BlockInfo::BlockInfo(bytesConstRef _block)
{
populate(_block, _number);
populate(_block);
}
bytes BlockInfo::createGenesisBlock()
@ -57,13 +56,11 @@ u256 BlockInfo::headerHashWithoutNonce() const
void BlockInfo::populateGenesis()
{
bytes genesisBlock = createGenesisBlock();
populate(&genesisBlock, 0);
populate(&genesisBlock);
}
void BlockInfo::populate(bytesConstRef _block, u256 _number)
void BlockInfo::populate(bytesConstRef _block)
{
number = _number;
RLP root(_block);
try
{
@ -82,6 +79,11 @@ void BlockInfo::populate(bytesConstRef _block, u256 _number)
{
throw InvalidBlockFormat();
}
// check it hashes according to proof of work.
Dagger d(headerHashWithoutNonce());
if (d.eval(nonce) >= difficulty)
throw InvalidNonce();
}
void BlockInfo::verifyInternals(bytesConstRef _block) const
@ -93,22 +95,11 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
if (sha3Uncles != sha3(root[2].data()))
throw InvalidUnclesHash();
// check it hashes according to proof of work.
Dagger d(headerHashWithoutNonce());
if (d.eval(nonce) >= difficulty)
throw InvalidNonce();
}
u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
{
/*
D(genesis_block) = 2^36
D(block) =
if block.timestamp >= block.parent.timestamp + 42: D(block.parent) - floor(D(block.parent) / 1024)
else: D(block.parent) + floor(D(block.parent) / 1024)
*/
if (number == 0)
if (!parentHash)
return (u256)1 << 36;
else
return timestamp >= _parent.timestamp + 42 ? _parent.difficulty - (_parent.difficulty >> 10) : (_parent.difficulty + (_parent.difficulty >> 10));
@ -116,15 +107,11 @@ u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
void BlockInfo::verifyParent(BlockInfo const& _parent) const
{
if (number == 0)
// Genesis block - no parent.
return;
// Check timestamp is after previous timestamp.
if (_parent.timestamp <= _parent.timestamp)
throw InvalidTimestamp();
// Check difficulty is correct given the two timestamps.
if (difficulty != calculateDifficulty(_parent))
throw InvalidDifficulty();
// Check timestamp is after previous timestamp.
if (parentHash && _parent.timestamp <= _parent.timestamp)
throw InvalidTimestamp();
}

11
libethereum/BlockInfo.h

@ -32,23 +32,22 @@ public:
u256 hash;
u256 parentHash;
u256 sha3Uncles;
u256 coinbaseAddress;
u160 coinbaseAddress;
u256 stateRoot;
u256 sha3Transactions;
u256 difficulty;
u256 timestamp;
u256 nonce;
u256 number;
BlockInfo();
explicit BlockInfo(bytesConstRef _block, u256 _number = 0);
explicit BlockInfo(bytesConstRef _block);
explicit operator bool() { return number != Invalid256; }
explicit operator bool() { return timestamp != Invalid256; }
bool operator==(BlockInfo const& _cmp) const { return hash == _cmp.hash && parentHash == _cmp.parentHash && nonce == _cmp.nonce && number == _cmp.number; }
bool operator==(BlockInfo const& _cmp) const { return hash == _cmp.hash && parentHash == _cmp.parentHash && nonce == _cmp.nonce; }
static BlockInfo const& genesis() { if (!s_genesis) (s_genesis = new BlockInfo)->populateGenesis(); return *s_genesis; }
void populate(bytesConstRef _block, u256 _number = 0);
void populate(bytesConstRef _block);
void verifyInternals(bytesConstRef _block) const;
void verifyParent(BlockInfo const& _parent) const;

61
libethereum/Instruction.h

@ -9,22 +9,22 @@ namespace eth
enum class Instruction: uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< Rx Ry Rz - sets Rz <- Rx + Ry mod 2^256
SUB, ///< Rx Ry Rz - sets Rz <- Rx - Ry mod 2^256
MUL, ///< Rx Ry Rz - sets Rz <- Rx * Ry mod 2^256
DIV, ///< Rx Ry Rz - sets Rz <- floor(Rx / Ry)
SDIV, ///< Rx Ry Rz - like DIV, except it treats values above 2^255 as negative (ie. 2^256 - x -> -x)
MOD, ///< Rx Ry Rz - sets Rz <- Rx mod Ry
SMOD, ///< Rx Ry Rz - like MOD, but for signed values just like SDIV (using Python's convention with negative numbers)
EXP, ///< Rx Ry Rz - sets Rz <- Rx ^ Ry mod 2^256
NEG, ///< Rx Ry - sets Ry <- 2^256 - Rx
LT, ///< Rx Ry Rz - sets Rz <- 1 if Rx < Ry else 0
LE, ///< Rx Ry Rz - sets Rz <- 1 if Rx <= Ry else 0
GT, ///< Rx Ry Rz - sets Rz <- 1 if Rx > Ry else 0
GE, ///< Rx Ry Rz - sets Rz <- 1 if Rx >= Ry else 0
EQ, ///< Rx Ry Rz - sets Rz <- 1 if Rx = Ry else 0
NOT, ///< Rx Ry - sets Ry <- 1 if Rx = 0 else 0
MYADDRESS = 0x10, ///< Rx - sets Rx to the contract's own address
ADD,
SUB,
MUL,
DIV,
SDIV,
MOD,
SMOD,
EXP,
NEG,
LT,
LE,
GT,
GE,
EQ,
NOT,
MYADDRESS = 0x10,
TXSENDER, ///< pushes the transaction sender
TXVALUE , ///< pushes the transaction value
TXFEE, ///< pushes the transaction fee
@ -35,13 +35,14 @@ enum class Instruction: uint8_t
BLK_TIMESTAMP, ///< pushes the timestamp of the current block
BLK_NUMBER, ///< pushes the current block number
BLK_DIFFICULTY, ///< pushes the difficulty of the current block
SHA256 = 0x20, ///< sets Ry <- SHA256(Rx)
RIPEMD160, ///< Rx Ry - sets Ry <- RIPEMD160(Rx)
ECMUL, ///< Rx Ry Rz Ra Rb - sets (Ra, Rb) = Rz * (Rx, Ry) in secp256k1, using (0,0) for the point at infinity
ECADD, ///< Rx Ry Rz Ra Rb Rc - sets (Rb, Rc) = (Rx, Ry) + (Ra, Rb)
ECSIGN, ///< Rx Ry Rz Ra Rb - sets(Rz, Ra, Rb)as the(r,s,prefix)values of an Electrum-style RFC6979 deterministic signature ofRxwith private keyRy`
ECRECOVER, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
ECVALID, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
SHA256 = 0x20,
RIPEMD160,
ECMUL,
ECADD,
ECSIGN,
ECRECOVER,
ECVALID,
SHA3,
PUSH = 0x30,
POP,
DUP,
@ -50,13 +51,13 @@ enum class Instruction: uint8_t
SWAPN,
LOAD,
STORE,
JMP = 0x40, ///< Rx - sets the index pointer to the value at Rx
JMPI, ///< Rx Ry - if Rx != 0, sets the index pointer to Ry
IND, ///< pushes the index pointer.
EXTRO = 0x50, ///< Rx Ry Rz - looks at the contract at address Rx and its memory state Ry, and outputs the result to Rz
BALANCE, ///< Rx - returns the ether balance of address Rx
MKTX = 0x60, ///< Rx Ry Rz Rw Rv - sends Ry ether to Rx plus Rz fee with Rw data items starting from memory index Rv (and then reading to (Rv + 1), (Rv + 2) etc). Note that if Rx = 0 then this creates a new contract.
SUICIDE = 0xff ///< Rx - destroys the contract and clears all memory, sending the entire balance plus the negative fee from clearing memory minus TXFEE to the address
JMP = 0x40,
JMPI,
IND,
EXTRO = 0x50,
BALANCE,
MKTX = 0x60,
SUICIDE = 0xff
};
}

13
libethereum/RLP.cpp

@ -167,6 +167,19 @@ RLPStream& RLPStream::append(uint _i)
return *this;
}
RLPStream& RLPStream::append(u160 _i)
{
if (_i < 0x18)
m_out.push_back((byte)_i);
else
{
auto br = bytesRequired(_i);
m_out.push_back(br + 0x17); // max 8 bytes.
pushInt(_i, br);
}
return *this;
}
RLPStream& RLPStream::append(u256 _i)
{
if (_i < 0x18)

2
libethereum/RLP.h

@ -273,6 +273,7 @@ public:
/// Append given data to the byte stream.
RLPStream& append(uint _s);
RLPStream& append(u160 _s);
RLPStream& append(u256 _s);
RLPStream& append(bigint _s);
RLPStream& append(std::string const& _s);
@ -282,6 +283,7 @@ public:
/// Shift operators for appending data items.
RLPStream& operator<<(uint _i) { return append(_i); }
RLPStream& operator<<(u160 _i) { return append(_i); }
RLPStream& operator<<(u256 _i) { return append(_i); }
RLPStream& operator<<(bigint _i) { return append(_i); }
RLPStream& operator<<(char const* _s) { return append(std::string(_s)); }

61
libethereum/State.cpp

@ -21,6 +21,7 @@
#include <secp256k1.h>
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#include <random>
#include "Trie.h"
@ -39,22 +40,26 @@ u256 const State::c_cryptoFee = 0;
u256 const State::c_newContractFee = 0;
u256 const State::c_txFee = 0;
State::State(Address _minerAddress): m_minerAddress(_minerAddress)
State::State(Address _coinbaseAddress): m_coinbaseAddress(_coinbaseAddress)
{
secp256k1_start();
m_previousBlock = BlockInfo::genesis();
m_currentBlock.number = 1;
}
void State::sync(BlockChain const& _bc, TransactionQueue& _tq)
void State::sync(BlockChain const& _bc)
{
// BLOCK
sync(_bc, _bc.currentHash());
}
void State::sync(BlockChain const& _bc, u256 _block)
{
// BLOCK
BlockInfo bi;
try
{
bi.populate(_bc.lastBlock(), _bc.lastBlockNumber());
bi.verifyInternals(_bc.lastBlock());
auto b = _bc.block(_block);
bi.populate(b);
bi.verifyInternals(_bc.block(_block));
}
catch (...)
{
@ -71,7 +76,7 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq)
m_current.clear();
m_transactions.clear();
m_currentBlock = BlockInfo();
m_currentBlock.number = m_previousBlock.number + 1;
++m_currentNumber;
}
else if (bi == m_previousBlock)
{
@ -81,9 +86,9 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq)
else
{
// New blocks available, or we've switched to a different branch. All change.
// TODO: Find most recent state dump and replay what's left.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
std::vector<u256> l = _bc.blockChain(u256Set()); // TODO: take u256Set "restorePoints" argument so it needs only give us the chain up until some restore point in the past where we stored the state.
std::vector<u256> l = _bc.blockChain(u256Set());
if (l.back() == BlockInfo::genesis().hash)
{
@ -102,12 +107,14 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq)
m_transactions.clear();
m_current.clear();
m_currentBlock = BlockInfo();
m_currentBlock.number = m_previousBlock.number + 1;
m_currentNumber = _bc.details(_bc.currentHash()).number + 1;
}
}
void State::sync(TransactionQueue& _tq)
{
// TRANSACTIONS
auto ts = _tq.transactions();
for (auto const& i: ts)
if (!m_transactions.count(i.first))
@ -134,7 +141,7 @@ void State::playback(bytesConstRef _block)
{
try
{
m_currentBlock.populate(_block, m_previousBlock.number + 1);
m_currentBlock.populate(_block);
m_currentBlock.verifyInternals(_block);
if (m_currentBlock.parentHash != m_previousBlock.hash)
throw InvalidParentHash();
@ -145,7 +152,7 @@ void State::playback(bytesConstRef _block)
execute(i.data());
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot != currentHash())
if (m_currentBlock.stateRoot != rootHash())
throw InvalidStateRoot();
m_previousBlock = m_currentBlock;
@ -158,9 +165,9 @@ void State::playback(bytesConstRef _block)
}
}
u256 State::currentHash() const
u256 State::rootHash() const
{
// TODO!
// TODO.
return 0;
}
@ -262,7 +269,7 @@ void State::execute(Transaction const& _t, Address _sender)
{
subBalance(_sender, _t.value + _t.fee);
addBalance(_t.receiveAddress, _t.value);
addBalance(m_minerAddress, _t.fee);
addBalance(m_coinbaseAddress, _t.fee);
if (isContractAddress(_t.receiveAddress))
{
@ -285,7 +292,7 @@ void State::execute(Transaction const& _t, Address _sender)
mem[i] = _t.data[i];
subBalance(_sender, _t.value + _t.fee);
addBalance(newAddress, _t.value);
addBalance(m_minerAddress, _t.fee);
addBalance(m_coinbaseAddress, _t.fee);
}
}
@ -487,7 +494,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _
stack.push_back(m_currentBlock.timestamp);
break;
case Instruction::BLK_NUMBER:
stack.push_back(m_currentBlock.number);
stack.push_back(m_currentNumber);
break;
case Instruction::BLK_DIFFICULTY:
stack.push_back(m_currentBlock.difficulty);
@ -647,6 +654,24 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _
stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), pub.size()) ? 1 : 0;
break;
}
case Instruction::SHA3:
{
uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32);
stack.pop_back();
CryptoPP::SHA3_256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(stack.back());
digest.Update(b.data(), (int)min<u256>(32, s)); // b.size() == 32
stack.pop_back();
}
array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::PUSH:
{
stack.push_back(mem(curPC + 1));

25
libethereum/State.h

@ -46,8 +46,11 @@ class BlockChain;
class State
{
public:
/// Construct null state object.
State() {}
/// Construct state object.
explicit State(Address _minerAddress);
explicit State(Address _coinbaseAddress);
/// Attempt to find valid nonce for block that this state represents.
/// @param _msTimeout Timeout before return in milliseconds.
@ -59,12 +62,18 @@ public:
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
/// We also sync our transactions, killing those from the queue that we have and assimilating those that we don't.
void sync(BlockChain const& _bc, TransactionQueue& _tq);
void sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block sync to the given block.
void sync(BlockChain const& _bc, u256 _blockHash);
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
void sync(TransactionQueue& _tq);
/// Execute a given transaction.
bool execute(bytes const& _rlp) { return execute(&_rlp); }
bool execute(bytesConstRef _rlp);
/// Check if the address is a valid normal (non-contract) account address.
bool isNormalAddress(Address _address) const;
@ -93,11 +102,14 @@ public:
/// @returns 0 if the address has never been used.
u256 transactionsFrom(Address _address) const;
/// The hash of the root of our state tree.
u256 rootHash() const;
private:
/// Fee-adder on destruction RAII class.
struct MinerFeeAdder
{
~MinerFeeAdder() { state->addBalance(state->m_minerAddress, fee); }
~MinerFeeAdder() { state->addBalance(state->m_coinbaseAddress, fee); }
State* state;
u256 fee;
};
@ -112,8 +124,6 @@ private:
/// Execute all transactions within a given block.
void playback(bytesConstRef _block);
u256 currentHash() const;
// TODO: std::hash<Address> and then move to unordered_map.
// Will need to sort on hash construction.
std::map<Address, AddressState> m_current; ///< The current state. We work with a C++ hash map rather than a Trie.
@ -122,8 +132,9 @@ private:
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
uint m_currentNumber;
Address m_minerAddress; ///< Our address (i.e. the address to which fees go).
Address m_coinbaseAddress; ///< Our address (i.e. the address to which fees go).
/// The fee structure. Values yet to be agreed on...
static const u256 c_stepFee;

Loading…
Cancel
Save