Browse Source

transaction recording, blocks, watches

cl-refactor
arkpar 10 years ago
parent
commit
d0ef317c7b
  1. 18
      mix/ClientModel.cpp
  2. 1
      mix/ClientModel.h
  3. 2
      mix/Exceptions.h
  4. 201
      mix/MixClient.cpp
  5. 32
      mix/MixClient.h
  6. 2
      mix/qml/WebPreview.qml

18
mix/ClientModel.cpp

@ -61,7 +61,7 @@ private:
};
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector())
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector()), m_contractAddress(Address())
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QEther*>("QEther*");
@ -92,8 +92,7 @@ QString ClientModel::apiCall(QString const& _message)
QString ClientModel::contractAddress() const
{
QString address = QString::fromStdString(dev::toJS(m_client->lastContractAddress()));
return address;
return QString::fromStdString(dev::toJS(m_contractAddress));
}
void ClientModel::debugDeployment()
@ -181,7 +180,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
//run contract creation first
m_client->resetState(_balance);
ExecutionResult debuggingContent = deployContract(contractCode);
Address address = debuggingContent.contractAddress;
Address address = m_contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
@ -238,19 +237,20 @@ ExecutionResult ClientModel::deployContract(bytes const& _code)
u256 gas = 125000;
u256 amount = 100;
Address lastAddress = m_client->lastContractAddress();
Address newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
if (newAddress != lastAddress)
if (newAddress != m_contractAddress)
{
m_contractAddress = newAddress;
contractAddressChanged();
}
ExecutionResult r = m_client->record().back().transactions.back();
return r;
}
ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = _contract;
ExecutionResult r = m_client->record().back().transactions.back();
return r;
}

1
mix/ClientModel.h

@ -124,6 +124,7 @@ private:
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
Address m_contractAddress;
};
}

2
mix/Exceptions.h

@ -35,9 +35,11 @@ namespace mix
struct QmlLoadException: virtual Exception {};
struct FileIoException: virtual Exception {};
struct InvalidBlockException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo;
typedef boost::error_info<struct tagFileError, std::string> FileError;
typedef boost::error_info<struct tagBlockIndex, unsigned> BlockIndex;
}
}

201
mix/MixClient.cpp

@ -29,6 +29,7 @@
#include <libethereum/ExtVM.h>
#include <libevm/VM.h>
#include "Exceptions.h"
#include "MixClient.h"
using namespace dev;
@ -46,6 +47,7 @@ void MixClient::resetState(u256 _balance)
WriteGuard l(x_state);
m_state = eth::State(Address(), m_stateDB, BaseState::Empty);
m_state.addBalance(m_userAccount.address(), _balance);
m_blocks = Blocks { Block() };
}
void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
@ -86,16 +88,48 @@ void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
d.contractAddress = m_lastExecutionResult.contractAddress;
m_lastExecutionResult = d;
d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_blocks.back().transactions.emplace_back(d);
h256Set changed;
Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
{
if ((unsigned)i.second.filter.latest() > m_blocks.size() - 1)
{
// acceptable number.
auto m = i.second.filter.matches(d.receipt);
if (m.size())
{
// filter catches them
for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_blocks.size()));
changed.insert(i.first);
}
}
}
noteChanged(changed);
}
void MixClient::validateBlock(int _block) const
{
//TODO: throw exception here if _block != 0
(void)_block;
if ((unsigned)_block >= m_blocks.size() - 1)
{
InvalidBlockException exception;
exception << BlockIndex(_block);
BOOST_THROW_EXCEPTION(exception);
}
}
void MixClient::mine()
{
WriteGuard l(x_state);
Block& block = m_blocks.back();
m_state.completeMine();
block.state = m_state;
block.info = m_state.info();
block.hash = block.info.hash;
m_blocks.push_back(Block());
}
void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
@ -115,7 +149,6 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
m_lastExecutionResult.contractAddress = address;
return address;
}
@ -140,92 +173,186 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
}
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last executoin state
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(&rlp, temp);
return m_lastExecutionResult.returnValue;
return m_blocks.back().transactions.back().returnValue;
}
u256 MixClient::balanceAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.balance(_a);
return m_blocks[_block].state.balance(_a);
}
u256 MixClient::countAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.transactionsFrom(_a);
return m_blocks[_block].state.transactionsFrom(_a);
}
u256 MixClient::stateAt(Address _a, u256 _l, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a, _l);
return m_blocks[_block].state.storage(_a, _l);
}
bytes MixClient::codeAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.code(_a);
return m_blocks[_block].state.code(_a);
}
std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a);
return m_blocks[_block].state.storage(_a);
}
eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const
{
(void)_watchId;
return LocalisedLogEntries();
Guard l(m_filterLock);
return logs(m_filters.at(m_watches.at(_watchId).id).filter);
}
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
{
LocalisedLogEntries ret;
unsigned blockNumber = m_blocks.size();
unsigned begin = std::min<unsigned>(blockNumber, (unsigned)_f.latest());
unsigned end = std::min(blockNumber, std::min(begin, (unsigned)_f.earliest()));
unsigned m = _f.max();
unsigned s = _f.skip();
// Handle pending transactions differently as they're not on the block chain.
if (begin > blockNumber)
{
ReadGuard l(x_state);
for (unsigned i = 0; i < m_state.pending().size(); ++i)
{
// Might have a transaction that contains a matching log.
TransactionReceipt const& tr = m_state.receipt(i);
LogEntries le = _f.matches(tr);
if (le.size())
{
for (unsigned j = 0; j < le.size() && ret.size() != m; ++j)
if (s)
s--;
else
ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin));
}
}
begin = blockNumber;
}
unsigned n = begin;
for (; ret.size() != m && n != end; n--)
{
// check block bloom
if (_f.matches(m_blocks[n].info.logBloom))
for (ExecutionResult const& t: m_blocks[n].transactions)
{
if (_f.matches(t.receipt.bloom()))
{
LogEntries le = _f.matches(t.receipt);
if (le.size())
{
for (unsigned j = 0; j < le.size() && ret.size() != m; ++j)
{
if (s)
s--;
else
ret.insert(ret.begin(), LocalisedLogEntry(le[j], n));
}
}
}
}
if (n == end)
break;
}
return ret;
}
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _filter) const
unsigned MixClient::installWatch(h256 _h)
{
(void)_filter;
return LocalisedLogEntries();
unsigned ret;
{
Guard l(m_filterLock);
ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h);
}
auto ch = logs(ret);
if (ch.empty())
ch.push_back(eth::InitialChange);
{
Guard l(m_filterLock);
swap(m_watches[ret].changes, ch);
}
return ret;
}
unsigned MixClient::installWatch(eth::LogFilter const& _filter)
unsigned MixClient::installWatch(eth::LogFilter const& _f)
{
(void)_filter;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
h256 h = _f.sha3();
{
Guard l(m_filterLock);
if (!m_filters.count(h))
m_filters.insert(std::make_pair(h, _f));
}
return installWatch(h);
}
unsigned MixClient::installWatch(h256 _filterId)
void MixClient::uninstallWatch(unsigned _i)
{
(void)_filterId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
Guard l(m_filterLock);
auto it = m_watches.find(_i);
if (it == m_watches.end())
return;
auto id = it->second.id;
m_watches.erase(it);
auto fit = m_filters.find(id);
if (fit != m_filters.end())
if (!--fit->second.refCount)
m_filters.erase(fit);
}
void MixClient::uninstallWatch(unsigned _watchId)
void MixClient::noteChanged(h256Set const& _filters)
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uninstallWatch"));
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes;
else
i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0));
}
for (auto& i: m_filters)
i.second.changes.clear();
}
eth::LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const
LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::peekWatch"));
Guard l(m_filterLock);
return m_watches.at(_watchId).changes;
}
eth::LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::checkWatch"));
Guard l(m_filterLock);
LocalisedLogEntries ret;
std::swap(ret, m_watches.at(_watchId).changes);
return ret;
}
h256 MixClient::hashFromNumber(unsigned _number) const
{
(void)_number;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::hashFromNumber"));
validateBlock(_number);
return m_blocks[_number].hash;
}
eth::BlockInfo MixClient::blockInfo(h256 _hash) const
@ -256,7 +383,7 @@ eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
unsigned MixClient::number() const
{
return 0;
return m_blocks.size() - 1;
}
eth::Transactions MixClient::pending() const

32
mix/MixClient.h

@ -23,7 +23,9 @@
#pragma once
#include <vector>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
namespace dev
{
@ -53,24 +55,36 @@ struct MachineState
*/
struct ExecutionResult
{
ExecutionResult(): receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {}
std::vector<MachineState> machineStates;
bytes executionCode;
bytesConstRef executionData;
Address contractAddress;
bool contentAvailable;
std::string message;
bytes returnValue;
dev::eth::TransactionReceipt receipt;
};
using ExecutionResults = std::vector<ExecutionResult>;
struct Block
{
ExecutionResults transactions;
h256 hash;
dev::eth::State state;
dev::eth::BlockInfo info;
};
using Blocks = std::vector<Block>;
class MixClient: public dev::eth::Interface
{
public:
MixClient();
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
const KeyPair& userAccount() const { return m_userAccount; }
const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; }
const Address lastContractAddress() const { ReadGuard l(x_state); return m_lastExecutionResult.contractAddress; }
KeyPair const& userAccount() const { return m_userAccount; }
void mine();
Blocks const& record() const { return m_blocks; }
//dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -113,12 +127,16 @@ public:
private:
void executeTransaction(bytesConstRef _rlp, eth::State& _state);
void validateBlock(int _block) const;
void noteChanged(h256Set const& _filters);
KeyPair m_userAccount;
eth::State m_state;
OverlayDB m_stateDB;
mutable boost::shared_mutex x_state;
ExecutionResult m_lastExecutionResult;
mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches;
Blocks m_blocks;
};
}

2
mix/qml/WebPreview.qml

@ -62,12 +62,12 @@ Item {
Connections {
target: clientModel
onContractAddressChanged: reload();
onRunComplete: reload();
}
Connections {
target: codeModel
onContractInterfaceChanged: reload();
onRunCompleted: reload();
}
Connections {

Loading…
Cancel
Save