diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index f798437e6..e12756d6e 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -44,14 +44,14 @@
0 bytes used
-
- -
-
-
-
-
-
-
+
+ -
+
+
+
+
+
+
-
@@ -224,6 +224,7 @@
&Config
+
@@ -1782,6 +1783,11 @@ font-size: 14pt
&Gas Prices...
+
+
+ &Sentinel...
+
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 821022abc..445b5fdac 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -296,6 +296,14 @@ void Main::on_gasPrices_triggered()
}
}
+void Main::on_sentinel_triggered()
+{
+ bool ok;
+ QString sentinel = QInputDialog::getText(nullptr, "Enter sentinel address", "Enter the sentinel address for bad block reporting (e.g. http://badblockserver.com:8080). Enter nothing to disable.", QLineEdit::Normal, QString::fromStdString(ethereum()->sentinel()), &ok);
+ if (ok)
+ ethereum()->setSentinel(sentinel.toStdString());
+}
+
void Main::on_newIdentity_triggered()
{
KeyPair kp = KeyPair::create();
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index f8a6fa6c7..a3f6328cd 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -196,6 +196,7 @@ private slots:
// Config
void on_gasPrices_triggered();
+ void on_sentinel_triggered();
void refreshWhisper();
void refreshBlockChain();
diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h
index ae8eaed92..af7d8e048 100644
--- a/ethkey/KeyAux.h
+++ b/ethkey/KeyAux.h
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include "BuildInfo.h"
using namespace std;
using namespace dev;
@@ -105,7 +106,9 @@ public:
ImportWithAddress,
Export,
Recode,
- Kill
+ Kill,
+ SignTx,
+ DecodeTx,
};
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {}
@@ -131,8 +134,13 @@ public:
auto v = argv[++i];
m_kdfParams[n] = v;
}
- else if (arg == "--new-bare")
- m_mode = OperationMode::NewBare;
+ else if (arg == "--sign-tx" && i + 1 < argc)
+ {
+ m_mode = OperationMode::SignTx;
+ m_signKey = argv[++i];
+ }
+ else if (arg == "--decode-tx")
+ m_mode = OperationMode::DecodeTx;
else if (arg == "--import-bare")
m_mode = OperationMode::ImportBare;
else if (arg == "--list-bare")
@@ -173,7 +181,7 @@ public:
m_mode = OperationMode::Recode;
else if (arg == "--no-icap")
m_icap = false;
- else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
+ else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
m_inputs.push_back(arg);
else
return false;
@@ -209,6 +217,127 @@ public:
}
}
}
+ else if (m_mode == OperationMode::DecodeTx)
+ {
+ string const& i = m_inputs[0];
+ bytes b = fromHex(i);
+ if (b.empty())
+ {
+ std::string s = contentsString(i);
+ b = fromHex(s);
+ if (b.empty())
+ b = asBytes(s);
+ }
+ if (b.empty())
+ cerr << "Unknown file or bad hex: " << i << endl;
+ else
+ try
+ {
+ TransactionBase t(b, CheckTransaction::Everything);
+ cout << "Transaction " << t.sha3().hex() << endl;
+ if (t.isCreation())
+ {
+ cout << " type: creation" << endl;
+ cout << " code: " << toHex(t.data()) << endl;
+ }
+ else
+ {
+ cout << " type: message" << endl;
+ cout << " to: " << t.to().hex() << endl;
+ cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl;
+ }
+ cout << " from: " << t.from().hex() << endl;
+ cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl;
+ cout << " nonce: " << t.nonce() << endl;
+ cout << " gas: " << t.gas() << endl;
+ cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl;
+ cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl;
+ cout << " v: " << (int)t.signature().v << endl;
+ cout << " r: " << t.signature().r << endl;
+ cout << " s: " << t.signature().s << endl;
+ }
+ catch (Exception& ex)
+ {
+ cerr << "Invalid transaction: " << ex.what() << endl;
+ }
+ }
+ else if (m_mode == OperationMode::SignTx)
+ {
+ Secret s;
+
+ string json = contentsString(m_signKey);
+ if (!json.empty())
+ {
+ SecretStore store(m_secretsPath);
+ s = Secret(store.secret(store.readKeyContent(json), [&](){ return getPassword("Enter password for key: "); }));
+ }
+ else
+ {
+ if (h128 u = fromUUID(m_signKey))
+ {
+ SecretStore store(m_secretsPath);
+ s = Secret(store.secret(u, [&](){ return getPassword("Enter password for key: "); }));
+ }
+ else if (Address a = Address(m_signKey))
+ {
+ KeyManager wallet(m_walletPath, m_secretsPath);
+ if (wallet.exists())
+ {
+ openWallet(wallet);
+ s = wallet.secret(a, [&](){ return getPassword("Enter password for key: "); });
+ }
+ else
+ {
+ cerr << "Wallet doesn't exist." << endl;
+ exit(-1);
+ }
+ }
+ else
+ {
+ cerr << "Bad file, UUID and address: " << m_signKey << endl;
+ exit(-1);
+ }
+ }
+ if (!s)
+ {
+ cerr << "UUID/address not found: " << m_signKey << endl;
+ exit(-1);
+ }
+
+ for (string const& i: m_inputs)
+ {
+ bytes b = fromHex(i);
+ bool isFile = false;
+ if (b.empty())
+ {
+ isFile = true;
+ std::string s = contentsString(i);
+ b = fromHex(s);
+ if (b.empty())
+ b = asBytes(s);
+ }
+ if (b.empty())
+ cerr << "Unknown file or bad hex: " << i << endl;
+ else
+ try
+ {
+ TransactionBase t(b, CheckTransaction::None);
+ t.sign(s);
+ cout << t.sha3() << ": ";
+ if (isFile)
+ {
+ writeFile(i + ".signed", t.data());
+ cout << i + ".signed" << endl;
+ }
+ else
+ cout << toHex(t.data()) << endl;
+ }
+ catch (Exception& ex)
+ {
+ cerr << "Invalid transaction: " << ex.what() << endl;
+ }
+ }
+ }
else if (m_mode < OperationMode::CreateWallet)
{
SecretStore store(m_secretsPath);
@@ -297,17 +426,7 @@ public:
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (wallet.exists())
- while (true)
- {
- if (wallet.load(m_masterPassword))
- break;
- if (!m_masterPassword.empty())
- {
- cout << "Password invalid. Try again." << endl;
- m_masterPassword.clear();
- }
- m_masterPassword = getPassword("Please enter your MASTER password: ");
- }
+ openWallet(wallet);
else
{
cerr << "Couldn't open wallet. Does it exist?" << endl;
@@ -419,6 +538,10 @@ public:
<< " --wallet-path Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl
<< " -m, --master Specify wallet (master) password." << endl
<< endl
+ << "Transaction operating modes:" << endl
+ << " -d,--decode-tx [|] Decode given transaction." << endl
+ << " -s,--sign-tx [ || ] [ | , ... ] (Re-)Sign given transaction." << endl
+ << endl
<< "Encryption configuration:" << endl
<< " --kdf Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param Specify a parameter for the KDF." << endl
@@ -445,6 +568,21 @@ public:
}
private:
+ void openWallet(KeyManager& _w)
+ {
+ while (true)
+ {
+ if (_w.load(m_masterPassword))
+ break;
+ if (!m_masterPassword.empty())
+ {
+ cout << "Password invalid. Try again." << endl;
+ m_masterPassword.clear();
+ }
+ m_masterPassword = getPassword("Please enter your MASTER password: ");
+ }
+ }
+
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; }
/// Operating mode.
@@ -468,6 +606,9 @@ private:
/// Importing
strings m_inputs;
+ /// Signing
+ string m_signKey;
+
string m_kdf = "scrypt";
map m_kdfParams;
// string m_cipher;
diff --git a/libethcore/CMakeLists.txt b/libethcore/CMakeLists.txt
index a527ad1f4..4dd626642 100644
--- a/libethcore/CMakeLists.txt
+++ b/libethcore/CMakeLists.txt
@@ -28,7 +28,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} ethash)
target_link_libraries(${EXECUTABLE} devcrypto)
-#target_link_libraries(${EXECUTABLE} evmcore)
+target_link_libraries(${EXECUTABLE} evmcore)
if (ETHASHCL)
target_link_libraries(${EXECUTABLE} ethash-cl)
diff --git a/libethcore/Transaction.cpp b/libethcore/Transaction.cpp
new file mode 100644
index 000000000..5e08acd4d
--- /dev/null
+++ b/libethcore/Transaction.cpp
@@ -0,0 +1,118 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file TransactionBase.cpp
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "Transaction.h"
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
+TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig)
+{
+ int field = 0;
+ RLP rlp(_rlpData);
+ try
+ {
+ if (!rlp.isList())
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
+
+ m_nonce = rlp[field = 0].toInt();
+ m_gasPrice = rlp[field = 1].toInt();
+ m_gas = rlp[field = 2].toInt();
+ m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
+ m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash(RLP::VeryStrict);
+ m_value = rlp[field = 4].toInt();
+
+ if (!rlp[field = 5].isData())
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
+
+ m_data = rlp[field = 5].toBytes();
+ byte v = rlp[field = 6].toInt() - 27;
+ h256 r = rlp[field = 7].toInt();
+ h256 s = rlp[field = 8].toInt();
+
+ if (rlp.itemCount() > 9)
+ BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
+
+ m_vrs = SignatureStruct{ r, s, v };
+ if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
+ BOOST_THROW_EXCEPTION(InvalidSignature());
+ if (_checkSig == CheckTransaction::Everything)
+ m_sender = sender();
+ }
+ catch (Exception& _e)
+ {
+ _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
+ throw;
+ }
+}
+
+Address const& TransactionBase::safeSender() const noexcept
+{
+ try
+ {
+ return sender();
+ }
+ catch (...)
+ {
+ cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
+ return ZeroAddress;
+ }
+}
+
+Address const& TransactionBase::sender() const
+{
+ if (!m_sender)
+ {
+ auto p = recover(m_vrs, sha3(WithoutSignature));
+ if (!p)
+ BOOST_THROW_EXCEPTION(InvalidSignature());
+ m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
+ }
+ return m_sender;
+}
+
+void TransactionBase::sign(Secret const& _priv)
+{
+ auto sig = dev::sign(_priv, sha3(WithoutSignature));
+ SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
+ if (sigStruct.isValid())
+ m_vrs = sigStruct;
+}
+
+void TransactionBase::streamRLP(RLPStream& _s, IncludeSignature _sig) const
+{
+ if (m_type == NullTransaction)
+ return;
+ _s.appendList((_sig ? 3 : 0) + 6);
+ _s << m_nonce << m_gasPrice << m_gas;
+ if (m_type == MessageCall)
+ _s << m_receiveAddress;
+ else
+ _s << "";
+ _s << m_value << m_data;
+ if (_sig)
+ _s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
+}
diff --git a/libethcore/Transaction.h b/libethcore/Transaction.h
new file mode 100644
index 000000000..e5eeb74b4
--- /dev/null
+++ b/libethcore/Transaction.h
@@ -0,0 +1,177 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file TransactionBase.h
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace dev
+{
+namespace eth
+{
+
+/// Named-boolean type to encode whether a signature be included in the serialisation process.
+enum IncludeSignature
+{
+ WithoutSignature = 0, ///< Do not include a signature.
+ WithSignature = 1, ///< Do include a signature.
+};
+
+enum class CheckTransaction
+{
+ None,
+ Cheap,
+ Everything
+};
+
+/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
+class TransactionBase
+{
+public:
+ /// Constructs a null transaction.
+ TransactionBase() {}
+
+ /// Constructs a signed message-call transaction.
+ TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
+
+ /// Constructs a signed contract-creation transaction.
+ TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
+
+ /// Constructs an unsigned message-call transaction.
+ TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
+
+ /// Constructs an unsigned contract-creation transaction.
+ TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
+
+ /// Constructs a transaction from the given RLP.
+ explicit TransactionBase(bytesConstRef _rlp, CheckTransaction _checkSig);
+
+ /// Constructs a transaction from the given RLP.
+ explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {}
+
+
+ /// Checks equality of transactions.
+ bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
+ /// Checks inequality of transactions.
+ bool operator!=(TransactionBase const& _c) const { return !operator==(_c); }
+
+ /// @returns sender of the transaction from the signature (and hash).
+ Address const& sender() const;
+ /// Like sender() but will never throw. @returns a null Address if the signature is invalid.
+ Address const& safeSender() const noexcept;
+ /// Force the sender to a particular value. This will result in an invalid transaction RLP.
+ void forceSender(Address const& _a) { m_sender = _a; }
+
+ /// @returns true if transaction is non-null.
+ explicit operator bool() const { return m_type != NullTransaction; }
+
+ /// @returns true if transaction is contract-creation.
+ bool isCreation() const { return m_type == ContractCreation; }
+
+ /// @returns true if transaction is message-call.
+ bool isMessageCall() const { return m_type == MessageCall; }
+
+ /// Serialises this transaction to an RLPStream.
+ void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
+
+ /// @returns the RLP serialisation of this transaction.
+ bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
+
+ /// @returns the SHA3 hash of the RLP serialisation of this transaction.
+ h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
+
+ /// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
+ u256 value() const { return m_value; }
+ /// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
+ u256 endowment() const { return m_value; }
+
+ /// @returns the base fee and thus the implied exchange rate of ETH to GAS.
+ u256 gasPrice() const { return m_gasPrice; }
+
+ /// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
+ u256 gas() const { return m_gas; }
+
+ /// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
+ Address receiveAddress() const { return m_receiveAddress; }
+
+ /// Synonym for receiveAddress().
+ Address to() const { return m_receiveAddress; }
+
+ /// Synonym for safeSender().
+ Address from() const { return safeSender(); }
+
+ /// @returns the data associated with this (message-call) transaction. Synonym for initCode().
+ bytes const& data() const { return m_data; }
+ /// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
+ bytes const& initCode() const { return m_data; }
+
+ /// @returns the transaction-count of the sender.
+ u256 nonce() const { return m_nonce; }
+
+ /// @returns the signature of the transaction. Encodes the sender.
+ SignatureStruct const& signature() const { return m_vrs; }
+
+ void sign(Secret const& _priv); ///< Sign the transaction.
+
+protected:
+ /// 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.
+ };
+
+ 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 h256 m_hashWith; ///< Cached hash of transaction with 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.
+using TransactionBases = std::vector;
+
+/// Simple human-readable stream-shift operator.
+inline std::ostream& operator<<(std::ostream& _out, TransactionBase const& _t)
+{
+ _out << _t.sha3().abridged() << "{";
+ if (_t.receiveAddress())
+ _out << _t.receiveAddress().abridged();
+ else
+ _out << "[CREATE]";
+
+ _out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
+ _out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
+ return _out;
+}
+
+}
+}
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index c7d7c60c4..1e2cbf940 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -339,7 +339,8 @@ Client::~Client()
stopWorking();
}
-static const Address c_canary("0x");
+static const Address c_canary("0x73c054b5865c427064641725a51032458d1d59b5");
+static const Addresses c_canaries = {};
bool Client::isChainBad() const
{
diff --git a/libethereum/Client.h b/libethereum/Client.h
index f38c7c099..98ea253ce 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -240,6 +240,8 @@ public:
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; }
+ /// Get the JSONRPC server to which we report bad blocks.
+ std::string const& sentinel() const { return m_sentinel; }
protected:
/// InterfaceStub methods
diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp
index 40a7914d3..70f82c6d2 100644
--- a/libethereum/Transaction.cpp
+++ b/libethereum/Transaction.cpp
@@ -94,99 +94,16 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException cons
return _out;
}
-Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig)
+Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig):
+ TransactionBase(_rlpData, _checkSig)
{
- int field = 0;
- RLP rlp(_rlpData);
- try
- {
- if (!rlp.isList())
- BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list"));
-
- m_nonce = rlp[field = 0].toInt();
- m_gasPrice = rlp[field = 1].toInt();
- m_gas = rlp[field = 2].toInt();
- m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
- m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash(RLP::VeryStrict);
- m_value = rlp[field = 4].toInt();
-
- if (!rlp[field = 5].isData())
- BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array"));
-
- m_data = rlp[field = 5].toBytes();
- byte v = rlp[field = 6].toInt() - 27;
- h256 r = rlp[field = 7].toInt();
- h256 s = rlp[field = 8].toInt();
-
- if (rlp.itemCount() > 9)
- BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP"));
-
- m_vrs = SignatureStruct{ r, s, v };
- if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid())
- BOOST_THROW_EXCEPTION(InvalidSignature());
- if (_checkSig == CheckTransaction::Everything)
- m_sender = sender();
- }
- catch (Exception& _e)
- {
- _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes()));
- throw;
- }
if (_checkSig >= CheckTransaction::Cheap && !checkPayment())
BOOST_THROW_EXCEPTION(OutOfGasIntrinsic() << RequirementError(gasRequired(), (bigint)gas()));
}
-Address const& Transaction::safeSender() const noexcept
-{
- try
- {
- return sender();
- }
- catch (...)
- {
- cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information();
- return ZeroAddress;
- }
-}
-
-Address const& Transaction::sender() const
-{
- if (!m_sender)
- {
- auto p = recover(m_vrs, sha3(WithoutSignature));
- if (!p)
- BOOST_THROW_EXCEPTION(InvalidSignature());
- m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p))));
- }
- 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));
- SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
- if (sigStruct.isValid())
- m_vrs = sigStruct;
-}
-
-void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const
-{
- if (m_type == NullTransaction)
- return;
- _s.appendList((_sig ? 3 : 0) + 6);
- _s << m_nonce << m_gasPrice << m_gas;
- if (m_type == MessageCall)
- _s << m_receiveAddress;
- else
- _s << "";
- _s << m_value << m_data;
- if (_sig)
- _s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s;
-}
diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h
index 4de9d7e92..95e1619fe 100644
--- a/libethereum/Transaction.h
+++ b/libethereum/Transaction.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
namespace dev
@@ -31,20 +32,6 @@ namespace dev
namespace eth
{
-/// Named-boolean type to encode whether a signature be included in the serialisation process.
-enum IncludeSignature
-{
- WithoutSignature = 0, ///< Do not include a signature.
- WithSignature = 1, ///< Do include a signature.
-};
-
-enum class CheckTransaction
-{
- None,
- Cheap,
- Everything
-};
-
enum class TransactionException
{
None = 0,
@@ -92,23 +79,13 @@ struct ExecutionResult
std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er);
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
-class Transaction
+class Transaction: public TransactionBase
{
public:
/// Constructs a null transaction.
Transaction() {}
- /// Constructs a signed message-call transaction.
- Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
-
- /// Constructs a signed contract-creation transaction.
- Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); }
-
- /// Constructs an unsigned message-call transaction.
- Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
-
- /// Constructs an unsigned contract-creation transaction.
- Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
+ using TransactionBase::TransactionBase;
/// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig);
@@ -116,68 +93,6 @@ public:
/// Constructs a transaction from the given RLP.
explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {}
-
- /// Checks equality of transactions.
- bool operator==(Transaction const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; }
- /// Checks inequality of transactions.
- bool operator!=(Transaction const& _c) const { return !operator==(_c); }
-
- /// @returns sender of the transaction from the signature (and hash).
- Address const& sender() const;
- /// Like sender() but will never throw. @returns a null Address if the signature is invalid.
- Address const& safeSender() const noexcept;
- /// Force the sender to a particular value. This will result in an invalid transaction RLP.
- void forceSender(Address const& _a) { m_sender = _a; }
-
- /// @returns true if transaction is non-null.
- explicit operator bool() const { return m_type != NullTransaction; }
-
- /// @returns true if transaction is contract-creation.
- bool isCreation() const { return m_type == ContractCreation; }
-
- /// @returns true if transaction is message-call.
- bool isMessageCall() const { return m_type == MessageCall; }
-
- /// Serialises this transaction to an RLPStream.
- void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const;
-
- /// @returns the RLP serialisation of this transaction.
- bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
-
- /// @returns the SHA3 hash of the RLP serialisation of this transaction.
- h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; }
-
- /// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment().
- u256 value() const { return m_value; }
- /// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value().
- u256 endowment() const { return m_value; }
-
- /// @returns the base fee and thus the implied exchange rate of ETH to GAS.
- u256 gasPrice() const { return m_gasPrice; }
-
- /// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
- u256 gas() const { return m_gas; }
-
- /// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions).
- Address receiveAddress() const { return m_receiveAddress; }
-
- /// Synonym for receiveAddress().
- Address to() const { return m_receiveAddress; }
-
- /// Synonym for safeSender().
- Address from() const { return safeSender(); }
-
- /// @returns the data associated with this (message-call) transaction. Synonym for initCode().
- bytes const& data() const { return m_data; }
- /// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data().
- bytes const& initCode() const { return m_data; }
-
- /// @returns the transaction-count of the sender.
- u256 nonce() const { return m_nonce; }
-
- /// @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(); }
@@ -188,46 +103,11 @@ public:
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.
- };
-
- 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.
-
- mutable h256 m_hashWith; ///< Cached hash of transaction with 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.
using Transactions = std::vector;
-/// Simple human-readable stream-shift operator.
-inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
-{
- _out << _t.sha3().abridged() << "{";
- if (_t.receiveAddress())
- _out << _t.receiveAddress().abridged();
- else
- _out << "[CREATE]";
-
- _out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice();
- _out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}";
- return _out;
-}
-
}
}