Browse Source

Gas pricing infrastructure and a couple of pricing mechanisms.

cl-refactor
Gav Wood 10 years ago
parent
commit
640f688b45
  1. 120
      eth/main.cpp
  2. 54
      libethereum/Client.cpp
  3. 28
      libethereum/Client.h
  4. 39
      libethereum/State.cpp
  5. 55
      libethereum/State.h
  6. 2
      test/blockchain.cpp

120
eth/main.cpp

@ -74,6 +74,9 @@ void interactiveHelp()
<< " jsonstop Stops the JSON-RPC server." << endl
<< " connect <addr> <port> Connects to a specific peer." << endl
<< " verbosity (<level>) Gets or sets verbosity level." << endl
<< " setetherprice <p> Resets the ether price." << endl
<< " setpriority <p> Resets the transaction priority." << endl
<< " minestart Starts mining." << endl
<< " minestart Starts mining." << endl
<< " minestop Stops mining." << endl
<< " mineforce <enable> Forces mining, even when there are no transactions." << endl
@ -85,12 +88,12 @@ void interactiveHelp()
<< " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl
<< " peers List the peers that are connected" << endl
<< " listAccounts List the accounts on the network." << endl
<< " listContracts List the contracts on the network." << endl
<< " setSecret <secret> Set the secret to the hex secret key." <<endl
<< " setAddress <addr> Set the coinbase (mining payout) address." <<endl
<< " exportConfig <path> Export the config (.RLP) to the path provided." <<endl
<< " importConfig <path> Import the config (.RLP) from the path provided." <<endl
<< " listaccounts List the accounts on the network." << endl
<< " listcontracts List the contracts on the network." << endl
<< " setsecret <secret> Set the secret to the hex secret key." <<endl
<< " setaddress <addr> Set the coinbase (mining payout) address." <<endl
<< " exportconfig <path> Export the config (.RLP) to the path provided." <<endl
<< " importconfig <path> Import the config (.RLP) from the path provided." <<endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " dumptrace <block> <index> <filename> <format> Dumps a transaction trace" << endl << "to <filename>. <format> should be one of pretty, standard, standard+." << endl
<< " dumpreceipt <block> <index> Dumps a transation receipt." << endl
@ -104,9 +107,11 @@ void help()
<< "Options:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (Default: 15)." << endl
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl
<< " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl
<< " -h,--help Show this help message and exit." << endl
<< " -i,--interactive Enter interactive mode (default: non-interactive)." << endl
@ -115,11 +120,12 @@ void help()
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl
#endif
<< " -l,--listen <port> Listen on the given port for incoming connected (default: 30303)." << endl
<< " -L,--local-networking Use peers whose addresses are local." << endl
<< " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (Default: off)" << endl
<< " -n,--upnp <on/off> Use upnp for NAT (default: on)." << endl
<< " -L,--local-networking Use peers whose addresses are local." << endl
<< " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
@ -128,7 +134,7 @@ void help()
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
<< " -V,--version Show the version and exit." << endl
#if ETH_EVMJIT
<< " --jit Use EVM JIT (default: off)." << endl
<< " --jit Use EVM JIT (default: off)." << endl
#endif
;
exit(0);
@ -212,6 +218,9 @@ int main(int argc, char** argv)
bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string clientName;
TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679;
double blockFees = 15.0;
// Init defaults
Defaults::get();
@ -289,6 +298,48 @@ int main(int argc, char** argv)
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try {
blockFees = stof(argv[++i]);
}
catch (...) {
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
}
else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc)
{
try {
etherPrice = stof(argv[++i]);
}
catch (...) {
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
}
else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
if (m == "lowest")
priority = TransactionPriority::Lowest;
else if (m == "low")
priority = TransactionPriority::Low;
else if (m == "medium" || m == "mid" || m == "default" || m == "normal")
priority = TransactionPriority::Medium;
else if (m == "high")
priority = TransactionPriority::High;
else if (m == "highest")
priority = TransactionPriority::Highest;
else
try {
priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100);
}
catch (...) {
cerr << "Unknown " << arg << " option: " << m << endl;
return -1;
}
}
else if ((arg == "-m" || arg == "--mining") && i + 1 < argc)
{
string m = argv[++i];
@ -301,7 +352,7 @@ int main(int argc, char** argv)
mining = stoi(m);
}
catch (...) {
cerr << "Unknown -m/--mining option: " << m << endl;
cerr << "Unknown " << arg << " option: " << m << endl;
return -1;
}
}
@ -373,10 +424,12 @@ int main(int argc, char** argv)
miners
);
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version);
if (c)
{
c->setGasPricer(gasPricer);
c->setForceMining(forceMining);
c->setAddress(coinbase);
}
@ -435,6 +488,7 @@ int main(int argc, char** argv)
istringstream iss(l);
string cmd;
iss >> cmd;
boost::to_lower(cmd);
if (cmd == "netstart")
{
iss >> netPrefs.listenPort;
@ -466,6 +520,42 @@ int main(int argc, char** argv)
iss >> enable;
c->setForceMining(isTrue(enable));
}
else if (c && cmd == "setblockfees")
{
iss >> blockFees;
gasPricer->setRefBlockFees(u256(blockFees * 1000));
cout << "Block fees: " << blockFees << endl;
}
else if (c && cmd == "setetherprice")
{
iss >> etherPrice;
gasPricer->setRefPrice(u256(double(ether / 1000) / etherPrice));
cout << "ether Price: " << etherPrice << endl;
}
else if (c && cmd == "setpriority")
{
string m;
iss >> m;
boost::to_lower(m);
if (m == "lowest")
priority = TransactionPriority::Lowest;
else if (m == "low")
priority = TransactionPriority::Low;
else if (m == "medium" || m == "mid" || m == "default" || m == "normal")
priority = TransactionPriority::Medium;
else if (m == "high")
priority = TransactionPriority::High;
else if (m == "highest")
priority = TransactionPriority::Highest;
else
try {
priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100);
}
catch (...) {
cerr << "Unknown priority: " << m << endl;
}
cout << "Priority: " << (int)priority << "/8" << endl;
}
else if (cmd == "verbosity")
{
if (iss.peek() != -1)
@ -582,7 +672,7 @@ int main(int argc, char** argv)
else
cwarn << "Require parameters: transact ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
}
else if (c && cmd == "listContracts")
else if (c && cmd == "listcontracts")
{
auto acs =c->addresses();
string ss;
@ -593,7 +683,7 @@ int main(int argc, char** argv)
cout << ss << endl;
}
}
else if (c && cmd == "listAccounts")
else if (c && cmd == "listaccounts")
{
auto acs =c->addresses();
string ss;
@ -809,7 +899,7 @@ int main(int argc, char** argv)
}
}
}
else if (cmd == "setSecret")
else if (cmd == "setsecret")
{
if (iss.peek() != -1)
{
@ -820,7 +910,7 @@ int main(int argc, char** argv)
else
cwarn << "Require parameter: setSecret HEXSECRETKEY";
}
else if (cmd == "setAddress")
else if (cmd == "setaddress")
{
if (iss.peek() != -1)
{
@ -848,7 +938,7 @@ int main(int argc, char** argv)
else
cwarn << "Require parameter: setAddress HEXADDRESS";
}
else if (cmd == "exportConfig")
else if (cmd == "exportconfig")
{
if (iss.peek() != -1)
{
@ -861,7 +951,7 @@ int main(int argc, char** argv)
else
cwarn << "Require parameter: exportConfig PATH";
}
else if (cmd == "importConfig")
else if (cmd == "importconfig")
{
if (iss.peek() != -1)
{

54
libethereum/Client.cpp

@ -60,16 +60,58 @@ void VersionChecker::setOk()
}
}
void BasicGasPricer::update(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
m_gasPerBlock = _bc.info(p).gasLimit;
map<u256, unsigned> dist;
unsigned total;
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash));
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();
total += (unsigned)gu;
}
}
p = bi.parentHash;
++c;
}
if (total > 0)
{
unsigned t = 0;
unsigned q = 1;
m_octiles[0] = dist.begin()->first;
for (auto const& i: dist)
{
for (; t <= total * q / 8 && t + i.second > total * q / 8; ++q)
m_octiles[q] = i.first;
if (q > 7)
break;
}
m_octiles[8] = dist.rbegin()->first;
}
}
Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
m_gp(u256("60000000000000")),
m_gp(new TrivialGasPricer),
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
m_gp.updateQuartiles(m_bc);
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
@ -85,16 +127,16 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean,
startWorking();
}
Client::Client(p2p::Host* _extNet, u256 weiPerCent, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
m_gp(weiPerCent),
m_gp(_gp),
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
m_gp.updateQuartiles(m_bc);
m_gp->update(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
@ -620,7 +662,7 @@ void Client::doWork()
// returns h256s as blooms, once for each transaction.
cwork << "postSTATE <== TQ";
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, m_gp);
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp);
if (newPendingReceipts.size())
{
for (size_t i = 0; i < newPendingReceipts.size(); i++)

28
libethereum/Client.h

@ -160,6 +160,27 @@ private:
State m_state;
};
class BasicGasPricer: public GasPricer
{
public:
explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {}
void setRefPrice(u256 _weiPerRef) { m_weiPerRef = _weiPerRef; }
void setRefBlockFees(u256 _refsPerBlock) { m_refsPerBlock = _refsPerBlock; }
protected:
u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); }
void update(BlockChain const& _bc) override;
private:
u256 m_weiPerRef;
u256 m_refsPerBlock;
u256 m_gasPerBlock = 1000000;
std::array<u256, 9> m_octiles;
};
/**
* @brief Main API hub for interfacing with Ethereum.
*/
@ -179,7 +200,7 @@ public:
explicit Client(
p2p::Host* _host,
u256 _weiPerCent,
std::shared_ptr<GasPricer> _gpForAdoption, // pass it in with new.
std::string const& _dbPath = std::string(),
bool _forceClean = false,
u256 _networkId = 0,
@ -189,6 +210,9 @@ public:
/// Destructor.
virtual ~Client();
/// Resets the gas pricer to some other object.
void setGasPricer(std::shared_ptr<GasPricer> _gp) { m_gp = _gp; }
/// Submits the given message-call transaction.
virtual void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo);
@ -361,7 +385,7 @@ private:
CanonBlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
GasPricer m_gp; ///< The gas pricer.
std::shared_ptr<GasPricer> m_gp; ///< The gas pricer.
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.

39
libethereum/State.cpp

@ -464,45 +464,6 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga
return ret;
}
void GasPricer::updateQuartiles(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
map<u256, unsigned> dist;
unsigned total;
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash));
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();
total += (unsigned)gu;
}
}
p = bi.parentHash;
++c;
}
if (total > 0)
{
unsigned t = 0;
unsigned q = 1;
for (auto const& i: dist)
{
for (; t <= total * q / 4 && t + i.second > total * q / 4; ++q)
m_quartiles[q - 1] = i.first;
if (q > 3)
break;
}
}
}
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
{
// m_currentBlock is assumed to be prepopulated and reset.

55
libethereum/State.h

@ -47,6 +47,7 @@ namespace eth
{
class BlockChain;
class State;
struct StateChat: public LogChannel { static const char* name() { return "-S-"; } static const int verbosity = 4; };
struct StateTrace: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 7; };
@ -55,7 +56,32 @@ struct StateSafeExceptions: public LogChannel { static const char* name() { retu
enum class BaseState { Empty, CanonGenesis };
class GasPricer;
enum class TransactionPriority
{
Lowest = 0,
Low = 2,
Medium = 4,
High = 6,
Highest = 8
};
class GasPricer
{
public:
GasPricer() {}
virtual u256 ask(State const&) const = 0;
virtual u256 bid(TransactionPriority _p = TransactionPriority::Medium) const = 0;
virtual void update(BlockChain const&) {}
};
class TrivialGasPricer: public GasPricer
{
protected:
u256 ask(State const&) const override { return 10 * szabo; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; }
};
/**
* @brief Model of the current state of the ledger.
@ -153,6 +179,7 @@ public:
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;
/// Returns the last few block hashes of the current chain.
LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const;
/// Execute a given transaction.
@ -370,32 +397,6 @@ void commit(std::map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Addr
}
}
enum class TransactionPriority
{
Low = 0,
Medium = 1,
High = 2
};
class GasPricer
{
public:
explicit GasPricer(u256 _weiPerCent): m_weiPerCent(_weiPerCent) {}
u256 ask(State const&) const { return m_weiPerCent * m_centsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const { return m_quartiles[(int)_p]; }
void updateRefPrice(u256 _weiPerCent) { m_weiPerCent = _weiPerCent; }
void updateRefRequirement(u256 _centsPerBlock) { m_centsPerBlock = _centsPerBlock; }
void updateQuartiles(BlockChain const& _bc);
private:
u256 m_weiPerCent;
u256 m_centsPerBlock = 15;
u256 m_gasPerBlock = 1000000;
std::array<u256, 100> m_quartiles;
};
}
}

2
test/blockchain.cpp

@ -87,7 +87,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// get txs
TransactionQueue txs;
GasPricer gp(10000);
TrivialGasPricer gp;
BOOST_REQUIRE(blObj.count("transactions"));
for (auto const& txObj: blObj["transactions"].get_array())
{

Loading…
Cancel
Save