Compare commits

...

59 Commits

Author SHA1 Message Date
POA 6e06824c23 Bump to v3.3.3 2022-01-13 12:34:02 +03:00
Rim Rakhimov 43d6da6b52
Made nodes data concatenate as RLP sequences instead of bytes (#598)
* Made nodes data concatenate as RLP sequences instead of bytes

* Add test for getNodeData dispatch

* Update getNodeData to return data for non-aura networks
2022-01-13 12:26:33 +03:00
Rim Rakhimov 412d797a3b
Implements eip-3607 (#593)
* implements eip-3607

* Tests fixed

* Added tests for eip3607 functionality

* rast fmt
2022-01-10 18:01:46 +03:00
Rim Rakhimov 63bab44e3c
Makes eth_mining to return False if not is not allowed to seal (#581) 2021-12-10 14:25:39 +03:00
Rim Rakhimov f703d01f23
Add type field for legacy transactions in RPC calls (#580)
* Added type field for legacy transactions when returned as rpc call response

* Remove invalid test
2021-12-09 11:17:42 +03:00
POA f13fa10b8a Bump to v3.3.2, London on Sokol, effective_gas_price function enhancement 2021-12-07 14:03:48 +03:00
POA 657100cebc Bump to v3.3.1 2021-11-30 10:55:54 +03:00
POA d2d19ec8c2 Merge branch 'dev' of https://github.com/openethereum/openethereum into dev 2021-11-30 10:39:06 +03:00
POA 0f872aff78 Add a bootnode for Kovan 2021-11-30 10:38:55 +03:00
Rim Rakhimov 405738c791
Fix for modexp overflow in debug mode (#578)
* fix modexp overflow panic in debug mode

* Added one more unit test for modexp

* Remove redundant bytes from modexp unit test

Co-authored-by: POA <33550681+poa@users.noreply.github.com>
2021-11-30 10:33:40 +03:00
Rim Rakhimov 3b19a79c37
Adds eth_maxPriorityFeePerGas implementaiton (#570)
* added eth_maxPriorityFeePerGas rpc call

* cargo fmt

* moved block_base_fee implementation into the trait

* added basic test for eth_maxPriorityFeePerGas

* added test for eth_maxPriorityFeePerGas calculation

* Added support for zero-cost transactions

* Added 'eip1559_not_activated' error

* Fixes 'chain::supplier::test::return_nodes' test

* cargo fmt

* cargo fmt

* made calculation of fallback priority fee to ignore zero-cost transactions

* cargo fmt

* made use of 'saturating_sub' instead of minus
2021-11-25 10:43:00 +03:00
POA 64a1614769 Bump to v3.3.0 2021-11-17 12:05:58 +03:00
POA b981f7beef Add validateServiceTransactionsTransition spec option 2021-11-12 13:06:19 +03:00
POA caa210107e Add additional service transactions checking to block verifier 2021-11-10 16:36:17 +03:00
POA 88eb7d3257 Revert eip1559BaseFeeMinValue activation on xDai 2021-11-04 15:50:48 +03:00
POA 73895aae88 Set eip1559BaseFeeMinValue for POA Core 2021-11-02 10:29:54 +03:00
POA c5719983b2 Set eip1559BaseFeeInitialValue equal to eip1559BaseFeeMinValue for xDai 2021-11-02 09:34:53 +03:00
POA f9b2db206a Replace eip1559BaseFeeFixedValue* spec options with eip1559BaseFeeMinValue and eip1559BaseFeeMinValueTransition 2021-11-01 13:20:58 +03:00
POA 437ba9b044 Add eip1559BaseFeeFixedValue and eip1559BaseFeeFixedValueTransition spec options and bump to v3.3.0-rc.14 2021-11-01 10:53:50 +03:00
uink45 98873fc0c0
EIP-4345 enabled (#561)
* EIP-4345 enabled

Co-authored-by: varasev <33550681+varasev@users.noreply.github.com>
2021-11-01 07:14:52 +03:00
POA 415a522429 POA Core London hard fork and bump to v3.3.0-rc.13 2021-10-29 16:37:12 +03:00
POA 646f49fdd0 xDai London hard fork and bump to v3.3.0-rc.12 2021-10-22 09:44:16 +03:00
POA 25ce4b2ec8 Enable GetNodeData for AuRa chains and bump to v3.3.0-rc.11 2021-10-19 11:25:32 +03:00
POA ac30783c82 Add eip1559FeeCollector and eip1559FeeCollectorTransition spec options and bump to v3.3.0-rc.10 2021-10-14 16:14:38 +03:00
POA 5e9b4c58ae Bump to v3.3.0-rc.9 2021-10-04 10:43:30 +03:00
POA 74e709fd55 Fix MinGasPrice config option 2021-09-29 10:30:28 +03:00
POA 3413343caa Add service transactions support for EIP-1559 2021-09-29 10:30:28 +03:00
sunce86 8bb02dd479 small fixes 2021-09-29 10:30:27 +03:00
sunce86 79be8f1ab8 min_gas_price becomes min_effective_priority_fee (effective_gas_price - base_fee) 2021-09-29 10:30:27 +03:00
sunce86 a049baf6b2 added version 4 for TxPermission contract 2021-09-29 10:30:27 +03:00
sunce86 d8305c52ea Bump to v3.3.0-rc.8 2021-09-03 17:07:40 +02:00
Jochen Müller 239d790df1
Ignore GetNodeData requests (#519) 2021-09-03 16:43:33 +02:00
Jochen Müller 298a1a0ecc Bump to v3.3.0-rc.7 2021-08-23 14:03:36 +02:00
Jochen Müller 36b3f125a4
Fix wrong sync packet id (#514) 2021-08-23 13:54:25 +02:00
sunce86 5d9ff63043 Bump to v3.3.0-rc.6 2021-08-04 18:15:06 +02:00
sunce86 01996c8867 Merge branch 'dev' 2021-08-04 18:05:31 +02:00
varasev 2ae294990a
London HF on Kovan (#502)
* London HF on Kovan

Block number 26741100

* Set wasmDisableTransition for Kovan

Co-authored-by: Dusan Stanivukovic <dusan.stanivukovic@gmail.com>
2021-08-03 14:02:37 +02:00
sunce86 745c4bd00c Revert "Sunce86/eip1559 for x dai (#499)"
This reverts commit 87ae05d99e.
2021-08-03 11:49:51 +02:00
sunce86 ecae5f1c47 Revert "Sunce86/eip1559 for x dai (#499)"
This reverts commit 87ae05d99e.
2021-08-03 11:47:01 +02:00
Dusan Stanivukovic 87ae05d99e
Sunce86/eip1559 for x dai (#499)
* added version 4 for TxPermission contract
* min_gas_price becomes min_effective_priority_fee (effective_gas_price - base_fee)
2021-08-02 19:47:39 +02:00
sunce86 a92e5e761b Bump to v3.3.0-rc.4 2021-07-14 21:46:58 +02:00
Dusan Stanivukovic 87603926b5
feeHistory implementation (#484)
* feeHistory implementation

* code review fix
2021-07-14 21:21:28 +02:00
Dusan Stanivukovic eb42f0c5d9
gasPrice is required field for Transaction object (#481) 2021-07-12 15:22:27 +02:00
Dusan Stanivukovic 33908e8361
fix state test for openethereum-evm (#480)
* fix state test for openethereum-evm
2021-07-10 08:53:53 +02:00
Dusan Stanivukovic aa09846200
bump ethereum tests 9.0.3 (#478) 2021-07-09 16:47:02 +02:00
Dusan Stanivukovic 67ab600bc9
Sunce86/effective gas price not omitting (#477)
* no omit effectiveGasPrice in receipts
2021-07-09 14:56:37 +02:00
Dusan Stanivukovic 4b437428bd
define london block at 12965000 (#476)
* define london block at 12965000
2021-07-08 22:55:53 +02:00
Jochen Müller 38e40f649c
Restore GetNodeData (#469)
* Accept GetNodeData requests

* Implement blockchain client method for node data requests

* Reuse old database read methods for node data

* fmt

* Copy & paste old tests...

* ... and make them work

* fmt
2021-07-05 17:06:35 +02:00
Jochen Müller 43ee520904
Implement eth/66 (#467)
* Allow eth/66

* Add eth/66 request ids

* fmt

* Remove some leftovers

* fmt

* Change behaviour in case of missing peer info

- Assume eth/66 protocol, not earlier one
- Log just a trace, not an error
2021-07-05 15:59:22 +02:00
sunce86 fdaee51ca0 Bump to v3.3.0-rc.3 2021-06-29 22:56:40 +02:00
Dusan Stanivukovic eec38b30e3
forcing base fee to zero if gas pricing is omitted (#460)
* forcing base fee to zero if gas pricing is omitted

* force base fee to zero for estimate_gas
2021-06-29 22:17:00 +02:00
Dusan Stanivukovic 287409f9f5
gasPrice substituted with effective_gas_price (#458) 2021-06-28 19:15:37 +02:00
Dusan Stanivukovic e5ae846de4
added effectiveGasPrice to eth_getTransactionReceipt return structure (#450) 2021-06-28 19:04:52 +02:00
Dusan Stanivukovic e6f3794dd4
rust version reverted to 1.52.1 (#454) 2021-06-28 16:39:18 +02:00
sunce86 5920f232d0 Bump to v3.3.0-rc.2 2021-06-17 11:32:11 +02:00
sunce86 3bce814090 Update changelog 2021-06-17 11:31:33 +02:00
sunce86 118051696e Merge branch 'dev' 2021-06-16 21:18:02 +02:00
Dusan Stanivukovic 17057eeedc
bump ethereum tests 9.0.2, revert london mainnet block (#435)
* bump ethereum tests 9.0.2
* london mainnet block removed
2021-06-16 20:48:23 +02:00
Dusan Stanivukovic 7aa1e987de
Update CHANGELOG.md 2021-06-16 09:33:16 +02:00
95 changed files with 2537 additions and 298 deletions

View File

@ -13,7 +13,7 @@ jobs:
platform:
- windows2019 # custom runner
toolchain:
- stable
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources

View File

@ -15,7 +15,7 @@ jobs:
- ubuntu-16.04
- macos-latest
toolchain:
- stable
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources

View File

@ -20,7 +20,7 @@ jobs:
- ubuntu-16.04
- macos-latest
toolchain:
- stable
- 1.52.1
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout sources

View File

@ -15,10 +15,10 @@ jobs:
uses: actions/checkout@main
with:
submodules: true
- name: Install stable toolchain
- name: Install 1.52.1 toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.52.1
profile: minimal
override: true
- name: Run cargo check 1/3

View File

@ -16,7 +16,7 @@ jobs:
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.52.1
profile: minimal
override: true
- name: Deploy to docker hub

View File

@ -17,7 +17,7 @@ jobs:
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.52.1
profile: minimal
override: true
- name: Deploy to docker hub

View File

@ -17,7 +17,7 @@ jobs:
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.52.1
profile: minimal
override: true
- name: Deploy to docker hub

View File

@ -11,7 +11,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
toolchain: 1.52.1
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1

View File

@ -1,4 +1,114 @@
## OpenEthereum v3.3.0-rc.1
## OpenEthereum v3.3.3
Enhancements:
* Implement eip-3607 (#593)
Bug fixes:
* Add type field for legacy transactions in RPC calls (#580)
* Makes eth_mining to return False if not is not allowed to seal (#581)
* Made nodes data concatenate as RLP sequences instead of bytes (#598)
## OpenEthereum v3.3.2
Enhancements:
* London hardfork block: Sokol (24114400)
Bug fixes:
* Fix for maxPriorityFeePerGas overflow
## OpenEthereum v3.3.1
Enhancements:
* Add eth_maxPriorityFeePerGas implementation (#570)
* Add a bootnode for Kovan
Bug fixes:
* Fix for modexp overflow in debug mode (#578)
## OpenEthereum v3.3.0
Enhancements:
* Add `validateServiceTransactionsTransition` spec option to be able to enable additional checking of zero gas price transactions by block verifier
## OpenEthereum v3.3.0-rc.15
* Revert eip1559BaseFeeMinValue activation on xDai at London hardfork block
## OpenEthereum v3.3.0-rc.14
Enhancements:
* Add eip1559BaseFeeMinValue and eip1559BaseFeeMinValueTransition spec options
* Activate eip1559BaseFeeMinValue on xDai at London hardfork block (19040000), set it to 20 GWei
* Activate eip1559BaseFeeMinValue on POA Core at block 24199500 (November 8, 2021), set it to 10 GWei
* Delay difficulty bomb to June 2022 for Ethereum Mainnet (EIP-4345)
## OpenEthereum v3.3.0-rc.13
Enhancements:
* London hardfork block: POA Core (24090200)
## OpenEthereum v3.3.0-rc.12
Enhancements:
* London hardfork block: xDai (19040000)
## OpenEthereum v3.3.0-rc.11
Bug fixes:
* Ignore GetNodeData requests only for non-AuRa chains
## OpenEthereum v3.3.0-rc.10
Enhancements:
* Add eip1559FeeCollector and eip1559FeeCollectorTransition spec options
## OpenEthereum v3.3.0-rc.9
Bug fixes:
* Add service transactions support for EIP-1559
* Fix MinGasPrice config option for POSDAO and EIP-1559
Enhancements:
* min_gas_price becomes min_effective_priority_fee
* added version 4 for TxPermission contract
## OpenEthereum v3.3.0-rc.8
Bug fixes:
* Ignore GetNodeData requests (#519)
## OpenEthereum v3.3.0-rc.7
Bug fixes:
* GetPooledTransactions is sent in invalid form (wrong packet id)
## OpenEthereum v3.3.0-rc.6
Enhancements:
* London hardfork block: kovan (26741100) (#502)
## OpenEthereum v3.3.0-rc.4
Enhancements:
* London hardfork block: mainnet (12,965,000) (#475)
* Support for eth/66 protocol version (#465)
* Bump ethereum/tests to v9.0.3
* Add eth_feeHistory
Bug fixes:
* GetNodeData from eth63 is missing (#466)
* Effective gas price not omitting (#477)
* London support in openethereum-evm (#479)
* gasPrice is required field for Transaction object (#481)
## OpenEthereum v3.3.0-rc.3
Bug fixes:
* Add effective_gas_price to eth_getTransactionReceipt #445 (#450)
* Update eth_gasPrice to support EIP-1559 #449 (#458)
* eth_estimateGas returns "Requires higher than upper limit of X" after London Ropsten Hard Fork #459 (#460)
## OpenEthereum v3.3.0-rc.2
Enhancements:
* EIP-1559: Fee market change for ETH 1.0 chain
@ -6,9 +116,9 @@ Enhancements:
* EIP-3529: Reduction in gas refunds
* EIP-3541: Reject new contracts starting with the 0xEF byte
* Delay difficulty bomb to December 2021 (EIP-3554)
* London hardfork blocks: mainnet (12,833,000), goerli (5,062,605), rinkeby (8,897,988), ropsten (10,499,401)
* London hardfork blocks: goerli (5,062,605), rinkeby (8,897,988), ropsten (10,499,401)
* Add chainspecs for aleut and baikal
* Bump ethereum/tests to v9.0.1
* Bump ethereum/tests to v9.0.2
## OpenEthereum v3.2.6

6
Cargo.lock generated
View File

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.14.1"
@ -2930,7 +2932,7 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "openethereum"
version = "3.3.0-rc.1"
version = "3.3.3"
dependencies = [
"ansi_term 0.10.2",
"atty",
@ -3280,7 +3282,7 @@ dependencies = [
[[package]]
name = "parity-version"
version = "3.3.0-rc.1"
version = "3.3.3"
dependencies = [
"parity-bytes",
"rlp",

View File

@ -2,7 +2,7 @@
description = "OpenEthereum"
name = "openethereum"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "3.3.0-rc.1"
version = "3.3.3"
license = "GPL-3.0"
authors = [
"OpenEthereum developers",

View File

@ -46,6 +46,8 @@ use bytes::Bytes;
use docopt::Docopt;
use ethcore::{json_tests, spec, TrieSpec};
use ethereum_types::{Address, U256};
use ethjson::spec::ForkSpec;
use evm::EnvInfo;
use rustc_hex::FromHex;
use std::{fmt, fs, path::PathBuf, sync::Arc};
use vm::{ActionParams, CallType};
@ -192,10 +194,18 @@ fn run_state_test(args: Args) {
}
let multitransaction = test.transaction;
let env_info = test.env.into();
let env_info: EnvInfo = test.env.into();
let pre = test.pre_state.into();
for (spec, states) in test.post_states {
//hardcode base fee for part of the london tests, that miss base fee field in env
let mut test_env = env_info.clone();
if spec >= ForkSpec::London {
if test_env.base_fee.is_none() {
test_env.base_fee = Some(0x0a.into());
}
}
if let Some(false) = only_chain
.as_ref()
.map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain)
@ -218,7 +228,7 @@ fn run_state_test(args: Args) {
&spec,
&pre,
post_root,
&env_info,
&test_env,
transaction,
display::json::Informant::new(config),
trie_spec,
@ -231,7 +241,7 @@ fn run_state_test(args: Args) {
&spec,
&pre,
post_root,
&env_info,
&test_env,
transaction,
display::std_json::Informant::err_only(config),
trie_spec,
@ -243,7 +253,7 @@ fn run_state_test(args: Args) {
&spec,
&pre,
post_root,
&env_info,
&test_env,
transaction,
display::std_json::Informant::out_only(config),
trie_spec,
@ -255,7 +265,7 @@ fn run_state_test(args: Args) {
&spec,
&pre,
post_root,
&env_info,
&test_env,
transaction,
display::std_json::Informant::new_default(config),
trie_spec,
@ -268,7 +278,7 @@ fn run_state_test(args: Args) {
&spec,
&pre,
post_root,
&env_info,
&test_env,
transaction,
display::simple::Informant::new(config),
trie_spec,

View File

@ -680,7 +680,7 @@ usage! {
ARG arg_min_gas_price: (Option<u64>) = None, or |c: &Config| c.mining.as_ref()?.min_gas_price.clone(),
"--min-gas-price=[STRING]",
"Minimum amount of Wei per GAS to be paid for a transaction to be accepted for mining. Overrides --usd-per-tx.",
"Minimum amount of Wei per GAS to be paid for a transaction on top of base fee, to be accepted for mining. Overrides --usd-per-tx.",
ARG arg_gas_price_percentile: (usize) = 50usize, or |c: &Config| c.mining.as_ref()?.gas_price_percentile,
"--gas-price-percentile=[PCT]",

View File

@ -640,7 +640,7 @@ impl Configuration {
fn pool_verification_options(&self) -> Result<pool::verifier::Options, String> {
Ok(pool::verifier::Options {
// NOTE min_gas_price,block_gas_limit and block_base_fee will be overwritten right after start.
// NOTE min_gas_price,block_gas_limit block_base_fee, and allow_non_eoa_sender will be overwritten right after start.
minimal_gas_price: U256::from(20_000_000) * 1_000u32,
block_gas_limit: U256::max_value(),
block_base_fee: None,
@ -649,6 +649,7 @@ impl Configuration {
None => U256::max_value(),
},
no_early_reject: self.args.flag_tx_queue_no_early_reject,
allow_non_eoa_sender: false,
})
}

View File

@ -370,7 +370,14 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<RunningClient
let base_fee = client
.engine()
.calculate_base_fee(&client.best_block_header());
miner.update_transaction_queue_limits(*client.best_block_header().gas_limit(), base_fee);
let allow_non_eoa_sender = client
.engine()
.allow_non_eoa_sender(client.best_block_header().number() + 1);
miner.update_transaction_queue_limits(
*client.best_block_header().gas_limit(),
base_fee,
allow_non_eoa_sender,
);
let connection_filter = connection_filter_address.map(|a| {
Arc::new(NodeFilter::new(

View File

@ -32,6 +32,8 @@ pub struct AccountDetails {
pub nonce: U256,
/// Current account balance
pub balance: U256,
/// Code hash associated with an account if any
pub code_hash: Option<H256>,
/// Is this account a local account?
pub is_local: bool,
}

View File

@ -188,6 +188,10 @@ impl txpool::VerifiedTransaction for VerifiedTransaction {
fn sender(&self) -> &Address {
&self.sender
}
fn has_zero_gas_price(&self) -> bool {
self.transaction.has_zero_gas_price()
}
}
impl ScoredTransaction for VerifiedTransaction {

View File

@ -656,11 +656,30 @@ impl TransactionQueue {
/// Returns gas price of currently the worst transaction in the pool.
pub fn current_worst_gas_price(&self) -> U256 {
match self.pool.read().worst_transaction() {
Some(tx) => tx.signed().tx().gas_price,
None => self.options.read().minimal_gas_price,
Some(tx) => tx
.signed()
.effective_gas_price(self.options.read().block_base_fee),
None => {
self.options.read().minimal_gas_price
+ self.options.read().block_base_fee.unwrap_or_default()
}
}
}
/// Returns effective priority fee gas price of currently the worst transaction in the pool.
/// If the worst transaction has zero gas price, the minimal gas price is returned.
pub fn current_worst_effective_priority_fee(&self) -> U256 {
self.pool
.read()
.worst_transaction()
.filter(|tx| !tx.signed().has_zero_gas_price())
.map(|tx| {
tx.signed()
.effective_priority_fee(self.options.read().block_base_fee)
})
.unwrap_or(self.options.read().minimal_gas_price)
}
/// Returns a status of the queue.
pub fn status(&self) -> Status {
let pool = self.pool.read();

View File

@ -43,6 +43,7 @@ impl Default for TestClient {
account_details: AccountDetails {
nonce: 123.into(),
balance: 63_100.into(),
code_hash: None,
is_local: false,
},
gas_required: 21_000.into(),
@ -68,6 +69,11 @@ impl TestClient {
self
}
pub fn with_code_hash<T: Into<H256>>(mut self, code_hash: T) -> Self {
self.account_details.code_hash = Some(code_hash.into());
self
}
pub fn with_gas_required<T: Into<U256>>(mut self, gas_required: T) -> Self {
self.gas_required = gas_required.into();
self

View File

@ -15,6 +15,7 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::U256;
use hash::KECCAK_EMPTY;
use txpool;
use types::transaction::{self, PendingTransaction};
@ -47,6 +48,7 @@ fn new_queue() -> TransactionQueue {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
)
@ -66,6 +68,7 @@ fn should_return_correct_nonces_when_dropped_because_of_limit() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -127,6 +130,7 @@ fn should_never_drop_local_transactions_from_different_senders() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -272,6 +276,71 @@ fn should_import_transaction_below_min_gas_price_threshold_if_local() {
assert_eq!(txq.status().status.transaction_count, 1);
}
#[test]
fn should_reject_transaction_from_non_eoa_if_non_eoa_sender_is_not_allowed() {
// given
let txq = new_queue();
let tx = Tx::default();
let code_hash = [
0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f,
0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a,
0x0f, 0x0e,
];
// when
let res = txq.import(
TestClient::new().with_code_hash(code_hash),
vec![tx.signed().unverified()],
);
// then
assert_eq!(res, vec![Err(transaction::Error::SenderIsNotEOA)]);
assert_eq!(txq.status().status.transaction_count, 0);
}
#[test]
fn should_import_transaction_from_non_eoa_if_non_eoa_sender_is_allowed() {
// given
let txq = new_queue();
let tx = Tx::default();
let code_hash = [
0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f,
0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a, 0x0f, 0x0e, 0x0c, 0x0a,
0x0f, 0x0e,
];
txq.set_verifier_options(verifier::Options {
allow_non_eoa_sender: true,
..Default::default()
});
// when
let res = txq.import(
TestClient::new().with_code_hash(code_hash),
vec![tx.signed().unverified()],
);
// then
assert_eq!(res, vec![Ok(())]);
assert_eq!(txq.status().status.transaction_count, 1);
}
#[test]
fn should_import_transaction_if_account_code_hash_is_keccak_empty() {
// given
let txq = new_queue();
let tx = Tx::default();
// when
let res = txq.import(
TestClient::new().with_code_hash(KECCAK_EMPTY),
vec![tx.signed().unverified()],
);
// then
assert_eq!(res, vec![Ok(())]);
assert_eq!(txq.status().status.transaction_count, 1);
}
#[test]
fn should_import_txs_from_same_sender() {
// given
@ -545,6 +614,7 @@ fn should_prefer_current_transactions_when_hitting_the_limit() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -1043,6 +1113,7 @@ fn should_include_local_transaction_to_a_full_pool() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -1076,6 +1147,7 @@ fn should_avoid_verifying_transaction_already_in_pool() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -1112,6 +1184,7 @@ fn should_avoid_reverifying_recently_rejected_transactions() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -1161,6 +1234,7 @@ fn should_reject_early_in_case_gas_price_is_less_than_min_effective() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);
@ -1204,6 +1278,7 @@ fn should_not_reject_early_in_case_gas_price_is_less_than_min_effective() {
tx_gas_limit: 1_000_000.into(),
no_early_reject: true,
block_base_fee: None,
allow_non_eoa_sender: false,
},
PrioritizationStrategy::GasPriceOnly,
);

View File

@ -31,6 +31,7 @@ use std::{
};
use ethereum_types::{H256, U256};
use hash::KECCAK_EMPTY;
use txpool;
use types::transaction;
@ -42,7 +43,7 @@ use super::{
/// Verification options.
#[derive(Debug, Clone, PartialEq)]
pub struct Options {
/// Minimal allowed gas price.
/// Minimal allowed gas price (actually minimal block producer reward = effective_priority_fee).
pub minimal_gas_price: U256,
/// Current block gas limit.
pub block_gas_limit: U256,
@ -52,6 +53,8 @@ pub struct Options {
pub tx_gas_limit: U256,
/// Skip checks for early rejection, to make sure that local transactions are always imported.
pub no_early_reject: bool,
/// Accept transactions from non EOAs (see EIP-3607)
pub allow_non_eoa_sender: bool,
}
#[cfg(test)]
@ -63,6 +66,7 @@ impl Default for Options {
block_base_fee: None,
tx_gas_limit: U256::max_value(),
no_early_reject: false,
allow_non_eoa_sender: false,
}
}
}
@ -122,6 +126,24 @@ impl Transaction {
}
}
/// Return effective fee - part of the transaction fee that goes to the miner
pub fn effective_priority_fee(&self, block_base_fee: Option<U256>) -> U256 {
match *self {
Transaction::Unverified(ref tx) => tx.effective_priority_fee(block_base_fee),
Transaction::Retracted(ref tx) => tx.effective_priority_fee(block_base_fee),
Transaction::Local(ref tx) => tx.effective_priority_fee(block_base_fee),
}
}
/// Check if transaction has zero gas price
pub fn has_zero_gas_price(&self) -> bool {
match *self {
Transaction::Unverified(ref tx) => tx.has_zero_gas_price(),
Transaction::Retracted(ref tx) => tx.has_zero_gas_price(),
Transaction::Local(ref tx) => tx.has_zero_gas_price(),
}
}
fn transaction(&self) -> &transaction::TypedTransaction {
match *self {
Transaction::Unverified(ref tx) => &*tx,
@ -225,24 +247,26 @@ impl<C: Client> txpool::Verifier<Transaction>
}
let is_own = tx.is_local();
let gas_price = tx.effective_gas_price(self.options.block_base_fee);
let has_zero_gas_price = tx.has_zero_gas_price();
// Quick exit for non-service and non-local transactions
//
// We're checking if the transaction is below configured minimal gas price
// or the effective minimal gas price in case the pool is full.
if !gas_price.is_zero() && !is_own {
if gas_price < self.options.minimal_gas_price {
if !has_zero_gas_price && !is_own {
let effective_priority_fee = tx.effective_priority_fee(self.options.block_base_fee);
if effective_priority_fee < self.options.minimal_gas_price {
trace!(
target: "txqueue",
"[{:?}] Rejected tx below minimal gas price threshold: {} < {}",
hash,
gas_price,
effective_priority_fee,
self.options.minimal_gas_price,
);
bail!(transaction::Error::InsufficientGasPrice {
minimal: self.options.minimal_gas_price,
got: gas_price,
got: effective_priority_fee,
});
}
@ -252,7 +276,7 @@ impl<C: Client> txpool::Verifier<Transaction>
target: "txqueue",
"[{:?}] Rejected tx early, cause it doesn't have any chance to get to the pool: (gas price: {} < {})",
hash,
gas_price,
tx.effective_gas_price(self.options.block_base_fee),
vtx.transaction.effective_gas_price(self.options.block_base_fee),
);
return Err(transaction::Error::TooCheapToReplace {
@ -260,7 +284,7 @@ impl<C: Client> txpool::Verifier<Transaction>
vtx.transaction
.effective_gas_price(self.options.block_base_fee),
),
new: Some(gas_price),
new: Some(tx.effective_gas_price(self.options.block_base_fee)),
});
}
}
@ -297,9 +321,24 @@ impl<C: Client> txpool::Verifier<Transaction>
let sender = transaction.sender();
let account_details = self.client.account_details(&sender);
let gas_price = transaction.tx().gas_price;
if !self.options.allow_non_eoa_sender {
if let Some(code_hash) = account_details.code_hash {
if code_hash != KECCAK_EMPTY {
debug!(
target: "txqueue",
"[{:?}] Rejected tx, sender is not an EOA: {}",
hash,
code_hash
);
bail!(transaction::Error::SenderIsNotEOA);
}
}
}
if gas_price < self.options.minimal_gas_price {
let effective_priority_fee =
transaction.effective_priority_fee(self.options.block_base_fee);
if effective_priority_fee < self.options.minimal_gas_price {
let transaction_type = self.client.transaction_type(&transaction);
if let TransactionType::Service = transaction_type {
debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash);
@ -310,16 +349,18 @@ impl<C: Client> txpool::Verifier<Transaction>
target: "txqueue",
"[{:?}] Rejected tx below minimal gas price threshold: {} < {}",
hash,
gas_price,
effective_priority_fee,
self.options.minimal_gas_price,
);
bail!(transaction::Error::InsufficientGasPrice {
minimal: self.options.minimal_gas_price,
got: gas_price,
got: effective_priority_fee,
});
}
}
let gas_price = transaction.tx().gas_price;
if gas_price < transaction.max_priority_fee_per_gas() {
bail!(transaction::Error::InsufficientGasPrice {
minimal: transaction.max_priority_fee_per_gas(),

View File

@ -45,7 +45,7 @@ impl ServiceTransactionChecker {
) -> Result<bool, String> {
let sender = tx.sender();
// Skip checking the contract if the transaction does not have zero gas price
if !tx.tx().gas_price.is_zero() {
if !tx.has_zero_gas_price() {
return Ok(false);
}
@ -72,7 +72,7 @@ impl ServiceTransactionChecker {
SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(),
BlockId::Latest,
)
.ok_or_else(|| "contract is not configured")?;
.ok_or_else(|| "Certifier contract is not configured")?;
self.call_contract(client, contract_address, sender)
.and_then(|allowed| {
if let Some(mut cache) = self.certified_addresses_cache.try_write() {

View File

@ -25,12 +25,14 @@ use std::{
use super::{
error_key_already_exists, error_negatively_reference_hash, memory_db::*, LATEST_ERA_KEY,
};
use bytes::Bytes;
use ethcore_db::{DBTransaction, DBValue, KeyValueDB};
use ethereum_types::H256;
use hash_db::HashDB;
use keccak_hasher::KeccakHasher;
use rlp::{decode, encode};
use traits::JournalDB;
use DB_PREFIX_LEN;
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay
/// and latent-removal semantics.
@ -214,12 +216,19 @@ impl JournalDB for ArchiveDB {
fn consolidate(&mut self, with: MemoryDB<KeccakHasher, DBValue>) {
self.overlay.consolidate(with);
}
fn state(&self, id: &H256) -> Option<Bytes> {
self.backing
.get_by_prefix(self.column, &id[0..DB_PREFIX_LEN])
.map(|b| b.into_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
use ethcore_db::InMemoryWithMetrics;
use hash_db::HashDB;
use keccak::keccak;
use JournalDB;
@ -497,4 +506,22 @@ mod tests {
assert!(jdb.get(&key).is_none());
}
#[test]
fn returns_state() {
let shared_db = Arc::new(InMemoryWithMetrics::create(0));
let key = {
let mut jdb = ArchiveDB::new(shared_db.clone(), None);
let key = jdb.insert(b"foo");
jdb.commit_batch(0, &keccak(b"0"), None).unwrap();
key
};
{
let jdb = ArchiveDB::new(shared_db, None);
let state = jdb.state(&key);
assert!(state.is_some());
}
}
}

View File

@ -35,6 +35,7 @@ use parity_util_mem::MallocSizeOf;
use parking_lot::RwLock;
use rlp::{decode, encode};
use util::{DatabaseKey, DatabaseValueRef, DatabaseValueView};
use DB_PREFIX_LEN;
#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)]
struct RefInfo {
@ -608,6 +609,12 @@ impl JournalDB for EarlyMergeDB {
fn consolidate(&mut self, with: MemoryDB<KeccakHasher, DBValue>) {
self.overlay.consolidate(with);
}
fn state(&self, id: &H256) -> Option<Bytes> {
self.backing
.get_by_prefix(self.column, &id[0..DB_PREFIX_LEN])
.map(|b| b.into_vec())
}
}
#[cfg(test)]

View File

@ -23,6 +23,7 @@ use std::{
};
use super::{error_negatively_reference_hash, JournalDB, DB_PREFIX_LEN, LATEST_ERA_KEY};
use bytes::Bytes;
use ethcore_db::{DBTransaction, DBValue, KeyValueDB};
use ethereum_types::H256;
use fastmap::H256FastMap;
@ -509,6 +510,26 @@ impl JournalDB for OverlayRecentDB {
fn consolidate(&mut self, with: MemoryDB<KeccakHasher, DBValue>) {
self.transaction_overlay.consolidate(with);
}
fn state(&self, key: &H256) -> Option<Bytes> {
let journal_overlay = self.journal_overlay.read();
let key = to_short_key(key);
journal_overlay
.backing_overlay
.get(&key)
.map(|v| v.into_vec())
.or_else(|| {
journal_overlay
.pending_overlay
.get(&key)
.map(|d| d.clone().into_vec())
})
.or_else(|| {
self.backing
.get_by_prefix(self.column, &key[0..DB_PREFIX_LEN])
.map(|b| b.into_vec())
})
}
}
impl HashDB<KeccakHasher, DBValue> for OverlayRecentDB {

View File

@ -23,6 +23,7 @@ use std::{
};
use super::{traits::JournalDB, LATEST_ERA_KEY};
use bytes::Bytes;
use ethcore_db::{DBTransaction, DBValue, KeyValueDB};
use ethereum_types::H256;
use hash_db::HashDB;
@ -32,6 +33,7 @@ use overlaydb::OverlayDB;
use parity_util_mem::{allocators::new_malloc_size_ops, MallocSizeOf};
use rlp::{decode, encode};
use util::{DatabaseKey, DatabaseValueRef, DatabaseValueView};
use DB_PREFIX_LEN;
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay
/// and latent-removal semantics.
@ -245,6 +247,12 @@ impl JournalDB for RefCountedDB {
}
}
}
fn state(&self, id: &H256) -> Option<Bytes> {
self.backing
.get_by_prefix(self.column, &id[0..DB_PREFIX_LEN])
.map(|b| b.into_vec())
}
}
#[cfg(test)]

View File

@ -18,6 +18,7 @@
use std::{io, sync::Arc};
use bytes::Bytes;
use ethcore_db::{DBTransaction, DBValue, KeyValueDB};
use ethereum_types::H256;
use hash_db::{AsHashDB, HashDB};
@ -95,6 +96,9 @@ pub trait JournalDB: KeyedHashDB {
/// Consolidate all the insertions and deletions in the given memory overlay.
fn consolidate(&mut self, overlay: ::memory_db::MemoryDB<KeccakHasher, DBValue>);
/// State data query
fn state(&self, id: &H256) -> Option<Bytes>;
/// Commit all changes in a single batch
#[cfg(test)]
fn commit_batch(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> io::Result<u32> {

View File

@ -138,7 +138,8 @@
"0x42ae50": "0x2dc6c0",
"0x6f1580": "0x1e8480",
"0x8c6180": "0x3d0900",
"0xc3d0e8": "0xaae60"
"0xc5d488": "0xaae60",
"0xd228c8": "0xf4240"
}
}
}
@ -173,10 +174,10 @@
"eip2028Transition": "0x8a61c8",
"eip2929Transition": "0xbad420",
"eip2930Transition": "0xbad420",
"eip1559Transition": "0xc3d0e8",
"eip3198Transition": "0xc3d0e8",
"eip3541Transition": "0xc3d0e8",
"eip3529Transition": "0xc3d0e8",
"eip1559Transition": "0xc5d488",
"eip3198Transition": "0xc5d488",
"eip3541Transition": "0xc5d488",
"eip3529Transition": "0xc5d488",
"eip1559BaseFeeMaxChangeDenominator": "0x8",
"eip1559ElasticityMultiplier": "0x2",
"eip1559BaseFeeInitialValue": "0x3B9ACA00"

View File

@ -59,6 +59,7 @@
"eip214Transition": "0x4d50f8",
"eip658Transition": "0x4d50f8",
"wasmActivationTransition": "0x64b540",
"wasmDisableTransition": "0x198096c",
"eip145Transition": "0x8c6180",
"eip1014Transition": "0x8c6180",
"eip1052Transition": "0x8c6180",
@ -71,6 +72,13 @@
"eip2028Transition": "0xd751a5",
"eip2929Transition": "0x179f954",
"eip2930Transition": "0x179f954",
"eip1559Transition": "0x198096c",
"eip3198Transition": "0x198096c",
"eip3541Transition": "0x198096c",
"eip3529Transition": "0x198096c",
"eip1559BaseFeeMaxChangeDenominator": "0x8",
"eip1559ElasticityMultiplier": "0x2",
"eip1559BaseFeeInitialValue": "0x3B9ACA00",
"kip4Transition": "0x8c6180",
"kip6Transition": "0x8c6180"
},
@ -257,6 +265,7 @@
},
"nodes": [
"enode://30499bde23362f7d310a34518a2a6ff765921870bf0c3e63d21153cfa7ba9cf39cc7c8e54e9dad2f2b3c07288b3e91b220656833cc2d843a54875c229f3f959a@8.9.8.175:30303",
"enode://30499bde23362f7d310a34518a2a6ff765921870bf0c3e63d21153cfa7ba9cf39cc7c8e54e9dad2f2b3c07288b3e91b220656833cc2d843a54875c229f3f959a@45.33.77.29:30303",
"enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303",
"enode://49a0e1aa38caa12cbf31222cb4e31cef1e8794cb4dd38012f84498ac867b19584e29bbf6d53201d7dfd3b5eb0998a4d908d096ed4ddb5f9102c623852cd331ec@54.87.247.5:30303"
]

View File

@ -47,7 +47,18 @@
"eip1884Transition": 12598600,
"eip2028Transition": 12598600,
"eip2929Transition": 21364900,
"eip2930Transition": 21364900
"eip2930Transition": 21364900,
"eip3198Transition": 24090200,
"eip3529Transition": 24090200,
"eip3541Transition": 24090200,
"eip1559Transition": 24090200,
"eip1559BaseFeeMaxChangeDenominator": "0x8",
"eip1559ElasticityMultiplier": "0x2",
"eip1559BaseFeeInitialValue": "0x3b9aca00",
"eip1559BaseFeeMinValue": "0x2540be400",
"eip1559BaseFeeMinValueTransition": 24199500,
"eip1559FeeCollector": "0x517F3AcfF3aFC2fb45e574718bca6F919b798e10",
"eip1559FeeCollectorTransition": 24090200
},
"genesis": {
"seal": {

View File

@ -52,7 +52,16 @@
"eip1884Transition": 12095200,
"eip2028Transition": 12095200,
"eip2929Transition": 21050600,
"eip2930Transition": 21050600
"eip2930Transition": 21050600,
"eip3198Transition": 24114400,
"eip3529Transition": 24114400,
"eip3541Transition": 24114400,
"eip1559Transition": 24114400,
"eip1559BaseFeeMaxChangeDenominator": "0x8",
"eip1559ElasticityMultiplier": "0x2",
"eip1559BaseFeeInitialValue": "0x3b9aca00",
"eip1559FeeCollector": "0xE8DDc5c7A2d2F0D7a9798459c0104fDf5E987ACA",
"eip1559FeeCollectorTransition": 24114400
},
"genesis": {
"seal": {

View File

@ -0,0 +1,65 @@
pragma solidity ^0.4.20;
// Adapted from https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
// and: https://github.com/poanetwork/posdao-contracts/blob/master/contracts/TxPermission.sol
contract TxPermission {
/// Allowed transaction types mask
uint32 constant None = 0;
uint32 constant All = 0xffffffff;
uint32 constant Basic = 0x01;
uint32 constant Call = 0x02;
uint32 constant Create = 0x04;
uint32 constant Private = 0x08;
/// Contract name
function contractName() public constant returns (string) {
return "TX_PERMISSION_CONTRACT";
}
/// Contract name hash
function contractNameHash() public constant returns (bytes32) {
return keccak256(contractName());
}
/// Contract version
function contractVersion() public constant returns (uint256) {
return 4;
}
/// @dev Defines the allowed transaction types which may be initiated by the specified sender with
/// the specified gas price and data. Used by node's engine each time a transaction is about to be
/// included into a block. See https://openethereum.github.io/Permissioning.html#how-it-works-1
/// @param _sender Transaction sender address.
/// @param _to Transaction recipient address. If creating a contract, the `_to` address is zero.
/// @param _value Transaction amount in wei.
/// @param _maxFeePerGas The `maxFeePerGas` in Wei for EIP-1559 transaction, or gas price for a legacy transaction.
/// @param _maxInclusionFeePerGas The `maxInclusionFeePerGas` in Wei for EIP-1559 transaction.
/// Equals to gas price for a legacy transaction.
/// @param _gasLimit Gas limit for the transaction.
/// @param _data Transaction data.
/// @return `uint32 typesMask` - Set of allowed transactions for `_sender` depending on tx `_to` address,
/// `_gasPrice`, and `_data`. The result is represented as a set of flags:
/// 0x01 - basic transaction (e.g. ether transferring to user wallet);
/// 0x02 - contract call;
/// 0x04 - contract creation;
/// 0x08 - private transaction.
/// `bool cache` - If `true` is returned, the same permissions will be applied from the same
/// `_sender` without calling this contract again.
function allowedTxTypes(
address _sender,
address _to,
uint256 _value,
uint256 _maxFeePerGas, // equals to gasPrice for legacy transactions
uint256 _maxInclusionFeePerGas, // equals to gasPrice for legacy transactions
uint256 _gasLimit,
bytes memory _data
)
public
view
returns(uint32 typesMask, bool cache)
{
if (_maxFeePerGas > 0 || _data.length < 4) return (All, false);
return (None, false);
}
}

View File

@ -0,0 +1,44 @@
{
"name": "TestNodeFilterContract",
"engine": {
"authorityRound": {
"params": {
"stepDuration": 1,
"startStep": 2,
"validators": {
"contract": "0x0000000000000000000000000000000000000000"
}
}
}
},
"params": {
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x69",
"gasLimitBoundDivisor": "0x0400",
"transactionPermissionContract": "0xAB5b100cf7C8deFB3c8f3C48474223997A50fB13",
"transactionPermissionContractTransition": "1"
},
"genesis": {
"seal": {
"generic": "0xc180"
},
"difficulty": "0x20000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x222222"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0xAB5b100cf7C8deFB3c8f3C48474223997A50fB13": {
"balance": "1",
"constructor": "608060405234801561001057600080fd5b50610370806100206000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063469ab1e31461006757806375d0c0dc1461009a578063a0a8e4601461012a578063e4e3b5e514610155575b600080fd5b34801561007357600080fd5b5061007c610251565b60405180826000191660001916815260200191505060405180910390f35b3480156100a657600080fd5b506100af6102c2565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100ef5780820151818401526020810190506100d4565b50505050905090810190601f16801561011c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561013657600080fd5b5061013f6102ff565b6040518082815260200191505060405180910390f35b34801561016157600080fd5b50610224600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610308565b604051808363ffffffff1663ffffffff168152602001821515151581526020019250505060405180910390f35b600061025b6102c2565b6040518082805190602001908083835b602083101515610290578051825260208201915060208101905060208303925061026b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905090565b60606040805190810160405280601681526020017f54585f5045524d495353494f4e5f434f4e545241435400000000000000000000815250905090565b60006004905090565b600080600086118061031b575060048351105b156103305763ffffffff600091509150610338565b600080915091505b975097955050505050505600a165627a7a72305820592b45ee74cc856b6c84f99ed8ddd4790d844a9065a07c7bbfc5dfa05cc394c50029"
}
}
}

View File

@ -0,0 +1,47 @@
{
"name": "Morden",
"engine": {
"null": {
"params": {}
}
},
"params": {
"gasLimitBoundDivisor": "0x0400",
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2",
"registrar" : "0x0000000000000000000000000000000000001337",
"eip140Transition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip658Transition": "0x0",
"eip3607Transition": "0x2"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x00006d6f7264656e",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
}
},
"difficulty": "0x20000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" },
"0x71562b71999873DB5b286dF957af199Ec94617F7": {
"balance": "1000000000000000000",
"nonce": "0",
"code": "0xB0B0FACE"
}
}
}

View File

@ -56,6 +56,15 @@
"eip2028Transition": 7298030,
"eip2929Transition": 16101500,
"eip2930Transition": 16101500,
"eip3198Transition": 19040000,
"eip3529Transition": 19040000,
"eip3541Transition": 19040000,
"eip1559Transition": 19040000,
"eip1559BaseFeeMaxChangeDenominator": "0x8",
"eip1559ElasticityMultiplier": "0x2",
"eip1559BaseFeeInitialValue": "0x3b9aca00",
"eip1559FeeCollector": "0x6BBe78ee9e474842Dbd4AB4987b3CeFE88426A92",
"eip1559FeeCollectorTransition": 19040000,
"registrar": "0x6B53721D4f2Fb9514B85f5C49b197D857e36Cf03",
"transactionPermissionContract": "0x7Dd7032AA75A37ea0b150f57F899119C7379A78b",
"transactionPermissionContractTransition": 9186425

View File

@ -0,0 +1,91 @@
[
{
"constant": true,
"inputs": [],
"name": "contractNameHash",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "contractName",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "contractVersion",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "sender",
"type": "address"
},
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "maxFeePerGas",
"type": "uint256"
},
{
"name": "maxPriorityFeePerGas",
"type": "uint256"
},
{
"name": "gasLimit",
"type": "uint256"
},
{
"name": "data",
"type": "bytes"
}
],
"name": "allowedTxTypes",
"outputs": [
{
"name": "",
"type": "uint32"
},
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

@ -1 +1 @@
Subproject commit fa0ab110f3f45d1f6786f978ea596a18ecbe8275
Subproject commit e20d7f39aae1e33394ae6b94590d15083e224fa5

View File

@ -488,6 +488,52 @@ impl Importer {
.epoch_transition(parent.number(), *header.parent_hash())
.is_some();
if header.number() >= engine.params().validate_service_transactions_transition {
// Check if zero gas price transactions are certified to be service transactions
// using the Certifier contract. If they are not certified, the block is treated as invalid.
let service_transaction_checker = self.miner.service_transaction_checker();
if service_transaction_checker.is_some() {
match service_transaction_checker.unwrap().refresh_cache(client) {
Ok(true) => {
trace!(target: "client", "Service transaction cache was refreshed successfully");
}
Ok(false) => {
trace!(target: "client", "Registrar or/and service transactions contract does not exist");
}
Err(e) => {
error!(target: "client", "Error occurred while refreshing service transaction cache: {}", e)
}
};
};
for t in &block.transactions {
if t.has_zero_gas_price() {
match self.miner.service_transaction_checker() {
None => {
let e = "Service transactions are not allowed. You need to enable Certifier contract.";
warn!(target: "client", "Service tx checker error: {:?}", e);
bail!(e);
}
Some(ref checker) => match checker.check(client, &t) {
Ok(true) => {}
Ok(false) => {
let e = format!(
"Service transactions are not allowed for the sender {:?}",
t.sender()
);
warn!(target: "client", "Service tx checker error: {:?}", e);
bail!(e);
}
Err(e) => {
debug!(target: "client", "Unable to verify service transaction: {:?}", e);
warn!(target: "client", "Service tx checker error: {:?}", e);
bail!(e);
}
},
}
};
}
}
// t_nb 8.0 Block enacting. Execution of transactions.
let enact_result = enact_verified(
block,
@ -1954,7 +2000,12 @@ impl Call for Client {
last_hashes: self.build_last_hashes(header.parent_hash()),
gas_used: U256::default(),
gas_limit: U256::max_value(),
base_fee: header.base_fee(),
//if gas pricing is not defined, force base_fee to zero
base_fee: if transaction.effective_gas_price(header.base_fee()).is_zero() {
Some(0.into())
} else {
header.base_fee()
},
};
let machine = self.engine.machine();
@ -1982,6 +2033,13 @@ impl Call for Client {
let machine = self.engine.machine();
for &(ref t, analytics) in transactions {
//if gas pricing is not defined, force base_fee to zero
if t.effective_gas_price(header.base_fee()).is_zero() {
env_info.base_fee = Some(0.into());
} else {
env_info.base_fee = header.base_fee()
}
let ret = Self::do_virtual_call(machine, &env_info, state, t, analytics)?;
env_info.gas_used = ret.cumulative_gas_used;
results.push(ret);
@ -2008,7 +2066,11 @@ impl Call for Client {
last_hashes: self.build_last_hashes(header.parent_hash()),
gas_used: U256::default(),
gas_limit: max,
base_fee: header.base_fee(),
base_fee: if t.effective_gas_price(header.base_fee()).is_zero() {
Some(0.into())
} else {
header.base_fee()
},
};
(init, max, env_info)
@ -2392,6 +2454,7 @@ impl BlockChainClient for Client {
let chain = self.chain.read();
let number = chain.block_number(&hash)?;
let body = chain.block_body(&hash)?;
let header = chain.block_header_data(&hash)?;
let mut receipts = chain.block_receipts(&hash)?.receipts;
receipts.truncate(address.index + 1);
@ -2404,6 +2467,11 @@ impl BlockChainClient for Client {
.into_iter()
.map(|receipt| receipt.logs.len())
.sum::<usize>();
let base_fee = if number >= self.engine().params().eip1559_transition {
Some(header.base_fee())
} else {
None
};
let receipt = transaction_receipt(
self.engine().machine(),
@ -2411,6 +2479,7 @@ impl BlockChainClient for Client {
receipt,
gas_used,
no_of_logs,
base_fee,
);
Some(receipt)
}
@ -2422,7 +2491,13 @@ impl BlockChainClient for Client {
let receipts = chain.block_receipts(&hash)?;
let number = chain.block_number(&hash)?;
let body = chain.block_body(&hash)?;
let header = chain.block_header_data(&hash)?;
let engine = self.engine.clone();
let base_fee = if number >= engine.params().eip1559_transition {
Some(header.base_fee())
} else {
None
};
let mut gas_used = 0.into();
let mut no_of_logs = 0;
@ -2439,6 +2514,7 @@ impl BlockChainClient for Client {
receipt,
gas_used,
no_of_logs,
base_fee,
);
gas_used = result.cumulative_gas_used;
no_of_logs += result.logs.len();
@ -2746,6 +2822,10 @@ impl BlockChainClient for Client {
fn registrar_address(&self) -> Option<Address> {
self.registrar_address.clone()
}
fn state_data(&self, hash: &H256) -> Option<Bytes> {
self.state_db.read().journal_db().state(hash)
}
}
impl IoClient for Client {
@ -3264,6 +3344,7 @@ fn transaction_receipt(
receipt: TypedReceipt,
prior_gas_used: U256,
prior_no_of_logs: usize,
base_fee: Option<U256>,
) -> LocalizedReceipt {
let sender = tx.sender();
let transaction_hash = tx.hash();
@ -3315,6 +3396,7 @@ fn transaction_receipt(
.collect(),
log_bloom: receipt.log_bloom,
outcome: receipt.outcome.clone(),
effective_gas_price: tx.effective_gas_price(base_fee),
}
}
@ -3643,7 +3725,7 @@ mod tests {
});
// when
let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1);
let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1, None);
// then
assert_eq!(
@ -3684,6 +3766,7 @@ mod tests {
],
log_bloom: Default::default(),
outcome: TransactionOutcome::StateRoot(state_root),
effective_gas_price: Default::default(),
}
);
}

View File

@ -1078,6 +1078,29 @@ impl BlockChainClient for TestBlockChainClient {
fn registrar_address(&self) -> Option<Address> {
None
}
fn state_data(&self, hash: &H256) -> Option<Bytes> {
let begins_with_f =
H256::from_str("f000000000000000000000000000000000000000000000000000000000000000")
.unwrap();
if *hash > begins_with_f {
let mut rlp = RlpStream::new();
rlp.append(&hash.clone());
return Some(rlp.out());
} else if *hash
== H256::from_str("000000000000000000000000000000000000000000000000000000000000000a")
.unwrap()
{
// for basic `return_node_data` tests
return Some(vec![0xaa, 0xaa]);
} else if *hash
== H256::from_str("000000000000000000000000000000000000000000000000000000000000000c")
.unwrap()
{
return Some(vec![0xcc]);
}
None
}
}
impl IoClient for TestBlockChainClient {

View File

@ -40,7 +40,7 @@ use types::{
pruning_info::PruningInfo,
receipt::LocalizedReceipt,
trace_filter::Filter as TraceFilter,
transaction::{self, Action, LocalizedTransaction, SignedTransaction},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, TypedTxId},
BlockNumber,
};
use vm::LastHashes;
@ -333,6 +333,9 @@ pub trait BlockChainClient:
/// Get all possible uncle hashes for a block.
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>>;
/// Get latest state node
fn state_data(&self, hash: &H256) -> Option<Bytes>;
/// Get block receipts data by block header hash.
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts>;
@ -395,10 +398,49 @@ pub trait BlockChainClient:
if block.number() == 0 {
return corpus.into();
}
block.transaction_views().iter().foreach(|t| {
corpus.push(t.effective_gas_price({
match t.transaction_type() {
TypedTxId::Legacy => None,
TypedTxId::AccessList => None,
TypedTxId::EIP1559Transaction => Some(block.header().base_fee()),
}
}))
});
h = block.parent_hash().clone();
}
}
corpus.into()
}
/// Sorted list of transaction priority gas prices from at least last sample_size blocks.
fn priority_gas_price_corpus(
&self,
sample_size: usize,
eip1559_transition: BlockNumber,
) -> ::stats::Corpus<U256> {
let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new();
while corpus.is_empty() {
for _ in 0..sample_size {
let block = match self.block(BlockId::Hash(h)) {
Some(block) => block,
None => return corpus.into(),
};
if block.number() == 0 || block.number() < eip1559_transition {
return corpus.into();
}
block
.transaction_views()
.iter()
.foreach(|t| corpus.push(t.gas_price()));
.filter(
|t| t.gas_price() > 0.into(), /* filter zero cost transactions */
)
.foreach(|t| {
// As block.number() >= eip_1559_transition, the base_fee should exist
corpus.push(t.effective_priority_gas_price(Some(block.header().base_fee())))
});
h = block.parent_hash().clone();
}
}

View File

@ -1557,6 +1557,43 @@ impl Engine<EthereumMachine> for AuthorityRound {
}
}
// Mostly is the same as `fn sealing_state(&self)` except that it does not
// check whether the node is a step proposer.
fn is_allowed_to_seal(&self) -> bool {
let our_addr = match *self.signer.read() {
Some(ref signer) => signer.address(),
None => return false,
};
let client = match self.upgrade_client_or("Not preparing block") {
Ok(client) => client,
Err(_) => return false,
};
let parent = match client.as_full_client() {
Some(full_client) => full_client.best_block_header(),
None => {
return false;
}
};
let validators = if self.immediate_transitions {
CowLike::Borrowed(&*self.validators)
} else {
let mut epoch_manager = self.epoch_manager.lock();
if !epoch_manager.zoom_to_after(
&*client,
&self.machine,
&*self.validators,
parent.hash(),
) {
return false;
}
CowLike::Owned(epoch_manager.validators().clone())
};
validators.contains(&parent.hash(), &our_addr)
}
fn sealing_state(&self) -> SealingState {
let our_addr = match *self.signer.read() {
Some(ref signer) => signer.address(),

View File

@ -470,6 +470,14 @@ pub trait Engine<M: Machine>: Sync + Send {
/// Register a component which signs consensus messages.
fn set_signer(&self, _signer: Option<Box<dyn EngineSigner>>) {}
/// Returns whether the current node is a validator and
/// actually may seal a block if AuRa engine is used.
///
/// Used by `eth_mining` rpc call.
fn is_allowed_to_seal(&self) -> bool {
true
}
/// Sign using the EngineSigner, to be used for consensus tx signing.
fn sign(&self, _hash: H256) -> Result<Signature, M::Error> {
unimplemented!()
@ -660,6 +668,14 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
fn min_gas_limit(&self) -> U256 {
self.params().min_gas_limit
}
/// Returns whether transactions from non externally owned accounts (EOA)
/// are allowed in the given block number (see EIP-3607).
///
/// That is only possible if EIP-3607 is still not activated.
fn allow_non_eoa_sender(&self, best_block_number: BlockNumber) -> bool {
self.params().eip3607_transition > best_block_number
}
}
// convenience wrappers for existing functions.

View File

@ -1207,7 +1207,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
}
// ensure that the user was willing to at least pay the base fee
if t.tx().gas_price < self.info.base_fee.unwrap_or_default() {
if t.tx().gas_price < self.info.base_fee.unwrap_or_default() && !t.has_zero_gas_price() {
return Err(ExecutionError::GasPriceLowerThanBaseFee {
gas_price: t.tx().gas_price,
base_fee: self.info.base_fee.unwrap_or_default(),
@ -1516,18 +1516,20 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
// Up until now, fees_value is calculated for each type of transaction based on their gas prices
// Now, if eip1559 is activated, burn the base fee
// miner only receives the inclusion fee; note that the base fee is not given to anyone (it is burned)
let fees_value = fees_value.saturating_sub(if schedule.eip1559 {
let (base_fee, overflow_3) =
let burnt_fee = if schedule.eip1559 && !t.has_zero_gas_price() {
let (fee, overflow_3) =
gas_used.overflowing_mul(self.info.base_fee.unwrap_or_default());
if overflow_3 {
return Err(ExecutionError::TransactionMalformed(
"U256 Overflow".to_string(),
));
}
base_fee
fee
} else {
U256::from(0)
});
};
let fees_value = fees_value.saturating_sub(burnt_fee);
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
t.tx().gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
@ -1552,6 +1554,17 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
substate.to_cleanup_mode(&schedule),
)?;
if burnt_fee > U256::from(0)
&& self.machine.params().eip1559_fee_collector.is_some()
&& self.info.number >= self.machine.params().eip1559_fee_collector_transition
{
self.state.add_balance(
&self.machine.params().eip1559_fee_collector.unwrap(),
&burnt_fee,
substate.to_cleanup_mode(&schedule),
)?;
};
// perform suicides
for address in &substate.suicides {
self.state.kill_account(address);
@ -2747,6 +2760,127 @@ mod tests {
}
}
evm_test! {test_too_big_max_priority_fee_with_not_enough_cash: test_too_big_max_priority_fee_with_not_enough_cash_int}
fn test_too_big_max_priority_fee_with_not_enough_cash(factory: Factory) {
let keypair = Random.generate();
let max_priority_fee_per_gas /* 2**256 - 1 */ = U256::from(340282366920938463463374607431768211455u128)
* U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128);
let t = TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction: AccessListTx::new(
Transaction {
action: Action::Create,
value: U256::from(17),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: max_priority_fee_per_gas,
nonce: U256::zero(),
},
vec![
(
H160::from_low_u64_be(10),
vec![H256::from_low_u64_be(102), H256::from_low_u64_be(103)],
),
(H160::from_low_u64_be(400), vec![]),
],
),
max_priority_fee_per_gas,
})
.sign(keypair.secret(), None);
let sender = t.sender();
let mut state = get_temp_state_with_factory(factory);
state
.add_balance(&sender, &U256::from(15000017), CleanupMode::NoEmpty)
.unwrap();
let mut info = EnvInfo::default();
info.gas_limit = U256::from(100_000);
info.base_fee = Some(U256::from(100));
let machine = make_london_machine(0);
let schedule = machine.schedule(info.number);
let res = {
let mut ex = Executive::new(&mut state, &info, &machine, &schedule);
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts)
};
match res {
Err(ExecutionError::NotEnoughCash { required, got })
if required
== U512::from(max_priority_fee_per_gas) * U512::from(100_000)
+ U512::from(17)
&& got == U512::from(15000017) =>
{
()
}
_ => assert!(false, "Expected not enough cash error. {:?}", res),
}
}
evm_test! {test_too_big_max_priority_fee_with_less_max_fee_per_gas: test_too_big_max_priority_fee_with_less_max_fee_per_gas_int}
fn test_too_big_max_priority_fee_with_less_max_fee_per_gas(factory: Factory) {
let keypair = Random.generate();
let max_priority_fee_per_gas /* 2**256 - 1 */ = U256::from(340282366920938463463374607431768211455u128)
* U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128);
let t = TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction: AccessListTx::new(
Transaction {
action: Action::Create,
value: U256::from(17),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::from(150),
nonce: U256::zero(),
},
vec![
(
H160::from_low_u64_be(10),
vec![H256::from_low_u64_be(102), H256::from_low_u64_be(103)],
),
(H160::from_low_u64_be(400), vec![]),
],
),
max_priority_fee_per_gas,
})
.sign(keypair.secret(), None);
let sender = t.sender();
let mut state = get_temp_state_with_factory(factory);
state
.add_balance(&sender, &U256::from(15000017), CleanupMode::NoEmpty)
.unwrap();
let mut info = EnvInfo::default();
info.gas_limit = U256::from(100_000);
info.base_fee = Some(U256::from(100));
let machine = make_london_machine(0);
let schedule = machine.schedule(info.number);
let res = {
let mut ex = Executive::new(&mut state, &info, &machine, &schedule);
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts)
};
match res {
Err(ExecutionError::TransactionMalformed(err))
if err.contains("maxPriorityFeePerGas higher than maxFeePerGas") =>
{
()
}
_ => assert!(
false,
"Expected maxPriorityFeePerGas higher than maxFeePerGas error. {:?}",
res
),
}
}
evm_test! {test_keccak: test_keccak_int}
fn test_keccak(factory: Factory) {
let code = "6064640fffffffff20600055".from_hex().unwrap();

View File

@ -376,7 +376,7 @@ impl EthereumMachine {
header: &Header,
) -> Result<SignedTransaction, transaction::Error> {
// ensure that the user was willing to at least pay the base fee
if t.tx().gas_price < header.base_fee().unwrap_or_default() {
if t.tx().gas_price < header.base_fee().unwrap_or_default() && !t.has_zero_gas_price() {
return Err(transaction::Error::GasPriceLowerThanBaseFee {
gas_price: t.tx().gas_price,
base_fee: header.base_fee().unwrap_or_default(),
@ -466,15 +466,32 @@ impl EthereumMachine {
/// Base fee is calculated based on the parent header (last block in blockchain / best block).
///
/// Introduced by EIP1559 to support new market fee mechanism.
///
/// Modified for xDai chain to have an ability to set min base fee
/// through eip1559BaseFeeMinValue spec option. The modification made
/// in v3.3.0-rc.14
pub fn calc_base_fee(&self, parent: &Header) -> Option<U256> {
// Block eip1559_transition - 1 has base_fee = None
if parent.number() + 1 < self.params().eip1559_transition {
return None;
}
let base_fee_min_value =
if parent.number() + 1 >= self.params().eip1559_base_fee_min_value_transition {
match self.params().eip1559_base_fee_min_value {
None => panic!("Base fee min value must be set in spec."),
Some(min_value) => min_value,
}
} else {
U256::zero()
};
// Block eip1559_transition has base_fee = self.params().eip1559_base_fee_initial_value
if parent.number() + 1 == self.params().eip1559_transition {
return Some(self.params().eip1559_base_fee_initial_value);
return Some(max(
self.params().eip1559_base_fee_initial_value,
base_fee_min_value,
));
}
// Block eip1559_transition + 1 has base_fee = calculated
@ -492,21 +509,23 @@ impl EthereumMachine {
panic!("Can't calculate base fee if parent gas target is zero.");
}
if parent.gas_used() == &parent_gas_target {
Some(parent_base_fee)
let result = if parent.gas_used() == &parent_gas_target {
parent_base_fee
} else if parent.gas_used() > &parent_gas_target {
let gas_used_delta = parent.gas_used() - parent_gas_target;
let base_fee_per_gas_delta = max(
parent_base_fee * gas_used_delta / parent_gas_target / base_fee_denominator,
U256::from(1),
);
Some(parent_base_fee + base_fee_per_gas_delta)
parent_base_fee + base_fee_per_gas_delta
} else {
let gas_used_delta = parent_gas_target - parent.gas_used();
let base_fee_per_gas_delta =
parent_base_fee * gas_used_delta / parent_gas_target / base_fee_denominator;
Some(max(parent_base_fee - base_fee_per_gas_delta, U256::zero()))
}
max(parent_base_fee - base_fee_per_gas_delta, U256::zero())
};
Some(max(result, base_fee_min_value))
}
}

View File

@ -190,6 +190,7 @@ impl Default for MinerOptions {
block_base_fee: None,
tx_gas_limit: U256::max_value(),
no_early_reject: false,
allow_non_eoa_sender: false,
},
}
}
@ -341,6 +342,7 @@ impl Miner {
block_base_fee: None,
tx_gas_limit: U256::max_value(),
no_early_reject: false,
allow_non_eoa_sender: false,
},
reseal_min_period: Duration::from_secs(0),
force_sealing,
@ -382,6 +384,7 @@ impl Miner {
&self,
block_gas_limit: U256,
block_base_fee: Option<U256>,
allow_non_eoa_sender: bool,
) {
trace!(target: "miner", "minimal_gas_price: recalibrating...");
let txq = self.transaction_queue.clone();
@ -391,6 +394,7 @@ impl Miner {
options.minimal_gas_price = gas_price;
options.block_gas_limit = block_gas_limit;
options.block_base_fee = block_base_fee;
options.allow_non_eoa_sender = allow_non_eoa_sender;
txq.set_verifier_options(options);
});
@ -1006,6 +1010,14 @@ impl miner::MinerService for Miner {
self.transaction_queue.current_worst_gas_price() * 110u32 / 100
}
fn sensible_max_priority_fee(&self) -> U256 {
// 10% above our minimum.
self.transaction_queue
.current_worst_effective_priority_fee()
* 110u32
/ 100
}
fn sensible_gas_limit(&self) -> U256 {
self.params.read().gas_range_target.0 / 5
}
@ -1295,6 +1307,7 @@ impl miner::MinerService for Miner {
logs: receipt.logs.clone(),
log_bloom: receipt.log_bloom,
outcome: receipt.outcome.clone(),
effective_gas_price: tx.effective_gas_price(pending.header.base_fee()),
}
})
.collect()
@ -1368,7 +1381,7 @@ impl miner::MinerService for Miner {
}
fn is_currently_sealing(&self) -> bool {
self.sealing.lock().enabled
self.sealing.lock().enabled && self.engine.is_allowed_to_seal()
}
fn work_package<C>(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)>
@ -1457,7 +1470,10 @@ impl miner::MinerService for Miner {
} else {
1
};
self.update_transaction_queue_limits(gas_limit, base_fee);
let allow_non_eoa_sender = self
.engine
.allow_non_eoa_sender(chain.best_block_header().number() + 1);
self.update_transaction_queue_limits(gas_limit, base_fee, allow_non_eoa_sender);
// t_nb 10.2 Then import all transactions from retracted blocks (retracted means from side chain).
let client = self.pool_client(chain);
@ -1599,7 +1615,9 @@ mod tests {
use client::{ChainInfo, EachBlockWith, ImportSealedBlock, TestBlockChainClient};
use miner::{MinerService, PendingOrdering};
use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec};
use test_helpers::{
dummy_engine_signer_with_address, generate_dummy_client, generate_dummy_client_with_spec,
};
use types::transaction::{Transaction, TypedTransaction};
#[test]
@ -1659,6 +1677,7 @@ mod tests {
block_base_fee: None,
tx_gas_limit: U256::max_value(),
no_early_reject: false,
allow_non_eoa_sender: false,
},
},
GasPricer::new_fixed(0u64.into()),
@ -1804,6 +1823,40 @@ mod tests {
);
}
#[test]
fn should_activate_eip_3607_according_to_spec() {
// given
let spec = Spec::new_test_eip3607();
let miner = Miner::new_for_tests(&spec, None);
let client = TestBlockChainClient::new_with_spec(spec);
let imported = [H256::zero()];
let empty = &[];
// the client best block is below EIP-3607 transition number
miner.chain_new_blocks(&client, &imported, empty, &imported, empty, false);
assert!(
miner.queue_status().options.allow_non_eoa_sender,
"The client best block is below EIP-3607 transition number. Non EOA senders should be allowed"
);
// the client best block equals EIP-3607 transition number
client.add_block(EachBlockWith::Nothing, |header| header);
miner.chain_new_blocks(&client, &imported, empty, &imported, empty, false);
assert!(
!miner.queue_status().options.allow_non_eoa_sender,
"The client best block equals EIP-3607 transition number. Non EOA senders should not be allowed"
);
// the client best block is above EIP-3607 transition number
client.add_block(EachBlockWith::Nothing, |header| header);
miner.chain_new_blocks(&client, &imported, empty, &imported, empty, false);
assert!(
!miner.queue_status().options.allow_non_eoa_sender,
"The client best block is above EIP-3607 transition number. Non EOA senders should not be allowed"
);
}
#[test]
fn should_treat_unfamiliar_locals_selectively() {
// given
@ -2069,6 +2122,31 @@ mod tests {
assert!(miner.is_currently_sealing());
}
#[test]
fn should_not_mine_if_is_not_allowed_to_seal() {
let spec = Spec::new_test_round();
let miner = Miner::new_for_tests_force_sealing(&spec, None, true);
assert!(!miner.is_currently_sealing());
}
#[test]
fn should_mine_if_is_allowed_to_seal() {
let verifier: Address = [
0x7d, 0x57, 0x7a, 0x59, 0x7b, 0x27, 0x42, 0xb4, 0x98, 0xcb, 0x5c, 0xf0, 0xc2, 0x6c,
0xdc, 0xd7, 0x26, 0xd3, 0x9e, 0x6e,
]
.into();
let spec = Spec::new_test_round();
let client: Arc<dyn EngineClient> = generate_dummy_client(2);
let miner = Miner::new_for_tests_force_sealing(&spec, None, true);
miner.engine.register_client(Arc::downgrade(&client));
miner.set_author(Author::Sealer(dummy_engine_signer_with_address(verifier)));
assert!(miner.is_currently_sealing());
}
#[test]
fn should_set_new_minimum_gas_price() {
// Creates a new GasPricer::Fixed behind the scenes

View File

@ -260,6 +260,9 @@ pub trait MinerService: Send + Sync {
/// Suggested gas price.
fn sensible_gas_price(&self) -> U256;
/// Suggested max priority fee gas price
fn sensible_max_priority_fee(&self) -> U256;
/// Suggested gas limit.
fn sensible_gas_limit(&self) -> U256;

View File

@ -30,7 +30,7 @@ use types::{
};
use call_contract::CallContract;
use client::{BlockInfo, Nonce, TransactionId};
use client::{BlockId, BlockInfo, Nonce, TransactionId};
use engines::EthEngine;
use miner;
use transaction_ext::Transaction;
@ -168,6 +168,7 @@ where
pool::client::AccountDetails {
nonce: self.cached_nonces.account_nonce(address),
balance: self.chain.latest_balance(address),
code_hash: self.chain.code_hash(address, BlockId::Latest),
is_local: self.accounts.is_local(address),
}
}

View File

@ -65,7 +65,7 @@ impl From<ethjson::spec::Genesis> for Genesis {
state_root: g.state_root.map(Into::into),
gas_used: g.gas_used.map_or_else(U256::zero, Into::into),
extra_data: g.extra_data.map_or_else(Vec::new, Into::into),
base_fee: g.base_fee.map(Into::into),
base_fee: g.base_fee_per_gas.map(Into::into),
}
}
}

View File

@ -147,6 +147,8 @@ pub struct CommonParams {
pub eip3529_transition: BlockNumber,
/// Number of first block where EIP-3541 rule begins.
pub eip3541_transition: BlockNumber,
/// Number of first block where EIP-3607 rule begins.
pub eip3607_transition: BlockNumber,
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
pub dust_protection_transition: BlockNumber,
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
@ -183,6 +185,16 @@ pub struct CommonParams {
pub eip1559_elasticity_multiplier: U256,
/// Default value for the block base fee
pub eip1559_base_fee_initial_value: U256,
/// Min value for the block base fee.
pub eip1559_base_fee_min_value: Option<U256>,
/// Block at which the min value for the base fee starts to be used.
pub eip1559_base_fee_min_value_transition: BlockNumber,
/// Address where EIP-1559 burnt fee will be accrued to.
pub eip1559_fee_collector: Option<Address>,
/// Block at which the fee collector should start being used.
pub eip1559_fee_collector_transition: BlockNumber,
/// Block at which zero gas price transactions start being checked with Certifier contract.
pub validate_service_transactions_transition: BlockNumber,
}
impl CommonParams {
@ -424,6 +436,7 @@ impl From<ethjson::spec::Params> for CommonParams {
dust_protection_transition: p
.dust_protection_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip3607_transition: p.eip3607_transition.map_or(0, Into::into),
nonce_cap_increment: p.nonce_cap_increment.map_or(64, Into::into),
remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false),
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
@ -459,6 +472,17 @@ impl From<ethjson::spec::Params> for CommonParams {
eip1559_base_fee_initial_value: p
.eip1559_base_fee_initial_value
.map_or_else(U256::zero, Into::into),
eip1559_base_fee_min_value: p.eip1559_base_fee_min_value.map(Into::into),
eip1559_base_fee_min_value_transition: p
.eip1559_base_fee_min_value_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip1559_fee_collector: p.eip1559_fee_collector.map(Into::into),
eip1559_fee_collector_transition: p
.eip1559_fee_collector_transition
.map_or_else(BlockNumber::max_value, Into::into),
validate_service_transactions_transition: p
.validate_service_transactions_transition
.map_or_else(BlockNumber::max_value, Into::into),
}
}
}
@ -734,6 +758,9 @@ impl Spec {
params.kip6_transition,
params.max_code_size_transition,
params.transaction_permission_contract_transition,
params.eip1559_fee_collector_transition,
params.eip1559_base_fee_min_value_transition,
params.validate_service_transactions_transition,
];
// BUG: Rinkeby has homestead transition at block 1 but we can't reflect that in specs for non-Ethash networks
if params.network_id == 0x4 {
@ -1127,6 +1154,13 @@ impl Spec {
load_bundled!("test/constructor")
}
/// Create a new Spec which is a NullEngine consensus with EIP3607 transition equal to 2,
/// and with a contract at address '0x71562b71999873DB5b286dF957af199Ec94617F7'.
#[cfg(any(test, feature = "test-helpers"))]
pub fn new_test_eip3607() -> Self {
load_bundled!("test/eip3607_test")
}
/// Create a new Spec with Autority Round randomness contract
#[cfg(any(test, feature = "test-helpers"))]
pub fn new_test_round_randomness_contract() -> Spec {

View File

@ -46,6 +46,8 @@ use block::{Drain, OpenBlock};
use client::{
ChainInfo, ChainMessageType, ChainNotify, Client, ClientConfig, ImportBlock, PrepareOpenBlock,
};
use engines::EngineSigner;
use ethjson::crypto::publickey::{Public, Signature};
use factory::Factories;
use miner::Miner;
use spec::Spec;
@ -644,3 +646,38 @@ impl ChainNotify for TestNotify {
self.messages.write().push(data);
}
}
/// Returns engine signer with specified address
pub fn dummy_engine_signer_with_address(addr: Address) -> Box<dyn EngineSigner> {
struct TestEngineSigner(Address);
impl TestEngineSigner {
fn with_address(addr: Address) -> Self {
Self(addr)
}
}
impl EngineSigner for TestEngineSigner {
fn sign(&self, _hash: H256) -> Result<Signature, ethjson::crypto::publickey::Error> {
unimplemented!()
}
fn address(&self) -> Address {
self.0
}
fn decrypt(
&self,
_auth_data: &[u8],
_cipher: &[u8],
) -> Result<Vec<u8>, parity_crypto::publickey::Error> {
unimplemented!()
}
fn public(&self) -> Option<Public> {
unimplemented!()
}
}
Box::new(TestEngineSigner::with_address(addr))
}

View File

@ -591,3 +591,12 @@ fn import_export_binary() {
assert!(client.block_header(BlockId::Number(17)).is_some());
assert!(client.block_header(BlockId::Number(16)).is_some());
}
#[test]
fn returns_state_root_basic() {
let client = generate_dummy_client(6);
let test_spec = Spec::new_test();
let genesis_header = test_spec.genesis_header();
assert!(client.state_data(genesis_header.state_root()).is_some());
}

View File

@ -39,6 +39,7 @@ use_contract!(
transact_acl_gas_price,
"res/contracts/tx_acl_gas_price.json"
);
use_contract!(transact_acl_1559, "res/contracts/tx_acl_1559.json");
const MAX_CACHE_SIZE: usize = 4096;
@ -104,6 +105,8 @@ impl TransactionFilter {
let sender = transaction.sender();
let value = transaction.tx().value;
let gas_price = transaction.tx().gas_price;
let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas();
let gas_limit = transaction.tx().gas;
let key = (*parent_hash, sender);
if let Some(permissions) = permission_cache.get_mut(&key) {
@ -161,6 +164,25 @@ impl TransactionFilter {
(tx_permissions::NONE, true)
})
}
4 => {
trace!(target: "tx_filter", "Using filter with maxFeePerGas and maxPriorityFeePerGas and data");
let (data, decoder) = transact_acl_1559::functions::allowed_tx_types::call(
sender,
to,
value,
gas_price,
max_priority_fee_per_gas,
gas_limit,
transaction.tx().data.clone(),
);
client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
.map(|(p, f)| (p.low_u32(), f))
.unwrap_or_else(|e| {
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
(tx_permissions::NONE, true)
})
}
_ => {
error!(target: "tx_filter", "Unknown version of tx permissions contract is used");
(tx_permissions::NONE, true)
@ -201,7 +223,9 @@ mod test {
use std::{str::FromStr, sync::Arc};
use tempdir::TempDir;
use test_helpers;
use types::transaction::{Action, Transaction, TypedTransaction};
use types::transaction::{
AccessListTx, Action, EIP1559TransactionTx, Transaction, TypedTransaction,
};
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
#[test]
@ -438,7 +462,7 @@ mod test {
));
}
/// Contract code: res/tx_permission_tests/contract_ver_3.sol
/// Contract code: res/chainspec/test/contract_ver_3.sol
#[test]
fn transaction_filter_ver_3() {
let spec_data = include_str!("../res/chainspec/test/contract_ver_3_genesis.json");
@ -506,6 +530,145 @@ mod test {
));
}
/// Contract code: res/chainspec/test/contract_ver_4.sol
#[test]
fn transaction_filter_ver_4_legacy() {
let spec_data = include_str!("../res/chainspec/test/contract_ver_4_genesis.json");
let db = test_helpers::new_db();
let tempdir = TempDir::new("").unwrap();
let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap();
let client = Client::new(
ClientConfig::default(),
&spec,
db,
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
)
.unwrap();
let key1 = KeyPair::from_secret(
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap(),
)
.unwrap();
// The only difference to version 2 is that the contract now knows the transaction's gas price and data.
// So we only test those: The contract allows only transactions with either nonzero gas price or short data.
let filter = TransactionFilter::from_params(spec.params()).unwrap();
let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.tx_mut().action =
Action::Call(Address::from_str("0000000000000000000000000000000000000042").unwrap());
tx.tx_mut().data = b"01234567".to_vec();
tx.tx_mut().gas_price = 0.into();
let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1;
// Data too long and gas price zero. This transaction is not allowed.
assert!(!filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
// But if we either set a nonzero gas price or short data or both, it is allowed.
tx.tx_mut().gas_price = 1.into();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
tx.tx_mut().data = b"01".to_vec();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
tx.tx_mut().gas_price = 0.into();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
}
/// Contract code: res/chainspec/test/contract_ver_4.sol
#[test]
fn transaction_filter_ver_4_1559() {
let spec_data = include_str!("../res/chainspec/test/contract_ver_4_genesis.json");
let db = test_helpers::new_db();
let tempdir = TempDir::new("").unwrap();
let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap();
let client = Client::new(
ClientConfig::default(),
&spec,
db,
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
)
.unwrap();
let key1 = KeyPair::from_secret(
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap(),
)
.unwrap();
// The only difference to version 2 is that the contract now knows the transaction's gas price and data.
// So we only test those: The contract allows only transactions with either nonzero gas price or short data.
let filter = TransactionFilter::from_params(spec.params()).unwrap();
let mut tx = TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction: AccessListTx::new(Transaction::default(), vec![]),
max_priority_fee_per_gas: U256::from(0),
});
tx.tx_mut().action =
Action::Call(Address::from_str("0000000000000000000000000000000000000042").unwrap());
tx.tx_mut().data = b"01234567".to_vec();
tx.tx_mut().gas_price = 0.into();
let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1;
// Data too long and gas price zero. This transaction is not allowed.
assert!(!filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
// But if we either set a nonzero gas price or short data or both, it is allowed.
tx.tx_mut().gas_price = 1.into();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
tx.tx_mut().data = b"01".to_vec();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
tx.tx_mut().gas_price = 0.into();
assert!(filter.transaction_allowed(
&genesis,
block_number,
&tx.clone().sign(key1.secret(), None),
&*client
));
}
/// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a
#[test]
fn transaction_filter_deprecated() {

View File

@ -33,7 +33,7 @@ use std::{
use chain::{
fork_filter::ForkFilterApi, ChainSyncApi, SyncState, SyncStatus as EthSyncStatus,
ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_65,
PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2,
ETH_PROTOCOL_VERSION_66, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2,
};
use ethcore::{
client::{BlockChainClient, ChainMessageType, ChainNotify, NewBlocks},
@ -571,6 +571,7 @@ impl ChainNotify for EthSync {
ETH_PROTOCOL_VERSION_63,
ETH_PROTOCOL_VERSION_64,
ETH_PROTOCOL_VERSION_65,
ETH_PROTOCOL_VERSION_66,
],
)
.unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e));

View File

@ -86,7 +86,7 @@ mod tests {
|| ethereum::new_foundation(&String::new()),
vec![
1_150_000, 1_920_000, 2_463_000, 2_675_000, 4_370_000, 7_280_000, 9_069_000,
9_200_000, 12_244_000, 12_833_000,
9_200_000, 12_244_000, 12_965_000, 13_773_000,
],
)
}

View File

@ -32,14 +32,17 @@ use std::{cmp, mem, time::Instant};
use sync_io::SyncIo;
use types::{block_status::BlockStatus, ids::BlockId, BlockNumber};
use super::sync_packet::{
PacketInfo,
SyncPacket::{self, *},
use super::{
request_id::strip_request_id,
sync_packet::{
PacketInfo,
SyncPacket::{self, *},
},
};
use super::{
BlockSet, ChainSync, ForkConfirmation, PacketProcessError, PeerAsking, PeerInfo, SyncRequester,
SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_65,
SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, ETH_PROTOCOL_VERSION_66,
MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2,
};
@ -55,27 +58,33 @@ impl SyncHandler {
packet_id: u8,
data: &[u8],
) {
let rlp = Rlp::new(data);
if let Some(packet_id) = SyncPacket::from_u8(packet_id) {
let result = match packet_id {
StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp),
BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp),
BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp),
ReceiptsPacket => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp),
NewBlockPacket => SyncHandler::on_peer_new_block(sync, io, peer, &rlp),
NewBlockHashesPacket => SyncHandler::on_peer_new_hashes(sync, io, peer, &rlp),
NewPooledTransactionHashesPacket => {
SyncHandler::on_peer_new_pooled_transaction_hashes(sync, io, peer, &rlp)
}
PooledTransactionsPacket => {
SyncHandler::on_peer_pooled_transactions(sync, io, peer, &rlp)
}
SnapshotManifestPacket => SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp),
SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp),
_ => {
debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id());
Ok(())
}
let rlp_result = strip_request_id(data, sync, &peer, &packet_id);
let result = match rlp_result {
Ok((rlp, _)) => match packet_id {
StatusPacket => SyncHandler::on_peer_status(sync, io, peer, &rlp),
BlockHeadersPacket => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp),
BlockBodiesPacket => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp),
ReceiptsPacket => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp),
NewBlockPacket => SyncHandler::on_peer_new_block(sync, io, peer, &rlp),
NewBlockHashesPacket => SyncHandler::on_peer_new_hashes(sync, io, peer, &rlp),
NewPooledTransactionHashesPacket => {
SyncHandler::on_peer_new_pooled_transaction_hashes(sync, io, peer, &rlp)
}
PooledTransactionsPacket => {
SyncHandler::on_peer_pooled_transactions(sync, io, peer, &rlp)
}
SnapshotManifestPacket => {
SyncHandler::on_snapshot_manifest(sync, io, peer, &rlp)
}
SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp),
_ => {
debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id());
Ok(())
}
},
Err(e) => Err(e.into()),
};
match result {
@ -797,7 +806,7 @@ impl SyncHandler {
|| peer.protocol_version > PAR_PROTOCOL_VERSION_2.0))
|| (!warp_protocol
&& (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0
|| peer.protocol_version > ETH_PROTOCOL_VERSION_65.0))
|| peer.protocol_version > ETH_PROTOCOL_VERSION_66.0))
{
trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version);
return Err(DownloaderImportError::Invalid);

View File

@ -90,6 +90,7 @@
pub mod fork_filter;
mod handler;
mod propagator;
pub mod request_id;
mod requester;
mod supplier;
pub mod sync_packet;
@ -153,6 +154,8 @@ impl From<DecoderError> for PacketProcessError {
}
}
/// Version 66 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
pub const ETH_PROTOCOL_VERSION_66: (u8, u8) = (66, 0x11);
/// Version 65 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count).
pub const ETH_PROTOCOL_VERSION_65: (u8, u8) = (65, 0x11);
/// 64 version of Ethereum protocol.
@ -166,6 +169,7 @@ pub const PAR_PROTOCOL_VERSION_2: (u8, u8) = (2, 0x16);
pub const MAX_BODIES_TO_SEND: usize = 256;
pub const MAX_HEADERS_TO_SEND: usize = 512;
pub const MAX_NODE_DATA_TO_SEND: usize = 1024;
pub const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256;
pub const MAX_TRANSACTIONS_TO_REQUEST: usize = 256;
const MIN_PEERS_PROPAGATION: usize = 4;
@ -1700,6 +1704,14 @@ pub mod tests {
assert!(!sync_status(SyncState::Idle).is_syncing(queue_info(0, 0)));
}
pub fn dummy_sync(client: &dyn BlockChainClient) -> ChainSync {
ChainSync::new(
SyncConfig::default(),
client,
ForkFilterApi::new_dummy(client),
)
}
pub fn dummy_sync_with_peer(
peer_latest_hash: H256,
client: &dyn BlockChainClient,

View File

@ -0,0 +1,148 @@
use bytes::Bytes;
use chain::{
sync_packet::{PacketInfo, SyncPacket},
ChainSync, PeerInfo,
};
use network::PeerId;
use rlp::{DecoderError, Rlp, RlpStream};
pub type RequestId = u64;
// Separate the eth/66 request id from a packet, if it exists.
pub fn strip_request_id<'a>(
data: &'a [u8],
sync: &ChainSync,
peer: &PeerId,
packet_id: &SyncPacket,
) -> Result<(Rlp<'a>, Option<RequestId>), DecoderError> {
let protocol_version = if let Some(peer_info) = sync.peers.get(peer) {
peer_info.protocol_version
} else {
trace!(
"Peer info missing for peer {}, assuming protocol version 66",
peer
);
66
};
let has_request_id = protocol_version >= 66 && packet_id.has_request_id_in_eth_66();
do_strip_request_id(data, has_request_id)
}
fn do_strip_request_id<'a>(
data: &'a [u8],
has_request_id: bool,
) -> Result<(Rlp<'a>, Option<RequestId>), DecoderError> {
let rlp = Rlp::new(data);
if has_request_id {
let request_id: RequestId = rlp.val_at(0)?;
let stripped_rlp = rlp.at(1)?;
Ok((stripped_rlp, Some(request_id)))
} else {
Ok((rlp, None))
}
}
// Add a given eth/66 request id to a packet being built.
pub fn prepend_request_id(rlp: RlpStream, request_id: Option<RequestId>) -> RlpStream {
match request_id {
Some(ref id) => {
let mut stream = RlpStream::new_list(2);
stream.append(id);
stream.append_raw(&rlp.out(), 1);
stream
}
None => rlp,
}
}
/// Prepend a new eth/66 request id to the packet if appropriate.
pub fn generate_request_id(
packet: Bytes,
peer: &PeerInfo,
packet_id: SyncPacket,
) -> (Bytes, Option<RequestId>) {
if peer.protocol_version >= 66 && packet_id.has_request_id_in_eth_66() {
do_generate_request_id(&packet)
} else {
(packet, None)
}
}
fn do_generate_request_id(packet: &Bytes) -> (Bytes, Option<RequestId>) {
let request_id: RequestId = rand::random();
let mut rlp = RlpStream::new_list(2);
rlp.append(&request_id);
rlp.append_raw(packet, 1);
(rlp.out(), Some(request_id))
}
#[cfg(test)]
mod tests {
use super::*;
use ethereum_types::H256;
#[test]
fn test_prepend_request_id() {
let mut request = RlpStream::new_list(2);
request.append(&H256::from_low_u64_be(1));
request.append(&H256::from_low_u64_be(2));
let with_id = prepend_request_id(request, Some(10));
let rlp = Rlp::new(with_id.as_raw());
let recovered_id: RequestId = rlp.val_at(0).unwrap();
let recovered_request: Vec<H256> = rlp.at(1).unwrap().as_list().unwrap();
assert_eq!(recovered_id, 10);
assert_eq!(
recovered_request,
[H256::from_low_u64_be(1), H256::from_low_u64_be(2)]
);
}
#[test]
fn test_strip_request_id() {
let request = vec![
H256::from_low_u64_be(1),
H256::from_low_u64_be(2),
H256::from_low_u64_be(3),
];
let mut request_with_id = RlpStream::new_list(2);
request_with_id.append(&20u64);
request_with_id.append_list(&request);
let data = request_with_id.out();
let (rlp, id) = do_strip_request_id(&data, true).unwrap();
assert_eq!(id, Some(20));
assert_eq!(rlp.as_list::<H256>().unwrap(), request);
}
#[test]
fn test_generate_request_id() {
let request = vec![
H256::from_low_u64_be(1),
H256::from_low_u64_be(2),
H256::from_low_u64_be(3),
];
let mut stream = RlpStream::new_list(3);
for hash in &request {
stream.append(hash);
}
let data = stream.out();
let (new_data, id) = do_generate_request_id(&data);
let recovered = Rlp::new(&new_data);
let recovered_id: RequestId = recovered.val_at(0).unwrap();
let recovered_request: Vec<H256> = recovered.at(1).unwrap().as_list().unwrap();
assert_eq!(recovered_id, id.unwrap());
assert_eq!(recovered_request, request);
}
}

View File

@ -23,7 +23,10 @@ use std::time::Instant;
use sync_io::SyncIo;
use types::BlockNumber;
use super::sync_packet::{SyncPacket::*, *};
use super::{
request_id::generate_request_id,
sync_packet::{SyncPacket::*, *},
};
use super::{BlockSet, ChainSync, PeerAsking};
@ -121,7 +124,7 @@ impl SyncRequester {
io,
peer_id,
PeerAsking::PooledTransactions,
PooledTransactionsPacket,
GetPooledTransactionsPacket,
rlp.out(),
)
}
@ -243,6 +246,8 @@ impl SyncRequester {
peer.asking = asking;
peer.ask_time = Instant::now();
let (packet, _) = generate_request_id(packet, peer, packet_id);
let result = io.send(peer_id, packet_id, packet);
if let Err(e) = result {

View File

@ -31,12 +31,17 @@ use types::{ids::BlockId, BlockNumber};
use sync_io::SyncIo;
use super::sync_packet::{PacketInfo, SyncPacket, SyncPacket::*};
use super::{
request_id::{prepend_request_id, strip_request_id, RequestId},
sync_packet::{PacketInfo, SyncPacket, SyncPacket::*},
};
use super::{
ChainSync, PacketProcessError, RlpResponseResult, SyncHandler, MAX_BODIES_TO_SEND,
MAX_HEADERS_TO_SEND, MAX_RECEIPTS_HEADERS_TO_SEND,
};
use chain::MAX_NODE_DATA_TO_SEND;
use std::borrow::Borrow;
/// The Chain Sync Supplier: answers requests from peers with available data
pub struct SyncSupplier;
@ -52,87 +57,107 @@ impl SyncSupplier {
packet_id: u8,
data: &[u8],
) {
let rlp = Rlp::new(data);
if let Some(id) = SyncPacket::from_u8(packet_id) {
let result = match id {
GetPooledTransactionsPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
SyncSupplier::return_pooled_transactions,
|e| format!("Error sending pooled transactions: {:?}", e),
),
let rlp_result = strip_request_id(data, sync.read().borrow(), &peer, &id);
GetBlockBodiesPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
SyncSupplier::return_block_bodies,
|e| format!("Error sending block bodies: {:?}", e),
),
let result = match rlp_result {
Ok((rlp, request_id)) => match id {
GetPooledTransactionsPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_pooled_transactions,
|e| format!("Error sending pooled transactions: {:?}", e),
),
GetBlockHeadersPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e),
),
GetBlockBodiesPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_block_bodies,
|e| format!("Error sending block bodies: {:?}", e),
),
GetReceiptsPacket => {
SyncSupplier::return_rlp(io, &rlp, peer, SyncSupplier::return_receipts, |e| {
format!("Error sending receipts: {:?}", e)
})
}
GetSnapshotManifestPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
SyncSupplier::return_snapshot_manifest,
|e| format!("Error sending snapshot manifest: {:?}", e),
),
GetBlockHeadersPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e),
),
GetSnapshotDataPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
SyncSupplier::return_snapshot_data,
|e| format!("Error sending snapshot data: {:?}", e),
),
GetNodeDataPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_node_data,
|e| format!("Error sending node data: {:?}", e),
),
StatusPacket => {
sync.write().on_packet(io, peer, packet_id, data);
Ok(())
}
// Packets that require the peer to be confirmed
_ => {
if !sync.read().peers.contains_key(&peer) {
debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_version(peer));
return;
GetReceiptsPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_receipts,
|e| format!("Error sending receipts: {:?}", e),
),
GetSnapshotManifestPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_snapshot_manifest,
|e| format!("Error sending snapshot manifest: {:?}", e),
),
GetSnapshotDataPacket => SyncSupplier::return_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_snapshot_data,
|e| format!("Error sending snapshot data: {:?}", e),
),
StatusPacket => {
sync.write().on_packet(io, peer, packet_id, data);
Ok(())
}
debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id);
// Packets that require the peer to be confirmed
_ => {
if !sync.read().peers.contains_key(&peer) {
debug!(target: "sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_version(peer));
return;
}
debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id);
match id {
ConsensusDataPacket => SyncHandler::on_consensus_packet(io, peer, &rlp),
TransactionsPacket => {
let res = {
let sync_ro = sync.read();
SyncHandler::on_peer_transactions(&*sync_ro, io, peer, &rlp)
};
if res.is_err() {
// peer sent invalid data, disconnect.
io.disable_peer(peer);
sync.write().deactivate_peer(io, peer);
match id {
ConsensusDataPacket => SyncHandler::on_consensus_packet(io, peer, &rlp),
TransactionsPacket => {
let res = {
let sync_ro = sync.read();
SyncHandler::on_peer_transactions(&*sync_ro, io, peer, &rlp)
};
if res.is_err() {
// peer sent invalid data, disconnect.
io.disable_peer(peer);
sync.write().deactivate_peer(io, peer);
}
}
_ => {
sync.write().on_packet(io, peer, packet_id, data);
}
}
_ => {
sync.write().on_packet(io, peer, packet_id, data);
}
Ok(())
}
Ok(())
}
},
Err(e) => Err(e.into()),
};
match result {
@ -156,22 +181,26 @@ impl SyncSupplier {
packet_id: u8,
data: &[u8],
) {
let rlp = Rlp::new(data);
if let Some(id) = SyncPacket::from_u8(packet_id) {
let result = match id {
GetBlockHeadersPacket => SyncSupplier::send_rlp(
io,
&rlp,
peer,
SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e),
),
let rlp_result = strip_request_id(data, sync.read().borrow(), &peer, &id);
_ => {
debug!(target:"sync", "Unexpected packet {} was dispatched for delayed processing", packet_id);
Ok(())
}
let result = match rlp_result {
Ok((rlp, request_id)) => match id {
GetBlockHeadersPacket => SyncSupplier::send_rlp(
io,
&rlp,
peer,
request_id,
SyncSupplier::return_block_headers,
|e| format!("Error sending block headers: {:?}", e),
),
_ => {
debug!(target: "sync", "Unexpected packet {} was dispatched for delayed processing", packet_id);
Ok(())
}
},
Err(e) => Err(e.into()),
};
match result {
@ -321,6 +350,37 @@ impl SyncSupplier {
Ok(Some((BlockBodiesPacket, rlp)))
}
fn return_node_data(io: &dyn SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult {
let count = cmp::min(rlp.item_count().unwrap_or(0), MAX_NODE_DATA_TO_SEND);
trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count);
if count == 0 {
debug!(target: "sync", "Empty GetNodeData request, ignoring.");
return Ok(None);
}
let mut added = 0usize;
let mut data = Vec::new();
let mut total_bytes = 0;
for i in 0..count {
if let Some(node_data) = io.chain().state_data(&rlp.val_at::<H256>(i)?) {
total_bytes += node_data.len();
// Check that the packet won't be oversized
if total_bytes > PAYLOAD_SOFT_LIMIT {
break;
}
data.push(node_data);
added += 1;
}
}
let mut rlp = RlpStream::new_list(added);
for d in data {
rlp.append(&d);
}
trace!(target: "sync", "{} -> GetNodeData: returned {} entries", peer_id, added);
Ok(Some((NodeDataPacket, rlp)))
}
fn return_receipts(io: &dyn SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult {
let mut count = rlp.item_count().unwrap_or(0);
trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count);
@ -394,6 +454,7 @@ impl SyncSupplier {
io: &mut dyn SyncIo,
rlp: &Rlp,
peer: PeerId,
request_id: Option<RequestId>,
rlp_func: FRlp,
error_func: FError,
) -> Result<(), PacketProcessError>
@ -403,6 +464,7 @@ impl SyncSupplier {
{
let response = rlp_func(io, rlp, peer);
if let Some((packet_id, rlp_stream)) = response? {
let rlp_stream = prepend_request_id(rlp_stream, request_id);
io.respond(packet_id.id(), rlp_stream.out())
.unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e)));
}
@ -413,6 +475,7 @@ impl SyncSupplier {
io: &mut dyn SyncIo,
rlp: &Rlp,
peer: PeerId,
request_id: Option<RequestId>,
rlp_func: FRlp,
error_func: FError,
) -> Result<(), PacketProcessError>
@ -424,6 +487,7 @@ impl SyncSupplier {
match response {
Err(e) => Err(e),
Ok(Some((packet_id, rlp_stream))) => {
let rlp_stream = prepend_request_id(rlp_stream, request_id);
io.send(peer, packet_id, rlp_stream.out())
.unwrap_or_else(|e| debug!(target: "sync", "{:?}", error_func(e)));
Ok(())
@ -438,7 +502,10 @@ mod test {
use super::{super::tests::*, *};
use blocks::SyncHeader;
use bytes::Bytes;
use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient};
use ethcore::{
client::{BlockChainClient, EachBlockWith, TestBlockChainClient},
spec::Spec,
};
use ethereum_types::H256;
use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
@ -700,4 +767,95 @@ mod test {
);
assert_eq!(1, io.packets.len());
}
#[test]
fn return_nodes() {
let mut client = TestBlockChainClient::new_with_spec(Spec::new_test_round());
let queue = RwLock::new(VecDeque::new());
let sync = dummy_sync_with_peer(H256::zero(), &client);
let ss = TestSnapshotService::new();
let mut io = TestIo::new(&mut client, &ss, &queue, None);
let mut node_list = RlpStream::new_list(3);
node_list.append(
&H256::from_str("0000000000000000000000000000000000000000000000005555555555555555")
.unwrap(),
);
node_list.append(
&H256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa")
.unwrap(),
);
node_list.append(
&H256::from_str("aff0000000000000000000000000000000000000000000000000000000000000")
.unwrap(),
);
let node_request = node_list.out();
// it returns rlp ONLY for hashes started with "f"
let result = SyncSupplier::return_node_data(&io, &Rlp::new(&node_request.clone()), 0);
assert!(result.is_ok());
let rlp_result = result.unwrap();
assert!(rlp_result.is_some());
// the length of one rlp-encoded hashe
let rlp = rlp_result.unwrap().1.out();
let rlp = Rlp::new(&rlp);
assert_eq!(Ok(1), rlp.item_count());
io.sender = Some(2usize);
SyncSupplier::dispatch_packet(
&RwLock::new(sync),
&mut io,
0usize,
GetNodeDataPacket.id(),
&node_request,
);
assert_eq!(1, io.packets.len());
}
#[test]
fn dispatch_get_node_data_request() {
let mut client = TestBlockChainClient::new_with_spec(Spec::new_test_round());
let queue = RwLock::new(VecDeque::new());
let sync = dummy_sync(&client);
let ss = TestSnapshotService::new();
let mut io = TestIo::new(&mut client, &ss, &queue, None);
let mut node_list = RlpStream::new_list(3);
node_list.append(
&H256::from_str("000000000000000000000000000000000000000000000000000000000000000a")
.unwrap(),
);
node_list.append(
&H256::from_str("000000000000000000000000000000000000000000000000000000000000000b")
.unwrap(),
);
node_list.append(
&H256::from_str("000000000000000000000000000000000000000000000000000000000000000c")
.unwrap(),
);
let node_request = node_list;
let node_request = prepend_request_id(node_request, Some(0x0b3a73ce2ff2));
io.sender = Some(2usize);
// it returns rlp ONLY for hashes ending with "a" and "c"
SyncSupplier::dispatch_packet(
&RwLock::new(sync),
&mut io,
0usize,
GetNodeDataPacket.id(),
&node_request.out(),
);
assert_eq!(1, io.packets.len());
assert_eq!(
&io.packets[0].data,
&vec![
0xcd, 0x86, 0x0b, 0x3a, 0x73, 0xce, 0x2f, 0xf2, 0xc5, 0x82, 0xaa, 0xaa, 0x81, 0xcc
]
);
}
}

View File

@ -48,8 +48,8 @@ pub enum SyncPacket {
GetPooledTransactionsPacket = 0x09,
PooledTransactionsPacket = 0x0a,
//GetNodeDataPacket = 0x0d,
//NodeDataPacket = 0x0e,
GetNodeDataPacket = 0x0d,
NodeDataPacket = 0x0e,
GetReceiptsPacket = 0x0f,
ReceiptsPacket = 0x10,
@ -68,6 +68,7 @@ use self::SyncPacket::*;
pub trait PacketInfo {
fn id(&self) -> PacketId;
fn protocol(&self) -> ProtocolId;
fn has_request_id_in_eth_66(&self) -> bool;
}
// The mechanism to match packet ids and protocol may be improved
@ -86,8 +87,8 @@ impl PacketInfo for SyncPacket {
| NewPooledTransactionHashesPacket
| GetPooledTransactionsPacket
| PooledTransactionsPacket
//| GetNodeDataPacket
//| NodeDataPacket
| GetNodeDataPacket
| NodeDataPacket
| GetReceiptsPacket
| ReceiptsPacket => ETH_PROTOCOL,
@ -102,6 +103,22 @@ impl PacketInfo for SyncPacket {
fn id(&self) -> PacketId {
(*self) as PacketId
}
fn has_request_id_in_eth_66(&self) -> bool {
match self {
GetBlockHeadersPacket
| BlockHeadersPacket
| GetBlockBodiesPacket
| BlockBodiesPacket
| GetPooledTransactionsPacket
| PooledTransactionsPacket
| GetNodeDataPacket
| NodeDataPacket
| GetReceiptsPacket
| ReceiptsPacket => true,
_ => false,
}
}
}
#[cfg(test)]

View File

@ -18,7 +18,7 @@ use api::PAR_PROTOCOL;
use bytes::Bytes;
use chain::{
sync_packet::{PacketInfo, SyncPacket},
ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_65, PAR_PROTOCOL_VERSION_2,
ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_66, PAR_PROTOCOL_VERSION_2,
};
use ethcore::{
client::{
@ -172,7 +172,7 @@ where
if protocol == PAR_PROTOCOL {
PAR_PROTOCOL_VERSION_2.0
} else {
ETH_PROTOCOL_VERSION_65.0
ETH_PROTOCOL_VERSION_66.0
}
}

View File

@ -279,6 +279,8 @@ pub struct RichReceipt {
pub to: Option<H160>,
/// Sender
pub from: H160,
/// Effective gas price
pub effective_gas_price: U256,
}
/// Receipt with additional info.
@ -312,6 +314,8 @@ pub struct LocalizedReceipt {
pub to: Option<H160>,
/// Sender
pub from: H160,
/// Effective gas price
pub effective_gas_price: U256,
}
#[cfg(test)]

View File

@ -93,6 +93,8 @@ pub enum Error {
InvalidRlp(String),
/// Transaciton is still not enabled.
TransactionTypeNotEnabled,
/// Transaction sender is not an EOA (see EIP-3607)
SenderIsNotEOA,
}
impl From<crypto::publickey::Error> for Error {
@ -154,6 +156,7 @@ impl fmt::Display for Error {
TransactionTypeNotEnabled => {
format!("Transaction type is not enabled for current block")
}
SenderIsNotEOA => "Transaction sender is not an EOA (see EIP-3607)".into(),
};
f.write_fmt(format_args!("Transaction error ({})", msg))

View File

@ -634,10 +634,16 @@ impl TypedTransaction {
pub fn effective_gas_price(&self, block_base_fee: Option<U256>) -> U256 {
match self {
Self::EIP1559Transaction(tx) => min(
self.tx().gas_price,
tx.max_priority_fee_per_gas + block_base_fee.unwrap_or_default(),
),
Self::EIP1559Transaction(tx) => {
let (v2, overflow) = tx
.max_priority_fee_per_gas
.overflowing_add(block_base_fee.unwrap_or_default());
if overflow {
self.tx().gas_price
} else {
min(self.tx().gas_price, v2)
}
}
Self::AccessList(_) => self.tx().gas_price,
Self::Legacy(_) => self.tx().gas_price,
}
@ -651,6 +657,22 @@ impl TypedTransaction {
}
}
pub fn effective_priority_fee(&self, block_base_fee: Option<U256>) -> U256 {
self.effective_gas_price(block_base_fee)
.checked_sub(block_base_fee.unwrap_or_default())
.unwrap_or_default()
}
pub fn has_zero_gas_price(&self) -> bool {
match self {
Self::EIP1559Transaction(tx) => {
tx.tx().gas_price.is_zero() && tx.max_priority_fee_per_gas.is_zero()
}
Self::AccessList(tx) => tx.tx().gas_price.is_zero(),
Self::Legacy(tx) => tx.gas_price.is_zero(),
}
}
fn decode_new(tx: &[u8]) -> Result<UnverifiedTransaction, DecoderError> {
if tx.is_empty() {
// at least one byte needs to be present
@ -1321,4 +1343,41 @@ mod tests {
test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "9bddad43f934d313c2b79ca28a432dd2b7281029");
test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "3c24d7329e92f84f08556ceb6df1cdb0104ca49f");
}
#[test]
fn should_not_panic_on_effective_gas_price_overflow() {
use self::publickey::{Generator, Random};
let key = Random.generate();
let gas_price /* 2**256 - 1 */ = U256::from(340282366920938463463374607431768211455u128)
* U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128)
+ U256::from(340282366920938463463374607431768211455u128);
let t = TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction: AccessListTx::new(
Transaction {
action: Action::Create,
nonce: U256::from(42),
gas_price,
gas: U256::from(50_000),
value: U256::from(1),
data: b"Hello!".to_vec(),
},
vec![
(
H160::from_low_u64_be(10),
vec![H256::from_low_u64_be(102), H256::from_low_u64_be(103)],
),
(H160::from_low_u64_be(400), vec![]),
],
),
max_priority_fee_per_gas: gas_price,
})
.sign(&key.secret(), Some(69));
let result = t.transaction.effective_gas_price(Some(124.into()));
assert_eq!(
gas_price, result,
"Invalid effective gas price, when max_priority_fee_per_gas is U256::max"
);
}
}

View File

@ -51,6 +51,7 @@ impl TypedTxId {
pub fn from_U64_option_id(n: Option<U64>) -> Option<Self> {
match n.map(|t| t.as_u64()) {
None => Some(Self::Legacy),
Some(0x00) => Some(Self::Legacy),
Some(0x01) => Some(Self::AccessList),
Some(0x02) => Some(Self::EIP1559Transaction),
_ => None,
@ -59,10 +60,7 @@ impl TypedTxId {
#[allow(non_snake_case)]
pub fn to_U64_option_id(self) -> Option<U64> {
match self {
Self::Legacy => None,
_ => Some(U64::from(self as u8)),
}
Some(U64::from(self as u8))
}
}
@ -93,7 +91,7 @@ mod tests {
#[test]
fn typed_tx_id_to_u64_option_id() {
assert_eq!(None, TypedTxId::Legacy.to_U64_option_id());
assert_eq!(Some(U64::from(0x00)), TypedTxId::Legacy.to_U64_option_id());
assert_eq!(
Some(U64::from(0x01)),
TypedTxId::AccessList.to_U64_option_id()

View File

@ -16,6 +16,8 @@
//! View onto transaction rlp
use std::cmp::min;
use crate::{
bytes::Bytes,
hash::keccak,
@ -116,6 +118,48 @@ impl<'a> TypedTransactionView<'a> {
}
}
/// Get the effective_gas_price field of the transaction.
pub fn effective_gas_price(&self, block_base_fee: Option<U256>) -> U256 {
match self.transaction_type {
TypedTxId::Legacy => self.gas_price(),
TypedTxId::AccessList => self.gas_price(),
TypedTxId::EIP1559Transaction => {
let max_priority_fee_per_gas: U256 =
view!(Self, &self.rlp.rlp.data().unwrap()[1..])
.rlp
.val_at(2);
min(
self.gas_price(),
max_priority_fee_per_gas + block_base_fee.unwrap_or_default(),
)
}
}
}
/// Get the actual priority gas price paid to the miner
pub fn effective_priority_gas_price(&self, block_base_fee: Option<U256>) -> U256 {
match self.transaction_type {
TypedTxId::Legacy => self
.gas_price()
.saturating_sub(block_base_fee.unwrap_or_default()),
TypedTxId::AccessList => self
.gas_price()
.saturating_sub(block_base_fee.unwrap_or_default()),
TypedTxId::EIP1559Transaction => {
let max_priority_fee_per_gas: U256 =
view!(Self, &self.rlp.rlp.data().unwrap()[1..])
.rlp
.val_at(2);
min(
max_priority_fee_per_gas,
self.gas_price()
.saturating_sub(block_base_fee.unwrap_or_default()),
)
}
}
}
/// Get the gas field of the transaction.
pub fn gas(&self) -> U256 {
match self.transaction_type {
@ -238,6 +282,8 @@ mod tests {
let view = view!(TypedTransactionView, &rlp);
assert_eq!(view.nonce(), 0.into());
assert_eq!(view.gas_price(), 1.into());
assert_eq!(view.effective_gas_price(None), 1.into());
assert_eq!(view.effective_priority_gas_price(None), 1.into());
assert_eq!(view.gas(), 0x61a8.into());
assert_eq!(view.value(), 0xa.into());
assert_eq!(
@ -263,6 +309,7 @@ mod tests {
let view = view!(TypedTransactionView, &rlp);
assert_eq!(view.nonce(), 0x1.into());
assert_eq!(view.gas_price(), 0xa.into());
assert_eq!(view.effective_priority_gas_price(None), 0xa.into());
assert_eq!(view.gas(), 0x1e241.into());
assert_eq!(view.value(), 0x0.into());
assert_eq!(view.data(), "".from_hex().unwrap());
@ -279,10 +326,15 @@ mod tests {
#[test]
fn test_eip1559_transaction_view() {
let rlp = "b8c202f8bf01010a0a8301e24194000000000000000000000000000000000000aaaa8080f85bf859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000080a082dc119130f280bd72e3fd4e10220e35b767031b84b8dd1f64085e0158f234dba072228551e678a8a6c6e9bae0ae786b8839c7fda0a994caddd23910f45f385cc0".from_hex().unwrap();
let rlp = "b8c202f8bf0101010a8301e24194000000000000000000000000000000000000aaaa8080f85bf859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000080a082dc119130f280bd72e3fd4e10220e35b767031b84b8dd1f64085e0158f234dba072228551e678a8a6c6e9bae0ae786b8839c7fda0a994caddd23910f45f385cc0".from_hex().unwrap();
let view = view!(TypedTransactionView, &rlp);
assert_eq!(view.nonce(), 0x1.into());
assert_eq!(view.gas_price(), 0xa.into());
assert_eq!(view.effective_gas_price(Some(0x07.into())), 0x08.into());
assert_eq!(
view.effective_priority_gas_price(Some(0x07.into())),
0x01.into()
);
assert_eq!(view.gas(), 0x1e241.into());
assert_eq!(view.value(), 0x0.into());
assert_eq!(view.data(), "".from_hex().unwrap());

View File

@ -89,7 +89,7 @@ impl BlockChain {
state_root: Some(self.genesis_block.state_root.clone()),
gas_used: Some(self.genesis_block.gas_used),
extra_data: Some(self.genesis_block.extra_data.clone()),
base_fee: self.genesis_block.base_fee,
base_fee_per_gas: self.genesis_block.base_fee_per_gas,
}
}
}

View File

@ -63,7 +63,7 @@ pub struct Header {
#[serde(rename = "uncleHash")]
pub uncles_hash: H256,
/// Base fee
pub base_fee: Option<Uint>,
pub base_fee_per_gas: Option<Uint>,
}
#[cfg(test)]

View File

@ -52,7 +52,7 @@ pub struct Genesis {
/// Extra data.
pub extra_data: Option<Bytes>,
/// Base fee.
pub base_fee: Option<Uint>,
pub base_fee_per_gas: Option<Uint>,
}
#[cfg(test)]
@ -124,7 +124,7 @@ mod tests {
)
.unwrap()
),
base_fee: None,
base_fee_per_gas: None,
}
);
}

View File

@ -120,6 +120,8 @@ pub struct Params {
/// See `CommonParams` docs.
pub eip3541_transition: Option<Uint>,
/// See `CommonParams` docs.
pub eip3607_transition: Option<Uint>,
/// See `CommonParams` docs.
pub dust_protection_transition: Option<Uint>,
/// See `CommonParams` docs.
pub nonce_cap_increment: Option<Uint>,
@ -158,6 +160,16 @@ pub struct Params {
pub eip1559_elasticity_multiplier: Option<Uint>,
/// Default value for the block base fee
pub eip1559_base_fee_initial_value: Option<Uint>,
/// Min value for the block base fee.
pub eip1559_base_fee_min_value: Option<Uint>,
/// Block at which the min value for the base fee starts to be used.
pub eip1559_base_fee_min_value_transition: Option<Uint>,
/// Address where EIP-1559 burnt fee will be accrued to.
pub eip1559_fee_collector: Option<Address>,
/// Block at which the fee collector should start being used.
pub eip1559_fee_collector_transition: Option<Uint>,
/// Block at which zero gas price transactions start being checked with Certifier contract.
pub validate_service_transactions_transition: Option<Uint>,
}
#[cfg(test)]

View File

@ -91,7 +91,10 @@ use ethcore::{client::BlockChainClient, miner::MinerService};
use ethereum_types::{Address, H256, H520, U256};
use ethkey::Password;
use hash::keccak;
use types::transaction::{PendingTransaction, SignedTransaction};
use types::{
transaction::{PendingTransaction, SignedTransaction},
BlockNumber,
};
use jsonrpc_core::{
futures::{future, Future, IntoFuture},
@ -397,6 +400,24 @@ where
.unwrap_or_else(|| miner.sensible_gas_price())
}
/// Extract the default priority gas price from a client and miner.
pub fn default_max_priority_fee_per_gas<C, M>(
client: &C,
miner: &M,
percentile: usize,
eip1559_transition: BlockNumber,
) -> U256
where
C: BlockChainClient,
M: MinerService,
{
client
.priority_gas_price_corpus(100, eip1559_transition)
.percentile(percentile)
.cloned()
.unwrap_or_else(|| miner.sensible_max_priority_fee())
}
/// Convert RPC confirmation payload to signer confirmation payload.
/// May need to resolve in the future to fetch things like gas price.
pub fn from_rpc<D>(

View File

@ -273,6 +273,10 @@ where
}
}
pub fn eip1559_not_activated() -> Error {
unsupported("EIP-1559 is not activated", None)
}
pub fn not_enough_data() -> Error {
Error {
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
@ -404,6 +408,7 @@ pub fn transaction_message(error: &TransactionError) -> String {
TooBig => "Transaction is too big, see chain specification for the limit.".into(),
InvalidRlp(ref descr) => format!("Invalid RLP data: {}", descr),
TransactionTypeNotEnabled => format!("Transaction type is not enabled for current block"),
SenderIsNotEOA => "Transaction sender is not an EOA (see EIP-3607)".into(),
}
}

View File

@ -53,29 +53,22 @@ pub fn sign_call(request: CallRequest) -> Result<SignedTransaction, Error> {
))
}
Some(TypedTxId::EIP1559Transaction) => {
if let Some(max_fee_per_gas) = request.max_fee_per_gas {
tx_legacy.gas_price = max_fee_per_gas;
} else {
return Err(Error::new(ErrorCode::InvalidParams));
}
tx_legacy.gas_price = request.max_fee_per_gas.unwrap_or_default();
if let Some(max_priority_fee_per_gas) = request.max_priority_fee_per_gas {
let transaction = AccessListTx::new(
tx_legacy,
request
.access_list
.unwrap_or_default()
.into_iter()
.map(Into::into)
.collect(),
);
TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction,
max_priority_fee_per_gas,
})
} else {
return Err(Error::new(ErrorCode::InvalidParams));
}
let transaction = AccessListTx::new(
tx_legacy,
request
.access_list
.unwrap_or_default()
.into_iter()
.map(Into::into)
.collect(),
);
TypedTransaction::EIP1559Transaction(EIP1559TransactionTx {
transaction,
max_priority_fee_per_gas: request.max_priority_fee_per_gas.unwrap_or_default(),
})
}
_ => return Err(Error::new(ErrorCode::InvalidParams)),
};

View File

@ -52,6 +52,7 @@ impl<C: BlockChainClient + 'static> Debug for DebugClient<C> {
.map(|(block, reason)| {
let number = block.header.number();
let hash = block.header.hash();
let base_fee = block.header.base_fee();
RichBlock {
inner: Block {
hash: Some(hash),
@ -76,7 +77,7 @@ impl<C: BlockChainClient + 'static> Debug for DebugClient<C> {
.cloned()
.map(Into::into)
.collect(),
base_fee_per_gas: block.header.base_fee(),
base_fee_per_gas: base_fee,
uncles: block.uncles.iter().map(Header::hash).collect(),
transactions: BlockTransactions::Full(
block
@ -84,13 +85,16 @@ impl<C: BlockChainClient + 'static> Debug for DebugClient<C> {
.into_iter()
.enumerate()
.map(|(transaction_index, signed)| {
Transaction::from_localized(LocalizedTransaction {
block_number: number,
block_hash: hash,
transaction_index,
signed,
cached_sender: None,
})
Transaction::from_localized(
LocalizedTransaction {
block_number: number,
block_hash: hash,
transaction_index,
signed,
cached_sender: None,
},
base_fee,
)
})
.collect(),
),

View File

@ -52,15 +52,15 @@ use v1::{
self,
block_import::is_major_importing,
deprecated::{self, DeprecationNotice},
dispatch::{default_gas_price, FullDispatcher},
dispatch::{default_gas_price, default_max_priority_fee_per_gas, FullDispatcher},
errors, fake_sign, limit_logs,
},
metadata::Metadata,
traits::Eth,
types::{
block_number_to_id, Block, BlockNumber, BlockTransactions, Bytes, CallRequest, EthAccount,
Filter, Index, Log, Receipt, RichBlock, StorageProof, SyncInfo, SyncStatus, Transaction,
Work,
EthFeeHistory, Filter, Index, Log, Receipt, RichBlock, StorageProof, SyncInfo, SyncStatus,
Transaction, Work,
},
};
@ -260,6 +260,11 @@ where
(Some(block), Some(total_difficulty)) => {
let view = block.header_view();
let eip1559_enabled = client.engine().schedule(view.number()).eip1559;
let base_fee = if eip1559_enabled {
Some(view.base_fee())
} else {
None
};
Ok(Some(RichBlock {
inner: Block {
hash: match is_pending {
@ -292,13 +297,7 @@ where
.into_iter()
.map(Into::into)
.collect(),
base_fee_per_gas: {
if eip1559_enabled {
Some(view.base_fee())
} else {
None
}
},
base_fee_per_gas: base_fee,
uncles: block.uncle_hashes(),
transactions: match include_txs {
true => BlockTransactions::Full(
@ -306,7 +305,7 @@ where
.view()
.localized_transactions()
.into_iter()
.map(Transaction::from_localized)
.map(|t| Transaction::from_localized(t, base_fee))
.collect(),
),
false => BlockTransactions::Hashes(block.transaction_hashes()),
@ -322,7 +321,23 @@ where
fn transaction(&self, id: PendingTransactionId) -> Result<Option<Transaction>> {
let client_transaction = |id| match self.client.block_transaction(id) {
Some(t) => Ok(Some(Transaction::from_localized(t))),
Some(t) => {
let block = self
.rich_block(BlockNumber::Num(t.block_number).into(), false)
.and_then(errors::check_block_number_existence(
&*self.client,
BlockNumber::Num(t.block_number).into(),
self.options,
));
let base_fee = match block {
Ok(block) => match block {
Some(block) => block.base_fee_per_gas,
None => return Ok(None),
},
Err(_) => return Ok(None),
};
Ok(Some(Transaction::from_localized(t, base_fee)))
}
None => Ok(None),
};
@ -361,7 +376,7 @@ where
cached_sender,
}
})
.map(Transaction::from_localized);
.map(|t| Transaction::from_localized(t, pending_block.header.base_fee()));
Ok(transaction)
}
@ -681,6 +696,224 @@ where
)))
}
fn max_priority_fee_per_gas(&self) -> BoxFuture<U256> {
let latest_block = self.client.chain_info().best_block_number;
let eip1559_transition = self.client.engine().params().eip1559_transition;
if latest_block + 1 >= eip1559_transition {
Box::new(future::ok(default_max_priority_fee_per_gas(
&*self.client,
&*self.miner,
self.options.gas_price_percentile,
eip1559_transition,
)))
} else {
Box::new(future::done(Err(errors::eip1559_not_activated())))
}
}
fn fee_history(
&self,
mut block_count: U256,
newest_block: BlockNumber,
reward_percentiles: Option<Vec<f64>>,
) -> BoxFuture<EthFeeHistory> {
let mut result = EthFeeHistory::default();
if block_count < 1.into() {
return Box::new(future::done(Ok(result)));
}
if block_count > 1024.into() {
block_count = 1024.into();
}
let latest_block = self.client.chain_info().best_block_number;
let pending_block = self.client.chain_info().best_block_number + 1;
let last_block = match newest_block {
BlockNumber::Hash {
hash: _,
require_canonical: _,
} => 0,
BlockNumber::Num(number) => {
if number <= pending_block {
number
} else {
0
}
}
BlockNumber::Latest => latest_block,
BlockNumber::Earliest => 0,
BlockNumber::Pending => pending_block,
};
let first_block = if last_block >= block_count.as_u64() - 1 {
last_block - (block_count.as_u64() - 1)
} else {
0
};
result.oldest_block = BlockNumber::Num(first_block);
let get_block_header = |i| {
self.client
.block_header(BlockId::Number(i))
.ok_or_else(errors::state_pruned)
.and_then(|h| {
h.decode(self.client.engine().params().eip1559_transition)
.map_err(errors::decode)
})
};
let calculate_base_fee = |h| {
self.client
.engine()
.calculate_base_fee(&h)
.unwrap_or_default()
};
let calculate_gas_used_ratio = |h: &Header| {
let gas_used = match self.client.block_receipts(&h.hash()) {
Some(receipts) => receipts
.receipts
.last()
.map_or(U256::zero(), |r| r.gas_used),
None => 0.into(),
};
(gas_used.as_u64() as f64) / (h.gas_limit().as_u64() as f64)
};
let get_block_transactions = |i| match self.client.block_body(BlockId::Number(i)) {
Some(body) => Some(body.transactions()),
None => None,
};
let reward_percentiles = reward_percentiles.unwrap_or_default();
let mut reward_final = vec![];
for i in first_block..=last_block + 1 {
let is_last = i == last_block + 1;
if i < pending_block {
match get_block_header(i) {
Ok(h) => {
let base_fee = h.base_fee();
result.base_fee_per_gas.push(base_fee.unwrap_or_default());
if !is_last {
result.gas_used_ratio.push(calculate_gas_used_ratio(&h));
if reward_percentiles.len() > 0 {
let mut gas_and_reward: Vec<(U256, U256)> = vec![];
if let Some(txs) = get_block_transactions(i) {
if let Some(receipt) = self.client.block_receipts(&h.hash()) {
if txs.len() == receipt.receipts.len() {
for i in 0..txs.len() {
let gas_used = if i == 0 {
receipt.receipts[i].gas_used
} else {
receipt.receipts[i].gas_used
- receipt.receipts[i - 1].gas_used
};
gas_and_reward.push((
gas_used,
txs[i]
.effective_gas_price(base_fee)
.saturating_sub(
base_fee.unwrap_or_default(),
),
));
}
}
}
}
gas_and_reward.sort_by(|a, b| a.1.cmp(&b.1));
reward_final.push(
reward_percentiles
.iter()
.map(|p| {
let target_gas = U256::from(
((h.gas_used().as_u64() as f64) * p / 100.0) as u64,
);
let mut sum_gas = U256::default();
for pair in &gas_and_reward {
sum_gas += pair.0;
if target_gas <= sum_gas {
return pair.1;
}
}
0.into()
})
.collect(),
);
}
}
}
Err(_) => break, //reorg happened, skip rest of the blocks
}
} else if i == pending_block {
match self.miner.pending_block_header(i - 1) {
Some(h) => {
result
.base_fee_per_gas
.push(h.base_fee().unwrap_or_default());
if !is_last {
result.gas_used_ratio.push(calculate_gas_used_ratio(&h));
if reward_percentiles.len() > 0 {
//zero values since can't be calculated for pending block
reward_final.push(vec![0.into(); reward_percentiles.len()]);
}
}
}
None => {
//calculate base fee based on the latest block
match get_block_header(i - 1) {
Ok(h) => {
result.base_fee_per_gas.push(calculate_base_fee(h));
if !is_last {
result.gas_used_ratio.push(0.into());
if reward_percentiles.len() > 0 {
//zero values since can't be calculated for pending block
reward_final.push(vec![0.into(); reward_percentiles.len()]);
}
}
}
Err(_) => break, //reorg happened, skip rest of the blocks
}
}
}
} else if i == pending_block + 1 {
//calculate base fee based on the pending block, if exist
match self.miner.pending_block_header(i - 1) {
Some(h) => {
result.base_fee_per_gas.push(calculate_base_fee(h));
}
None => {
result.base_fee_per_gas.push(0.into());
}
}
} else {
unreachable!();
};
}
if !reward_final.is_empty() {
result.reward = Some(reward_final);
}
Box::new(future::done(Ok(result)))
}
fn accounts(&self) -> Result<Vec<H160>> {
self.deprecation_notice
.print("eth_accounts", deprecated::msgs::ACCOUNTS);

View File

@ -19,7 +19,7 @@ use std::{env, sync::Arc};
use accounts::AccountProvider;
use ethcore::{
client::{BlockChainClient, ChainInfo, Client, ClientConfig, ImportBlock},
client::{BlockChainClient, ChainInfo, Client, ClientConfig, EvmTestClient, ImportBlock},
ethereum,
miner::Miner,
spec::{Genesis, Spec},
@ -67,7 +67,7 @@ fn snapshot_service() -> Arc<TestSnapshotService> {
fn make_spec(chain: &BlockChain) -> Spec {
let genesis = Genesis::from(chain.genesis());
let mut spec = ethereum::new_frontier_test();
let mut spec = EvmTestClient::spec_from_json(&chain.network).unwrap();
let state = chain.pre_state.clone().into();
spec.set_genesis_state(state)
.expect("unable to set genesis state");
@ -281,6 +281,26 @@ fn eth_get_block() {
);
}
#[test]
fn eth_get_max_priority_fee_per_gas() {
let chain = extract_non_legacy_chain!(
"BlockchainTests/ValidBlocks/bcEIP1559/transType",
ForkSpec::London
);
let tester = EthTester::from_chain(&chain);
let request = r#"{"method":"eth_maxPriorityFeePerGas","params":[],"id":1,"jsonrpc":"2.0"}"#;
// We are expecting for 50-th percentile of the previous 100 blocks transactions priority fees.
//
// Sorted priority fees: 0x64 0x64 0x64 0x7d 0x7d 0xea 0x149.
// Currently, the way 50-th percentile is calculated, the 3rd fee would be the result.
let response = r#"{"jsonrpc":"2.0","result":"0x64","id":1}"#;
assert_eq!(
tester.handler.handle_request_sync(request).unwrap(),
response
)
}
#[test]
fn eth_get_block_by_hash() {
let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test");

View File

@ -335,6 +335,7 @@ impl MinerService for TestMinerService {
tx_gas_limit: 5_000_000.into(),
no_early_reject: false,
block_base_fee: None,
allow_non_eoa_sender: false,
},
status: txpool::LightStatus {
mem_usage: 1_000,
@ -359,6 +360,10 @@ impl MinerService for TestMinerService {
20_000_000_000u64.into()
}
fn sensible_max_priority_fee(&self) -> U256 {
2_000_000_000u64.into()
}
fn sensible_gas_limit(&self) -> U256 {
0x5208.into()
}

View File

@ -23,7 +23,7 @@ use std::{
use accounts::AccountProvider;
use ethcore::{
client::{BlockChainClient, EachBlockWith, Executed, TestBlockChainClient},
client::{BlockChainClient, EachBlockWith, EvmTestClient, Executed, TestBlockChainClient},
miner::{self, MinerService},
};
use ethereum_types::{Address, Bloom, H160, H256, U256};
@ -51,6 +51,12 @@ fn blockchain_client() -> Arc<TestBlockChainClient> {
Arc::new(client)
}
fn eip1559_blockchain_client() -> Arc<TestBlockChainClient> {
let spec = EvmTestClient::spec_from_json(&ethjson::spec::ForkSpec::London).unwrap();
let client = TestBlockChainClient::new_with_spec(spec);
Arc::new(client)
}
fn accounts_provider() -> Arc<AccountProvider> {
Arc::new(AccountProvider::transient_provider())
}
@ -89,8 +95,25 @@ impl Default for EthTester {
impl EthTester {
pub fn new_with_options(options: EthClientOptions) -> Self {
let runtime = Runtime::with_thread_count(1);
let client = blockchain_client();
EthTester::new_with_client_and_options(client, options)
}
fn new_eip1559_with_options(options: EthClientOptions) -> Self {
let client = eip1559_blockchain_client();
EthTester::new_with_client_and_options(client, options)
}
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
self.client.add_blocks(count, with);
self.sync.increase_imported_block_number(count as u64);
}
fn new_with_client_and_options(
client: Arc<TestBlockChainClient>,
options: EthClientOptions,
) -> Self {
let runtime = Runtime::with_thread_count(1);
let sync = sync_provider();
let ap = accounts_provider();
let ap2 = ap.clone();
@ -126,11 +149,6 @@ impl EthTester {
hashrates,
}
}
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
self.client.add_blocks(count, with);
self.sync.increase_imported_block_number(count as u64);
}
}
#[test]
@ -537,6 +555,33 @@ fn rpc_eth_gas_price() {
);
}
#[test]
fn rpc_eth_get_max_priority_fee_per_gas() {
let tester = EthTester::new_eip1559_with_options(Default::default());
let request = r#"{"method":"eth_maxPriorityFeePerGas","params":[],"id":1,"jsonrpc":"2.0"}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x77359400","id":1}"#; // 2 GWei
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_eth_get_max_priority_fee_per_gas_error() {
let tester = EthTester::default();
let request = r#"{"method":"eth_maxPriorityFeePerGas","params":[],"id":1,"jsonrpc":"2.0"}"#;
let response =
r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"EIP-1559 is not activated"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_eth_accounts() {
let tester = EthTester::default();
@ -720,7 +765,7 @@ fn rpc_eth_pending_transaction_by_hash() {
.insert(H256::zero(), tx);
}
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"type":"0x0","v":"0x1b","value":"0xa"},"id":1}"#;
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
@ -1184,6 +1229,7 @@ fn rpc_eth_transaction_receipt() {
}],
log_bloom: Bloom::zero(),
outcome: TransactionOutcome::StateRoot(H256::zero()),
effective_gas_price: Default::default(),
};
let hash =
@ -1199,7 +1245,7 @@ fn rpc_eth_transaction_receipt() {
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","removed":false,"topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","effectiveGasPrice":"0x0","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","removed":false,"topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"0x0"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
@ -1242,6 +1288,7 @@ fn rpc_eth_pending_receipt() {
logs: Vec::new(),
log_bloom: Bloom::zero(),
outcome: TransactionOutcome::Unknown,
effective_gas_price: Default::default(),
};
let tester = EthTester::default();
@ -1253,7 +1300,7 @@ fn rpc_eth_pending_receipt() {
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238","transactionIndex":"0x0"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"contractAddress":null,"cumulativeGasUsed":"0x20","effectiveGasPrice":"0x0","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238","transactionIndex":"0x0","type":"0x0"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())

View File

@ -561,6 +561,7 @@ fn rpc_parity_block_receipts() {
outcome: TransactionOutcome::Unknown,
to: None,
from: Address::from_low_u64_be(9),
effective_gas_price: Default::default(),
},
);
let io = deps.default_client();
@ -571,7 +572,7 @@ fn rpc_parity_block_receipts() {
"params": [],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","effectiveGasPrice":"0x0","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0","type":"0x0"}],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@ -202,7 +202,7 @@ fn rpc_parity_remove_transaction() {
.to_owned()
+ &format!("0x{:x}", hash)
+ r#""], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x49569012bc8523519642c337fded3f20ba987beab31e14c67223b3d31359956f","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a801f0101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x1f","value":"0x9184e72a"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x49569012bc8523519642c337fded3f20ba987beab31e14c67223b3d31359956f","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a801f0101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"type":"0x0","v":"0x1f","value":"0x9184e72a"},"id":1}"#;
miner.pending_transactions.lock().insert(hash, signed);
assert_eq!(io.handle_request_sync(&request), Some(response.to_owned()));

View File

@ -656,6 +656,7 @@ fn should_confirm_sign_transaction_with_rlp() {
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ r#""type":"0x0","#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.v()))
+ r#""value":"0x1""#
+ r#"}},"id":1}"#;

View File

@ -427,6 +427,7 @@ fn should_add_sign_transaction_to_the_queue() {
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ r#""type":"0x0","#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.v()))
+ r#""value":"0x9184e72a""#
+ r#"}},"id":1}"#;

View File

@ -227,6 +227,7 @@ fn rpc_eth_sign_transaction() {
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ r#""type":"0x0","#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.v()))
+ r#""value":"0x9184e72a""#
+ r#"}},"id":1}"#;

View File

@ -51,6 +51,23 @@ macro_rules! register_test {
};
}
macro_rules! extract_non_legacy_chain {
($file: expr, $network: expr) => {{
const RAW_DATA: &'static [u8] = include_bytes!(concat!(
"../../../../ethcore/res/json_tests/",
$file,
".json"
));
::ethjson::blockchain::Test::load(RAW_DATA)
.unwrap()
.into_iter()
.filter(|&(_, ref t)| t.network == $network)
.next()
.unwrap()
.1
}};
}
#[cfg(test)]
mod eth;
#[cfg(test)]

View File

@ -20,8 +20,8 @@ use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_derive::rpc;
use v1::types::{
BlockNumber, Bytes, CallRequest, EthAccount, Filter, FilterChanges, Index, Log, Receipt,
RichBlock, SyncStatus, Transaction, Work,
BlockNumber, Bytes, CallRequest, EthAccount, EthFeeHistory, Filter, FilterChanges, Index, Log,
Receipt, RichBlock, SyncStatus, Transaction, Work,
};
/// Eth rpc interface.
@ -60,6 +60,15 @@ pub trait Eth {
#[rpc(name = "eth_gasPrice")]
fn gas_price(&self) -> BoxFuture<U256>;
/// Returns current max_priority_fee
#[rpc(name = "eth_maxPriorityFeePerGas")]
fn max_priority_fee_per_gas(&self) -> BoxFuture<U256>;
/// Returns transaction fee history.
#[rpc(name = "eth_feeHistory")]
fn fee_history(&self, _: U256, _: BlockNumber, _: Option<Vec<f64>>)
-> BoxFuture<EthFeeHistory>;
/// Returns accounts list.
#[rpc(name = "eth_accounts")]
fn accounts(&self) -> Result<Vec<H160>>;

View File

@ -229,7 +229,7 @@ mod tests {
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(
serialized,
r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"v":"0x0","r":"0x0","s":"0x0","condition":null}]"#
r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"v":"0x0","r":"0x0","s":"0x0","condition":null}]"#
);
let t = BlockTransactions::Hashes(vec![H256::default().into()]);

View File

@ -0,0 +1,30 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Return types for RPC calls
use ethereum_types::U256;
use v1::types::BlockNumber;
/// Account information.
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EthFeeHistory {
pub oldest_block: BlockNumber,
pub base_fee_per_gas: Vec<U256>,
pub gas_used_ratio: Vec<f64>,
pub reward: Option<Vec<Vec<U256>>>,
}

View File

@ -29,6 +29,7 @@ pub use self::{
},
derivation::{Derive, DeriveHash, DeriveHierarchical},
eip191::{EIP191Version, PresignedTransaction},
fee_history::EthFeeHistory,
filter::{Filter, FilterChanges},
histogram::Histogram,
index::Index,
@ -62,6 +63,7 @@ mod call_request;
mod confirmations;
mod derivation;
mod eip191;
mod fee_history;
mod filter;
mod histogram;
mod index;

View File

@ -55,6 +55,8 @@ pub struct Receipt {
// NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it
#[serde(skip_serializing_if = "Option::is_none", rename = "status")]
pub status_code: Option<U64>,
/// Effective gas price
pub effective_gas_price: U256,
}
impl Receipt {
@ -90,6 +92,7 @@ impl From<LocalizedReceipt> for Receipt {
status_code: Self::outcome_to_status_code(&r.outcome),
state_root: Self::outcome_to_state_root(r.outcome),
logs_bloom: r.log_bloom,
effective_gas_price: r.effective_gas_price,
}
}
}
@ -111,6 +114,7 @@ impl From<RichReceipt> for Receipt {
status_code: Self::outcome_to_status_code(&r.outcome),
state_root: Self::outcome_to_state_root(r.outcome),
logs_bloom: r.log_bloom,
effective_gas_price: r.effective_gas_price,
}
}
}
@ -134,6 +138,7 @@ impl From<TypedReceipt> for Receipt {
status_code: Self::outcome_to_status_code(&legacy_receipt.outcome),
state_root: Self::outcome_to_state_root(legacy_receipt.outcome),
logs_bloom: legacy_receipt.log_bloom,
effective_gas_price: Default::default(),
}
}
}
@ -147,7 +152,7 @@ mod tests {
#[test]
fn receipt_serialization() {
let s = r#"{"type":"0x1","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from":null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined","removed":false}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1"}"#;
let s = r#"{"type":"0x1","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from":null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined","removed":false}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","status":"0x1","effectiveGasPrice":"0x0"}"#;
let receipt = Receipt {
from: None,
@ -191,6 +196,7 @@ mod tests {
logs_bloom: Bloom::from_low_u64_be(15),
state_root: Some(H256::from_low_u64_be(10)),
status_code: Some(1u64.into()),
effective_gas_price: Default::default(),
};
let serialized = serde_json::to_string(&receipt).unwrap();

View File

@ -50,8 +50,7 @@ pub struct Transaction {
/// Transfered value
pub value: U256,
/// Gas Price
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_price: Option<U256>,
pub gas_price: U256,
/// Max fee per gas
#[serde(skip_serializing_if = "Option::is_none")]
pub max_fee_per_gas: Option<U256>,
@ -190,7 +189,7 @@ impl RichRawTransaction {
impl Transaction {
/// Convert `LocalizedTransaction` into RPC Transaction.
pub fn from_localized(mut t: LocalizedTransaction) -> Transaction {
pub fn from_localized(mut t: LocalizedTransaction, base_fee: Option<U256>) -> Transaction {
let signature = t.signature();
let scheme = CreateContractAddress::FromSenderAndNonce;
@ -209,17 +208,11 @@ impl Transaction {
TypedTransaction::Legacy(_) => None,
};
let (gas_price, max_fee_per_gas) = match t.as_unsigned() {
TypedTransaction::Legacy(_) => (Some(t.tx().gas_price), None),
TypedTransaction::AccessList(_) => (Some(t.tx().gas_price), None),
TypedTransaction::EIP1559Transaction(_) => (None, Some(t.tx().gas_price)),
};
let max_priority_fee_per_gas =
let (max_fee_per_gas, max_priority_fee_per_gas) =
if let TypedTransaction::EIP1559Transaction(tx) = t.as_unsigned() {
Some(tx.max_priority_fee_per_gas)
(Some(tx.tx().gas_price), Some(tx.max_priority_fee_per_gas))
} else {
None
(None, None)
};
let standard_v = if t.tx_type() == TypedTxId::Legacy {
@ -240,7 +233,7 @@ impl Transaction {
Action::Call(ref address) => Some(*address),
},
value: t.tx().value,
gas_price,
gas_price: t.effective_gas_price(base_fee),
max_fee_per_gas,
gas: t.tx().gas,
input: Bytes::new(t.tx().data.clone()),
@ -284,17 +277,11 @@ impl Transaction {
TypedTransaction::Legacy(_) => None,
};
let (gas_price, max_fee_per_gas) = match t.as_unsigned() {
TypedTransaction::Legacy(_) => (Some(t.tx().gas_price), None),
TypedTransaction::AccessList(_) => (Some(t.tx().gas_price), None),
TypedTransaction::EIP1559Transaction(_) => (None, Some(t.tx().gas_price)),
};
let max_priority_fee_per_gas =
let (max_fee_per_gas, max_priority_fee_per_gas) =
if let TypedTransaction::EIP1559Transaction(tx) = t.as_unsigned() {
Some(tx.max_priority_fee_per_gas)
(Some(tx.tx().gas_price), Some(tx.max_priority_fee_per_gas))
} else {
None
(None, None)
};
let standard_v = if t.tx_type() == TypedTxId::Legacy {
@ -315,7 +302,7 @@ impl Transaction {
Action::Call(ref address) => Some(*address),
},
value: t.tx().value,
gas_price,
gas_price: t.tx().gas_price,
max_fee_per_gas,
gas: t.tx().gas,
input: Bytes::new(t.tx().data.clone()),
@ -387,7 +374,7 @@ mod tests {
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(
serialized,
r#"{"type":"0x1","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"v":"0x0","r":"0x0","s":"0x0","condition":null,"accessList":[{"address":"0x0000000000000000000000000000000000000000","storageKeys":[]}]}"#
r#"{"type":"0x1","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"v":"0x0","r":"0x0","s":"0x0","condition":null,"accessList":[{"address":"0x0000000000000000000000000000000000000000","storageKeys":[]}]}"#
);
}

View File

@ -113,4 +113,7 @@ pub trait VerifiedTransaction: fmt::Debug {
/// Transaction sender
fn sender(&self) -> &Self::Sender;
/// Does it have zero gas price?
fn has_zero_gas_price(&self) -> bool;
}

View File

@ -650,7 +650,9 @@ where
match self.ready.is_ready(&tx) {
Readiness::Ready => {
//return transaction with score higher or equal to desired
if score >= &self.includable_boundary {
if score >= &self.includable_boundary
|| tx.transaction.has_zero_gas_price()
{
return Some(tx.transaction.clone());
}
}
@ -737,7 +739,9 @@ where
if tx_state == Readiness::Ready {
//return transaction with score higher or equal to desired
if best.score >= self.includable_boundary {
if best.score >= self.includable_boundary
|| best.transaction.transaction.has_zero_gas_price()
{
return Some(best.transaction.transaction);
}
}

View File

@ -50,6 +50,9 @@ impl VerifiedTransaction for Transaction {
fn sender(&self) -> &Address {
&self.sender
}
fn has_zero_gas_price(&self) -> bool {
false
}
}
pub type SharedTransaction = Arc<Transaction>;

View File

@ -1,7 +1,7 @@
[package]
name = "parity-version"
# NOTE: this value is used for OpenEthereum version string (via env CARGO_PKG_VERSION)
version = "3.3.0-rc.1"
version = "3.3.3"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@ -203,11 +203,11 @@ impl ModexpPricer {
let (base_len, exp_len) = (base_len_u256.low_u64(), exp_len_u256.low_u64());
// read fist 32-byte word of the exponent.
let exp_low = if base_len + 96 >= input.len() as u64 {
let exp_low = if base_len.wrapping_add(96) >= input.len() as u64 {
U256::zero()
} else {
buf.iter_mut().for_each(|b| *b = 0);
let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0));
let mut reader = input[(base_len as usize).wrapping_add(96)..].chain(io::repeat(0));
let len = min(exp_len, 32) as usize;
reader
.read_exact(&mut buf[(32 - len)..])
@ -1728,6 +1728,33 @@ mod tests {
native: EthereumBuiltin::from_str("modexp").unwrap(),
};
// test for potential base len overflow
{
let input = hex!(
"
00000000000000000000000000000000ffffffffffffffffffffffffffffffff
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000001
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
03
ff"
);
let expected_cost = U256::max_value();
assert_eq!(f.cost(&input[..], 0), expected_cost);
}
// another test for potential base len overflow
{
let input = hex!(
"
00000000000000000000000000000000ffffffffffffffffffffffffffffffff
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020"
);
let expected_cost = U256::max_value();
assert_eq!(f.cost(&input[..], 0), expected_cost);
}
// test for potential gas cost multiplication overflow
{
let input = hex!("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3");
@ -1840,6 +1867,38 @@ mod tests {
}
}
#[test]
fn modexp2565() {
let pricer = Modexp2565Pricer {};
// test for potential base len overflow
{
let input = hex!(
"
00000000000000000000000000000000ffffffffffffffffffffffffffffffff
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000001
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
03
ff"
);
let expected_cost = U256::max_value();
assert_eq!(pricer.cost(&input[..]), expected_cost);
}
// another test for potential base len overflow
{
let input = hex!(
"
00000000000000000000000000000000ffffffffffffffffffffffffffffffff
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020"
);
let expected_cost = U256::max_value();
assert_eq!(pricer.cost(&input[..]), expected_cost);
}
}
#[test]
fn bn128_add() {
let f = Builtin {