提交 98006fd6 编写于 作者: B Bucky Kittinger

Merge branch 'slim' of https://github.com/eosio/eos into fix/ram_usage_checks

......@@ -40,6 +40,7 @@ libraries/wasm-jit/Source/Programs/Test
libraries/wasm-jit/Source/Programs/wavm
programs/cli_wallet/cli_wallet
programs/cleos/cleos
programs/js_operation_serializer/js_operation_serializer
programs/witness_node/witness_node
programs/data-dir
......
Exchange Deposit & Withdraw Documentation
-----------------------------------------
This document is targeted toward exchanges who wish to automate deposit
and withdraw of standard-conforming EOSIO token contracts. The blockchain's
native token conforms to the standard.
Configuring Nodeos
------------------
This tutorial uses the `cleos` commandline tool to query a local `nodeos` server
which should be connected to an eosio blockchain. `nodeos` will need to be configured
with the following plugins:
1. eosio::wallet_api_plugin
2. eosio::history_api_plugin
3. eosio::chain_api_plugin
By default the history plugin will log the history of all accounts, but this is not
the recomended configuration as it will consume tens of gigabytes of RAM in the
medium term. For a more optimized memory footprint you should configure the history
plugin to only log activity relevant to your account(s). This can be achieved with
the following config param placed in your config.ini or passed on the commandline.
```
$ nodeos --filter_on_accounts youraccount
```
Replaying the Blockchain
------------------------
If you have already synced the blockchain without the history plugin, then you may need to
replay the blockchain to pickup any historical activity.
```
$ nodeos --replay --filter_on_accounts youraccount
```
You only need to replay once, subsequent runs of nodeos should not use the replay flag or
your startup times will be unnecessiarlly long.
Accepting Deposits
-----------
When designing this tutorial we assume that an exchange will poll `nodeos` for incoming
transactions and will want to know when a transfer is considered irreversible or final.
With eosio based chains, finality of a transaction occurs once 2/3+1 of block produers have
either directly or indirectly confirmed the block. This could take from less than a second to
a couple of minutes, but either way nodeos will keep you posted on the status.
## Initial Condition
```
./cleos get currency balance eosio.token scott EOS
900.0000 EOS
```
We will now deposit some funds to exchange:
```
./cleos transfer scott exchange "1.0000 EOS"
executed transaction: 5ec797175dd24612acd8fc5a8685fa44caa8646cec0a87b12568db22a3df02fb 256 bytes 8k cycles
# eosio.token <= eosio.token::transfer {"from":"scott","to":"exchange","quantity":"1.0000 EOS","memo":""}
>> transfer
# scott <= eosio.token::transfer {"from":"scott","to":"exchange","quantity":"1.0000 EOS","memo":""}
# exchange <= eosio.token::transfer {"from":"scott","to":"exchange","quantity":"1.0000 EOS","memo":""}
warning: transaction executed locally, but may not be confirmed by the network yet
```
This output indicates that the action "eosio.token::transfer" was delivered to 3 accounts/contracts, (eosio.token, scott, and exchange).
The eosio token standard requires that both the sender and receiver account/contract be notified of all transfer actions so those
accounts can run custom logic. At this time neither `scott` nor `exchange` has any contact set, but the transaction log
still notes that they were notified.
## Polling Account History
The account history consists of all actions which were either authorized by the account or received by the account. Since the
exchange received the `eosio.token::transfer` action it is listed in the history. If you are using the console confirmed and
irreversible transactions are printed in "green" while unconfirmed transactions are printed in "yellow". Without color you
can tell whether a transaction is confirmed or not by the first character, '#' for irreversible and '?' for potentially reversable.
```
./cleos get actions exchange
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-04-29T01:09:45.000 eosio.token::transfer => exchange 5ec79717... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
```
Do a few more transfers:
```
./cleos get actions exchange
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-04-29T01:09:45.000 eosio.token::transfer => exchange 5ec79717... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
# 1 2018-04-29T01:16:25.000 eosio.token::transfer => exchange 2269828c... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
? 2 2018-04-29T01:19:54.000 eosio.token::transfer => exchange 213f3797... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
```
The last transfer is still pending, waiting on irreversibility.
The "seq" column represents the index of actions for your specific account, it will always increment as new relevant actions are added.
The `cleos get actions` command allows you some control over which actions are fetched, you can view the help for this command with `-h`
```
./cleos get actions -h
Usage: ./cleos get actions [OPTIONS] account_name [pos] [offset]
Positionals:
account_name TEXT name of account to query on
pos INT sequence number of action for this account, -1 for last
offset INT get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset
Options:
-j,--json print full json
--full don't truncate action json
--pretty pretty print full action json
--console print console output generated by action
```
To get only the last action you would do the following...
```
./cleos get actions exchange -1 -1
# seq when contract::action => receiver trx id... args
================================================================================================================
# 2 2018-04-29T01:19:54.000 eosio.token::transfer => exchange 213f3797... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
```
This says go to the last sequence number (indicated by pos = -1) and then fetch "1" item prior to it (offset = -1). This should
return sequence in the range [3-1,3) or [2,3) which is only row 2. In this case "-1" position means "one past the last sequence" and
operates like and end iterator from c++ containers.
### Fetching only "New" Actions
Since we presume your exchange is running a polling micro-service, it will want to fetch the "next unprocessed deposit". In this case the
microservice will need to track the seq number of the "last processed seq". For the sake of this example, we will assume that
"seq 0" has been processed and that we want to fetch "seq 1" if any.
We pass pos=1 and offset=0 to get the range [1,1+0] or [1,1].
```
./cleos get actions exchange 1 0
# seq when contract::action => receiver trx id... args
================================================================================================================
# 1 2018-04-29T01:16:25.000 eosio.token::transfer => exchange 2269828c... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
```
We can call this in a loop procesing each confirmed action (those starting with #) until we either run out of items or
we find an unconfirmed action (starting with ?).
```
./cleos get actions exchange 3 0
# seq when contract::action => receiver trx id... args
================================================================================================================
```
### Machine Readable Account History (JSON)
So far this tutorial has focused on using `cleos` to fetch and display the history, but cleos is merely a light-weight
wrapper around a json-rpc interface. `cleos` can dump the raw json returned from the json-rpc request or you can make
your own json-rpc request.
Here is the JSON returned when querying sequence 2.
```
./cleos get actions exchange 2 0 -j
{
"actions": [{
"global_action_seq": 32856,
"account_action_seq": 2,
"block_num": 32759,
"block_time": "2018-04-29T01:19:54.000",
"action_trace": {
"receipt": {
"receiver": "exchange",
"act_digest": "00686ff415fe97951a942889dbaed2b880043e3ae6ac2d5579318bbb2d30060f",
"global_sequence": 32856,
"recv_sequence": 3,
"auth_sequence": [[
"scott",
43
]
]
},
"act": {
"account": "eosio.token",
"name": "transfer",
"authorization": [{
"actor": "scott",
"permission": "active"
}
],
"data": {
"from": "scott",
"to": "exchange",
"quantity": "1.0000 EOS",
"memo": ""
},
"hex_data": "00000000809c29c20000008a4dd35057102700000000000004454f530000000000"
},
"elapsed": 52,
"cpu_usage": 1000,
"console": "",
"total_inline_cpu_usage": 1000,
"trx_id": "213f37972498cbae5abf6bcb5aec82e09967df7f04cf90f67b7d63a6bb871d58",
"inline_traces": []
}
}
],
"last_irreversible_block": 35062
}
```
Given this JSON, an action is irreversible (final) if `"block_num" < "last_irreversible_block"`.
You can identify irreversible deposits by the following:
```
actions[0].action_trace.act.account == "eosio.token" &&
actions[0].action_trace.act.name == "transfer" &&
actions[0].action_trace.act.data.quantity == "X.0000 EOS" &&
actions[0].action_trace.to == "exchange" &&
actions[0].action_trace.memo == "KEY TO IDENTIFY INTERNAL ACCOUNT" &&
actions[0].action_trace.receipt.receiver == "exchange" &&
actions[0].block_num < last_irreversible_block
```
In practice you should give your customers a "memo" that identifies which of your internal accounts you should
credit with the deposit.
## WARNING
It is critical that you validate all of the conditions above, including the token symbol name. Users can create
other contracts with "transfer" actions that "notify" your account. If you do not validate all of the above properties
then you may process "false deposits".
```
actions[0].action_trace.act.account == "eosio.token" &&
actions[0].action_trace.receipt.receiver == "exchange"
```
### Validating Balance
Now that we have received 3 deposits we should see that the exchange has a balance of 3.0000 EOS.
```
./cleos get currency balance eosio.token exchange EOS
3.0000 EOS
```
# Processing Withdraws
(note, while generating this tutorial scott deposited another 1.0000 EOS (seq 3) for total exchange balance of 4.0000 EOS.)
When a user requests a withdraw from your exchange they will need to provide you with their eosio account name and
the amount to be withdrawn. You can then run the cleos command which will interact with the "unlocked" wallet
running on `nodeos` which should only enable localhost connections. More advanced usage would have a separate
key-server (`keos`), but that will be covered later.
Lets assume scott wants to withdraw `1.0000 EOS`:
```
./cleos transfer exchange scott "1.0000 EOS"
executed transaction: 93e785202e7502bb1383ad10e786cc20f7dd738d3fd3da38712b3fb38fb9af26 256 bytes 8k cycles
# eosio.token <= eosio.token::transfer {"from":"exchange","to":"scott","quantity":"1.0000 EOS","memo":""}
>> transfer
# exchange <= eosio.token::transfer {"from":"exchange","to":"scott","quantity":"1.0000 EOS","memo":""}
# scott <= eosio.token::transfer {"from":"exchange","to":"scott","quantity":"1.0000 EOS","memo":""}
warning: transaction executed locally, but may not be confirmed by the network yet
```
At this stage your local `nodeos` client accepted the transaction and likely broadcast it to the broader network.
Now we can get the history and see that there are "3" new actions listed all with trx id `93e78520...` which is what
our transfer command returned to us. Because `exchange` authorized the transaction it is informed of all accounts which
processed and accepted the 'transfer'. In this case the 'eosio.token' contract processed it and updated balances, the
sender ('exchange') processed it and so did the receiver ('scott') and all 3 contracts/accounts approved it and/or performed
state transitions based upon the action.
```
./cleos get actions exchange -1 -8
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-04-29T01:09:45.000 eosio.token::transfer => exchange 5ec79717... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
# 1 2018-04-29T01:16:25.000 eosio.token::transfer => exchange 2269828c... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
# 2 2018-04-29T01:19:54.000 eosio.token::transfer => exchange 213f3797... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
# 3 2018-04-29T01:53:57.000 eosio.token::transfer => exchange 8b7766ac... {"from":"scott","to":"exchange","quantity":"1.0000 EOS","mem...
# 4 2018-04-29T01:54:17.500 eosio.token::transfer => eosio.token 93e78520... {"from":"exchange","to":"scott","quantity":"1.0000 EOS","mem...
# 5 2018-04-29T01:54:17.500 eosio.token::transfer => exchange 93e78520... {"from":"exchange","to":"scott","quantity":"1.0000 EOS","mem...
# 6 2018-04-29T01:54:17.500 eosio.token::transfer => scott 93e78520... {"from":"exchange","to":"scott","quantity":"1.0000 EOS","mem...
```
By processing the history we can also be informed when our transaction was confirmed. In practice it may be useful to embed an exchange-specify memo
on the withdraw request which you can use to map to your private database state which tracks the withdraw process *or* you could simply use the
transaction ID. When your account history microservice comes across seq 5 and sees it is irreversible it can then mark your withdaw as complete.
### Handling Errors
Sometimes network issues may cause a transaction to fail and never be included in a block. Your internal database will need to know when this has happend
so that it can inform the user and/or try again. If you do not get an immediate error when you submit your local transfer, then you must wait for
the transaction to expire. Every transaction has an "expiration" after which the transaction can never be applied. Once the last irreversible block has
moved past the expiration time you can safely mark your attempted withdaw as failed and not worry about it "floating around the ether" to be applied
when you least expect.
By default cleos sets an expiration window of just 2 minutes. This is long enough to allow all 21 producers an opportunity to include the transaction.
```
./cleos transfer exchange scott "1.0000 EOS" -j -d
{
"expiration": "2018-04-29T01:58:12",
"ref_block_num": 37282,
"ref_block_prefix": 351570603,
"max_net_usage_words": 0,
"max_kcpu_usage": 0,
"delay_sec": 0,
"context_free_actions": [],
...
```
Your microservice can query the last irreversible block number and the head block time using cleos.
```
./cleos get info
{
"server_version": "0812f84d",
"head_block_num": 39313,
"last_irreversible_block_num": 39298,
"last_irreversible_block_id": "000099823bfc4f0b936d8e48c70fc3f1619eb8d21989d160a9fe23655f1f5c79",
"head_block_id": "000099912473a7a3699ad682f731d1874ebddcf4b60eff79f8e6e4216077278d",
"head_block_time": "2018-04-29T02:14:31",
"head_block_producer": "producer2"
}
```
### Exchange Security
This tutorial shows the minimal viable deposit/withdraw handlers and assumes a single wallet which contains all keys necessary to
authorize deposits and withdaws. A security-focused exchange would take the following additional steps:
1. keep vast majority of funds in a time-delayed, multi-sig controlled account
2. use multi-sig on the hot wallet with several independent processes/servers double-checking all withdraws
3. deploy a custom contract that only allows withdraws to KYC'd accounts and require multi-sig to white-list accounts
4. deploy a custom contract that only accepts deposits of known tokens from KYC'd accounts
5. deploy a custom contract that enforces a mandatory 24 hour waiting period for all withdraws
6. utilize hardware wallets for all signing, even automated withdraw
Customer's want immediate withdraws, but they also want the exchange to be protected. The blockchain-enforced 24 hour period
lets the customer know the money is "on the way" while also informing potential-hackers that the exchange has 24 hours to
respond to unauthorized access. Furthermore, if the exchange emails/text messages users upon start of withdraw, users have
24 hours to contact the exchange and fix any unauthorized access to their individual account.
Information on how to utilize these more advanced techniques will be available in a future document.
......@@ -18,6 +18,7 @@ add_subdirectory(exchange)
add_subdirectory(test.inline)
#add_subdirectory(bancor)
add_subdirectory(hello)
add_subdirectory(asserter)
add_subdirectory(infinite)
add_subdirectory(proxy)
......
......@@ -77,17 +77,24 @@ namespace eosiosystem {
typedef eosio::multi_index< N(refunds), refund_request> refunds_table;
void system_contract::delegatebw( const account_name from, const account_name receiver,
const asset stake_net_quantity, const asset stake_cpu_quantity,
const asset stake_storage_quantity )
const asset& stake_net_quantity, const asset& stake_cpu_quantity,
const asset& stake_storage_quantity )
{
require_auth( from );
eosio_assert( stake_cpu_quantity.amount >= 0, "must stake a positive amount" );
eosio_assert( stake_net_quantity.amount >= 0, "must stake a positive amount" );
eosio_assert( stake_storage_quantity.amount >= 0, "must stake a positive amount" );
if( stake_storage_quantity.amount > 0 ) {
eosio_assert( from == receiver, "you may only stake storage to yourself" );
}
print( "adding stake...", stake_net_quantity, " ", stake_cpu_quantity, " ", stake_storage_quantity );
asset total_stake = stake_cpu_quantity + stake_net_quantity + stake_storage_quantity;
print( "\ntotal stake: ", total_stake );
eosio_assert( total_stake.amount > 0, "must stake a positive amount" );
require_auth( from );
//eosio_assert( is_account( receiver ), "can only delegate resources to an existing account" );
int64_t storage_bytes = 0;
......@@ -95,6 +102,11 @@ namespace eosiosystem {
global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
const eosio::asset token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name());
print( " token supply: ", token_supply, " \n" );
print( " max storage size: ", parameters.max_storage_size, "\n");
print( " total reserved: ", parameters.total_storage_bytes_reserved, "\n");
//make sure that there is no posibility of overflow here
int64_t storage_bytes_estimated = int64_t( parameters.max_storage_size - parameters.total_storage_bytes_reserved )
* int64_t(parameters.storage_reserve_ratio) * stake_storage_quantity
......@@ -107,6 +119,7 @@ namespace eosiosystem {
eosio_assert( 0 < storage_bytes, "stake is too small to increase storage even by 1 byte" );
parameters.total_storage_bytes_reserved += uint64_t(storage_bytes);
print( "\ntotal storage stake: ", parameters.total_storage_stake, "\n" );
parameters.total_storage_stake += stake_storage_quantity;
gs.set( parameters, _self );
}
......@@ -151,7 +164,7 @@ namespace eosiosystem {
});
}
//set_resource_limits( tot_itr->owner, tot_itr->storage_bytes, tot_itr->net_weight.quantity, tot_itr->cpu_weight.quantity );
set_resource_limits( tot_itr->owner, tot_itr->storage_bytes, tot_itr->net_weight.amount, tot_itr->cpu_weight.amount );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {from,N(active)},
{ from, N(eosio), total_stake, std::string("stake bandwidth") } );
......@@ -162,6 +175,20 @@ namespace eosiosystem {
} // delegatebw
void system_contract::setparams( uint64_t max_storage_size, uint32_t storage_reserve_ratio ) {
require_auth( _self );
eosio_assert( storage_reserve_ratio > 0, "invalid reserve ratio" );
global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
eosio_assert( max_storage_size > parameters.total_storage_bytes_reserved, "attempt to set max below reserved" );
parameters.max_storage_size = max_storage_size;
parameters.storage_reserve_ratio = storage_reserve_ratio;
gs.set( parameters, _self );
}
void system_contract::undelegatebw( const account_name from, const account_name receiver,
const asset unstake_net_quantity, const asset unstake_cpu_quantity,
......@@ -212,7 +239,7 @@ namespace eosiosystem {
tot.storage_bytes -= unstake_storage_bytes;
});
//set_resource_limits( totals.owner, totals.storage_bytes, totals.net_weight.quantity, totals.cpu_weight.quantity );
set_resource_limits( totals.owner, totals.storage_bytes, totals.net_weight.amount, totals.cpu_weight.amount );
refunds_table refunds_tbl( _self, from );
//create refund request
......
......@@ -95,27 +95,28 @@
"name": "blockchain_parameters",
"base": "",
"fields": [
{"name":"max_block_net_usage", "type": "uint64"},
{"name":"target_block_net_usage_pct", "type": "uint32"},
{"name":"max_transaction_net_usage", "type":"uint32"},
{"name":"base_per_transaction_net_usage", "type":"uint32"},
{"name":"context_free_discount_net_usage_num", "type":"uint64"},
{"name":"context_free_discount_net_usage_den", "type":"uint64"},
{"name":"max_block_cpu_usage", "type": "uint64"},
{"name":"target_block_cpu_usage_pct", "type": "uint32"},
{"name":"max_transaction_cpu_usage", "type":"uint32"},
{"name":"base_per_transaction_cpu_usage", "type":"uint32"},
{"name":"base_per_action_cpu_usage", "type":"uint32"},
{"name":"base_setcode_cpu_usage", "type":"uint32"},
{"name":"per_signature_cpu_usage", "type":"uint32"},
{"name":"per_lock_net_usage", "type":"uint32"},
{"name":"context_free_discount_cpu_usage_num", "type":"uint64"},
{"name":"context_free_discount_cpu_usage_den", "type":"uint64"},
{"name":"max_transaction_cpu_usage", "type":"uint32"},
{"name":"max_transaction_net_usage", "type":"uint32"},
{"name":"max_block_cpu_usage", "type": "uint64"},
{"name":"target_block_cpu_usage_pct", "type": "uint32"},
{"name":"max_block_net_usage", "type": "uint64"},
{"name":"target_block_net_usage_pct", "type": "uint32"},
{"name":"max_transaction_lifetime", "type":"uint32"},
{"name":"max_transaction_exec_time", "type":"uint32"},
{"name":"max_authority_depth", "type":"uint16"},
{"name":"max_inline_depth", "type":"uint16"},
{"name":"deferred_trx_expiration_window", "type":"uint32"},
{"name":"max_transaction_delay", "type":"uint32"},
{"name":"max_inline_action_size", "type":"uint32"},
{"name":"max_generated_transaction_count", "type":"uint32"},
{"name":"max_transaction_delay", "type":"uint32"}
{"name":"max_inline_action_depth", "type":"uint16"},
{"name":"max_authority_depth", "type":"uint16"},
{"name":"max_generated_transaction_count", "type":"uint32"}
]
},{
"name": "eosio_parameters",
......@@ -139,18 +140,17 @@
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"total_votes", "type":"uint128"},
{"name":"prefs", "type":"eosio_parameters"},
{"name":"packed_key", "type":"uint8[]"},
{"name":"packed_key", "type":"public_key"},
{"name":"per_block_payments", "type":"uint64"},
{"name":"last_claim_time", "type":"uint32"}
{"name":"last_claim_time", "type":"time"}
]
},{
"name": "regproducer",
"base": "",
"fields": [
{"name":"producer", "type":"account_name"},
{"name":"producer_key", "type":"bytes"},
{"name":"prefs", "type":"eosio_parameters"}
{"name":"producer_key", "type":"public_key"},
{"name":"url", "type":"string"}
]
},{
"name": "unregprod",
......@@ -158,6 +158,13 @@
"fields": [
{"name":"producer", "type":"account_name"}
]
},{
"name": "setparams",
"base": "",
"fields": [
{"name":"max_ram_size", "type":"uint64"},
{"name":"ram_reserve_ratio", "type":"uint32"}
]
},{
"name": "regproxy",
"base": "",
......@@ -184,7 +191,7 @@
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"proxy", "type":"account_name"},
{"name":"last_update", "type":"uint32"},
{"name":"last_update", "type":"time"},
{"name":"is_proxy", "type":"uint32"},
{"name":"staked", "type":"asset"},
{"name":"unstaking", "type":"asset"},
......@@ -226,6 +233,10 @@
"name": "regproducer",
"type": "regproducer",
"ricardian_contract": ""
},{
"name": "setparams",
"type": "setparams",
"ricardian_contract": ""
},{
"name": "unregprod",
"type": "unregprod",
......@@ -258,6 +269,18 @@
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["uint64"]
},{
"name": "global",
"type": "eosio_global_state",
"index_type": "i64",
"key_names" : [],
"key_types" : []
},{
"name": "voters",
"type": "voter_info",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["account_name"]
},{
"name": "totalband",
"type": "total_resources",
......
......@@ -6,11 +6,13 @@
#include "voting.cpp"
EOSIO_ABI( eosiosystem::system_contract,
(setparams)
// delegate_bandwith.cpp
(delegatebw)(undelegatebw)(refund)
(regproxy)
// voting.cpp
(unregproxy)(regproducer)(unregprod)(voteproducer)(onblock)
(unregproxy)(regproducer)(unregprod)(voteproducer)
(onblock)
// producer_pay.cpp
(claimrewards)
// native.hpp
......
......@@ -25,20 +25,19 @@ namespace eosiosystem {
time timestamp;
checksum256 transaction_mroot;
checksum256 action_mroot;
checksum256 block_mroot;
account_name producer;
uint32_t schedule_version;
eosio::optional<eosio::producer_schedule> new_producers;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(block_header, (previous)(timestamp)(transaction_mroot)(action_mroot)(block_mroot)
EOSLIB_SERIALIZE(block_header, (previous)(timestamp)(transaction_mroot)(action_mroot)
(producer)(schedule_version)(new_producers))
};
struct eosio_parameters : eosio::blockchain_parameters {
uint64_t max_storage_size = 10 * 1024 * 1024;
uint64_t max_storage_size = 1024 * 1024 * 1024;
uint32_t percent_of_max_inflation_rate = 0;
uint32_t storage_reserve_ratio = 1000; // ratio * 1000
uint32_t storage_reserve_ratio = 2000; // ratio * 1000
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE_DERIVED( eosio_parameters, eosio::blockchain_parameters, (max_storage_size)(percent_of_max_inflation_rate)(storage_reserve_ratio) )
......@@ -63,8 +62,7 @@ namespace eosiosystem {
struct producer_info {
account_name owner;
uint128_t total_votes = 0;
eosio_parameters prefs;
eosio::bytes packed_key; /// a packed public key object
eosio::public_key producer_key; /// a packed public key object
eosio::asset per_block_payments;
time last_rewards_claim = 0;
time time_became_active = 0;
......@@ -72,10 +70,10 @@ namespace eosiosystem {
uint64_t primary_key()const { return owner; }
uint128_t by_votes()const { return total_votes; }
bool active() const { return 0 < packed_key.size(); }
bool active() const { return producer_key != public_key(); }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs)(packed_key)
EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(producer_key)
(per_block_payments)(last_rewards_claim)
(time_became_active)(last_produced_block_time) )
};
......@@ -99,8 +97,8 @@ namespace eosiosystem {
// functions defined in delegate_bandwidth.cpp
void delegatebw( const account_name from, const account_name receiver,
const asset stake_net_quantity, const asset stake_cpu_quantity,
const asset stake_storage_quantity );
const asset& stake_net_quantity, const asset& stake_cpu_quantity,
const asset& stake_storage_quantity );
void undelegatebw( const account_name from, const account_name receiver,
const asset unstake_net_quantity, const asset unstake_cpu_quantity,
......@@ -110,13 +108,12 @@ namespace eosiosystem {
// functions defined in voting.cpp
void regproducer( const account_name producer, const bytes& producer_key, const eosio_parameters& prefs );
void regproducer( const account_name producer, const public_key& producer_key, const std::string& url );
void unregprod( const account_name producer );
eosio::asset payment_per_block(uint32_t percent_of_max_inflation_rate);
void setparams( uint64_t max_storage_size, uint32_t storage_reserve_ratio );
void update_elected_producers(time cycle_time);
void voteproducer( const account_name voter, const account_name proxy, const std::vector<account_name>& producers );
......@@ -133,6 +130,10 @@ namespace eosiosystem {
void claimrewards( const account_name& owner );
private:
eosio::asset payment_per_block(uint32_t percent_of_max_inflation_rate);
void update_elected_producers(time cycle_time);
// Implementation details:
//defined in voting.hpp
......
......@@ -7,6 +7,8 @@
#include <eosiolib/action.hpp>
#include <eosiolib/public_key.hpp>
#include <eosiolib/types.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/privileged.h>
namespace eosiosystem {
using eosio::permission_level;
......@@ -47,11 +49,15 @@ namespace eosiosystem {
class native {
public:
void newaccount( /*account_name creator,
account_name name,
void newaccount( account_name creator,
account_name newact
/*
const authority& owner,
const authority& active,
const authority& recovery*/ ) {}
const authority& recovery*/ ) {
eosio::print( eosio::name{creator}, " created ", eosio::name{newact});
set_resource_limits( newact, 1000, 0, 0 );
}
void updateauth( /*account_name account,
permission_name permission,
......
......@@ -59,27 +59,25 @@ namespace eosiosystem {
* @pre authority of producer to register
*
*/
void system_contract::regproducer( const account_name producer, const bytes& packed_producer_key, const eosio_parameters& prefs ) {
void system_contract::regproducer( const account_name producer, const public_key& producer_key, const std::string& url ) { //, const eosio_parameters& prefs ) {
eosio_assert( url.size() < 512, "url too long" );
//eosio::print("produce_key: ", producer_key.size(), ", sizeof(public_key): ", sizeof(public_key), "\n");
require_auth( producer );
producers_table producers_tbl( _self, _self );
auto prod = producers_tbl.find( producer );
//check that we can unpack producer key
public_key producer_key = eosio::unpack<public_key>( packed_producer_key );
if ( prod != producers_tbl.end() ) {
producers_tbl.modify( prod, producer, [&]( producer_info& info ){
info.prefs = prefs;
info.packed_key = eosio::pack<public_key>( producer_key );
});
if( producer_key != prod->producer_key ) {
producers_tbl.modify( prod, producer, [&]( producer_info& info ){
info.producer_key = producer_key;
});
}
} else {
producers_tbl.emplace( producer, [&]( producer_info& info ){
info.owner = producer;
info.total_votes = 0;
info.prefs = prefs;
info.packed_key = eosio::pack<public_key>( producer_key );
info.producer_key = producer_key;
});
}
}
......@@ -92,7 +90,7 @@ namespace eosiosystem {
eosio_assert( prod != producers_tbl.end(), "producer not found" );
producers_tbl.modify( prod, 0, [&]( producer_info& info ){
info.packed_key.clear();
info.producer_key = public_key();
});
}
......@@ -205,30 +203,6 @@ namespace eosiosystem {
producers_table producers_tbl( _self, _self );
auto idx = producers_tbl.template get_index<N(prototalvote)>();
std::array<uint32_t, 21> base_per_transaction_net_usage;
std::array<uint32_t, 21> base_per_transaction_cpu_usage;
std::array<uint32_t, 21> base_per_action_cpu_usage;
std::array<uint32_t, 21> base_setcode_cpu_usage;
std::array<uint32_t, 21> per_signature_cpu_usage;
std::array<uint32_t, 21> per_lock_net_usage;
std::array<uint64_t, 21> context_free_discount_cpu_usage_num;
std::array<uint64_t, 21> context_free_discount_cpu_usage_den;
std::array<uint32_t, 21> max_transaction_cpu_usage;
std::array<uint32_t, 21> max_transaction_net_usage;
std::array<uint64_t, 21> max_block_cpu_usage;
std::array<uint32_t, 21> target_block_cpu_usage_pct;
std::array<uint64_t, 21> max_block_net_usage;
std::array<uint32_t, 21> target_block_net_usage_pct;
std::array<uint32_t, 21> max_transaction_lifetime;
std::array<uint16_t, 21> max_authority_depth;
std::array<uint32_t, 21> max_transaction_exec_time;
std::array<uint16_t, 21> max_inline_depth;
std::array<uint32_t, 21> max_inline_action_size;
std::array<uint32_t, 21> max_generated_transaction_count;
std::array<uint32_t, 21> max_transaction_delay;
std::array<uint32_t, 21> percent_of_max_inflation_rate;
std::array<uint32_t, 21> storage_reserve_ratio;
eosio::producer_schedule schedule;
schedule.producers.reserve(21);
size_t n = 0;
......@@ -236,98 +210,21 @@ namespace eosiosystem {
if ( it->active() ) {
schedule.producers.emplace_back();
schedule.producers.back().producer_name = it->owner;
//eosio_assert( sizeof(schedule.producers.back().block_signing_key) == it->packed_key.size(), "size mismatch" );
schedule.producers.back().block_signing_key = eosio::unpack<public_key>( it->packed_key );
//std::copy( it->packed_key.begin(), it->packed_key.end(), schedule.producers.back().block_signing_key.data.data() );
base_per_transaction_net_usage[n] = it->prefs.base_per_transaction_net_usage;
base_per_transaction_cpu_usage[n] = it->prefs.base_per_transaction_cpu_usage;
base_per_action_cpu_usage[n] = it->prefs.base_per_action_cpu_usage;
base_setcode_cpu_usage[n] = it->prefs.base_setcode_cpu_usage;
per_signature_cpu_usage[n] = it->prefs.per_signature_cpu_usage;
per_lock_net_usage[n] = it->prefs.per_lock_net_usage;
context_free_discount_cpu_usage_num[n] = it->prefs.context_free_discount_cpu_usage_num;
context_free_discount_cpu_usage_den[n] = it->prefs.context_free_discount_cpu_usage_den;
max_transaction_cpu_usage[n] = it->prefs.max_transaction_cpu_usage;
max_transaction_net_usage[n] = it->prefs.max_transaction_net_usage;
max_block_cpu_usage[n] = it->prefs.max_block_cpu_usage;
target_block_cpu_usage_pct[n] = it->prefs.target_block_cpu_usage_pct;
max_block_net_usage[n] = it->prefs.max_block_net_usage;
target_block_net_usage_pct[n] = it->prefs.target_block_net_usage_pct;
max_transaction_lifetime[n] = it->prefs.max_transaction_lifetime;
max_authority_depth[n] = it->prefs.max_authority_depth;
max_transaction_exec_time[n] = it->prefs.max_transaction_exec_time;
max_inline_depth[n] = it->prefs.max_inline_depth;
max_inline_action_size[n] = it->prefs.max_inline_action_size;
max_generated_transaction_count[n] = it->prefs.max_generated_transaction_count;
max_transaction_delay[n] = it->prefs.max_transaction_delay;
storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
schedule.producers.back().block_signing_key = it->producer_key;
++n;
}
}
if ( n == 0 ) { //no active producers with votes > 0
return;
}
if ( 1 < n ) {
std::sort( base_per_transaction_net_usage.begin(), base_per_transaction_net_usage.begin()+n );
std::sort( base_per_transaction_cpu_usage.begin(), base_per_transaction_cpu_usage.begin()+n );
std::sort( base_per_action_cpu_usage.begin(), base_per_action_cpu_usage.begin()+n );
std::sort( base_setcode_cpu_usage.begin(), base_setcode_cpu_usage.begin()+n );
std::sort( per_signature_cpu_usage.begin(), per_signature_cpu_usage.begin()+n );
std::sort( per_lock_net_usage.begin(), per_lock_net_usage.begin()+n );
std::sort( context_free_discount_cpu_usage_num.begin(), context_free_discount_cpu_usage_num.begin()+n );
std::sort( context_free_discount_cpu_usage_den.begin(), context_free_discount_cpu_usage_den.begin()+n );
std::sort( max_transaction_cpu_usage.begin(), max_transaction_cpu_usage.begin()+n );
std::sort( max_transaction_net_usage.begin(), max_transaction_net_usage.begin()+n );
std::sort( max_block_cpu_usage.begin(), max_block_cpu_usage.begin()+n );
std::sort( target_block_cpu_usage_pct.begin(), target_block_cpu_usage_pct.begin()+n );
std::sort( max_block_net_usage.begin(), max_block_net_usage.begin()+n );
std::sort( target_block_net_usage_pct.begin(), target_block_net_usage_pct.begin()+n );
std::sort( max_transaction_lifetime.begin(), max_transaction_lifetime.begin()+n );
std::sort( max_transaction_exec_time.begin(), max_transaction_exec_time.begin()+n );
std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
std::sort( max_inline_depth.begin(), max_inline_depth.begin()+n );
std::sort( max_inline_action_size.begin(), max_inline_action_size.begin()+n );
std::sort( max_generated_transaction_count.begin(), max_generated_transaction_count.begin()+n );
std::sort( max_transaction_delay.begin(), max_transaction_delay.begin()+n );
std::sort( storage_reserve_ratio.begin(), storage_reserve_ratio.begin()+n );
std::sort( percent_of_max_inflation_rate.begin(), percent_of_max_inflation_rate.begin()+n );
}
// should use producer_schedule_type from libraries/chain/include/eosio/chain/producer_schedule.hpp
bytes packed_schedule = pack(schedule);
set_active_producers( packed_schedule.data(), packed_schedule.size() );
size_t median = n/2;
global_state_singleton gs( _self, _self );
auto parameters = gs.exists() ? gs.get() : get_default_parameters();
parameters.base_per_transaction_net_usage = base_per_transaction_net_usage[median];
parameters.base_per_transaction_cpu_usage = base_per_transaction_cpu_usage[median];
parameters.base_per_action_cpu_usage = base_per_action_cpu_usage[median];
parameters.base_setcode_cpu_usage = base_setcode_cpu_usage[median];
parameters.per_signature_cpu_usage = per_signature_cpu_usage[median];
parameters.per_lock_net_usage = per_lock_net_usage[median];
parameters.context_free_discount_cpu_usage_num = context_free_discount_cpu_usage_num[median];
parameters.context_free_discount_cpu_usage_den = context_free_discount_cpu_usage_den[median];
parameters.max_transaction_cpu_usage = max_transaction_cpu_usage[median];
parameters.max_transaction_net_usage = max_transaction_net_usage[median];
parameters.max_block_cpu_usage = max_block_cpu_usage[median];
parameters.target_block_cpu_usage_pct = target_block_cpu_usage_pct[median];
parameters.max_block_net_usage = max_block_net_usage[median];
parameters.target_block_net_usage_pct = target_block_net_usage_pct[median];
parameters.max_transaction_lifetime = max_transaction_lifetime[median];
parameters.max_transaction_exec_time = max_transaction_exec_time[median];
parameters.max_authority_depth = max_authority_depth[median];
parameters.max_inline_depth = max_inline_depth[median];
parameters.max_inline_action_size = max_inline_action_size[median];
parameters.max_generated_transaction_count = max_generated_transaction_count[median];
parameters.max_transaction_delay = max_transaction_delay[median];
parameters.storage_reserve_ratio = storage_reserve_ratio[median];
parameters.percent_of_max_inflation_rate = percent_of_max_inflation_rate[median];
// not voted on
parameters.first_block_time_in_cycle = cycle_time;
......
......@@ -45,7 +45,7 @@ namespace eosiosystem {
struct producer_info {
account_name owner;
uint128_t total_votes = 0;
eosio_parameters prefs;
// eosio_parameters prefs;
eosio::bytes packed_key; /// a packed public key object
system_token_type per_block_payments;
time last_rewards_claim = 0;
......@@ -56,7 +56,7 @@ namespace eosiosystem {
uint128_t by_votes()const { return total_votes; }
bool active() const { return packed_key.size() == sizeof(public_key); }
EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs)(packed_key)
EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(packed_key)
(per_block_payments)(last_rewards_claim)
(time_became_active)(last_produced_block_time) )
};
......@@ -86,60 +86,6 @@ namespace eosiosystem {
typedef eosio::multi_index< N(voters), voter_info> voters_table;
ACTION( SystemAccount, regproducer ) {
account_name producer;
bytes producer_key;
eosio_parameters prefs;
EOSLIB_SERIALIZE( regproducer, (producer)(producer_key)(prefs) )
};
/**
* This method will create a producer_config and producer_info object for 'producer'
*
* @pre producer is not already registered
* @pre producer to register is an account
* @pre authority of producer to register
*
*/
static void on( const regproducer& reg ) {
require_auth( reg.producer );
producers_table producers_tbl( SystemAccount, SystemAccount );
auto prod = producers_tbl.find( reg.producer );
if ( prod != producers_tbl.end() ) {
producers_tbl.modify( prod, reg.producer, [&]( producer_info& info ){
info.prefs = reg.prefs;
info.packed_key = reg.producer_key;
});
} else {
producers_tbl.emplace( reg.producer, [&]( producer_info& info ){
info.owner = reg.producer;
info.total_votes = 0;
info.prefs = reg.prefs;
info.packed_key = reg.producer_key;
});
}
}
ACTION( SystemAccount, unregprod ) {
account_name producer;
EOSLIB_SERIALIZE( unregprod, (producer) )
};
static void on( const unregprod& unreg ) {
require_auth( unreg.producer );
producers_table producers_tbl( SystemAccount, SystemAccount );
auto prod = producers_tbl.find( unreg.producer );
eosio_assert( prod != producers_tbl.end(), "producer not found" );
producers_tbl.modify( prod, 0, [&]( producer_info& info ){
info.packed_key.clear();
});
}
static void increase_voting_power( account_name acnt, system_token_type amount ) {
voters_table voters_tbl( SystemAccount, SystemAccount );
......@@ -238,30 +184,39 @@ namespace eosiosystem {
return (system_token_type(payment));
}
static void update_elected_producers(time cycle_time);
#if 0
static void update_elected_producers(time cycle_time) {
producers_table producers_tbl( SystemAccount, SystemAccount );
auto idx = producers_tbl.template get_index<N(prototalvote)>();
std::array<uint64_t, 21> max_block_net_usage;
std::array<uint32_t, 21> target_block_net_usage_pct;
std::array<uint32_t, 21> base_per_transaction_net_usage;
std::array<uint32_t, 21> max_transaction_net_usage;
std::array<uint64_t, 21> context_free_discount_net_usage_num;
std::array<uint64_t, 21> context_free_discount_net_usage_den;
std::array<uint64_t, 21> max_block_cpu_usage;
std::array<uint32_t, 21> target_block_cpu_usage_pct;
std::array<uint32_t, 21> max_transaction_cpu_usage;
std::array<uint32_t, 21> base_per_transaction_cpu_usage;
std::array<uint32_t, 21> base_per_action_cpu_usage;
std::array<uint32_t, 21> base_setcode_cpu_usage;
std::array<uint32_t, 21> per_signature_cpu_usage;
std::array<uint32_t, 21> per_lock_net_usage;
std::array<uint64_t, 21> context_free_discount_cpu_usage_num;
std::array<uint64_t, 21> context_free_discount_cpu_usage_den;
std::array<uint32_t, 21> max_transaction_cpu_usage;
std::array<uint32_t, 21> max_transaction_net_usage;
std::array<uint64_t, 21> max_block_cpu_usage;
std::array<uint32_t, 21> target_block_cpu_usage_pct;
std::array<uint64_t, 21> max_block_net_usage;
std::array<uint32_t, 21> target_block_net_usage_pct;
std::array<uint32_t, 21> max_transaction_lifetime;
std::array<uint16_t, 21> max_authority_depth;
std::array<uint32_t, 21> max_transaction_exec_time;
std::array<uint16_t, 21> max_inline_depth;
std::array<uint32_t, 21> deferred_trx_expiration_window;
std::array<uint32_t, 21> max_transaction_delay;
std::array<uint32_t, 21> max_inline_action_size;
std::array<uint16_t, 21> max_inline_action_depth;
std::array<uint16_t, 21> max_authority_depth;
std::array<uint32_t, 21> max_generated_transaction_count;
std::array<uint32_t, 21> max_storage_size;
std::array<uint32_t, 21> percent_of_max_inflation_rate;
std::array<uint32_t, 21> storage_reserve_ratio;
......@@ -275,27 +230,32 @@ namespace eosiosystem {
eosio_assert( sizeof(schedule.back().block_signing_key) == it->packed_key.size(), "size mismatch" );
std::copy( it->packed_key.begin(), it->packed_key.end(), schedule.back().block_signing_key.data );
max_block_net_usage[n] = it->prefs.max_block_net_usage;
target_block_net_usage_pct[n] = it->prefs.target_block_net_usage_pct;
max_transaction_net_usage[n] = it->prefs.max_transaction_net_usage;
base_per_transaction_net_usage[n] = it->prefs.base_per_transaction_net_usage;
context_free_discount_net_usage_num[n] = it->prefs.context_free_discount_net_usage_num;
context_free_discount_net_usage_den[n] = it->prefs.context_free_discount_net_usage_den;
max_block_cpu_usage[n] = it->prefs.max_block_cpu_usage;
target_block_cpu_usage_pct[n] = it->prefs.target_block_cpu_usage_pct;
max_transaction_cpu_usage[n] = it->prefs.max_transaction_cpu_usage;
base_per_transaction_cpu_usage[n] = it->prefs.base_per_transaction_cpu_usage;
base_per_action_cpu_usage[n] = it->prefs.base_per_action_cpu_usage;
base_setcode_cpu_usage[n] = it->prefs.base_setcode_cpu_usage;
per_signature_cpu_usage[n] = it->prefs.per_signature_cpu_usage;
per_lock_net_usage[n] = it->prefs.per_lock_net_usage;
context_free_discount_cpu_usage_num[n] = it->prefs.context_free_discount_cpu_usage_num;
context_free_discount_cpu_usage_den[n] = it->prefs.context_free_discount_cpu_usage_den;
max_transaction_cpu_usage[n] = it->prefs.max_transaction_cpu_usage;
max_transaction_net_usage[n] = it->prefs.max_transaction_net_usage;
max_block_cpu_usage[n] = it->prefs.max_block_cpu_usage;
target_block_cpu_usage_pct[n] = it->prefs.target_block_cpu_usage_pct;
max_block_net_usage[n] = it->prefs.max_block_net_usage;
target_block_net_usage_pct[n] = it->prefs.target_block_net_usage_pct;
max_transaction_lifetime[n] = it->prefs.max_transaction_lifetime;
max_authority_depth[n] = it->prefs.max_authority_depth;
max_transaction_exec_time[n] = it->prefs.max_transaction_exec_time;
max_inline_depth[n] = it->prefs.max_inline_depth;
deferred_trx_expiration_window[n] = it->prefs.deferred_trx_expiration_window;
max_transaction_delay[n] = it->prefs.max_transaction_delay;
max_inline_action_size[n] = it->prefs.max_inline_action_size;
max_inline_action_depth[n] = it->prefs.max_inline_action_depth;
max_authority_depth[n] = it->prefs.max_authority_depth;
max_generated_transaction_count[n] = it->prefs.max_generated_transaction_count;
max_storage_size[n] = it->prefs.max_storage_size;
storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
++n;
......@@ -305,26 +265,32 @@ namespace eosiosystem {
return;
}
if ( 1 < n ) {
std::sort( max_block_net_usage.begin(), max_block_net_usage.begin()+n );
std::sort( target_block_net_usage_pct.begin(), target_block_net_usage_pct.begin()+n );
std::sort( max_transaction_net_usage.begin(), max_transaction_net_usage.begin()+n );
std::sort( base_per_transaction_net_usage.begin(), base_per_transaction_net_usage.begin()+n );
std::sort( context_free_discount_net_usage_num.begin(), context_free_discount_net_usage_num.begin()+n );
std::sort( context_free_discount_net_usage_den.begin(), context_free_discount_net_usage_den.begin()+n );
std::sort( max_block_cpu_usage.begin(), max_block_cpu_usage.begin()+n );
std::sort( target_block_cpu_usage_pct.begin(), target_block_cpu_usage_pct.begin()+n );
std::sort( max_transaction_cpu_usage.begin(), max_transaction_cpu_usage.begin()+n );
std::sort( base_per_transaction_cpu_usage.begin(), base_per_transaction_cpu_usage.begin()+n );
std::sort( base_per_action_cpu_usage.begin(), base_per_action_cpu_usage.begin()+n );
std::sort( base_setcode_cpu_usage.begin(), base_setcode_cpu_usage.begin()+n );
std::sort( per_signature_cpu_usage.begin(), per_signature_cpu_usage.begin()+n );
std::sort( per_lock_net_usage.begin(), per_lock_net_usage.begin()+n );
std::sort( context_free_discount_cpu_usage_num.begin(), context_free_discount_cpu_usage_num.begin()+n );
std::sort( context_free_discount_cpu_usage_den.begin(), context_free_discount_cpu_usage_den.begin()+n );
std::sort( max_transaction_cpu_usage.begin(), max_transaction_cpu_usage.begin()+n );
std::sort( max_transaction_net_usage.begin(), max_transaction_net_usage.begin()+n );
std::sort( max_block_cpu_usage.begin(), max_block_cpu_usage.begin()+n );
std::sort( target_block_cpu_usage_pct.begin(), target_block_cpu_usage_pct.begin()+n );
std::sort( max_block_net_usage.begin(), max_block_net_usage.begin()+n );
std::sort( target_block_net_usage_pct.begin(), target_block_net_usage_pct.begin()+n );
std::sort( max_transaction_lifetime.begin(), max_transaction_lifetime.begin()+n );
std::sort( max_transaction_exec_time.begin(), max_transaction_exec_time.begin()+n );
std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
std::sort( max_inline_depth.begin(), max_inline_depth.begin()+n );
std::sort( deferred_trx_expiration_window.begin(), deferred_trx_expiration_window.begin()+n );
std::sort( max_transaction_delay.begin(), max_transaction_delay.begin()+n );
std::sort( max_inline_action_size.begin(), max_inline_action_size.begin()+n );
std::sort( max_inline_action_depth.begin(), max_inline_action_depth.begin()+n );
std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
std::sort( max_generated_transaction_count.begin(), max_generated_transaction_count.begin()+n );
std::sort( max_storage_size.begin(), max_storage_size.begin()+n );
std::sort( storage_reserve_ratio.begin(), storage_reserve_ratio.begin()+n );
std::sort( percent_of_max_inflation_rate.begin(), percent_of_max_inflation_rate.begin()+n );
}
......@@ -336,26 +302,32 @@ namespace eosiosystem {
auto parameters = global_state_singleton::exists() ? global_state_singleton::get()
: common<SystemAccount>::get_default_parameters();
parameters.max_block_net_usage = max_block_net_usage[median];
parameters.target_block_net_usage_pct = target_block_net_usage_pct[median];
parameters.max_transaction_net_usage = max_transaction_net_usage[median];
parameters.base_per_transaction_net_usage = base_per_transaction_net_usage[median];
parameters.context_free_discount_net_usage_num = context_free_discount_net_usage_num[median];
parameters.context_free_discount_net_usage_den = context_free_discount_net_usage_den[median];
parameters.max_block_cpu_usage = max_block_cpu_usage[median];
parameters.target_block_cpu_usage_pct = target_block_cpu_usage_pct[median];
parameters.max_transaction_cpu_usage = max_transaction_cpu_usage[median];
parameters.base_per_transaction_cpu_usage = base_per_transaction_cpu_usage[median];
parameters.base_per_action_cpu_usage = base_per_action_cpu_usage[median];
parameters.base_setcode_cpu_usage = base_setcode_cpu_usage[median];
parameters.per_signature_cpu_usage = per_signature_cpu_usage[median];
parameters.per_lock_net_usage = per_lock_net_usage[median];
parameters.context_free_discount_cpu_usage_num = context_free_discount_cpu_usage_num[median];
parameters.context_free_discount_cpu_usage_den = context_free_discount_cpu_usage_den[median];
parameters.max_transaction_cpu_usage = max_transaction_cpu_usage[median];
parameters.max_transaction_net_usage = max_transaction_net_usage[median];
parameters.max_block_cpu_usage = max_block_cpu_usage[median];
parameters.target_block_cpu_usage_pct = target_block_cpu_usage_pct[median];
parameters.max_block_net_usage = max_block_net_usage[median];
parameters.target_block_net_usage_pct = target_block_net_usage_pct[median];
parameters.max_transaction_lifetime = max_transaction_lifetime[median];
parameters.max_transaction_exec_time = max_transaction_exec_time[median];
parameters.max_authority_depth = max_authority_depth[median];
parameters.max_inline_depth = max_inline_depth[median];
parameters.deferred_trx_expiration_window = deferred_trx_expiration_window[median];
parameters.max_transaction_delay = max_transaction_delay[median];
parameters.max_inline_action_size = max_inline_action_size[median];
parameters.max_inline_action_depth = max_inline_action_depth[median];
parameters.max_authority_depth = max_authority_depth[median];
parameters.max_generated_transaction_count = max_generated_transaction_count[median];
parameters.max_storage_size = max_storage_size[median];
parameters.storage_reserve_ratio = storage_reserve_ratio[median];
parameters.percent_of_max_inflation_rate = percent_of_max_inflation_rate[median];
......@@ -378,6 +350,7 @@ namespace eosiosystem {
set_blockchain_parameters(parameters);
global_state_singleton::set(parameters);
}
#endif
ACTION( SystemAccount, voteproducer ) {
account_name voter;
......
......@@ -6,11 +6,6 @@
#include <eosiolib/types.hpp>
#include <eosiolib/action.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/math.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/contract.hpp>
......@@ -203,7 +203,7 @@ namespace eosio {
* @return if c1 > c2, return true, otherwise false
*/
template<size_t Size>
bool operator>(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
bool operator>(const fixed_key<Size>& c1, const fixed_key<Size>& c2) {
return c1._data > c2._data;
}
......
......@@ -5,7 +5,6 @@
#pragma once
#include <eosiolib/print.h>
#include <eosiolib/types.hpp>
#include <eosiolib/math.hpp>
#include <eosiolib/fixed_key.hpp>
#include <utility>
......
......@@ -6,38 +6,44 @@
namespace eosio {
struct blockchain_parameters {
uint64_t max_block_net_usage;
uint32_t target_block_net_usage_pct;
uint32_t max_transaction_net_usage;
uint32_t base_per_transaction_net_usage;
uint64_t context_free_discount_net_usage_num;
uint64_t context_free_discount_net_usage_den;
uint64_t max_block_cpu_usage;
uint32_t target_block_cpu_usage_pct;
uint32_t max_transaction_cpu_usage;
uint32_t base_per_transaction_cpu_usage;
uint32_t base_per_action_cpu_usage;
uint32_t base_setcode_cpu_usage;
uint32_t per_signature_cpu_usage;
uint32_t per_lock_net_usage;
uint64_t context_free_discount_cpu_usage_num;
uint64_t context_free_discount_cpu_usage_den;
uint32_t max_transaction_cpu_usage;
uint32_t max_transaction_net_usage;
uint64_t max_block_cpu_usage;
uint32_t target_block_cpu_usage_pct;
uint64_t max_block_net_usage;
uint32_t target_block_net_usage_pct;
uint32_t max_transaction_lifetime;
uint32_t max_transaction_exec_time;
uint16_t max_authority_depth;
uint16_t max_inline_depth;
uint32_t deferred_trx_expiration_window;
uint32_t max_transaction_delay;
uint32_t max_inline_action_size;
uint16_t max_inline_action_depth;
uint16_t max_authority_depth;
uint32_t max_generated_transaction_count;
uint32_t max_transaction_delay;
EOSLIB_SERIALIZE( blockchain_parameters,
(base_per_transaction_net_usage)(base_per_transaction_cpu_usage)(base_per_action_cpu_usage)
(base_setcode_cpu_usage)(per_signature_cpu_usage)(per_lock_net_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_cpu_usage)(max_transaction_net_usage)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_lifetime)(max_transaction_exec_time)(max_authority_depth)
(max_inline_depth)(max_inline_action_size)(max_generated_transaction_count)
(max_transaction_delay)
(max_transaction_net_usage)(base_per_transaction_net_usage)
(context_free_discount_net_usage_num)(context_free_discount_net_usage_den)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_transaction_cpu_usage)(base_per_transaction_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_lifetime)(deferred_trx_expiration_window)(max_transaction_delay)
(max_inline_action_size)(max_inline_action_depth)
(max_authority_depth)(max_generated_transaction_count)
)
};
......
......@@ -7,6 +7,12 @@ namespace eosio {
unsigned_int type;
std::array<char,33> data;
friend bool operator == ( const public_key& a, const public_key& b ) {
return std::tie(a.type,a.data) == std::tie(b.type,b.data);
}
friend bool operator != ( const public_key& a, const public_key& b ) {
return std::tie(a.type,a.data) != std::tie(b.type,b.data);
}
EOSLIB_SERIALIZE( public_key, (type)(data) )
};
}
......@@ -53,10 +53,10 @@
"body": "WAIVER OF CONTRACTUAL RIGHT. The failure of either party to enforce any provision of this Contract shall not be construed as a waiver or limitation of that party's right to subsequently enforce and compel strict compliance with every provision of this Contract. \n\n"
},{
"id": "Arbitrator's Fees to Prevailing Party",
"body": "ARBITRATORS FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.\n \n"
"body": "ARBITRATOR'S FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.\n \n"
},{
"id": "Construction and Interpretation",
"body": "CONSTRUCTION AND INTERPRETATION. The rule requiring construction or interpretation against the drafter is waived. The document shall be deemed as if it were drafted by both parties in a mutual effort. \n \n"
}
]
}
\ No newline at end of file
}
......@@ -31,7 +31,7 @@ NOTICE. Any notice or communication required or permitted under this Contract sh
WAIVER OF CONTRACTUAL RIGHT. The failure of either party to enforce any provision of this Contract shall not be construed as a waiver or limitation of that party's right to subsequently enforce and compel strict compliance with every provision of this Contract.
### CLAUSE NAME: Arbitrator's Fees to Prevailing Party
ARBITRATORS FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.
ARBITRATOR'S FEES TO PREVAILING PARTY. In any action arising hereunder or any separate action pertaining to the validity of this Agreement, both sides shall pay half the initial cost of arbitration, and the prevailing party shall be awarded reasonable arbitrator's fees and costs.
### CLAUSE NAME: Construction and Interpretation
CONSTRUCTION AND INTERPRETATION. The rule requiring construction or interpretation against the drafter is waived. The document shall be deemed as if it were drafted by both parties in a mutual effort.
......
......@@ -135,6 +135,7 @@ extern "C" {
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_expiring_late);
WASM_TEST_HANDLER(test_transaction, deferred_print);
WASM_TEST_HANDLER_EX(test_transaction, send_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_deferred_tx_given_payer);
WASM_TEST_HANDLER(test_transaction, cancel_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_cf_action);
WASM_TEST_HANDLER(test_transaction, send_cf_action_fail);
......
......@@ -166,6 +166,7 @@ struct test_transaction {
static void send_action_sender(uint64_t receiver, uint64_t code, uint64_t action);
static void deferred_print();
static void send_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action);
static void send_deferred_tx_given_payer();
static void cancel_deferred_transaction();
static void send_cf_action();
static void send_cf_action_fail();
......
......@@ -256,6 +256,18 @@ void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, ui
trx.send( 0xffffffffffffffff, receiver );
}
void test_transaction::send_deferred_tx_given_payer() {
using namespace eosio;
uint64_t payer;
read_action_data(&payer, action_data_size());
auto trx = transaction();
test_action_action<N(testapi), WASM_TEST_ACTION("test_transaction", "deferred_print")> test_action;
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.delay_sec = 2;
trx.send( 0xffffffffffffffff, payer );
}
void test_transaction::cancel_deferred_transaction() {
using namespace eosio;
cancel_deferred( 0xffffffffffffffff ); //use the same id (0) as in send_deferred_transaction
......
Subproject commit 4f8980c8398a90efe30d94fc7b988c290560e1fd
Subproject commit 7ba0b53193be81548c0ccb3bb4ee64c13a2b9c86
......@@ -317,7 +317,6 @@ namespace eosio { namespace chain {
uint32_t i = 0;
if (va.size() > 0) {
for( const auto& field : st.fields ) {
idump((field.type)(va[i])(i));
if( va.size() > i )
variant_to_binary(field.type, va[i], ds);
else
......
#include <algorithm>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/controller.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
......@@ -14,29 +15,44 @@
using boost::container::flat_set;
namespace eosio { namespace chain {
static inline void print_debug(account_name receiver, const action_trace& ar) {
if(fc::logger::get(DEFAULT_LOGGER).is_enabled(fc::log_level::debug)) {
if (!ar.console.empty()) {
auto prefix = fc::format_string(
"\n[(${a},${n})->${r}]",
fc::mutable_variant_object()
("a", ar.act.account)
("n", ar.act.name)
("r", receiver));
dlog(prefix + ": CONSOLE OUTPUT BEGIN =====================\n"
+ ar.console
+ prefix + ": CONSOLE OUTPUT END =====================" );
}
}
}
action_trace apply_context::exec_one()
{
const auto& gpo = control.get_global_properties();
auto start = fc::time_point::now();
cpu_usage = gpo.configuration.base_per_action_cpu_usage;
cpu_usage = 0;
checktime( control.get_global_properties().configuration.base_per_action_cpu_usage );
try {
const auto &a = control.get_account(receiver);
privileged = a.privileged;
auto native = mutable_controller.find_apply_handler(receiver, act.account, act.name);
auto native = control.find_apply_handler(receiver, act.account, act.name);
if (native) {
(*native)(*this);
}
if (a.code.size() > 0 && !(act.name == N(setcode) && act.account == config::system_account_name)) {
try {
mutable_controller.get_wasm_interface().apply(a.code_version, a.code, *this);
control.get_wasm_interface().apply(a.code_version, a.code, *this);
} catch ( const wasm_exit& ){}
}
} FC_CAPTURE_AND_RETHROW((_pending_console_output.str()));
action_receipt r;
r.receiver = receiver;
r.act_digest = digest_type::hash(act);
......@@ -48,14 +64,17 @@ action_trace apply_context::exec_one()
}
action_trace t(r);
t.trx_id = trx_context.id;
t.act = act;
t.cpu_usage = cpu_usage;
t.total_inline_cpu_usage = cpu_usage;
t.total_cpu_usage = cpu_usage;
t.console = _pending_console_output.str();
executed.emplace_back( move(r) );
total_cpu_usage += cpu_usage;
print_debug(receiver, t);
reset_console();
t.elapsed = fc::time_point::now() - start;
......@@ -64,43 +83,35 @@ action_trace apply_context::exec_one()
void apply_context::exec()
{
_notified.push_back(act.account);
for( uint32_t i = 0; i < _notified.size(); ++i ) {
_notified.push_back(receiver);
trace = exec_one();
for( uint32_t i = 1; i < _notified.size(); ++i ) {
receiver = _notified[i];
if( i == 0 ) { /// first one is the root, of this trace
trace = exec_one();
} else {
trace.inline_traces.emplace_back( exec_one() );
}
trace.inline_traces.emplace_back( exec_one() );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
for( uint32_t i = 0; i < _cfa_inline_actions.size(); ++i ) {
EOS_ASSERT( recurse_depth < config::max_recursion_depth, transaction_exception, "inline action recursion depth reached" );
apply_context ncontext( mutable_controller, _cfa_inline_actions[i], trx, recurse_depth + 1 );
ncontext.context_free = true;
ncontext.id = id;
if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) {
EOS_ASSERT( recurse_depth < control.get_global_properties().configuration.max_inline_action_depth,
transaction_exception, "inline action recursion depth reached" );
}
ncontext.processing_deadline = processing_deadline;
ncontext.published_time = published_time;
for( const auto& inline_action : _cfa_inline_actions ) {
apply_context ncontext( control, trx_context, inline_action, recurse_depth + 1 );
ncontext.context_free = true;
ncontext.exec();
fc::move_append( executed, move(ncontext.executed) );
total_cpu_usage += ncontext.total_cpu_usage;
trace.total_inline_cpu_usage += ncontext.trace.total_inline_cpu_usage;
trace.total_cpu_usage += ncontext.trace.total_cpu_usage;
trace.inline_traces.emplace_back(ncontext.trace);
}
for( uint32_t i = 0; i < _inline_actions.size(); ++i ) {
EOS_ASSERT( recurse_depth < config::max_recursion_depth, transaction_exception, "inline action recursion depth reached" );
apply_context ncontext( mutable_controller, _inline_actions[i], trx, recurse_depth + 1 );
ncontext.processing_deadline = processing_deadline;
ncontext.published_time = published_time;
ncontext.id = id;
for( const auto& inline_action : _inline_actions ) {
apply_context ncontext( control, trx_context, inline_action, recurse_depth + 1 );
ncontext.exec();
fc::move_append( executed, move(ncontext.executed) );
total_cpu_usage += ncontext.total_cpu_usage;
trace.total_inline_cpu_usage += ncontext.trace.total_inline_cpu_usage;
trace.total_cpu_usage += ncontext.trace.total_cpu_usage;
trace.inline_traces.emplace_back(ncontext.trace);
}
......@@ -193,7 +204,7 @@ void apply_context::execute_inline( action&& a ) {
flat_set<public_key_type>(),
false,
{receiver} ) );
FC_ASSERT( published_time + delay <= control.pending_block_time(),
FC_ASSERT( trx_context.published + delay <= control.pending_block_time(),
"authorization for inline action imposes a delay of ${delay} seconds that is not met",
("delay", delay.to_seconds()) );
......@@ -213,16 +224,17 @@ void apply_context::execute_context_free_inline( action&& a ) {
void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx ) {
trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary
//QUESTION: Do we need to validate other things about the transaction at this point such as:
// * uniqueness?
// * that there is at least one action and at least one authorization in the transaction?
// * that the context free actions have no authorizations?
// * that the max_kcpu_usage and max_net_usage fields do not cause overflow?
//trx.validate(); // Not needed anymore since overflow is prevented by using uint64_t instead of uint32_t
FC_ASSERT( trx.context_free_actions.size() == 0, "context free actions are not currently allowed in generated transactions" );
control.validate_referenced_accounts( trx );
control.validate_expiration( trx );
// Charge ahead of time for the additional net usage needed to retire the deferred transaction
// whether that be by successfully executing, soft failure, hard failure, or expiration.
const auto& cfg = control.get_global_properties().configuration;
trx_context.add_net_usage( static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
+ static_cast<uint64_t>(config::transaction_id_net_usage) ); // Will exit early if net usage cannot be payed.
fc::microseconds required_delay;
if( !privileged ) {
......@@ -249,15 +261,13 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
}
}
auto id = trx.id();
auto delay = fc::seconds(trx.delay_sec);
EOS_ASSERT( delay >= required_delay, transaction_exception,
"authorization imposes a delay (${required_delay} sec) greater than the delay specified in transaction header (${specified_delay} sec)",
("required_delay", required_delay.to_seconds())("specified_delay", delay.to_seconds()) );
uint32_t trx_size = 0;
uint32_t trx_size = 0;
auto& d = control.db();
if ( auto ptr = d.find<generated_transaction_object,by_sender_id>(boost::make_tuple(receiver, sender_id)) ) {
d.modify<generated_transaction_object>( *ptr, [&]( auto& gtx ) {
......@@ -266,26 +276,26 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
gtx.payer = payer;
gtx.published = control.pending_block_time();
gtx.delay_until = gtx.published + delay;
gtx.expiration = gtx.delay_until + fc::milliseconds(config::deferred_trx_expiration_window_ms);
gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
trx_size = gtx.set( trx );
});
} else {
d.create<generated_transaction_object>( [&]( auto& gtx ) {
gtx.trx_id = id;
gtx.trx_id = trx_context.id;
gtx.sender = receiver;
gtx.sender_id = sender_id;
gtx.payer = payer;
gtx.published = control.pending_block_time();
gtx.delay_until = gtx.published + delay;
gtx.expiration = gtx.delay_until + fc::milliseconds(config::deferred_trx_expiration_window_ms);
gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
trx_size = gtx.set( trx );
});
}
auto& rl = control.get_mutable_resource_limits_manager();
rl.add_pending_account_ram_usage( payer, config::billable_size_v<generated_transaction_object> + trx_size );
control.get_mutable_resource_limits_manager()
.add_pending_account_ram_usage( payer, (config::billable_size_v<generated_transaction_object> + trx_size) );
checktime( trx_size * 4 ); /// 4 instructions per byte of packed generated trx (estimated)
}
......@@ -342,16 +352,12 @@ void apply_context::reset_console() {
}
void apply_context::checktime(uint32_t instruction_count) {
if( BOOST_UNLIKELY(fc::time_point::now() > processing_deadline) ) {
throw checktime_exceeded();
}
cpu_usage += instruction_count;
FC_ASSERT( cpu_usage <= max_cpu, "contract consumed more cpu cycles than allowed" );
trx_context.add_cpu_usage_and_check_time( instruction_count );
}
bytes apply_context::get_packed_transaction() {
auto r = fc::raw::pack( static_cast<const transaction&>(trx) );
auto r = fc::raw::pack( static_cast<const transaction&>(trx_context.trx) );
checktime( r.size() );
return r;
}
......@@ -361,34 +367,39 @@ void apply_context::update_db_usage( const account_name& payer, int64_t delta )
if( !(privileged || payer == account_name(receiver)) ) {
require_authorization( payer );
}
mutable_controller.get_mutable_resource_limits_manager().add_pending_account_ram_usage(payer, delta);
control.get_mutable_resource_limits_manager().add_pending_account_ram_usage(payer, delta);
}
}
int apply_context::get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const
{
const action* act = nullptr;
const auto& trx = trx_context.trx;
const action* act_ptr = nullptr;
if( type == 0 ) {
if( index >= trx.context_free_actions.size() )
return -1;
act = &trx.context_free_actions[index];
act_ptr = &trx.context_free_actions[index];
}
else if( type == 1 ) {
if( index >= trx.actions.size() )
return -1;
act = &trx.actions[index];
act_ptr = &trx.actions[index];
}
auto ps = fc::raw::pack_size( *act );
auto ps = fc::raw::pack_size( *act_ptr );
if( ps <= buffer_size ) {
fc::datastream<char*> ds(buffer, buffer_size);
fc::raw::pack( ds, *act );
fc::raw::pack( ds, *act_ptr );
}
return ps;
}
int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t buffer_size )const {
int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t buffer_size )const
{
const auto& trx = trx_context.trx;
if( index >= trx.context_free_data.size() ) return -1;
auto s = trx.context_free_data[index].size();
......
此差异已折叠。
......@@ -6,6 +6,7 @@
#include <eosio/chain/contract_table_objects.hpp>
#include <eosio/chain/controller.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/transaction.hpp>
#include <eosio/chain/exceptions.hpp>
......@@ -36,7 +37,7 @@ uint128_t transaction_id_to_sender_id( const transaction_id_type& tid ) {
void validate_authority_precondition( const apply_context& context, const authority& auth ) {
for(const auto& a : auth.accounts) {
context.db.get<account_object, by_name>(a.permission.actor);
context.mutable_controller.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission});
context.control.get_authorization_manager().get_permission({a.permission.actor, a.permission.permission});
}
}
......@@ -48,8 +49,8 @@ void apply_eosio_newaccount(apply_context& context) {
try {
context.require_authorization(create.creator);
// context.require_write_lock( config::eosio_auth_scope );
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& authorization = context.mutable_controller.get_mutable_authorization_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto& authorization = context.control.get_mutable_authorization_manager();
EOS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority");
EOS_ASSERT( validate(create.active), action_validate_exception, "Invalid active authority");
......@@ -111,7 +112,7 @@ void apply_eosio_newaccount(apply_context& context) {
void apply_eosio_setcode(apply_context& context) {
auto& db = context.db;
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto act = context.act.data_as<setcode>();
context.require_authorization(act.account);
// context.require_write_lock( config::eosio_auth_scope );
......@@ -129,6 +130,7 @@ void apply_eosio_setcode(apply_context& context) {
int64_t old_size = (int64_t)account.code.size() * config::setcode_ram_bytes_multiplier;
int64_t new_size = code_size * config::setcode_ram_bytes_multiplier;
context.checktime( act.code.size() * 20 );
FC_ASSERT( account.code_version != code_id, "contract is already running this version of code" );
// wlog( "set code: ${size}", ("size",act.code.size()));
......@@ -154,7 +156,7 @@ void apply_eosio_setcode(apply_context& context) {
void apply_eosio_setabi(apply_context& context) {
auto& db = context.db;
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto act = context.act.data_as<setabi>();
context.require_authorization(act.account);
......@@ -173,6 +175,8 @@ void apply_eosio_setabi(apply_context& context) {
int64_t old_size = (int64_t)account.abi.size();
int64_t new_size = (int64_t)fc::raw::pack_size(act.abi);
context.checktime( new_size * 2 );
db.modify( account, [&]( auto& a ) {
a.set_abi( act.abi );
});
......@@ -186,13 +190,12 @@ void apply_eosio_setabi(apply_context& context) {
}
void apply_eosio_updateauth(apply_context& context) {
// context.require_write_lock( config::eosio_auth_scope );
auto update = context.act.data_as<updateauth>();
context.require_authorization(update.account); // only here to mark the single authority on this action as used
auto& authorization = context.mutable_controller.get_mutable_authorization_manager();
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& authorization = context.control.get_mutable_authorization_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto& db = context.db;
EOS_ASSERT(!update.permission.empty(), action_validate_exception, "Cannot create authority with empty name");
......@@ -216,6 +219,9 @@ void apply_eosio_updateauth(apply_context& context) {
validate_authority_precondition(context, update.auth);
context.checktime( 5000 );
auto permission = authorization.find_permission({update.account, update.permission});
// If a parent_id of 0 is going to be used to indicate the absence of a parent, then we need to make sure that the chain
......@@ -265,8 +271,8 @@ void apply_eosio_deleteauth(apply_context& context) {
EOS_ASSERT(remove.permission != config::active_name, action_validate_exception, "Cannot delete active authority");
EOS_ASSERT(remove.permission != config::owner_name, action_validate_exception, "Cannot delete owner authority");
auto& authorization = context.mutable_controller.get_authorization_manager();
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& authorization = context.control.get_authorization_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto& db = context.db;
const auto& permission = authorization.get_permission({remove.account, remove.permission});
......@@ -290,12 +296,14 @@ void apply_eosio_deleteauth(apply_context& context) {
-(int64_t)(config::billable_size_v<permission_object> + permission.auth.get_billable_size())
);
db.remove(permission);
context.checktime( 3000 );
}
void apply_eosio_linkauth(apply_context& context) {
// context.require_write_lock( config::eosio_auth_scope );
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto requirement = context.act.data_as<linkauth>();
try {
EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty");
......@@ -337,13 +345,15 @@ void apply_eosio_linkauth(apply_context& context) {
(int64_t)(config::billable_size_v<permission_link_object>)
);
}
context.checktime( 3000 );
} FC_CAPTURE_AND_RETHROW((requirement))
}
void apply_eosio_unlinkauth(apply_context& context) {
// context.require_write_lock( config::eosio_auth_scope );
auto& resources = context.mutable_controller.get_mutable_resource_limits_manager();
auto& resources = context.control.get_mutable_resource_limits_manager();
auto& db = context.db;
auto unlink = context.act.data_as<unlinkauth>();
......@@ -358,6 +368,7 @@ void apply_eosio_unlinkauth(apply_context& context) {
);
db.remove(*link);
context.checktime( 3000 );
}
void apply_eosio_onerror(apply_context& context) {
......@@ -452,7 +463,7 @@ void apply_eosio_postrecovery(apply_context& context) {
.auth = recover_act.auth
}, update);
const uint128_t request_id = transaction_id_to_sender_id(context.id);
const uint128_t request_id = transaction_id_to_sender_id(context.trx_context.id);
auto record_data = mutable_variant_object()
("account", account)
("request_id", request_id)
......@@ -482,6 +493,8 @@ void apply_eosio_postrecovery(apply_context& context) {
}
context.console_append_formatted("Recovery Started for account ${account} : ${memo}\n", mutable_variant_object()("account", account)("memo", recover_act.memo));
context.checktime( 3000 );
}
static void remove_pending_recovery(apply_context& context, const account_name& account) {
......@@ -513,6 +526,8 @@ void apply_eosio_passrecovery(apply_context& context) {
remove_pending_recovery(context, account);
context.console_append_formatted("Account ${account} successfully recovered!\n", mutable_variant_object()("account", account));
context.checktime( 3000 );
}
void apply_eosio_vetorecovery(apply_context& context) {
......@@ -529,6 +544,8 @@ void apply_eosio_vetorecovery(apply_context& context) {
remove_pending_recovery(context, account);
context.console_append_formatted("Recovery for account ${account} vetoed!\n", mutable_variant_object()("account", account));
context.checktime( 3000 );
}
void apply_eosio_canceldelay(apply_context& context) {
......@@ -551,6 +568,7 @@ void apply_eosio_canceldelay(apply_context& context) {
found = true;
break;
}
context.checktime( 20 );
}
if( found ) break;
}
......@@ -558,6 +576,8 @@ void apply_eosio_canceldelay(apply_context& context) {
FC_ASSERT (found, "canceling_auth in canceldelay action was not found as authorization in the original delayed transaction");
context.cancel_deferred_transaction(transaction_id_to_sender_id(trx_id), account_name());
context.checktime( 1000 );
}
} } // namespace eosio::chain
......@@ -74,7 +74,9 @@ namespace eosio { namespace chain {
}
}
fork_database::~fork_database() {
void fork_database::close() {
if( my->index.size() == 0 ) return;
fc::datastream<size_t> ps;
vector<block_state> states;
states.reserve( my->index.size() );
......@@ -90,6 +92,23 @@ namespace eosio { namespace chain {
else
fc::raw::pack( out, block_id_type() );
idump((states.size()));
/// we don't normally indicate the head block as irreversible
/// we cannot normally prune the lib if it is the head block because
/// the next block needs to build off of the head block. We are exiting
/// now so we can prune this block as irreversible before exiting.
auto lib = my->head->dpos_last_irreversible_blocknum;
auto oldest = *my->index.get<by_block_num>().begin();
if( oldest->block_num <= lib ) {
prune( oldest );
}
my->index.clear();
}
fork_database::~fork_database() {
close();
}
void fork_database::set( block_state_ptr s ) {
......@@ -111,10 +130,19 @@ namespace eosio { namespace chain {
FC_ASSERT( inserted.second, "duplicate block added?" );
my->head = *my->index.get<by_lib_block_num>().begin();
auto lib = my->head->dpos_last_irreversible_blocknum;
auto oldest = *my->index.get<by_block_num>().begin();
if( oldest->block_num < lib ) {
prune( oldest );
}
return n;
}
block_state_ptr fork_database::add( signed_block_ptr b ) {
FC_ASSERT( b, "attempt to add null block" );
FC_ASSERT( my->head, "no head block set" );
const auto& by_id_idx = my->index.get<by_block_id>();
......@@ -125,6 +153,7 @@ namespace eosio { namespace chain {
FC_ASSERT( prior != by_id_idx.end(), "unlinkable block", ("id", b->id())("previous", b->previous) );
auto result = std::make_shared<block_state>( **prior, move(b) );
FC_ASSERT( result );
return add(result);
}
......@@ -188,6 +217,7 @@ namespace eosio { namespace chain {
previtr = previdx.find(id);
}
}
wdump((my->index.size()));
}
void fork_database::set_validity( const block_state_ptr& h, bool valid ) {
......@@ -215,9 +245,18 @@ namespace eosio { namespace chain {
void fork_database::prune( const block_state_ptr& h ) {
auto num = h->block_num;
auto& by_bn = my->index.get<by_block_num>();
auto bni = by_bn.begin();
while( bni != by_bn.end() && (*bni)->block_num < num ) {
prune( *bni );
bni = by_bn.begin();
}
auto itr = my->index.find( h->id );
if( itr != my->index.end() )
if( itr != my->index.end() ) {
irreversible(*itr);
my->index.erase(itr);
}
auto& numidx = my->index.get<by_block_num>();
auto nitr = numidx.lower_bound( num );
......
......@@ -4,10 +4,11 @@
*/
#pragma once
#include <eosio/chain/contract_types.hpp>
#include <eosio/chain/trace.hpp>
#include <eosio/chain/exceptions.hpp>
#include <fc/variant_object.hpp>
namespace eosio { namespace chain {
namespace eosio { namespace chain {
using std::map;
using std::string;
......@@ -95,6 +96,9 @@ namespace impl {
constexpr bool single_type_requires_abi_v() {
return std::is_base_of<transaction, T>::value ||
std::is_same<T, packed_transaction>::value ||
std::is_same<T, transaction_trace>::value ||
std::is_same<T, action_trace>::value ||
std::is_same<T, signed_transaction>::value ||
std::is_same<T, action>::value;
}
......@@ -170,6 +174,19 @@ namespace impl {
mvo(name, std::move(array));
}
/**
* template which overloads add for shared_ptr of types which contain ABI information in their trees
* for these members we call ::add in order to trigger further processing
*/
template<typename M, typename Resolver, require_abi_t<M> = 1>
static void add( mutable_variant_object &mvo, const char* name, const std::shared_ptr<M>& v, Resolver resolver )
{
if( !v ) return;
mutable_variant_object obj_mvo;
add(obj_mvo, "_", *v, resolver);
mvo(name, std::move(obj_mvo["_"]));
}
template<typename Resolver, typename... Args>
static void add( mutable_variant_object &mvo, const char* name, const fc::static_variant<Args...>& v, Resolver resolver )
{
......@@ -201,6 +218,26 @@ namespace impl {
out(name, std::move(mvo));
}
/**
* overload of to_variant_object for actions
* @tparam Resolver
* @param act
* @param resolver
* @return
template<typename Resolver>
static void add(mutable_variant_object &out, const char* name, const action_trace& act, Resolver resolver) {
mutable_variant_object mvo;
mvo("receipt", act.receipt);
mvo("elapsed", act.elapsed);
mvo("cpu_usage", act.cpu_usage);
mvo("console", act.console);
mvo("total_cpu_usage", act.total_inline_cpu_usage);
mvo("inline_traces", act.inline_traces);
out(name, std::move(mvo));
}
*/
/**
* overload of to_variant_object for packed_transaction
* @tparam Resolver
......@@ -293,6 +330,19 @@ namespace impl {
}
}
/**
* template which overloads extract for shared_ptr of types which contain ABI information in their trees
* for these members we call ::extract in order to trigger further processing
*/
template<typename M, typename Resolver, require_abi_t<M> = 1>
static void extract( const variant& v, std::shared_ptr<M>& o, Resolver resolver )
{
const variant_object& vo = v.get_object();
M obj;
extract(vo, obj, resolver);
o = std::make_shared<M>(obj);
}
/**
* Non templated overload that has priority for the action structure
* this type has members which must be directly translated by the ABI so it is
......@@ -340,7 +390,6 @@ namespace impl {
template<typename Resolver>
static void extract( const variant& v, packed_transaction& ptrx, Resolver resolver ) {
const variant_object& vo = v.get_object();
wdump((vo));
EOS_ASSERT(vo.contains("signatures"), packed_transaction_type_exception, "Missing signatures");
EOS_ASSERT(vo.contains("compression"), packed_transaction_type_exception, "Missing compression");
from_variant(vo["signatures"], ptrx.signatures);
......
......@@ -21,7 +21,7 @@ namespace eosio { namespace chain {
uint8_t vm_version = 0;
bool privileged = false;
time_point_sec last_code_update;
time_point last_code_update;
digest_type code_version;
block_timestamp_type creation_date;
......
......@@ -16,6 +16,7 @@ namespace chainbase { class database; }
namespace eosio { namespace chain {
class controller;
class transaction_context;
class apply_context {
private:
......@@ -99,7 +100,7 @@ class apply_context {
inline size_t end_iterator_to_index( int ei )const { return (-ei - 2); }
/// Precondition: indx < _end_iterator_to_table.size() <= std::numeric_limits<int>::max()
inline int index_to_end_iterator( size_t indx )const { return -(indx + 2); }
};
}; /// class iterator_cache
template<typename>
struct array_size;
......@@ -445,37 +446,44 @@ class apply_context {
private:
apply_context& context;
iterator_cache<ObjectType> itr_cache;
};
}; /// class generic_index
apply_context(controller& con, const action& a, const signed_transaction& t, uint32_t depth=0)
:control(con),
db(con.db()),
act(a),
mutable_controller(con),
used_authorizations(act.authorization.size(), false),
trx(t),
idx64(*this),
idx128(*this),
idx256(*this),
idx_double(*this),
idx_long_double(*this),
recurse_depth(depth)
/// Constructor
public:
apply_context(controller& con, transaction_context& trx_ctx, const action& a, uint32_t depth=0)
:control(con)
,db(con.db())
,trx_context(trx_ctx)
,act(a)
,receiver(act.account)
,used_authorizations(act.authorization.size(), false)
,recurse_depth(depth)
,idx64(*this)
,idx128(*this)
,idx256(*this)
,idx_double(*this)
,idx_long_double(*this)
{
reset_console();
}
void exec();
void execute_inline( action &&a );
void execute_context_free_inline( action &&a );
void schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction &&trx );
/// Execution methods:
public:
action_trace exec_one();
void exec();
void execute_inline( action&& a );
void execute_context_free_inline( action&& a );
void schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx );
void cancel_deferred_transaction( const uint128_t& sender_id, account_name sender );
void cancel_deferred_transaction( const uint128_t& sender_id ) { cancel_deferred_transaction(sender_id, receiver); }
/// Authorization methods:
public:
/**
* @brief Require @ref account to have approved of this message
* @param account The account whose approval is required
......@@ -488,8 +496,6 @@ class apply_context {
void require_authorization(const account_name& account);
bool has_authorization(const account_name& account) const;
void require_authorization(const account_name& account, const permission_name& permission);
// void require_write_lock(const scope_name& scope);
// void require_read_lock(const account_name& account, const scope_name& scope);
/**
* @return true if account exists, false if it does not
......@@ -510,38 +516,15 @@ class apply_context {
bool all_authorizations_used()const;
vector<permission_level> unused_authorizations()const;
vector<account_name> get_active_producers() const;
bytes get_packed_transaction();
controller& control;
chainbase::database& db; ///< database where state is stored
const action& act; ///< message being applied
account_name receiver; ///< the code that is currently running
bool privileged = false;
bool context_free = false;
bool used_context_free_api = false;
controller& mutable_controller;
///< Parallel to act.authorization; tracks which permissions have been used while processing the message
vector<bool> used_authorizations;
void check_auth( const transaction& trx, const vector<permission_level>& perm );
const signed_transaction& trx;
transaction_id_type id;
/*
struct apply_results {
vector<action_trace> applied_actions;
vector<fc::static_variant<deferred_transaction, deferred_reference>> deferred_transaction_requests;
size_t deferred_transactions_count = 0;
};
apply_results results;
*/
/// Console methods:
public:
std::ostringstream& get_console_stream() { return _pending_console_output; }
void reset_console();
std::ostringstream& get_console_stream() { return _pending_console_output; }
const std::ostringstream& get_console_stream()const { return _pending_console_output; }
template<typename T>
void console_append(T val) {
......@@ -558,76 +541,85 @@ class apply_context {
console_append(fc::format_string(fmt, vo));
}
void checktime(uint32_t instruction_count);
int get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const;
int get_context_free_data( uint32_t index, char* buffer, size_t buffer_size )const;
/// Database methods:
public:
void update_db_usage( const account_name& payer, int64_t delta );
void check_auth( const transaction& trx, const vector<permission_level>& perm );
int db_store_i64( uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size );
void db_update_i64( int iterator, account_name payer, const char* buffer, size_t buffer_size );
void db_remove_i64( int iterator );
int db_get_i64( int iterator, char* buffer, size_t buffer_size );
int db_next_i64( int iterator, uint64_t& primary );
int db_previous_i64( int iterator, uint64_t& primary );
int db_find_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_lowerbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_upperbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_end_i64( uint64_t code, uint64_t scope, uint64_t table );
int db_get_i64( int iterator, char* buffer, size_t buffer_size );
int db_next_i64( int iterator, uint64_t& primary );
int db_previous_i64( int iterator, uint64_t& primary );
int db_find_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_lowerbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_upperbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id );
int db_end_i64( uint64_t code, uint64_t scope, uint64_t table );
generic_index<index64_object> idx64;
generic_index<index128_object> idx128;
generic_index<index256_object, uint128_t*, const uint128_t*> idx256;
generic_index<index_double_object> idx_double;
generic_index<index_long_double_object> idx_long_double;
private:
uint32_t recurse_depth; // how deep inline actions can recurse
fc::time_point published_time;
fc::time_point processing_deadline;
uint64_t max_cpu = uint64_t(-1);
const table_id_object* find_table( name code, name scope, name table );
const table_id_object& find_or_create_table( name code, name scope, name table, const account_name &payer );
void remove_table( const table_id_object& tid );
vector<action_receipt> executed;
action_trace trace;
int db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size );
uint64_t cpu_usage;
uint64_t total_cpu_usage;
/// Misc methods:
public:
int get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const;
int get_context_free_data( uint32_t index, char* buffer, size_t buffer_size )const;
vector<account_name> get_active_producers() const;
bytes get_packed_transaction();
void checktime(uint32_t instruction_count);
uint64_t next_global_sequence();
uint64_t next_recv_sequence( account_name receiver );
uint64_t next_auth_sequence( account_name actor );
private:
iterator_cache<key_value_object> keyval_cache;
void validate_referenced_accounts( const transaction& t )const;
void validate_expiration( const transaction& t )const;
/*
void append_results(apply_results &&other) {
fc::move_append(results.applied_actions, std::move(other.applied_actions));
fc::move_append(results.deferred_transaction_requests, std::move(other.deferred_transaction_requests));
results.deferred_transactions_count += other.deferred_transactions_count;
}
*/
void reset_console();
/// Fields:
public:
action_trace exec_one();
controller& control;
chainbase::database& db; ///< database where state is stored
transaction_context& trx_context; ///< transaction context in which the action is running
const action& act; ///< message being applied
account_name receiver; ///< the code that is currently running
vector<bool> used_authorizations; ///< Parallel to act.authorization; tracks which permissions have been used while processing the message
uint32_t recurse_depth; ///< how deep inline actions can recurse
bool privileged = false;
bool context_free = false;
bool used_context_free_api = false;
const table_id_object* find_table( name code, name scope, name table );
const table_id_object& find_or_create_table( name code, name scope, name table, const account_name &payer );
void remove_table( const table_id_object& tid );
generic_index<index64_object> idx64;
generic_index<index128_object> idx128;
generic_index<index256_object, uint128_t*, const uint128_t*> idx256;
generic_index<index_double_object> idx_double;
generic_index<index_long_double_object> idx_long_double;
int db_store_i64( uint64_t code, uint64_t scope, uint64_t table, const account_name& payer, uint64_t id, const char* buffer, size_t buffer_size );
vector<action_receipt> executed;
action_trace trace;
uint64_t cpu_usage = 0;
uint64_t total_cpu_usage = 0;
private:
iterator_cache<key_value_object> keyval_cache;
vector<account_name> _notified; ///< keeps track of new accounts to be notifed of current message
vector<action> _inline_actions; ///< queued inline messages
vector<action> _cfa_inline_actions; ///< queued inline messages
std::ostringstream _pending_console_output;
//bytes _cached_trx;
};
......
......@@ -56,6 +56,10 @@ struct authority {
friend bool operator == ( const authority& lhs, const authority& rhs ) {
return tie( lhs.threshold, lhs.delay_sec, lhs.keys, lhs.accounts ) == tie( rhs.threshold, rhs.delay_sec, rhs.keys, rhs.accounts );
}
friend bool operator != ( const authority& lhs, const authority& rhs ) {
return tie( lhs.threshold, lhs.delay_sec, lhs.keys, lhs.accounts ) != tie( rhs.threshold, rhs.delay_sec, rhs.keys, rhs.accounts );
}
};
......
......@@ -17,56 +17,55 @@ namespace eosio { namespace chain {
* values specified by the producers.
*/
struct chain_config {
uint64_t max_block_net_usage; ///< the maxiumum net usage in instructions for a block
uint32_t target_block_net_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum net usage; exceeding this triggers congestion handling
uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits
uint32_t base_per_transaction_net_usage; ///< the base amount of net usage billed for a transaction to cover incidentals
uint64_t context_free_discount_net_usage_num; ///< the numerator for the discount on net usage of context-free data
uint64_t context_free_discount_net_usage_den; ///< the denominator for the discount on net usage of context-free data
uint32_t base_per_transaction_net_usage; ///< the base amount of net usage billed for a transaction to cover incidentals
uint32_t base_per_transaction_cpu_usage; ///< the base amount of cpu usage billed for a transaction to cover incidentals
uint32_t base_per_action_cpu_usage; ///< the base amount of cpu usage billed for an action to cover incidentals
uint32_t base_setcode_cpu_usage; ///< the base amount of cpu usage billed for a setcode action to cover compilation/etc
uint32_t per_signature_cpu_usage; ///< the cpu usage billed for every signature on a transaction
uint32_t per_lock_net_usage; ///< the net usage billed for every lock on a transaction to cover overhead in the block shards
uint64_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block
uint32_t target_block_cpu_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum cpu usage; exceeding this triggers congestion handling
uint32_t max_transaction_cpu_usage; ///< the maximum objectively measured cpu usage that the chain will allow regardless of account limits
uint32_t base_per_transaction_cpu_usage; ///< the base amount of cpu usage billed for a transaction to cover incidentals
uint32_t base_per_action_cpu_usage; ///< the base amount of cpu usage billed for an action to cover incidentals
uint32_t base_setcode_cpu_usage; ///< the base amount of cpu usage billed for a setcode action to cover compilation/etc
uint32_t per_signature_cpu_usage; ///< the cpu usage billed for every signature on a transaction
uint64_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage of context-free actions
uint64_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage of context-free actions
uint64_t context_free_discount_cpu_usage_num; ///< the numerator for the discount on cpu usage for CFA's
uint64_t context_free_discount_cpu_usage_den; ///< the denominator for the discount on cpu usage for CFA's
uint32_t max_transaction_cpu_usage; ///< the maximum objectively measured cpu usage that the chain will allow regardless of account limits
uint32_t max_transaction_net_usage; ///< the maximum objectively measured net usage that the chain will allow regardless of account limits
uint64_t max_block_cpu_usage; ///< the maxiumum cpu usage in instructions for a block
uint32_t target_block_cpu_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum cpu usage; exceeding this triggers congestion handling
uint64_t max_block_net_usage; ///< the maxiumum net usage in instructions for a block
uint32_t target_block_net_usage_pct; ///< the target percent (1% == 100, 100%= 10,000) of maximum net usage; exceeding this triggers congestion handling
uint32_t max_transaction_lifetime;
uint32_t max_transaction_exec_time;
uint16_t max_authority_depth;
uint16_t max_inline_depth;
uint32_t max_inline_action_size;
uint32_t max_generated_transaction_count;
uint32_t max_transaction_delay;
static chain_config get_median_values( vector<chain_config> votes );
uint32_t max_transaction_lifetime; ///< the maximum number of seconds that an input transaction's expiration can be ahead of the time of the block in which it is first included
uint32_t deferred_trx_expiration_window; ///< the number of seconds after the time a deferred transaction can first execute until it expires
uint32_t max_transaction_delay; ///< the maximum number of seconds that can be imposed as a delay requirement by authorization checks
uint32_t max_inline_action_size; ///< maximum allowed size (in bytes) of an inline action
uint16_t max_inline_action_depth; ///< recursion depth limit on sending inline actions
uint16_t max_authority_depth; ///< recursion depth limit for checking if an authority is satisfied
uint32_t max_generated_transaction_count; ///< the number of generated transactions per action (TODO: implement?)
template<typename Stream>
friend Stream& operator << ( Stream& out, const chain_config& c ) {
return out << "Base Per-Transaction Net Usage: " << c.base_per_transaction_net_usage << ", "
return out << "Max Block Net Usage: " << c.max_block_net_usage << ", "
<< "Target Block Net Usage Percent: " << ((double)c.target_block_net_usage_pct / (double)config::percent_1) << "%, "
<< "Max Transaction Net Usage: " << c.max_transaction_net_usage << ", "
<< "Base Per-Transaction Net Usage: " << c.base_per_transaction_net_usage << ", "
<< "Context-Free Data Net Usage Discount: " << (double)c.context_free_discount_net_usage_num * 100.0 / (double)c.context_free_discount_net_usage_den << "% , "
<< "Max Block CPU Usage: " << c.max_block_cpu_usage << ", "
<< "Target Block CPU Usage Percent: " << ((double)c.target_block_cpu_usage_pct / (double)config::percent_1) << "%, "
<< "Max Transaction CPU Usage: " << c.max_transaction_cpu_usage << ", "
<< "Base Per-Transaction CPU Usage: " << c.base_per_transaction_cpu_usage << ", "
<< "Base Per-Action CPU Usage: " << c.base_per_action_cpu_usage << ", "
<< "Base Setcode CPU Usage: " << c.base_setcode_cpu_usage << ", "
<< "Per-Signature CPU Usage: " << c.per_signature_cpu_usage << ", "
<< "Per-Lock NET Usage: " << c.per_lock_net_usage << ", "
<< "Context-Free Action CPU Usage Discount: " << (double)c.context_free_discount_cpu_usage_num * 100.0 / (double)c.context_free_discount_cpu_usage_den << "% , "
<< "Max Transaction CPU Usage: " << c.max_transaction_cpu_usage << ", "
<< "Max Transaction Net Usage: " << c.max_transaction_net_usage << ", "
<< "Max Block CPU Usage: " << c.max_block_cpu_usage << ", "
<< "Target Block CPU Usage Percent: " << c.target_block_cpu_usage_pct << ", "
<< "Max Block NET Usage: " << c.max_block_net_usage << ", "
<< "Target Block NET Usage Percent: " << ((double)c.target_block_net_usage_pct / (double)config::percent_1) << "%, "
<< "Max Transaction Lifetime: " << ((double)c.max_transaction_lifetime / (double)config::percent_1) << "%, "
<< "Max Authority Depth: " << c.max_authority_depth << ", "
<< "Max Inline Depth: " << c.max_inline_depth << ", "
<< "Max Transaction Lifetime: " << c.max_transaction_lifetime << ", "
<< "Deferred Transaction Expiration Window: " << c.deferred_trx_expiration_window << ", "
<< "Max Transaction Delay: " << c.max_transaction_delay << ", "
<< "Max Inline Action Size: " << c.max_inline_action_size << ", "
<< "Max Generated Transaction Count: " << c.max_generated_transaction_count << "\n"
<< "Max Transaction Delay: " << c.max_transaction_delay << "\n";
<< "Max Inline Action Depth: " << c.max_inline_action_depth << ", "
<< "Max Authority Depth: " << c.max_authority_depth << ", "
<< "Max Generated Transaction Count: " << c.max_generated_transaction_count << "\n";
}
};
......@@ -76,16 +75,17 @@ inline bool operator!=(const chain_config& a, const chain_config& b) { return !(
} } // namespace eosio::chain
FC_REFLECT(eosio::chain::chain_config,
(base_per_transaction_net_usage)
(base_per_transaction_cpu_usage)
(base_per_action_cpu_usage)
(base_setcode_cpu_usage)
(per_signature_cpu_usage)(per_lock_net_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_cpu_usage)(max_transaction_net_usage)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_block_net_usage)(target_block_net_usage_pct)
(max_transaction_lifetime)(max_transaction_exec_time)(max_authority_depth)
(max_inline_depth)(max_inline_action_size)(max_generated_transaction_count)
(max_transaction_delay)
(max_transaction_net_usage)(base_per_transaction_net_usage)
(context_free_discount_net_usage_num)(context_free_discount_net_usage_den)
(max_block_cpu_usage)(target_block_cpu_usage_pct)
(max_transaction_cpu_usage)(base_per_transaction_cpu_usage)
(base_per_action_cpu_usage)(base_setcode_cpu_usage)(per_signature_cpu_usage)
(context_free_discount_cpu_usage_num)(context_free_discount_cpu_usage_den)
(max_transaction_lifetime)(deferred_trx_expiration_window)(max_transaction_delay)
(max_inline_action_size)(max_inline_action_depth)
(max_authority_depth)(max_generated_transaction_count)
)
......@@ -14,7 +14,7 @@ typedef __uint128_t uint128_t;
const static auto default_block_log_dir = "block_log";
const static auto default_shared_memory_dir = "shared_mem";
const static auto default_shared_memory_size = 32*1024*1024*1024ll;
const static auto default_shared_memory_size = 1*1024*1024*1024ll;
const static uint64_t system_account_name = N(eosio);
const static uint64_t nobody_account_name = N(nobody);
......@@ -25,7 +25,6 @@ const static uint64_t majority_producers_permission_name = N(prod.major); // gre
const static uint64_t minority_producers_permission_name = N(prod.minor); // greater than 1/3 of producers needed to authorize0
const static uint64_t any_producer_permission_name = N(prod.any); // any producer needed to authorize
const static uint64_t eosio_auth_scope = N(eosio.auth);
const static uint64_t eosio_all_scope = N(eosio.all);
......@@ -47,39 +46,44 @@ static const uint32_t account_cpu_usage_average_window_ms = 24*60*60*1000l;
static const uint32_t account_net_usage_average_window_ms = 24*60*60*1000l;
static const uint32_t block_cpu_usage_average_window_ms = 60*1000l;
static const uint32_t block_size_average_window_ms = 60*1000l;
static const uint32_t deferred_trx_expiration_window_ms = 10*60*1000l; // TODO: make 10 minutes configurable by system
const static uint32_t default_max_block_net_usage = 1024 * 1024; /// at 500ms blocks and 200byte trx, this enables ~10,000 TPS burst
const static int default_target_block_net_usage_pct = 10 * percent_1; /// we target 1000 TPS
const static uint32_t default_max_block_cpu_usage = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_cpu_usage_pct = 10 * percent_1; /// target 1000 TPS
const static uint64_t default_max_storage_size = 10 * 1024;
const static uint32_t default_max_trx_lifetime = 60*60;
const static uint16_t default_max_auth_depth = 6;
const static uint32_t default_max_trx_runtime = 10*1000;
const static uint16_t default_max_inline_depth = 4;
const static uint32_t default_max_inline_action_size = 4 * 1024;
const static uint32_t default_max_gen_trx_size = 64 * 1024; ///
const static uint32_t default_max_gen_trx_count = 16; ///< the number of generated transactions per action
const static uint32_t default_max_trx_delay = 45*24*3600; // 45 days
const static uint32_t rate_limiting_precision = 1000*1000;
const static uint32_t producers_authority_threshold_pct = 66 * config::percent_1;
//const static uint64_t default_max_storage_size = 10 * 1024;
//const static uint32_t default_max_trx_runtime = 10*1000;
//const static uint32_t default_max_gen_trx_size = 64 * 1024;
const static uint32_t rate_limiting_precision = 1000*1000;
const static uint16_t max_recursion_depth = 6;
const static uint32_t default_base_per_transaction_net_usage = 100; // 100 bytes minimum (for signature and misc overhead)
const static uint32_t default_base_per_transaction_cpu_usage = 500; // TODO: is this reasonable?
const static uint32_t default_base_per_action_cpu_usage = 1000;
const static uint32_t default_base_setcode_cpu_usage = 2 * 1024 * 1024; /// overbilling cpu usage for setcode to cover incidental
const static uint32_t default_per_signature_cpu_usage = 100 * 1000; // TODO: is this reasonable?
const static uint32_t default_per_lock_net_usage = 32;
const static uint64_t default_context_free_discount_cpu_usage_num = 20;
const static uint64_t default_context_free_discount_cpu_usage_den = 100;
const static uint32_t default_max_transaction_cpu_usage = default_max_block_cpu_usage / 10;
const static uint32_t default_max_transaction_net_usage = default_max_block_net_usage / 10;
const static uint32_t default_max_block_net_usage = 1024 * 1024; /// at 500ms blocks and 200byte trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_net_usage_pct = 10 * percent_1; /// we target 1000 TPS
const static uint32_t default_max_transaction_net_usage = default_max_block_net_usage / 10;
const static uint32_t default_base_per_transaction_net_usage = 12; // 12 bytes (11 bytes for worst case of transaction_receipt_header + 1 byte for static_variant tag)
const static uint64_t default_context_free_discount_net_usage_num = 20; // TODO: is this reasonable?
const static uint64_t default_context_free_discount_net_usage_den = 100;
const static uint32_t transaction_id_net_usage = 32; // 32 bytes for the size of a transaction id
const static uint32_t default_max_block_cpu_usage = 100 * 1024 * 1024; /// at 500ms blocks and 20000instr trx, this enables ~10,000 TPS burst
const static uint32_t default_target_block_cpu_usage_pct = 10 * percent_1; /// target 1000 TPS
const static uint32_t default_max_transaction_cpu_usage = default_max_block_cpu_usage / 10;
const static uint32_t default_base_per_transaction_cpu_usage = 512; // TODO: is this reasonable?
const static uint32_t default_base_per_action_cpu_usage = 1024;
const static uint32_t default_base_setcode_cpu_usage = 2 * 1024 * 1024; /// overbilling cpu usage for setcode to cover incidental
const static uint32_t default_per_signature_cpu_usage = 100 * 1024; // TODO: is this reasonable?
const static uint64_t default_context_free_discount_cpu_usage_num = 20;
const static uint64_t default_context_free_discount_cpu_usage_den = 100;
const static uint32_t default_max_trx_lifetime = 60*60; // 1 hour
const static uint32_t default_deferred_trx_expiration_window = 10*60; // 10 minutes
//static const uint32_t deferred_trx_expiration_window_ms = 10*60*1000l; // TODO: make 10 minutes configurable by system
const static uint32_t default_max_trx_delay = 45*24*3600; // 45 days
const static uint32_t default_max_inline_action_size = 4 * 1024; // 4 KB
const static uint16_t default_max_inline_action_depth = 4;
const static uint16_t default_max_auth_depth = 6;
const static uint32_t default_max_gen_trx_count = 16;
const static uint32_t resource_processing_cpu_overhead_per_billed_account = 256; // TODO: is this reasonable?
const static uint32_t determine_payers_cpu_overhead_per_authorization = 64; // TODO: is this reasonable?
const static uint32_t overhead_per_row_per_index_ram_bytes = 32; ///< overhead accounts for basic tracking structures in a row per index
const static uint32_t overhead_per_account_ram_bytes = 2*1024; ///< overhead accounts for basic account storage and pre-pays features like account recovery
......
......@@ -4,6 +4,9 @@
#include <eosio/chain/genesis_state.hpp>
#include <boost/signals2/signal.hpp>
#include <eosio/chain/abi_serializer.hpp>
#include <eosio/chain/account_object.hpp>
namespace chainbase {
class database;
}
......@@ -78,7 +81,7 @@ namespace eosio { namespace chain {
bool push_next_unapplied_transaction( fc::time_point deadline = fc::time_point::maximum() );
transaction_trace_ptr sync_push( const transaction_metadata_ptr& trx, fc::time_point deadline = fc::time_point::now() + fc::milliseconds(25) );
transaction_trace_ptr sync_push( const transaction_metadata_ptr& trx, fc::time_point deadline = fc::time_point::now() + fc::milliseconds(30) );
/**
* Attempt to execute a specific transaction in our deferred trx database
......@@ -110,14 +113,6 @@ namespace eosio { namespace chain {
chainbase::database& db()const;
uint32_t head_block_num()const;
time_point head_block_time()const;
time_point pending_block_time()const;
block_state_ptr head_block_state()const;
block_state_ptr pending_block_state()const;
const account_object& get_account( account_name n )const;
const global_property_object& get_global_properties()const;
const dynamic_global_property_object& get_dynamic_global_properties()const;
......@@ -129,9 +124,15 @@ namespace eosio { namespace chain {
fc::microseconds limit_delay( fc::microseconds delay )const;
uint32_t head_block_num()const;
time_point head_block_time()const;
block_id_type head_block_id()const;
account_name head_block_producer()const;
const block_header& head_block_header()const;
block_state_ptr head_block_state()const;
time_point pending_block_time()const;
block_state_ptr pending_block_state()const;
const producer_schedule_type& active_producers()const;
const producer_schedule_type& pending_producers()const;
......@@ -146,10 +147,12 @@ namespace eosio { namespace chain {
void validate_referenced_accounts( const transaction& t )const;
void validate_expiration( const transaction& t )const;
void validate_tapos( const transaction& t )const;
uint64_t validate_net_usage( const transaction_metadata_ptr& trx )const;
bool set_proposed_producers( vector<producer_key> producers );
signal<void(const block_state_ptr&)> accepted_block_header;
signal<void(const block_state_ptr&)> accepted_block;
signal<void(const block_state_ptr&)> irreversible_block;
......@@ -170,7 +173,24 @@ namespace eosio { namespace chain {
const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const;
wasm_interface& get_wasm_interface();
optional<abi_serializer> get_abi_serializer( account_name n )const {
const auto& a = get_account(n);
abi_def abi;
if( abi_serializer::to_abi( a.abi, abi ) )
return abi_serializer(abi);
return optional<abi_serializer>();
}
template<typename T>
fc::variant to_variant_with_abi( const T& obj ) {
fc::variant pretty_output;
abi_serializer::to_variant( obj, pretty_output, [&]( account_name n ){ return get_abi_serializer( n ); });
return pretty_output;
}
private:
std::unique_ptr<controller_impl> my;
};
......
......@@ -25,6 +25,8 @@ namespace eosio { namespace chain {
fork_database( const fc::path& data_dir );
~fork_database();
void close();
block_state_ptr get_block(const block_id_type& id)const;
block_state_ptr get_block_in_current_chain_by_num( uint32_t n )const;
// vector<block_state_ptr> get_blocks_by_number(uint32_t n)const;
......@@ -64,9 +66,9 @@ namespace eosio { namespace chain {
/**
* This signal is emited when a block state becomes irreversible, once irreversible
* it is removed;
* it is removed unless it is the head block.
*/
signal<void(block_state_ptr)> irreverisble;
signal<void(block_state_ptr)> irreversible;
private:
void set_bft_irreversible( block_id_type id );
......
......@@ -37,11 +37,11 @@ namespace eosio { namespace chain {
shared_vector<char> packed_trx;
uint32_t set( const transaction& trx ) {
auto trxsize = fc::raw::pack_size( static_cast<const transaction&>( trx ) );
packed_trx.resize(trxsize);
fc::datastream<char*> ds( packed_trx.data(), trxsize );
fc::raw::pack( ds, trx );
return trxsize;
auto trxsize = fc::raw::pack_size( trx );
packed_trx.resize( trxsize );
fc::datastream<char*> ds( packed_trx.data(), trxsize );
fc::raw::pack( ds, trx );
return trxsize;
}
};
......@@ -56,13 +56,13 @@ namespace eosio { namespace chain {
indexed_by<
ordered_unique< tag<by_id>, BOOST_MULTI_INDEX_MEMBER(generated_transaction_object, generated_transaction_object::id_type, id)>,
ordered_unique< tag<by_trx_id>, BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, transaction_id_type, trx_id)>,
ordered_unique< tag<by_expiration>,
ordered_unique< tag<by_expiration>,
composite_key< generated_transaction_object,
BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, time_point, expiration),
BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, generated_transaction_object::id_type, id)
>
>,
ordered_unique< tag<by_delay>,
ordered_unique< tag<by_delay>,
composite_key< generated_transaction_object,
BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, time_point, delay_until),
BOOST_MULTI_INDEX_MEMBER( generated_transaction_object, generated_transaction_object::id_type, id)
......@@ -89,4 +89,3 @@ namespace eosio { namespace chain {
} } // eosio::chain
CHAINBASE_SET_INDEX_TYPE(eosio::chain::generated_transaction_object, eosio::chain::generated_transaction_multi_index)
......@@ -13,31 +13,34 @@
#include <string>
#include <vector>
namespace eosio { namespace chain {
namespace eosio { namespace chain {
struct genesis_state {
chain_config initial_configuration = {
.base_per_transaction_net_usage = config::default_base_per_transaction_net_usage,
.base_per_transaction_cpu_usage = config::default_base_per_transaction_cpu_usage,
.base_per_action_cpu_usage = config::default_base_per_action_cpu_usage,
.base_setcode_cpu_usage = config::default_base_setcode_cpu_usage,
.per_signature_cpu_usage = config::default_per_signature_cpu_usage,
.per_lock_net_usage = config::default_per_lock_net_usage,
.context_free_discount_cpu_usage_num = config::default_context_free_discount_cpu_usage_num,
.context_free_discount_cpu_usage_den = config::default_context_free_discount_cpu_usage_den,
.max_transaction_cpu_usage = config::default_max_transaction_cpu_usage,
.max_transaction_net_usage = config::default_max_transaction_net_usage,
.max_block_cpu_usage = config::default_max_block_cpu_usage,
.target_block_cpu_usage_pct = config::default_target_block_cpu_usage_pct,
.max_block_net_usage = config::default_max_block_net_usage,
.target_block_net_usage_pct = config::default_target_block_net_usage_pct,
.max_transaction_lifetime = config::default_max_trx_lifetime,
.max_transaction_exec_time = 0, // TODO: unused?
.max_authority_depth = config::default_max_auth_depth,
.max_inline_depth = config::default_max_inline_depth,
.max_inline_action_size = config::default_max_inline_action_size,
.max_generated_transaction_count = config::default_max_gen_trx_count,
.max_transaction_delay = config::default_max_trx_delay
.max_block_net_usage = config::default_max_block_net_usage,
.target_block_net_usage_pct = config::default_target_block_net_usage_pct,
.max_transaction_net_usage = config::default_max_transaction_net_usage,
.base_per_transaction_net_usage = config::default_base_per_transaction_net_usage,
.context_free_discount_net_usage_num = config::default_context_free_discount_net_usage_num,
.context_free_discount_net_usage_den = config::default_context_free_discount_net_usage_den,
.max_block_cpu_usage = config::default_max_block_cpu_usage,
.target_block_cpu_usage_pct = config::default_target_block_cpu_usage_pct,
.max_transaction_cpu_usage = config::default_max_transaction_cpu_usage,
.base_per_transaction_cpu_usage = config::default_base_per_transaction_cpu_usage,
.base_per_action_cpu_usage = config::default_base_per_action_cpu_usage,
.base_setcode_cpu_usage = config::default_base_setcode_cpu_usage,
.per_signature_cpu_usage = config::default_per_signature_cpu_usage,
.context_free_discount_cpu_usage_num = config::default_context_free_discount_cpu_usage_num,
.context_free_discount_cpu_usage_den = config::default_context_free_discount_cpu_usage_den,
.max_transaction_lifetime = config::default_max_trx_lifetime,
.deferred_trx_expiration_window = config::default_deferred_trx_expiration_window,
.max_transaction_delay = config::default_max_trx_delay,
.max_inline_action_size = config::default_max_inline_action_size,
.max_inline_action_depth = config::default_max_inline_action_depth,
.max_authority_depth = config::default_max_auth_depth,
.max_generated_transaction_count = config::default_max_gen_trx_count,
};
time_point initial_timestamp = fc::time_point::from_iso_string( "2018-03-02T12:00:00" );;
......
......@@ -56,6 +56,7 @@ namespace eosio { namespace chain { namespace resource_limits {
int64_t get_account_cpu_limit( const account_name& name ) const;
int64_t get_account_net_limit( const account_name& name ) const;
int64_t get_account_ram_usage( const account_name& name ) const;
private:
chainbase::database& _db;
......
......@@ -20,8 +20,8 @@ namespace eosio { namespace chain {
uint64_t cpu_usage = 0;
string console;
uint64_t total_inline_cpu_usage = 0; /// total of inline_traces[x].cpu_usage + cpu_usage
uint64_t total_cpu_usage = 0; /// total of inline_traces[x].cpu_usage + cpu_usage
transaction_id_type trx_id; ///< the transaction that generated this action
};
struct action_trace : public base_action_trace {
......@@ -30,22 +30,25 @@ namespace eosio { namespace chain {
vector<action_trace> inline_traces;
};
struct transaction_trace;
typedef std::shared_ptr<transaction_trace> transaction_trace_ptr;
struct transaction_trace {
transaction_id_type id;
transaction_receipt_header receipt;
fc::microseconds elapsed;
uint64_t net_usage = 0;
uint64_t cpu_usage = 0;
bool scheduled = false;
vector<action_trace> action_traces; ///< disposable
fc::optional<fc::exception> soft_except;
fc::optional<fc::exception> hard_except;
std::exception_ptr soft_except_ptr;
std::exception_ptr hard_except_ptr;
transaction_trace_ptr failed_dtrx_trace;
fc::optional<fc::exception> soft_except;
fc::optional<fc::exception> hard_except;
std::exception_ptr soft_except_ptr;
std::exception_ptr hard_except_ptr;
uint32_t kcpu_usage()const { return (cpu_usage + 1023)/1024; }
};
typedef std::shared_ptr<transaction_trace> transaction_trace_ptr;
struct block_trace {
fc::microseconds elapsed;
......@@ -57,10 +60,11 @@ namespace eosio { namespace chain {
} } /// namespace eosio::chain
FC_REFLECT( eosio::chain::base_action_trace,
(receipt)(act)(elapsed)(cpu_usage)(console)(total_inline_cpu_usage) )
(receipt)(act)(elapsed)(cpu_usage)(console)(total_cpu_usage)(trx_id) )
FC_REFLECT_DERIVED( eosio::chain::action_trace,
(eosio::chain::base_action_trace), (inline_traces) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(cpu_usage)(scheduled)(action_traces)(soft_except)(hard_except) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(cpu_usage)(scheduled)(action_traces)
(failed_dtrx_trace)(soft_except)(hard_except) )
FC_REFLECT( eosio::chain::block_trace, (elapsed)(cpu_usage)(trx_traces) )
......@@ -43,6 +43,7 @@ namespace eosio { namespace chain {
}
void set_reference_block( const block_id_type& reference_block );
bool verify_reference_block( const block_id_type& reference_block )const;
void validate()const;
};
/**
......@@ -153,7 +154,7 @@ namespace eosio { namespace chain {
deferred_transaction() = default;
deferred_transaction(uint128_t sender_id, account_name sender, account_name payer,time_point_sec execute_after,
deferred_transaction(uint128_t sender_id, account_name sender, account_name payer,time_point_sec execute_after,
const signed_transaction& txn)
: signed_transaction(txn),
sender_id(sender_id),
......
......@@ -6,52 +6,84 @@ namespace eosio { namespace chain {
class transaction_context {
private:
// Common private constructor
transaction_context( controller& c,
transaction_trace_ptr& trace_ptr,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
fc::time_point published,
uint64_t initial_net_usage,
uint64_t initial_cpu_usage );
static uint64_t calculate_initial_net_usage( const controller& c,
const signed_transaction& t,
uint64_t packed_trx_billable_size );
public:
transaction_context( controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id )
:control(c),
id(trx_id),
trx(t),
undo_session(c.db().start_undo_session(true))
{
trace = std::make_shared<transaction_trace>();
trace->id = id;
executed.reserve( trx.total_actions() );
}
// For deferred transactions
transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
fc::time_point published );
// For implicit or input transactions
transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point deadline,
bool is_implicit,
uint64_t packed_trx_billable_size,
uint64_t initial_cpu_usage = 0 );
void exec();
void squash();
inline void add_net_usage( uint64_t u ) { net_usage += u; check_net_usage(); }
inline void add_cpu_usage( uint64_t u ) { cpu_usage += u; check_cpu_usage(); }
inline void add_cpu_usage_and_check_time( uint64_t u ) { check_time(); cpu_usage += u; check_cpu_usage(); }
void check_net_usage()const;
void check_cpu_usage()const;
void check_time()const;
private:
void dispatch_action( const action& a, account_name receiver, bool context_free = false );
inline void dispatch_action( const action& a, bool context_free = false ) {
dispatch_action(a, a.account, context_free);
};
void schedule_transaction();
void record_transaction( const transaction_id_type& id, fc::time_point_sec expire );
bool apply_context_free = true;
bool apply_actions = true;
bool is_input = true;
fc::microseconds delay;
vector<action_receipt> executed;
/// Fields:
public:
controller& control;
const signed_transaction& trx;
transaction_id_type id;
chainbase::database::session undo_session;
transaction_trace_ptr trace;
fc::time_point deadline;
fc::time_point published;
vector<action_receipt> executed;
flat_set<account_name> bill_to_accounts;
uint64_t max_net = 0; /// the maximum number of network usage bytes the transaction can consume
uint64_t max_cpu = 0; /// the maximum number of CPU instructions the transaction may consume
fc::microseconds delay;
bool is_input = false;
bool apply_context_free = true;
transaction_trace_ptr trace;
private:
fc::time_point deadline;
controller& control;
transaction_id_type id;
uint64_t& net_usage; /// reference to trace->net_usage
uint64_t& cpu_usage; /// reference to trace->cpu_usage
fc::time_point published;
const signed_transaction& trx;
uint64_t max_cpu = 0; /// the maximum number of CPU cycles the transaction may consume
uint32_t net_usage = 0;
//const transaction_metadata_ptr& trx_meta;
chainbase::database::session undo_session;
flat_set<account_name> bill_to_accounts;
};
} }
......@@ -24,8 +24,8 @@ class transaction_metadata {
std::function<void(const transaction_trace_ptr&)> on_result;
transaction_metadata( const signed_transaction& t )
:trx(t),packed_trx(t,packed_transaction::zlib) {
transaction_metadata( const signed_transaction& t, packed_transaction::compression_type c = packed_transaction::none )
:trx(t),packed_trx(t, c) {
id = trx.id();
//raw_packed = fc::raw::pack( static_cast<const transaction&>(trx) );
signed_id = digest_type::hash(packed_trx);
......
......@@ -13,6 +13,7 @@ namespace eosio { namespace chain { namespace wasm_constraints {
constexpr unsigned maximum_table_elements = 1024; //elements
constexpr unsigned maximum_linear_memory_init = 64*1024; //bytes
constexpr unsigned maximum_func_local_bytes = 8192; //bytes
constexpr unsigned maximum_call_depth = 250; //nested calls
static constexpr unsigned wasm_page_size = 64*1024;
......
#pragma once
#include <eosio/chain/wasm_eosio_binary_ops.hpp>
#include <eosio/chain/wasm_eosio_constraints.hpp>
#include <eosio/chain/webassembly/common.hpp>
#include <fc/exception/exception.hpp>
#include <eosio/chain/exceptions.hpp>
......@@ -102,7 +103,6 @@ namespace eosio { namespace chain { namespace wasm_injections {
static void inject( IR::Module& m );
static void initializer();
};
struct memories_injection_visitor {
static void inject( IR::Module& m );
......@@ -207,6 +207,78 @@ namespace eosio { namespace chain { namespace wasm_injections {
};
struct call_depth_check {
static constexpr bool kills = true;
static constexpr bool post = false;
static int32_t global_idx;
static void init() {
global_idx = -1;
}
static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) {
if ( global_idx == -1 ) {
arg.module->globals.defs.push_back({{ValueType::i32, true}, {(I32) eosio::chain::wasm_constraints::maximum_call_depth}});
}
global_idx = arg.module->globals.size()-1;
int32_t assert_idx;
injector_utils::add_import<ResultType::none>(*(arg.module), "call_depth_assert", assert_idx);
wasm_ops::op_types<>::call_t call_assert;
wasm_ops::op_types<>::get_global_t get_global_inst;
wasm_ops::op_types<>::set_global_t set_global_inst;
wasm_ops::op_types<>::i32_eqz_t eqz_inst;
wasm_ops::op_types<>::i32_const_t const_inst;
wasm_ops::op_types<>::i32_add_t add_inst;
wasm_ops::op_types<>::end_t end_inst;
wasm_ops::op_types<>::if__t if_inst;
wasm_ops::op_types<>::else__t else_inst;
call_assert.field = assert_idx;
get_global_inst.field = global_idx;
set_global_inst.field = global_idx;
const_inst.field = -1;
#define INSERT_INJECTED(X) \
tmp = X.pack(); \
injected.insert( injected.end(), tmp.begin(), tmp.end() )
std::vector<U8> injected;
std::vector<U8> tmp;
INSERT_INJECTED(get_global_inst);
INSERT_INJECTED(eqz_inst);
INSERT_INJECTED(if_inst);
INSERT_INJECTED(call_assert);
INSERT_INJECTED(else_inst);
INSERT_INJECTED(const_inst);
INSERT_INJECTED(get_global_inst);
INSERT_INJECTED(add_inst);
INSERT_INJECTED(set_global_inst);
INSERT_INJECTED(end_inst);
/* print the correct call type */
if ( inst->get_code() == wasm_ops::call_code ) {
wasm_ops::op_types<>::call_t* call_inst = reinterpret_cast<wasm_ops::op_types<>::call_t*>(inst);
tmp = call_inst->pack();
}
else {
wasm_ops::op_types<>::call_indirect_t* call_inst = reinterpret_cast<wasm_ops::op_types<>::call_indirect_t*>(inst);
tmp = call_inst->pack();
}
injected.insert( injected.end(), tmp.begin(), tmp.end() );
const_inst.field = 1;
INSERT_INJECTED(get_global_inst);
INSERT_INJECTED(const_inst);
INSERT_INJECTED(add_inst);
INSERT_INJECTED(set_global_inst);
#undef INSERT_INJECTED
arg.new_code->insert( arg.new_code->end(), injected.begin(), injected.end() );
}
};
// float injections
constexpr const char* inject_which_op( uint16_t opcode ) {
switch ( opcode ) {
......@@ -574,8 +646,8 @@ namespace eosio { namespace chain { namespace wasm_injections {
using br_if_t = wasm_ops::br_if <instruction_counter>;
using br_table_t = wasm_ops::br_table <instruction_counter>;
using return__t = wasm_ops::return_ <instruction_counter>;
using call_t = wasm_ops::call <instruction_counter>;
using call_indirect_t = wasm_ops::call_indirect <instruction_counter>;
using call_t = wasm_ops::call <instruction_counter, call_depth_check>;
using call_indirect_t = wasm_ops::call_indirect <instruction_counter, call_depth_check>;
using drop_t = wasm_ops::drop <instruction_counter>;
using select_t = wasm_ops::select <instruction_counter>;
......@@ -792,6 +864,7 @@ namespace eosio { namespace chain { namespace wasm_injections {
injector_utils::init( mod );
instruction_counter::init();
checktime_injector::init();
call_depth_check::init();
}
void inject() {
......
......@@ -57,10 +57,9 @@ namespace eosio { namespace chain {
} catch(Serialization::FatalSerializationException& e) {
EOS_ASSERT(false, wasm_serialization_error, e.message.c_str());
}
wasm_injections::wasm_binary_injection injector(module);
injector.inject();
std::vector<U8> bytes;
try {
Serialization::ArrayOutputStream outstream;
......
......@@ -147,6 +147,10 @@ void resource_limits_manager::add_pending_account_ram_usage( const account_name
}
}
int64_t resource_limits_manager::get_account_ram_usage( const account_name& name )const {
return _db.get<resource_usage_object,by_owner>( name ).ram_usage;
}
void resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) {
const auto& usage = _db.get<resource_usage_object,by_owner>( account );
......@@ -293,9 +297,12 @@ int64_t resource_limits_manager::get_account_cpu_limit( const account_name& name
return -1;
}
auto total_cpu_weight = state.total_cpu_weight;
if( total_cpu_weight == 0 ) total_cpu_weight = 1;
uint128_t consumed_ex = (uint128_t)usage.cpu_usage.consumed * (uint128_t)config::rate_limiting_precision;
uint128_t virtual_capacity_ex = (uint128_t)state.virtual_cpu_limit * (uint128_t)config::rate_limiting_precision;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.cpu_weight) / (uint128_t)state.total_cpu_weight;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.cpu_weight) / (uint128_t)total_cpu_weight;
if (usable_capacity_ex < consumed_ex) {
return 0;
......@@ -314,7 +321,11 @@ int64_t resource_limits_manager::get_account_net_limit( const account_name& name
uint128_t consumed_ex = (uint128_t)usage.net_usage.consumed * (uint128_t)config::rate_limiting_precision;
uint128_t virtual_capacity_ex = (uint128_t)state.virtual_net_limit * (uint128_t)config::rate_limiting_precision;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.net_weight) / (uint128_t)state.total_net_weight;
auto total_net_weight = state.total_net_weight;
if( total_net_weight == 0 ) total_net_weight = 1;
uint128_t usable_capacity_ex = (uint128_t)(virtual_capacity_ex * limits.net_weight) / (uint128_t)total_net_weight;
if (usable_capacity_ex < consumed_ex) {
return 0;
......
......@@ -57,6 +57,13 @@ bool transaction_header::verify_reference_block( const block_id_type& reference_
ref_block_prefix == (decltype(ref_block_prefix))reference_block._hash[1];
}
void transaction_header::validate()const {
EOS_ASSERT( max_kcpu_usage.value < UINT32_MAX / 1024UL, transaction_exception,
"declared max_kcpu_usage overflows when expanded to max cpu usage" );
EOS_ASSERT( max_net_usage_words.value < UINT32_MAX / 8UL, transaction_exception,
"declared max_net_usage_words overflows when expanded to max net usage" );
}
transaction_id_type transaction::id() const {
digest_type::encoder enc;
fc::raw::pack( enc, *this );
......
......@@ -4,94 +4,223 @@
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/transaction_object.hpp>
#include <eosio/chain/global_property_object.hpp>
namespace eosio { namespace chain {
void transaction_context::exec() {
transaction_context::transaction_context( controller& c,
transaction_trace_ptr& trace_ptr,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
fc::time_point p,
uint64_t initial_net_usage,
uint64_t initial_cpu_usage )
:control(c)
,trx(t)
,id(trx_id)
,undo_session(c.db().start_undo_session(true))
,trace(std::make_shared<transaction_trace>())
,deadline(d)
,published(p)
,net_usage(trace->net_usage)
,cpu_usage(trace->cpu_usage)
{
trace->id = id;
trace_ptr = trace;
executed.reserve( trx.total_actions() );
// Record accounts to be billed for network and CPU usage
uint64_t determine_payers_cpu_cost = 0;
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization ) {
bill_to_accounts.insert( auth.actor );
determine_payers_cpu_cost += config::determine_payers_cpu_overhead_per_authorization;
}
}
// Calculate network and CPU usage limits and initial usage:
// Start with limits set in dynamic configuration
const auto& cfg = control.get_global_properties().configuration;
max_net = cfg.max_transaction_net_usage;
max_cpu = cfg.max_transaction_cpu_usage;
// Potentially lower limits to what is optionally set in the transaction header
uint64_t trx_specified_net_usage_limit = static_cast<uint64_t>(trx.max_net_usage_words.value)*8;
if( trx_specified_net_usage_limit > 0 )
max_net = std::min( max_net, trx_specified_net_usage_limit );
uint64_t trx_specified_cpu_usage_limit = static_cast<uint64_t>(trx.max_kcpu_usage.value)*1024;
if( trx_specified_cpu_usage_limit > 0 )
max_cpu = std::min( max_cpu, trx_specified_cpu_usage_limit );
// Initial billing for network usage
if( initial_net_usage > 0 )
add_net_usage( initial_net_usage );
// Initial billing for CPU usage known to be soon consumed
add_cpu_usage( initial_cpu_usage
+ static_cast<uint64_t>(cfg.base_per_transaction_cpu_usage)
+ determine_payers_cpu_cost
+ bill_to_accounts.size() * config::resource_processing_cpu_overhead_per_billed_account );
// Fails early if current CPU usage is already greater than the current limit (which may still go lower).
// Update usage values of accounts to reflect new time
auto& rl = control.get_mutable_resource_limits_manager();
rl.add_transaction_usage( bill_to_accounts, 0, 0, block_timestamp_type(control.pending_block_time()).slot );
// Lower limits to what the billed accounts can afford to pay
max_net = std::min( max_net, rl.get_block_net_limit() );
max_cpu = std::min( max_cpu, rl.get_block_cpu_limit() );
for( const auto& a : bill_to_accounts ) {
auto net_limit = rl.get_account_net_limit(a);
if( net_limit >= 0 )
max_net = std::min( max_net, static_cast<uint64_t>(net_limit) ); // reduce max_net to the amount the account is able to pay
auto cpu_limit = rl.get_account_cpu_limit(a);
if( cpu_limit >= 0 )
max_cpu = std::min( max_cpu, static_cast<uint64_t>(cpu_limit) ); // reduce max_cpu to the amount the account is able to pay
}
EOS_ASSERT( trx.max_kcpu_usage.value < UINT32_MAX / 1024UL, transaction_exception, "declared max_kcpu_usage overflows when expanded to max cpu usage" );
EOS_ASSERT( trx.max_net_usage_words.value < UINT32_MAX / 8UL, transaction_exception, "declared max_net_usage_words overflows when expanded to max net usage" );
// Round down network and CPU usage limits so that comparison to actual usage is more efficient
max_net = (max_net/8)*8; // Round down to nearest multiple of word size (8 bytes)
max_cpu = (max_cpu/1024)*1024; // Round down to nearest multiple of 1024
if( initial_net_usage > 0 )
check_net_usage();
check_cpu_usage(); // Fail early if current CPU usage is already greater than the calculated limit
}
uint64_t transaction_context::calculate_initial_net_usage( const controller& c,
const signed_transaction& t,
uint64_t packed_trx_billable_size ) {
const auto& cfg = c.get_global_properties().configuration;
uint64_t initial_net_usage = static_cast<uint64_t>(cfg.base_per_transaction_net_usage) + packed_trx_billable_size;
if( t.delay_sec.value > 0 ) {
// If delayed, also charge ahead of time for the additional net usage needed to retire the delayed transaction
// whether that be by successfully executing, soft failure, hard failure, or expiration.
initial_net_usage += static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
+ static_cast<uint64_t>(config::transaction_id_net_usage);
}
return initial_net_usage;
}
transaction_context::transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
fc::time_point p )
:transaction_context( c, trace_ptr, t, trx_id, d, p, 0, 0 )
{
trace->scheduled = true;
is_input = false;
apply_context_free = false;
}
transaction_context::transaction_context( transaction_trace_ptr& trace_ptr,
controller& c,
const signed_transaction& t,
const transaction_id_type& trx_id,
fc::time_point d,
bool is_implicit,
uint64_t packed_trx_billable_size,
uint64_t initial_cpu_usage )
:transaction_context( c, trace_ptr, t, trx_id, d, c.pending_block_time(),
calculate_initial_net_usage(c, t, packed_trx_billable_size), initial_cpu_usage )
{
trace->scheduled = false;
is_input = !is_implicit;
}
void transaction_context::exec() {
//trx.validate(); // Not needed anymore since overflow is prevented by using uint64_t instead of uint32_t
control.validate_tapos( trx );
control.validate_referenced_accounts( trx );
if( is_input ) { /// signed transaction from user vs implicit transaction scheduled by contracts
if( is_input ) { /// signed transaction from user rather than a deferred transaction
control.validate_expiration( trx );
record_transaction( id, trx.expiration ); /// checks for dupes
}
if( trx.max_kcpu_usage.value != 0 ) {
max_cpu = uint64_t(trx.max_kcpu_usage.value)*1024;
} else {
max_cpu = uint64_t(-1);
}
if( apply_context_free ) {
for( const auto& act : trx.context_free_actions ) {
dispatch_action( act, act.account, true );
dispatch_action( act, true );
}
}
if( delay == fc::microseconds() ) {
for( const auto& act : trx.actions ) {
dispatch_action( act, act.account, false );
dispatch_action( act );
}
} else {
schedule_transaction();
schedule_transaction();
}
for( const auto& act : trx.actions ) {
for( const auto& auth : act.authorization )
bill_to_accounts.insert( auth.actor );
}
FC_ASSERT( trace->cpu_usage <= max_cpu, "transaction consumed too many CPU cycles" );
net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
cpu_usage = ((cpu_usage + 1023)/1024)*1024; // Round up to nearest multiple of 1024
control.get_mutable_resource_limits_manager()
.add_transaction_usage( bill_to_accounts, cpu_usage, net_usage,
block_timestamp_type(control.pending_block_time()).slot );
}
auto& rl = control.get_mutable_resource_limits_manager();
void transaction_context::squash() {
undo_session.squash();
}
flat_set<account_name> bta( bill_to_accounts.begin(), bill_to_accounts.end() );
rl.add_transaction_usage( bta, trace->cpu_usage, net_usage, block_timestamp_type(control.pending_block_time()).slot );
void transaction_context::check_net_usage()const {
EOS_ASSERT( BOOST_LIKELY(net_usage <= max_net), tx_resource_exhausted,
"net usage of transaction is too high: ${actual_net_usage} > ${net_usage_limit}",
("actual_net_usage", net_usage)("net_usage_limit", max_net) );
}
void transaction_context::check_cpu_usage()const {
EOS_ASSERT( BOOST_LIKELY(cpu_usage <= max_cpu), tx_resource_exhausted,
"cpu usage of transaction is too high: ${actual_net_usage} > ${cpu_usage_limit}",
("actual_net_usage", cpu_usage)("cpu_usage_limit", max_cpu) );
}
void transaction_context::squash() {
undo_session.squash();
void transaction_context::check_time()const {
if( BOOST_UNLIKELY(fc::time_point::now() > deadline) ) {
wlog( "deadline passed" );
throw checktime_exceeded();
}
}
void transaction_context::dispatch_action( const action& a, account_name receiver, bool context_free ) {
apply_context acontext( control, a, trx );
acontext.id = id;
acontext.context_free = context_free;
acontext.receiver = receiver;
acontext.processing_deadline = deadline;
acontext.published_time = published;
acontext.max_cpu = max_cpu - trace->cpu_usage;
apply_context acontext( control, *this, a );
acontext.context_free = context_free;
acontext.receiver = receiver;
acontext.exec();
fc::move_append(executed, move(acontext.executed) );
trace->cpu_usage += acontext.trace.total_inline_cpu_usage;
trace->action_traces.emplace_back( move(acontext.trace) );
}
void transaction_context::schedule_transaction() {
// Charge ahead of time for the additional net usage needed to retire the delayed transaction
// whether that be by successfully executing, soft failure, hard failure, or expiration.
if( trx.delay_sec.value == 0 ) { // Do not double bill. Only charge if we have not already charged for the delay.
const auto& cfg = control.get_global_properties().configuration;
add_net_usage( static_cast<uint64_t>(cfg.base_per_transaction_net_usage)
+ static_cast<uint64_t>(config::transaction_id_net_usage) ); // Will exit early if net usage cannot be payed.
}
auto first_auth = trx.first_authorizor();
uint32_t trx_size = 0;
const auto& cgto = control.db().create<generated_transaction_object>( [&]( auto& gto ) {
gto.trx_id = id;
gto.payer = first_auth;
gto.sender = account_name(); /// auto-boxed trxs have no sender
gto.sender = account_name(); /// delayed transactions have no sender
gto.sender_id = transaction_id_to_sender_id( gto.trx_id );
gto.published = control.pending_block_time();
gto.delay_until = gto.published + delay;
gto.expiration = gto.delay_until + fc::milliseconds(config::deferred_trx_expiration_window_ms);
gto.set( trx );
gto.expiration = gto.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
trx_size = gto.set( trx );
});
control.get_mutable_resource_limits_manager().add_pending_account_ram_usage(cgto.payer,
(config::billable_size_v<generated_transaction_object> + cgto.packed_trx.size()));
control.get_mutable_resource_limits_manager()
.add_pending_account_ram_usage( cgto.payer, (config::billable_size_v<generated_transaction_object> + trx_size) );
}
void transaction_context::record_transaction( const transaction_id_type& id, fc::time_point_sec expire ) {
......
......@@ -35,6 +35,7 @@ void max_memory_injection_visitor::inject( Module& m ) {
}
void max_memory_injection_visitor::initializer() {}
int32_t call_depth_check::global_idx = -1;
uint32_t instruction_counter::icnt = 0;
int32_t checktime_injector::checktime_idx = -1;
......
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/controller.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/producer_schedule.hpp>
#include <eosio/chain/exceptions.hpp>
#include <boost/core/ignore_unused.hpp>
......@@ -16,7 +17,6 @@
#include <fc/crypto/sha256.hpp>
#include <fc/crypto/sha1.hpp>
#include <fc/io/raw.hpp>
#include <fc/utf8.hpp>
#include <softfloat.hpp>
#include <boost/asio.hpp>
......@@ -49,7 +49,7 @@ namespace eosio { namespace chain {
//there are a couple opportunties for improvement here--
//Easy: Cache the Module created here so it can be reused for instantiaion
//Hard: Kick off instantiation in a separate thread at this location
}
}
void wasm_interface::apply( const digest_type& code_id, const shared_vector<char>& code, apply_context& context ) {
my->get_instantiated_module(code_id, code)->apply(context);
......@@ -132,7 +132,7 @@ class privileged_api : public context_aware_api {
EOS_ASSERT(ram_bytes >= -1, wasm_execution_error, "invalid value for ram resource limit expected [-1,INT64_MAX]");
EOS_ASSERT(net_weight >= -1, wasm_execution_error, "invalid value for net resource weight expected [-1,INT64_MAX]");
EOS_ASSERT(cpu_weight >= -1, wasm_execution_error, "invalid value for cpu resource weight expected [-1,INT64_MAX]");
context.mutable_controller.get_mutable_resource_limits_manager().set_account_limits(account, ram_bytes, net_weight, cpu_weight);
context.control.get_mutable_resource_limits_manager().set_account_limits(account, ram_bytes, net_weight, cpu_weight);
}
void get_resource_limits( account_name account, int64_t& ram_bytes, int64_t& net_weight, int64_t& cpu_weight ) {
......@@ -151,7 +151,7 @@ class privileged_api : public context_aware_api {
unique_producers.insert(p.producer_name);
}
EOS_ASSERT( producers.size() == unique_producers.size(), wasm_execution_error, "duplicate producer name in producer schedule" );
return context.mutable_controller.set_proposed_producers( std::move(producers) );
return context.control.set_proposed_producers( std::move(producers) );
}
uint32_t get_blockchain_parameters_packed( array_ptr<char> packed_blockchain_parameters, size_t buffer_size) {
......@@ -783,17 +783,6 @@ class permission_api : public context_aware_api {
}
};
class string_api : public context_aware_api {
public:
using context_aware_api::context_aware_api;
void assert_is_utf8(array_ptr<const char> str, size_t datalen, null_terminated_ptr msg) {
const bool test = fc::is_utf8(std::string( str, datalen ));
FC_ASSERT( test, "assertion failed: ${s}", ("s",msg.value) );
}
};
class system_api : public context_aware_api {
public:
explicit system_api( apply_context& ctx )
......@@ -841,7 +830,7 @@ class action_api : public context_aware_api {
}
uint64_t publication_time() {
return static_cast<uint64_t>( context.published_time.time_since_epoch().count() );
return static_cast<uint64_t>( context.trx_context.published.time_since_epoch().count() );
}
name current_receiver() {
......@@ -1137,8 +1126,9 @@ class transaction_api : public context_aware_api {
using context_aware_api::context_aware_api;
void send_inline( array_ptr<char> data, size_t data_len ) {
// TODO: use global properties object for dynamic configuration of this default_max_gen_trx_size
FC_ASSERT( data_len < config::default_max_inline_action_size, "inline action too big" );
//TODO: Why is this limit even needed? And why is it not consistently checked on actions in input or deferred transactions
FC_ASSERT( data_len < context.control.get_global_properties().configuration.max_inline_action_size,
"inline action too big" );
action act;
fc::raw::unpack<action>(data, data_len, act);
......@@ -1146,8 +1136,9 @@ class transaction_api : public context_aware_api {
}
void send_context_free_inline( array_ptr<char> data, size_t data_len ) {
// TODO: use global properties object for dynamic configuration of this default_max_gen_trx_size
FC_ASSERT( data_len < config::default_max_inline_action_size, "inline action too big" );
//TODO: Why is this limit even needed? And why is it not consistently checked on actions in input or deferred transactions
FC_ASSERT( data_len < context.control.get_global_properties().configuration.max_inline_action_size,
"inline action too big" );
action act;
fc::raw::unpack<action>(data, data_len, act);
......@@ -1191,14 +1182,14 @@ class context_free_transaction_api : public context_aware_api {
}
int expiration() {
return context.trx.expiration.sec_since_epoch();
return context.trx_context.trx.expiration.sec_since_epoch();
}
int tapos_block_num() {
return context.trx.ref_block_num;
return context.trx_context.trx.ref_block_num;
}
int tapos_block_prefix() {
return context.trx.ref_block_prefix;
return context.trx_context.trx.ref_block_prefix;
}
int get_action( uint32_t type, uint32_t index, array_ptr<char> buffer, size_t buffer_size )const {
......@@ -1432,6 +1423,108 @@ class compiler_builtins : public context_aware_api {
static constexpr uint32_t SHIFT_WIDTH = (sizeof(uint64_t)*8)-1;
};
class math_api : public context_aware_api {
public:
math_api( apply_context& ctx )
:context_aware_api(ctx,true){}
void diveq_i128(unsigned __int128* self, const unsigned __int128* other) {
fc::uint128_t s(*self);
const fc::uint128_t o(*other);
FC_ASSERT( o != 0, "divide by zero" );
s = s/o;
*self = (unsigned __int128)s;
}
void multeq_i128(unsigned __int128* self, const unsigned __int128* other) {
fc::uint128_t s(*self);
const fc::uint128_t o(*other);
s *= o;
*self = (unsigned __int128)s;
}
uint64_t double_add(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a))
+ DOUBLE(*reinterpret_cast<double *>(&b));
double res = c.convert_to<double>();
return *reinterpret_cast<uint64_t *>(&res);
}
uint64_t double_mult(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a))
* DOUBLE(*reinterpret_cast<double *>(&b));
double res = c.convert_to<double>();
return *reinterpret_cast<uint64_t *>(&res);
}
uint64_t double_div(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
DOUBLE divisor = DOUBLE(*reinterpret_cast<double *>(&b));
FC_ASSERT(divisor != 0, "divide by zero");
DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a)) / divisor;
double res = c.convert_to<double>();
return *reinterpret_cast<uint64_t *>(&res);
}
uint32_t double_eq(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
return DOUBLE(*reinterpret_cast<double *>(&a)) == DOUBLE(*reinterpret_cast<double *>(&b));
}
uint32_t double_lt(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
return DOUBLE(*reinterpret_cast<double *>(&a)) < DOUBLE(*reinterpret_cast<double *>(&b));
}
uint32_t double_gt(uint64_t a, uint64_t b) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
return DOUBLE(*reinterpret_cast<double *>(&a)) > DOUBLE(*reinterpret_cast<double *>(&b));
}
uint64_t double_to_i64(uint64_t n) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
return DOUBLE(*reinterpret_cast<double *>(&n)).convert_to<int64_t>();
}
uint64_t i64_to_double(int64_t n) {
using DOUBLE = boost::multiprecision::cpp_bin_float_50;
double res = DOUBLE(n).convert_to<double>();
return *reinterpret_cast<uint64_t *>(&res);
}
};
/*
* This api will be removed with fix for `eos #2561`
*/
class call_depth_api : public context_aware_api {
public:
call_depth_api( apply_context& ctx )
:context_aware_api(ctx,true){}
void call_depth_assert() {
FC_THROW_EXCEPTION(wasm_execution_error, "Exceeded call depth maximum");
}
};
REGISTER_INJECTED_INTRINSICS(call_depth_api,
(call_depth_assert, void() )
);
REGISTER_INTRINSICS(math_api,
(diveq_i128, void(int, int) )
(multeq_i128, void(int, int) )
(double_add, int64_t(int64_t, int64_t) )
(double_mult, int64_t(int64_t, int64_t) )
(double_div, int64_t(int64_t, int64_t) )
(double_eq, int32_t(int64_t, int64_t) )
(double_lt, int32_t(int64_t, int64_t) )
(double_gt, int32_t(int64_t, int64_t) )
(double_to_i64, int64_t(int64_t) )
(i64_to_double, int64_t(int64_t) )
);
REGISTER_INTRINSICS(compiler_builtins,
(__ashlti3, void(int, int64_t, int64_t, int) )
(__ashrti3, void(int, int64_t, int64_t, int) )
......@@ -1549,10 +1642,6 @@ REGISTER_INTRINSICS(permission_api,
(check_authorization, int(int64_t, int64_t, int, int))
);
REGISTER_INTRINSICS(string_api,
(assert_is_utf8, void(int, int, int) )
);
REGISTER_INTRINSICS(system_api,
(abort, void() )
(eosio_assert, void(int, int) )
......
......@@ -155,10 +155,6 @@ namespace eosio { namespace testing {
void set_code( account_name name, const vector<uint8_t> wasm );
void set_abi( account_name name, const char* abi_json );
unique_ptr<controller> control;
std::map<chain::public_key_type, chain::private_key_type> block_signing_private_keys;
bool chain_has_transaction( const transaction_id_type& txid ) const;
const transaction_receipt& get_transaction_receipt( const transaction_id_type& txid ) const;
......@@ -166,59 +162,71 @@ namespace eosio { namespace testing {
const symbol& asset_symbol,
const account_name& account ) const;
vector<char> get_row_by_account( uint64_t code, uint64_t scope, uint64_t table, const account_name& act );
vector<char> get_row_by_account( uint64_t code, uint64_t scope, uint64_t table, const account_name& act );
static vector<uint8_t> to_uint8_vector(const string& s);
static vector<uint8_t> to_uint8_vector(const string& s);
static vector<uint8_t> to_uint8_vector(uint64_t x);
static vector<uint8_t> to_uint8_vector(uint64_t x);
static uint64_t to_uint64(fc::variant x);
static uint64_t to_uint64(fc::variant x);
static string to_string(fc::variant x);
static string to_string(fc::variant x);
static action_result success() { return string(); }
static action_result success() { return string(); }
static action_result error(const string& msg) { return msg; }
static action_result error(const string& msg) { return msg; }
auto get_resolver() {
return [this](const account_name &name) -> optional<abi_serializer> {
try {
const auto &accnt = control->db().get<account_object, by_name>(name);
abi_def abi;
if (abi_serializer::to_abi(accnt.abi, abi)) {
return abi_serializer(abi);
}
return optional<abi_serializer>();
} FC_RETHROW_EXCEPTIONS(error, "Failed to find or parse ABI for ${name}", ("name", name))
};
}
auto get_resolver() {
return [this](const account_name &name) -> optional<abi_serializer> {
try {
const auto &accnt = control->db().get<account_object, by_name>(name);
abi_def abi;
if (abi_serializer::to_abi(accnt.abi, abi)) {
return abi_serializer(abi);
}
return optional<abi_serializer>();
} FC_RETHROW_EXCEPTIONS(error, "Failed to find or parse ABI for ${name}", ("name", name))
};
}
void sync_with(base_tester& other);
void sync_with(base_tester& other);
const table_id_object* find_table( name code, name scope, name table );
const table_id_object* find_table( name code, name scope, name table );
// method treats key as a name type, if this is not appropriate in your case, pass require == false and report the correct behavior
template<typename Object>
bool get_table_entry(Object& obj, account_name code, account_name scope, account_name table, uint64_t key, bool require = true) {
auto* maybe_tid = find_table(code, scope, table);
if(maybe_tid == nullptr)
BOOST_FAIL("table for code=\"" + code.to_string() + "\" scope=\"" + scope.to_string() + "\" table=\"" + table.to_string() + "\" does not exist");
// method treats key as a name type, if this is not appropriate in your case, pass require == false and report the correct behavior
template<typename Object>
bool get_table_entry(Object& obj, account_name code, account_name scope, account_name table, uint64_t key, bool require = true) {
auto* maybe_tid = find_table(code, scope, table);
if( maybe_tid == nullptr ) {
BOOST_FAIL( "table for code=\"" + code.to_string()
+ "\" scope=\"" + scope.to_string()
+ "\" table=\"" + table.to_string()
+ "\" does not exist" );
}
auto* o = control->db().find<key_value_object, by_scope_primary>(boost::make_tuple(maybe_tid->id, key));
if(o == nullptr) {
if (require)
BOOST_FAIL("object does not exist for primary_key=\"" + name(key).to_string() + "\"");
auto* o = control->db().find<key_value_object, by_scope_primary>(boost::make_tuple(maybe_tid->id, key));
if( o == nullptr ) {
if( require )
BOOST_FAIL("object does not exist for primary_key=\"" + name(key).to_string() + "\"");
return false;
}
return false;
}
fc::raw::unpack(o->value.data(), o->value.size(), obj);
return true;
}
fc::raw::unpack(o->value.data(), o->value.size(), obj);
return true;
}
protected:
protected:
signed_block_ptr _produce_block( fc::microseconds skip_time, bool skip_pending_trxs = false, uint32_t skip_flag = 0 );
// Fields:
protected:
// tempdir field must come before control so that during destruction the tempdir is deleted only after controller finishes
fc::temp_directory tempdir;
public:
unique_ptr<controller> control;
std::map<chain::public_key_type, chain::private_key_type> block_signing_private_keys;
protected:
controller::config cfg;
map<transaction_id_type, transaction_receipt> chain_transactions;
};
......
......@@ -51,7 +51,7 @@ void chain_api_plugin::plugin_initialize(const variables_map&) {}
} catch (fc::exception& e) { \
error_results results{500, "Internal Service Error", e}; \
cb(500, fc::json::to_string(results)); \
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e.to_detail_string())); \
} \
}}
......
......@@ -11,6 +11,7 @@
#include <eosio/chain/config.hpp>
#include <eosio/chain/types.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/eosio_contract.hpp>
......@@ -245,6 +246,7 @@ namespace chain_apis {
const string read_only::KEYi64 = "i64";
read_only::get_info_results read_only::get_info(const read_only::get_info_params&) const {
const auto& rm = db.get_resource_limits_manager();
return {
eosio::utilities::common::itoh(static_cast<uint32_t>(app().version())),
db.head_block_num(),
......@@ -253,6 +255,10 @@ read_only::get_info_results read_only::get_info(const read_only::get_info_params
db.head_block_id(),
db.head_block_time(),
db.head_block_producer(),
rm.get_virtual_block_cpu_limit(),
rm.get_virtual_block_net_limit(),
rm.get_block_cpu_limit(),
rm.get_block_net_limit()
//std::bitset<64>(db.get_dynamic_global_properties().recent_slots_filled).to_string(),
//__builtin_popcountll(db.get_dynamic_global_properties().recent_slots_filled) / 64.0
};
......@@ -397,8 +403,8 @@ read_write::push_transaction_results read_write::push_transaction(const read_wri
auto trx_trace_ptr = db.sync_push( std::make_shared<transaction_metadata>(move(pretty_input)) );
fc::variant pretty_output;
abi_serializer::to_variant(*trx_trace_ptr, pretty_output, resolver);
fc::variant pretty_output = db.to_variant_with_abi( *trx_trace_ptr );;
//abi_serializer::to_variant(*trx_trace_ptr, pretty_output, resolver);
return read_write::push_transaction_results{ trx_trace_ptr->id, pretty_output };
}
......@@ -443,6 +449,18 @@ read_only::get_account_results read_only::get_account( const get_account_params&
result.account_name = params.account_name;
const auto& d = db.db();
const auto& rm = db.get_resource_limits_manager();
rm.get_account_limits( result.account_name, result.ram_quota, result.net_weight, result.cpu_weight );
const auto& a = db.get_account(result.account_name);
result.privileged = a.privileged;
result.last_code_update = a.last_code_update;
result.created = a.creation_date;
result.net_limit = rm.get_account_net_limit( result.account_name );
result.cpu_limit = rm.get_account_cpu_limit( result.account_name );
result.ram_usage = rm.get_account_ram_usage( result.account_name );
const auto& permissions = d.get_index<permission_index,by_owner>();
auto perm = permissions.lower_bound( boost::make_tuple( params.account_name ) );
......
......@@ -65,6 +65,12 @@ public:
chain::block_id_type head_block_id;
fc::time_point_sec head_block_time;
account_name head_block_producer;
uint64_t virtual_block_cpu_limit = 0;
uint64_t virtual_block_net_limit = 0;
uint64_t block_cpu_limit = 0;
uint64_t block_net_limit = 0;
//string recent_slots;
//double participation_rate = 0;
};
......@@ -77,6 +83,18 @@ public:
struct get_account_results {
name account_name;
bool privileged = false;
fc::time_point last_code_update;
fc::time_point created;
int64_t ram_quota = 0;
int64_t net_weight = 0;
int64_t cpu_weight = 0;
int64_t net_limit = 0;
int64_t cpu_limit = 0;
int64_t ram_usage = 0;
vector<permission> permissions;
};
......@@ -107,8 +125,6 @@ public:
};
struct abi_json_to_bin_result {
vector<char> binargs;
vector<name> required_scope;
vector<name> required_auth;
};
abi_json_to_bin_result abi_json_to_bin( const abi_json_to_bin_params& params )const;
......@@ -349,7 +365,7 @@ private:
FC_REFLECT( eosio::chain_apis::permission, (perm_name)(parent)(required_auth) )
FC_REFLECT(eosio::chain_apis::empty, )
FC_REFLECT(eosio::chain_apis::read_only::get_info_results,
(server_version)(head_block_num)(last_irreversible_block_num)(last_irreversible_block_id)(head_block_id)(head_block_time)(head_block_producer) )
(server_version)(head_block_num)(last_irreversible_block_num)(last_irreversible_block_id)(head_block_id)(head_block_time)(head_block_producer)(virtual_block_cpu_limit)(virtual_block_net_limit)(block_cpu_limit)(block_net_limit) )
FC_REFLECT(eosio::chain_apis::read_only::get_block_params, (block_num_or_id))
FC_REFLECT( eosio::chain_apis::read_write::push_transaction_results, (transaction_id)(processed) )
......@@ -361,13 +377,13 @@ FC_REFLECT( eosio::chain_apis::read_only::get_currency_balance_params, (code)(ac
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_params, (code)(symbol));
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_result, (supply)(max_supply)(issuer));
FC_REFLECT( eosio::chain_apis::read_only::get_account_results, (account_name)(permissions) )
FC_REFLECT( eosio::chain_apis::read_only::get_account_results, (account_name)(privileged)(last_code_update)(created)(ram_quota)(net_weight)(cpu_weight)(net_limit)(cpu_limit)(ram_usage)(permissions) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_results, (account_name)(code_hash)(wast)(abi) )
FC_REFLECT( eosio::chain_apis::read_only::get_account_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::producer_info, (producer_name) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_params, (code)(action)(args) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_result, (binargs)(required_scope)(required_auth) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_result, (binargs) )
FC_REFLECT( eosio::chain_apis::read_only::abi_bin_to_json_params, (code)(action)(binargs) )
FC_REFLECT( eosio::chain_apis::read_only::abi_bin_to_json_result, (args)(required_scope)(required_auth) )
FC_REFLECT( eosio::chain_apis::read_only::get_required_keys_params, (transaction)(available_keys) )
......
......@@ -48,6 +48,7 @@ void history_api_plugin::plugin_startup() {
app().get_plugin<http_plugin>().add_api({
// CHAIN_RO_CALL(get_transaction),
CHAIN_RO_CALL(get_actions),
CHAIN_RO_CALL(get_transaction),
// CHAIN_RO_CALL(get_key_accounts),
// CHAIN_RO_CALL(get_controlled_accounts)
});
......
......@@ -27,7 +27,10 @@ namespace eosio {
id_type id;
uint64_t action_sequence_num; ///< the sequence number of the relevant action
shared_vector<char> packed_action_trace;
shared_vector<char> packed_action_trace;
uint32_t block_num;
block_timestamp_type block_time;
transaction_id_type trx_id;
};
using account_history_id_type = account_history_object::id_type;
using action_history_id_type = action_history_object::id_type;
......@@ -35,12 +38,19 @@ namespace eosio {
struct by_action_sequence_num;
struct by_account_action_seq;
struct by_trx_id;
using action_history_index = chainbase::shared_multi_index_container<
action_history_object,
indexed_by<
ordered_unique<tag<by_id>, member<action_history_object, action_history_object::id_type, &action_history_object::id>>,
ordered_unique<tag<by_action_sequence_num>, member<action_history_object, uint64_t, &action_history_object::action_sequence_num>>
ordered_unique<tag<by_action_sequence_num>, member<action_history_object, uint64_t, &action_history_object::action_sequence_num>>,
ordered_unique<tag<by_trx_id>,
composite_key< action_history_object,
member<action_history_object, transaction_id_type, &action_history_object::trx_id>,
member<action_history_object, uint64_t, &action_history_object::action_sequence_num >
>
>
>
>;
......@@ -104,32 +114,33 @@ namespace eosio {
if( itr->account == n )
asn = itr->account_sequence_num + 1;
idump((n)(act.receipt.global_sequence)(asn));
//idump((n)(act.receipt.global_sequence)(asn));
const auto& a = db.create<account_history_object>( [&]( auto& aho ) {
aho.account = n;
aho.action_sequence_num = act.receipt.global_sequence;
aho.account_sequence_num = asn;
});
idump((a.account)(a.action_sequence_num)(a.action_sequence_num));
//idump((a.account)(a.action_sequence_num)(a.action_sequence_num));
}
void on_action_trace( const action_trace& at ) {
if( filter( at ) ) {
idump((fc::json::to_pretty_string(at)));
//idump((fc::json::to_pretty_string(at)));
auto& chain = chain_plug->chain();
auto& db = chain.db();
idump((at.receipt.global_sequence));
db.create<action_history_object>( [&]( auto& aho ) {
auto ps = fc::raw::pack_size( at );
aho.packed_action_trace.resize(ps);
datastream<char*> ds( aho.packed_action_trace.data(), ps );
fc::raw::pack( ds, at );
aho.action_sequence_num = at.receipt.global_sequence;
aho.block_num = chain.pending_block_state()->block_num;
aho.block_time = chain.pending_block_time();
aho.trx_id = at.trx_id;
});
auto aset = account_set( at );
idump((aset));
for( auto a : aset ) {
record_account_action( a, at );
}
......@@ -197,7 +208,7 @@ namespace eosio {
auto& chain = history->chain_plug->chain();
const auto& db = chain.db();
const auto& idx = db.get_index<account_history_index, by_account_action_seq>();
const auto& idx = db.get_index<account_history_index, by_account_action_seq>();
int32_t start = 0;
int32_t pos = params.pos ? *params.pos : -1;
......@@ -216,7 +227,7 @@ namespace eosio {
pos = itr->account_sequence_num + 1;
}
if( pos== -1 ) pos = 0xffffff;
if( pos== -1 ) pos = 0xfffffff;
if( offset > 0 ) {
start = pos;
......@@ -237,6 +248,7 @@ namespace eosio {
auto end_time = start_time;
get_actions_result result;
result.last_irreversible_block = chain.last_irreversible_block_num();
while( start_itr != end_itr ) {
const auto& a = db.get<action_history_object, by_action_sequence_num>( start_itr->action_sequence_num );
fc::datastream<const char*> ds( a.packed_action_trace.data(), a.packed_action_trace.size() );
......@@ -245,7 +257,8 @@ namespace eosio {
result.actions.emplace_back( ordered_action_result{
start_itr->action_sequence_num,
start_itr->account_sequence_num,
fc::variant(t)
a.block_num, a.block_time,
chain.to_variant_with_abi(t)
});
end_time = fc::time_point::now();
......@@ -255,10 +268,68 @@ namespace eosio {
}
++start_itr;
}
return result;
}
read_only::get_transaction_result read_only::get_transaction( const read_only::get_transaction_params& p )const {
auto& chain = history->chain_plug->chain();
get_transaction_result result;
result.id = p.id;
result.last_irreversible_block = chain.last_irreversible_block_num();
const auto& db = chain.db();
const auto& idx = db.get_index<action_history_index, by_trx_id>();
auto itr = idx.lower_bound( boost::make_tuple(p.id) );
if( itr == idx.end() ) {
return result;
}
result.id = itr->trx_id;
result.block_num = itr->block_num;
result.block_time = itr->block_time;
if( fc::variant(result.id).as_string().substr(0,8) != fc::variant(p.id).as_string().substr(0,8) )
return result;
while( itr != idx.end() && itr->trx_id == result.id ) {
fc::datastream<const char*> ds( itr->packed_action_trace.data(), itr->packed_action_trace.size() );
action_trace t;
fc::raw::unpack( ds, t );
result.traces.emplace_back( chain.to_variant_with_abi(t) );
++itr;
}
auto blk = chain.fetch_block_by_number( result.block_num );
for( const auto& receipt: blk->transactions ) {
if( receipt.trx.contains<packed_transaction>() ) {
auto& pt = receipt.trx.get<packed_transaction>();
auto mtrx = transaction_metadata(pt);
if( mtrx.id == result.id ) {
fc::mutable_variant_object r( "receipt", receipt );
r( "trx", chain.to_variant_with_abi(mtrx.trx) );
result.trx = move(r);
break;
}
} else {
auto& id = receipt.trx.get<transaction_id_type>();
if( id == result.id ) {
fc::mutable_variant_object r( "receipt", receipt );
result.trx = move(r);
break;
}
}
}
return result;
}
} /// history_apis
} /// namespace eosio
......@@ -49,13 +49,16 @@ class read_only {
};
struct ordered_action_result {
uint64_t global_action_seq = 0;
int32_t account_action_seq = 0;
fc::variant action_trace;
uint64_t global_action_seq = 0;
int32_t account_action_seq = 0;
uint32_t block_num;
chain::block_timestamp_type block_time;
fc::variant action_trace;
};
struct get_actions_result {
vector<ordered_action_result> actions;
uint32_t last_irreversible_block;
optional<bool> time_limit_exceeded_error;
};
......@@ -63,6 +66,24 @@ class read_only {
get_actions_result get_actions( const get_actions_params& )const;
struct get_transaction_params {
transaction_id_type id;
};
struct get_transaction_result {
transaction_id_type id;
fc::variant trx;
chain::block_timestamp_type block_time;
uint32_t block_num = 0;
uint32_t last_irreversible_block = 0;
vector<fc::variant> traces;
};
get_transaction_result get_transaction( const get_transaction_params& )const;
/*
struct ordered_transaction_results {
uint32_t seq_num;
......@@ -128,8 +149,11 @@ class history_plugin : public plugin<history_plugin> {
} /// namespace eosio
FC_REFLECT( eosio::history_apis::read_only::get_actions_params, (account_name)(pos)(offset) )
FC_REFLECT( eosio::history_apis::read_only::get_actions_result, (actions)(time_limit_exceeded_error) )
FC_REFLECT( eosio::history_apis::read_only::ordered_action_result, (global_action_seq)(account_action_seq)(action_trace) )
FC_REFLECT( eosio::history_apis::read_only::get_actions_result, (actions)(last_irreversible_block)(time_limit_exceeded_error) )
FC_REFLECT( eosio::history_apis::read_only::ordered_action_result, (global_action_seq)(account_action_seq)(block_num)(block_time)(action_trace) )
FC_REFLECT( eosio::history_apis::read_only::get_transaction_params, (id) )
FC_REFLECT( eosio::history_apis::read_only::get_transaction_result, (id)(trx)(block_time)(block_num)(last_irreversible_block)(traces) )
/*
FC_REFLECT(eosio::history_apis::read_only::get_transaction_params, (transaction_id) )
FC_REFLECT(eosio::history_apis::read_only::get_transaction_results, (transaction_id)(transaction) )
......
......@@ -883,7 +883,7 @@ namespace eosio {
handshake_initializer::populate(last_handshake_sent);
last_handshake_sent.generation = ++sent_handshake_count;
fc_dlog(logger, "Sending handshake generation ${g} to ${ep}",
("g",last_handshake_sent.generation)("ep", peer_addr));
("g",last_handshake_sent.generation)("ep", peer_name()));
enqueue(last_handshake_sent);
}
......@@ -1416,6 +1416,9 @@ namespace eosio {
if( req.req_blocks.mode == catch_up ) {
c->fork_head = id;
c->fork_head_num = num;
ilog ("got a catch_up notice while in ${s}, fork head num = ${fhn} target LIB = ${lib} next_expected = ${ne}", ("s",stage_str(state))("fhn",num)("lib",sync_known_lib_num)("ne", sync_next_expected_num));
if (state == lib_catchup)
return;
set_state(head_catchup);
}
else {
......@@ -1444,6 +1447,7 @@ namespace eosio {
}
void sync_manager::recv_block (connection_ptr c, const block_id_type &blk_id, uint32_t blk_num, bool accepted) {
fc_dlog(logger," got block ${bn} from ${p}",("bn",blk_num)("p",c->peer_name()));
if (!accepted) {
uint32_t head_num = chain_plug->chain().head_block_num();
if (head_num != last_repeated) {
......@@ -2775,7 +2779,6 @@ namespace eosio {
( "peer-key", bpo::value<vector<string>>()->composing()->multitoken(), "Optional public key of peer allowed to connect. May be used multiple times.")
( "peer-private-key", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Tuple of [PublicKey, WIF private key] (may specify multiple times)")
( "log-level-net-plugin", bpo::value<string>()->default_value("info"), "Log level: one of 'all', 'debug', 'info', 'warn', 'error', or 'off'")
( "max-clients", bpo::value<int>()->default_value(def_max_clients), "Maximum number of clients from which connections are accepted, use 0 for no limit")
( "connection-cleanup-period", bpo::value<int>()->default_value(def_conn_retry_wait), "number of seconds to wait before cleaning up dead connections")
( "network-version-match", bpo::value<bool>()->default_value(false),
......
......@@ -25,9 +25,9 @@ namespace eosio { namespace client { namespace http {
const string history_func_base = "/v1/history";
const string get_actions_func = history_func_base + "/get_actions";
const string get_transaction_func = history_func_base + "/get_transaction";
const string account_history_func_base = "/v1/account_history";
const string get_transaction_func = account_history_func_base + "/get_transaction";
const string get_transactions_func = account_history_func_base + "/get_transactions";
const string get_key_accounts_func = account_history_func_base + "/get_key_accounts";
const string get_controlled_accounts_func = account_history_func_base + "/get_controlled_accounts";
......
此差异已折叠。
......@@ -46,11 +46,11 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/consensus-validation-malicious-produc
#Manually run chain_test for all supported runtimes
#To run chain_test with all log from blockchain displayed, put --verbose after --, i.e. chain_test -- --verbose
add_test(NAME chain_test_binaryen COMMAND chain_test --report_level=detailed --color_output -- --binaryen)
add_test(NAME chain_test_wavm COMMAND chain_test --report_level=detailed --color_output --catch_system_errors=no -- --wavm)
add_test(NAME nodeos_run_test COMMAND tests/nodeos_run_test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME nodeos_run_remote_test COMMAND tests/nodeos_run_remote_test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME p2p_dawn515_test COMMAND tests/p2p_tests/dawn_515/test.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: #add_test(NAME chain_test_binaryen COMMAND chain_test --report_level=detailed --color_output -- --binaryen)
# TODO removed on slim: add_test(NAME chain_test_wavm COMMAND chain_test --report_level=detailed --color_output --catch_system_errors=no -- --wavm)
# TODO removed on slim: add_test(NAME nodeos_run_test COMMAND tests/nodeos_run_test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: add_test(NAME nodeos_run_remote_test COMMAND tests/nodeos_run_remote_test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: add_test(NAME p2p_dawn515_test COMMAND tests/p2p_tests/dawn_515/test.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#if(BUILD_MONGO_DB_PLUGIN)
# add_test(NAME nodeos_run_test-mongodb COMMAND tests/nodeos_run_test.py --mongodb -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#endif()
......@@ -60,9 +60,9 @@ add_test(NAME p2p_dawn515_test COMMAND tests/p2p_tests/dawn_515/test.sh WORKING_
# TODO: add_test(NAME p2p_sync_test_p2_d10 COMMAND tests/p2p_tests/sync/test.sh -p 2 -d 10 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO: add_test(NAME message_storm COMMAND tests/p2p_tests/sync/test.sh -m -p 21 -n 21 -d 5 -l WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO: add_test(NAME trans_sync_across_mixed_cluster_test COMMAND tests/trans_sync_across_mixed_cluster_test.sh -p 1 -n 2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME distributed-transactions-test COMMAND tests/distributed-transactions-test.py -p 1 -n 4 -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME distributed-transactions-remote-test COMMAND tests/distributed-transactions-remote-test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME restart-scenarios-test_resync COMMAND tests/restart-scenarios-test.py -c resync -p4 -v --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: add_test(NAME distributed-transactions-test COMMAND tests/distributed-transactions-test.py -p 1 -n 4 -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: add_test(NAME distributed-transactions-remote-test COMMAND tests/distributed-transactions-remote-test.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO removed on slim: add_test(NAME restart-scenarios-test_resync COMMAND tests/restart-scenarios-test.py -c resync -p4 -v --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# add_test(NAME restart-scenarios-test_replay COMMAND tests/restart-scenarios-test.py -c replay -p4 -v --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO: add_test(NAME consensus-validation-malicious-producers COMMAND tests/consensus-validation-malicious-producers.py -w 80 --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
......
此差异已折叠。
......@@ -1234,8 +1234,6 @@ class Cluster(object):
cmdArr.append("--nogen")
nodeosArgs=""
if Utils.Debug:
nodeosArgs += "--log-level-net-plugin debug"
if not self.walletd:
nodeosArgs += " --plugin eosio::wallet_api_plugin"
if self.enableMongo:
......
......@@ -2,20 +2,69 @@
#include <eosio/chain/webassembly/common.hpp>
// These are handcrafted or otherwise tricky to generate with our tool chain
/*
static const char f32_add_wast[] = R"=====(
static const char call_depth_almost_limit_wast[] = R"=====(
(module
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(type (;0;) (func (param i64)))
(import "env" "printi" (func $printi (type 0)))
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_foo" (func $_foo))
(export "apply" (func $apply))
(func $_foo (param $0 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(call $_foo
(i32.add
(get_local $0)
(i32.const -1)
)
)
)
)
(func $apply (param $a i64) (param $b i64) (param $c i64)
(call $_foo
(i32.const 249)
)
)
)
)=====";
static const char call_depth_limit_wast[] = R"=====(
(module
(type (;0;) (func (param i64)))
(import "env" "printi" (func $printi (type 0)))
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_foo" (func $_foo))
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64)
(call $eosio_assert (i32.eq (i32.trunc_u/f32 (f32.const 0x3f800000)) (i32.const 0x0)) (i32.const 0))
(func $_foo (param $0 i32)
(block $label$0
(br_if $label$0
(i32.eqz
(get_local $0)
)
)
(call $_foo
(i32.add
(get_local $0)
(i32.const -1)
)
)
)
)
(func $apply (param $a i64) (param $b i64) (param $c i64)
(call $_foo
(i32.const 250)
)
)
)
)=====";
*/
static const char aligned_ref_wast[] = R"=====(
(module
......@@ -50,7 +99,6 @@ static const char aligned_ptr_wast[] = R"=====(
)
)=====";
static const char aligned_const_ref_wast[] = R"=====(
(module
(import "env" "sha256" (func $sha256 (param i32 i32 i32)))
......@@ -158,7 +206,6 @@ static const char misaligned_const_ref_wast[] = R"=====(
static const char entry_wast[] = R"=====(
(module
(import "env" "require_auth" (func $require_auth (param i64)))
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(import "env" "now" (func $now (result i32)))
(table 0 anyfunc)
......@@ -176,7 +223,6 @@ static const char entry_wast[] = R"=====(
)
(func $apply (param $0 i64) (param $1 i64) (param $2 i64)
(block
(call $require_auth (i64.const 6121376101093867520))
(call $eosio_assert
(i32.eq
(i32.load offset=4
......
此差异已折叠。
configure_file( eosiocpp.in eosiocpp @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/llvm-gcov.sh ${CMAKE_CURRENT_BINARY_DIR}/llvm-gcov.sh COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ctestwrapper.sh ${CMAKE_CURRENT_BINARY_DIR}/ctestwrapper.sh COPYONLY)
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/eosiocpp DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
PERMISSIONS OWNER_READ
OWNER_WRITE
......
#!/bin/sh
# run ctest, disregard failure code
ctest $@
exit 0
......@@ -31,43 +31,45 @@ add_dependencies(unit_test asserter test_api test_api_mem test_api_db test_api_m
#Manually run chain_test for all supported runtimes
#To run chain_test with all log from blockchain displayed, put --verbose after --, i.e. chain_test -- --verbose
add_test(NAME unit_test_binaryen COMMAND chain_test --report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND chain_test --report_level=detailed --color_output -- --wavm)
add_test(NAME unit_test_binaryen COMMAND unit_test -t \!wasm_tests/weighted_cpu_limit_tests -t \!wasm_tests/cpu_usage_tests -t \!auth_tests/linkauth_special -t \!eosio_system_tests/* -t \!delay_tests/* --report_level=detailed --color_output -- --binaryen)
add_test(NAME unit_test_wavm COMMAND unit_test -t \!wasm_tests/weighted_cpu_limit_tests -t \!wasm_tests/cpu_usage_tests -t \!auth_tests/linkauth_special -t \!eosio_system_tests/* -t \!delay_tests/* --report_level=detailed --color_output -- --wavm)
if(ENABLE_COVERAGE_TESTING)
set(Coverage_NAME ${PROJECT_NAME}_coverage)
set(Coverage_EXECUTABLE unit_test)
set(Coverage_NAME ${PROJECT_NAME}_ut_coverage)
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT LLVMCOV_PATH)
message(FATAL_ERROR "llvm-cov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# no spaces allowed within tests list
set(ctest_tests 'unit_test_binaryen|unit_test_wavm')
set(ctest_exclude_tests '')
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup lcov
COMMAND ${LCOV_PATH} --directory . --zerocounters
# Create baseline to make sure untouched files show up in the report
COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
# Run tests
COMMAND ${Coverage_EXECUTABLE}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
# add baseline counters
COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
COMMAND ./tools/ctestwrapper.sh -R ${ctest_tests} -E ${ctest_exclude_tests}
COMMAND ${LCOV_PATH} --directory . --capture --gcov-tool ./tools/llvm-gcov.sh --output-file ${Coverage_NAME}.info
COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info
COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.info ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
# Show info where to find the report
......
此差异已折叠。
此差异已折叠。
#include <boost/test/unit_test.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <multi_index_test/multi_index_test.wast.hpp>
#include <multi_index_test/multi_index_test.abi.hpp>
......
......@@ -1028,7 +1028,7 @@ BOOST_FIXTURE_TEST_CASE(eosio_abi, TESTER) try {
fc::variant pretty_output;
// verify to_variant works on eos native contract type: newaccount
// see abi_serializer::to_abi()
abi_serializer::to_variant(result, pretty_output, get_resolver());
abi_serializer::to_variant(*result, pretty_output, get_resolver());
BOOST_TEST(fc::json::to_string(pretty_output).find("newaccount") != std::string::npos);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册