diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 33ced7e98..94a58dec0 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1156,7 +1156,7 @@ void Main::refreshBlockChain() auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data(), CheckSignature::Sender); + Transaction t(i.data(), CheckTransaction::Everything); QString s = t.receiveAddress() ? QString(" %2 %5> %3: %1 [%4]") .arg(formatBalance(t.value()).c_str()) @@ -1536,7 +1536,7 @@ void Main::on_blocks_currentItemChanged() else { unsigned txi = item->data(Qt::UserRole + 1).toInt(); - Transaction tx(block[1][txi].data(), CheckSignature::Sender); + Transaction tx(block[1][txi].data(), CheckTransaction::Everything); auto ss = tx.safeSender(); h256 th = sha3(rlpList(ss, tx.nonce())); TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi]; @@ -1597,7 +1597,7 @@ void Main::on_debugCurrent_triggered() State s(ethereum()->state(txi, h)); Executive e(s, ethereum()->blockChain()); Debugger dw(this, this); - dw.populate(e, Transaction(t, CheckSignature::Sender)); + dw.populate(e, Transaction(t, CheckTransaction::Everything)); dw.exec(); } } diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1ff3427e3..f1f7477fe 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -306,7 +306,7 @@ void Transact::rejigData() htmlInfo += "

Hex

" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + ""; // Determine the minimum amount of gas we need to play... - qint64 baseGas = (qint64)Interface::txGas(m_data, 0); + qint64 baseGas = (qint64)Transaction::gasRequired(m_data, 0); qint64 gasNeeded = 0; if (b < value() + baseGas * gasPrice()) diff --git a/eth/main.cpp b/eth/main.cpp index 761f7a9fd..6308c6cfb 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -667,7 +667,7 @@ int main(int argc, char** argv) cnote << ssbd.str(); int ssize = sechex.length(); int size = hexAddr.length(); - u256 minGas = (u256)Client::txGas(data, 0); + u256 minGas = (u256)Transaction::gasRequired(data, 0); if (size < 40) { if (size > 0) @@ -746,7 +746,7 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(bytes(), 0); + u256 minGas = (u256)Transaction::gasRequired(bytes(), 0); try { Address dest = h160(fromHex(hexAddr, WhenError::Throw)); @@ -811,7 +811,7 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init, 0); + u256 minGas = (u256)Transaction::gasRequired(init, 0); if (!init.size()) cwarn << "Contract creation aborted, no init code."; else if (endowment < 0) diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index db2718fc4..3471a958f 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info; using BadFieldError = boost::tuple; struct DatabaseAlreadyOpen: virtual dev::Exception {}; +struct OutOfGasBase: virtual dev::Exception {}; struct NotEnoughAvailableSpace: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {}; struct GasPriceTooLow: virtual dev::Exception {}; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 4ad20b403..37ef15680 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -94,7 +94,7 @@ void BasicGasPricer::update(BlockChain const& _bc) for (unsigned i = 0; i < r[1].size(); ++i) { auto gu = brs.receipts[i].gasUsed(); - dist[Transaction(r[1][i].data(), CheckSignature::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed(); + dist[Transaction(r[1][i].data(), CheckTransaction::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed(); total += (unsigned)gu; } } @@ -511,7 +511,7 @@ void Client::doWork() clog(ClientNote) << "Dead block:" << h.abridged(); for (auto const& t: m_bc.transactions(h)) { - clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckSignature::None); + clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); m_tq.import(t); } } diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index b9c2fa878..37ce0a907 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -302,7 +302,7 @@ BlockDetails ClientBase::blockDetails(h256 _hash) const Transaction ClientBase::transaction(h256 _transactionHash) const { - return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); + return Transaction(bc().transaction(_transactionHash), CheckTransaction::Cheap); } Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const @@ -310,7 +310,7 @@ Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); + return Transaction(b[1][_i].data(), CheckTransaction::Cheap); else return Transaction(); } @@ -321,7 +321,7 @@ Transactions ClientBase::transactions(h256 _blockHash) const RLP b(bl); Transactions res; for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); + res.emplace_back(b[1][i].data(), CheckTransaction::Cheap); return res; } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 3bd7ecab3..2f44ae860 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -334,7 +334,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case GetTransactionsPacket: break; // DEPRECATED. case TransactionsPacket: { - clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; + clogS(NetAllDetail) << "Transactions (" << dec << _r.itemCount() << "entries)"; Guard l(x_knownTransactions); for (unsigned i = 0; i < _r.itemCount(); ++i) { diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 0b75a07af..d4ad0ef1d 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -31,8 +31,6 @@ using namespace std; using namespace dev; using namespace dev::eth; -#define ETH_VMTRACE 1 - Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), @@ -69,12 +67,11 @@ void Executive::initialize(Transaction const& _transaction) } // Check gas cost is enough. - m_gasRequired = Interface::txGas(m_t.data()); - if (m_t.gas() < m_gasRequired) + if (!m_t.checkPayment()) { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas(); + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_t.gasRequired() << " Got" << m_t.gas(); m_excepted = TransactionException::OutOfGas; - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas())); + BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(m_t.gasRequired(), (bigint)m_t.gas())); } // Avoid invalid transactions. @@ -119,9 +116,9 @@ bool Executive::execute() m_s.subBalance(m_t.sender(), m_gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_gasRequired, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_gasRequired, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 3efdf6f0f..c55341cb8 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -71,7 +71,7 @@ public: void operator=(Executive) = delete; /// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this. - void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckSignature::None)); } + void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckTransaction::None)); } void initialize(Transaction const& _transaction); /// Finalise a transaction previously set up with initialize(). /// @warning Only valid after initialize() and execute(), and possibly go(). diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 529ddc093..c90debe94 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -143,9 +143,6 @@ public: virtual Addresses addresses() const { return addresses(m_default); } virtual Addresses addresses(BlockNumber _block) const = 0; - /// Get the fee associated for a transaction with the given data. - template static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } - /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a58ba9d0f..c1f37a553 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -574,7 +574,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) k << i; transactionsTrie.insert(&k.out(), tr.data()); - execute(lh, Transaction(tr.data(), CheckSignature::Sender)); + execute(lh, Transaction(tr.data(), CheckTransaction::Everything)); RLPStream receiptrlp; m_receipts.back().streamRLP(receiptrlp); diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 3228eca70..411c8bd99 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -25,6 +25,7 @@ #include #include #include +#include "Interface.h" #include "Transaction.h" using namespace std; using namespace dev; @@ -53,7 +54,7 @@ TransactionException dev::eth::toTransactionException(VMException const& _e) return TransactionException::Unknown; } -Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) +Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig) { int field = 0; RLP rlp(_rlpData); @@ -81,9 +82,9 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP")); m_vrs = SignatureStruct{ r, s, v }; - if (_checkSig >= CheckSignature::Range && !m_vrs.isValid()) + if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid()) BOOST_THROW_EXCEPTION(InvalidSignature()); - if (_checkSig == CheckSignature::Sender) + if (_checkSig == CheckTransaction::Everything) m_sender = sender(); } catch (Exception& _e) @@ -91,6 +92,8 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes())); throw; } + if (_checkSig >= CheckTransaction::Cheap && !checkPayment()) + BOOST_THROW_EXCEPTION(OutOfGasBase() << RequirementError(gasRequired(), (bigint)gas())); } Address const& Transaction::safeSender() const noexcept @@ -118,6 +121,13 @@ Address const& Transaction::sender() const return m_sender; } +bigint Transaction::gasRequired() const +{ + if (!m_gasRequired) + m_gasRequired = Transaction::gasRequired(m_data); + return m_gasRequired; +} + void Transaction::sign(Secret _priv) { auto sig = dev::sign(_priv, sha3(WithoutSignature)); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index ab2c12b50..faf5ea07a 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -24,7 +24,7 @@ #include #include #include - +#include namespace dev { namespace eth @@ -37,11 +37,11 @@ enum IncludeSignature WithSignature = 1, ///< Do include a signature. }; -enum class CheckSignature +enum class CheckTransaction { None, - Range, - Sender + Cheap, + Everything }; enum class TransactionException @@ -119,10 +119,10 @@ public: Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs a transaction from the given RLP. - explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig); + explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig); /// Constructs a transaction from the given RLP. - explicit Transaction(bytes const& _rlp, CheckSignature _checkSig): Transaction(&_rlp, _checkSig) {} + explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {} /// Checks equality of transactions. @@ -178,27 +178,37 @@ public: /// @returns the signature of the transaction. Encodes the sender. SignatureStruct const& signature() const { return m_vrs; } + /// @returns true if the transaction contains enough gas for the basic payment. + bool checkPayment() const { return m_gas >= gasRequired(); } + + /// @returns the gas required to run this transaction. + bigint gasRequired() const; + + /// Get the fee associated for a transaction with the given data. + template static bigint gasRequired(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } + private: /// Type of transaction. enum Type { - NullTransaction, ///< Null transaction. - ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. - MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. + NullTransaction, ///< Null transaction. + ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. + MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. }; - void sign(Secret _priv); ///< Sign the transaction. + void sign(Secret _priv); ///< Sign the transaction. - Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? - u256 m_nonce; ///< The transaction-count of the sender. - u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. - Address m_receiveAddress; ///< The receiving address of the transaction. - u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. - u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. - bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. - SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. + Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? + u256 m_nonce; ///< The transaction-count of the sender. + u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. + Address m_receiveAddress; ///< The receiving address of the transaction. + u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. + u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. + bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. + SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. - mutable Address m_sender; ///< Cached sender, determined from signature. + mutable Address m_sender; ///< Cached sender, determined from signature. + mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run. }; /// Nice name for vector of Transaction. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 26962ed90..38f3b9429 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -44,21 +44,23 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) // Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender. // If it doesn't work, the signature is bad. // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). - Transaction t(_transactionRLP, CheckSignature::Sender); + Transaction t(_transactionRLP, CheckTransaction::Everything); UpgradeGuard ul(l); // If valid, append to blocks. m_current[h] = t; m_known.insert(h); + + ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); } catch (Exception const& _e) { - cwarn << "Ignoring invalid transaction: " << diagnostic_information(_e); + ctxq << "Ignoring invalid transaction: " << diagnostic_information(_e); return ImportResult::Malformed; } catch (std::exception const& _e) { - cwarn << "Ignoring invalid transaction: " << _e.what(); + ctxq << "Ignoring invalid transaction: " << _e.what(); return ImportResult::Malformed; } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index cf40e1209..73ce24fbd 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "libethcore/Common.h" #include "Transaction.h" @@ -34,6 +35,8 @@ namespace eth class BlockChain; +struct TransactionQueueChannel: public LogChannel { static const char* name() { return "->Q"; } static const int verbosity = 4; }; +#define ctxq dev::LogOutputStream() /** * @brief A queue of Transactions, each stored as RLP. diff --git a/neth/main.cpp b/neth/main.cpp index 7d7f95b89..91b02fc24 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -886,7 +886,7 @@ int main(int argc, char** argv) ssbd << bbd; cnote << ssbd.str(); int ssize = fields[4].length(); - u256 minGas = (u256)Client::txGas(data, 0); + u256 minGas = (u256)Transaction::gasRequired(data, 0); if (size < 40) { if (size > 0) @@ -952,7 +952,7 @@ int main(int argc, char** argv) auto h = bc.currentHash(); auto blockData = bc.block(h); BlockInfo info(blockData); - u256 minGas = (u256)Client::txGas(bytes(), 0); + u256 minGas = (u256)Transaction::gasRequired(bytes(), 0); try { Address dest = h160(fromHex(fields[0], WhenError::Throw)); @@ -1040,7 +1040,7 @@ int main(int argc, char** argv) cnote << "Init:"; cnote << ssc.str(); } - u256 minGas = (u256)Client::txGas(init, 0); + u256 minGas = (u256)Transaction::gasRequired(init, 0); if (!init.size()) cwarn << "Contract creation aborted, no init code."; else if (endowment < 0) @@ -1126,7 +1126,7 @@ int main(int argc, char** argv) auto b = bc.block(h); for (auto const& i: RLP(b)[1]) { - Transaction t(i.data(), CheckSignature::Sender); + Transaction t(i.data(), CheckTransaction::Everything); auto s = t.receiveAddress() ? boost::format(" %1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index dd7c09eab..757a6e7a6 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -172,7 +172,7 @@ void ImportTest::importTransaction(json_spirit::mObject& _o) { RLPStream transactionRLPStream = createRLPStreamFromTransactionFields(_o); RLP transactionRLP(transactionRLPStream.out()); - m_transaction = Transaction(transactionRLP.data(), CheckSignature::Sender); + m_transaction = Transaction(transactionRLP.data(), CheckTransaction::Everything); } } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index a223e8d4e..6c1cfebd6 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -383,7 +383,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) try { - Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckSignature::Sender); + Transaction t(createRLPStreamFromTransactionFields(tx).out(), CheckTransaction::Everything); txsFromField.push_back(t); } catch (Exception const& _e) @@ -400,7 +400,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) RLP root(blockRLP); for (auto const& tr: root[1]) { - Transaction tx(tr.data(), CheckSignature::Sender); + Transaction tx(tr.data(), CheckTransaction::Everything); txsFromRlp.push_back(tx); } diff --git a/test/transaction.cpp b/test/transaction.cpp index 4c57326ba..7aa073f29 100644 --- a/test/transaction.cpp +++ b/test/transaction.cpp @@ -44,7 +44,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) { bytes stream = importByteArray(o["rlp"].get_str()); RLP rlp(stream); - txFromRlp = Transaction(rlp.data(), CheckSignature::Sender); + txFromRlp = Transaction(rlp.data(), CheckTransaction::Everything); if (!txFromRlp.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); } @@ -64,7 +64,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) BOOST_REQUIRE(o.count("transaction") > 0); mObject tObj = o["transaction"].get_obj(); - Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckSignature::Sender); + Transaction txFromFields(createRLPStreamFromTransactionFields(tObj).out(), CheckTransaction::Everything); //Check the fields restored from RLP to original fields BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!"); @@ -91,7 +91,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) try { - Transaction txFromFields(rlpStream.out(), CheckSignature::Sender); + Transaction txFromFields(rlpStream.out(), CheckTransaction::Everything); if (!txFromFields.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") );