[stable] Backports (#8133)
* updater: apply exponential backoff after download failure (#8059)
* updater: apply exponential backoff after download failure
* updater: reset backoff on new release
* Limit incoming connections. (#8060)
* Limit ingress connections
* Optimized handshakes logging
* Max code size on Kovan (#8067)
* Enable code size limit on kovan
* Fix formatting.
* add some dos protection (#8084)
* more dos protection (#8104)
* Const time comparison (#8113)
* Use `subtle::slices_equal` for constant time comparison.
Also update the existing version of subtle in `ethcrypto` from
0.1 to 0.5
* Test specifically for InvalidPassword error.
* revert removing blooms (#8066)
* Revert "fix traces, removed bloomchain crate, closes #7228, closes #7167"
This reverts commit 1bf62038678295e5586f02a38a0c5aab9a9efe62.
* Revert "fixed broken logs (#7934)"
This reverts commit f8a2e53f3e.
* fixed broken logs
* bring back old lock order
* remove migration v13
* revert CURRENT_VERSION to 12 in migration.rs
* Fix compilation.
* Check one step deeper if we're on release track branches
* add missing pr
* Fix blooms?
* Fix tests compiilation.
* Fix size.
This commit is contained in:
committed by
Marek Kotewicz
parent
3c15a501ca
commit
feef2f8791
@@ -8,6 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.9"
|
||||
bloomchain = { path = "../util/bloomchain" }
|
||||
bn = { git = "https://github.com/paritytech/bn" }
|
||||
byteorder = "1.0"
|
||||
common-types = { path = "types" }
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": "0x0",
|
||||
"eip98Transition": "0xffffffffffffffff",
|
||||
"eip140Transition": "0x0",
|
||||
"eip211Transition": "0x0",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": "0x0",
|
||||
"eip98Transition": "0xffffffffffffffff",
|
||||
"eip140Transition": "0x0",
|
||||
"eip210Transition": "0x0",
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"maxCodeSize": 24576
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": "0x7fffffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"maxCodeSize": 24576
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": "0x0"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff",
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": 2675000,
|
||||
"eip140Transition": 4370000,
|
||||
"eip211Transition": 4370000,
|
||||
"eip214Transition": 4370000,
|
||||
|
||||
@@ -34,9 +34,11 @@
|
||||
"networkID" : "0x2A",
|
||||
"forkBlock": 4297256,
|
||||
"forkCanonHash": "0x0a66d93c2f727dca618fabaf70c39b37018c73d78b939d8b11efbbd09034778f",
|
||||
"validateReceiptsTransition" : 1000000,
|
||||
"eip155Transition": 1000000,
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": 6500000,
|
||||
"validateChainIdTransition": 1000000,
|
||||
"validateReceiptsTransition" : 1000000,
|
||||
"eip140Transition": 5067000,
|
||||
"eip211Transition": 5067000,
|
||||
"eip214Transition": 5067000,
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"eip211Transition":"0x7fffffffffffff",
|
||||
"eip214Transition":"0x7fffffffffffff",
|
||||
"eip658Transition":"0x7fffffffffffff",
|
||||
"maxCodeSize":"0x6000"
|
||||
"maxCodeSize":"0x6000",
|
||||
"maxCodeSizeTransition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"eip211Transition":"0x2a",
|
||||
"eip214Transition":"0x2a",
|
||||
"eip658Transition":"0x2a",
|
||||
"maxCodeSize":"0x6000"
|
||||
"maxCodeSize":"0x6000",
|
||||
"maxCodeSizeTransition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"eip211Transition":"0x21e88e",
|
||||
"eip214Transition":"0x21e88e",
|
||||
"eip658Transition":"0x21e88e",
|
||||
"maxCodeSize":"0x6000"
|
||||
"maxCodeSize":"0x6000",
|
||||
"maxCodeSizeTransition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"forkBlock": 641350,
|
||||
"forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb",
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": 10,
|
||||
"eip155Transition": 10,
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": "0",
|
||||
"eip98Transition": "5",
|
||||
"eip140Transition": "5",
|
||||
"eip211Transition": "5",
|
||||
|
||||
@@ -32,7 +32,7 @@ pub struct BlockInfo {
|
||||
}
|
||||
|
||||
/// Describes location of newly inserted block.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BlockLocation {
|
||||
/// It's part of the canon chain.
|
||||
CanonChain,
|
||||
@@ -44,7 +44,7 @@ pub enum BlockLocation {
|
||||
BranchBecomingCanonChain(BranchBecomingCanonChainData),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BranchBecomingCanonChainData {
|
||||
/// Hash of the newest common ancestor with old canon chain.
|
||||
pub ancestor: H256,
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
//! Blockchain database.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{HashMap, HashSet, hash_map};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
use itertools::Itertools;
|
||||
use bloomchain as bc;
|
||||
use bloomchain::Bloom;
|
||||
use heapsize::HeapSizeOf;
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::{H256, H2048};
|
||||
use bigint::hash::{H256};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use bytes::Bytes;
|
||||
use rlp::*;
|
||||
@@ -32,6 +34,7 @@ use transaction::*;
|
||||
use views::*;
|
||||
use log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use receipt::Receipt;
|
||||
use blooms::{BloomGroup, GroupPosition};
|
||||
use blockchain::best_block::{BestBlock, BestAncientBlock};
|
||||
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
@@ -46,6 +49,9 @@ use rayon::prelude::*;
|
||||
use ansi_term::Colour;
|
||||
use kvdb::{DBTransaction, KeyValueDB};
|
||||
|
||||
const LOG_BLOOMS_LEVELS: usize = 3;
|
||||
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
|
||||
|
||||
/// Interface for querying blocks by hash and by number.
|
||||
pub trait BlockProvider {
|
||||
/// Returns true if the given block is known
|
||||
@@ -145,7 +151,7 @@ pub trait BlockProvider {
|
||||
}
|
||||
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
fn blocks_with_blooms(&self, bloom: &[H2048], from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
||||
fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
||||
|
||||
/// Returns logs matching given filter.
|
||||
fn logs<F>(&self, blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
@@ -163,14 +169,26 @@ enum CacheId {
|
||||
BlockDetails(H256),
|
||||
BlockHashes(BlockNumber),
|
||||
TransactionAddresses(H256),
|
||||
BlocksBlooms(GroupPosition),
|
||||
BlockReceipts(H256),
|
||||
}
|
||||
|
||||
impl bc::group::BloomGroupDatabase for BlockChain {
|
||||
fn blooms_at(&self, position: &bc::group::GroupPosition) -> Option<bc::group::BloomGroup> {
|
||||
let position = GroupPosition::from(position.clone());
|
||||
let result = self.db.read_with_cache(db::COL_EXTRA, &self.blocks_blooms, &position).map(Into::into);
|
||||
self.cache_man.lock().note_used(CacheId::BlocksBlooms(position));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure providing fast access to blockchain data.
|
||||
///
|
||||
/// **Does not do input data verification.**
|
||||
pub struct BlockChain {
|
||||
// All locks must be captured in the order declared here.
|
||||
blooms_config: bc::Config,
|
||||
|
||||
best_block: RwLock<BestBlock>,
|
||||
// Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps.
|
||||
// Only updated with `insert_unordered_block`.
|
||||
@@ -187,6 +205,7 @@ pub struct BlockChain {
|
||||
block_details: RwLock<HashMap<H256, BlockDetails>>,
|
||||
block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
||||
transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
|
||||
blocks_blooms: RwLock<HashMap<GroupPosition, BloomGroup>>,
|
||||
block_receipts: RwLock<HashMap<H256, BlockReceipts>>,
|
||||
|
||||
db: Arc<KeyValueDB>,
|
||||
@@ -336,14 +355,13 @@ impl BlockProvider for BlockChain {
|
||||
result
|
||||
}
|
||||
|
||||
fn blocks_with_blooms(&self, blooms: &[H2048], from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber> {
|
||||
// +1, cause it's inclusive range
|
||||
(from_block..to_block + 1)
|
||||
.into_par_iter()
|
||||
.filter_map(|number| self.block_hash(number).map(|hash| (number, hash)))
|
||||
.map(|(number, hash)| (number, self.block_header_data(&hash).expect("hash exists; qed")))
|
||||
.filter(|&(_, ref header)| blooms.iter().any(|bloom| header.view().log_bloom().contains(bloom)))
|
||||
.map(|(number, _)| number)
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber> {
|
||||
let range = from_block as bc::Number..to_block as bc::Number;
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
chain.with_bloom(&range, bloom)
|
||||
.into_iter()
|
||||
.map(|b| b as BlockNumber)
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -359,15 +377,19 @@ impl BlockProvider for BlockChain {
|
||||
.filter_map(|number| self.block_hash(*number).map(|hash| (*number, hash)))
|
||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes())))
|
||||
.flat_map(|(number, hash, receipts, hashes)| {
|
||||
assert_eq!(receipts.len(), hashes.len(), "Block {} ({}) has different number of receipts ({}) to transactions ({})", number, hash, receipts.len(), hashes.len());
|
||||
let mut log_index: usize = receipts.iter().map(|r| r.logs.len()).sum();
|
||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||
if receipts.len() != hashes.len() {
|
||||
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
|
||||
assert!(false);
|
||||
}
|
||||
let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
||||
|
||||
let receipts_len = receipts.len();
|
||||
hashes.reverse();
|
||||
receipts.reverse();
|
||||
receipts.into_iter()
|
||||
.map(|receipt| receipt.logs)
|
||||
.zip(hashes)
|
||||
.rev()
|
||||
.enumerate()
|
||||
.flat_map(move |(index, (mut logs, tx_hash))| {
|
||||
let current_log_index = log_index;
|
||||
@@ -471,6 +493,10 @@ impl BlockChain {
|
||||
let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400);
|
||||
|
||||
let mut bc = BlockChain {
|
||||
blooms_config: bc::Config {
|
||||
levels: LOG_BLOOMS_LEVELS,
|
||||
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
||||
},
|
||||
first_block: None,
|
||||
best_block: RwLock::new(BestBlock::default()),
|
||||
best_ancient_block: RwLock::new(None),
|
||||
@@ -479,6 +505,7 @@ impl BlockChain {
|
||||
block_details: RwLock::new(HashMap::new()),
|
||||
block_hashes: RwLock::new(HashMap::new()),
|
||||
transaction_addresses: RwLock::new(HashMap::new()),
|
||||
blocks_blooms: RwLock::new(HashMap::new()),
|
||||
block_receipts: RwLock::new(HashMap::new()),
|
||||
db: db.clone(),
|
||||
cache_man: Mutex::new(cache_man),
|
||||
@@ -730,6 +757,7 @@ impl BlockChain {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
info: info,
|
||||
timestamp: header.timestamp(),
|
||||
@@ -778,6 +806,7 @@ impl BlockChain {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: update,
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
info: info,
|
||||
timestamp: header.timestamp(),
|
||||
@@ -925,6 +954,7 @@ impl BlockChain {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
info: info.clone(),
|
||||
timestamp: header.timestamp(),
|
||||
@@ -982,22 +1012,50 @@ impl BlockChain {
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_receipts, update.block_receipts, CacheUpdatePolicy::Remove);
|
||||
}
|
||||
|
||||
{
|
||||
let mut write_blocks_blooms = self.blocks_blooms.write();
|
||||
// update best block
|
||||
match update.info.location {
|
||||
BlockLocation::Branch => (),
|
||||
BlockLocation::BranchBecomingCanonChain(_) => {
|
||||
// clear all existing blooms, cause they may be created for block
|
||||
// number higher than current best block
|
||||
*write_blocks_blooms = update.blocks_blooms;
|
||||
for (key, value) in write_blocks_blooms.iter() {
|
||||
batch.write(db::COL_EXTRA, key, value);
|
||||
}
|
||||
},
|
||||
BlockLocation::CanonChain => {
|
||||
// update all existing blooms groups
|
||||
for (key, value) in update.blocks_blooms {
|
||||
match write_blocks_blooms.entry(key) {
|
||||
hash_map::Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().accrue_bloom_group(&value);
|
||||
batch.write(db::COL_EXTRA, entry.key(), entry.get());
|
||||
},
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
batch.write(db::COL_EXTRA, entry.key(), &value);
|
||||
entry.insert(value);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// These cached values must be updated last with all four locks taken to avoid
|
||||
// cache decoherence
|
||||
{
|
||||
let mut best_block = self.pending_best_block.write();
|
||||
match update.info.location {
|
||||
BlockLocation::Branch => (),
|
||||
_ => if is_best {
|
||||
batch.put(db::COL_EXTRA, b"best", &update.info.hash);
|
||||
*best_block = Some(BestBlock {
|
||||
hash: update.info.hash,
|
||||
number: update.info.number,
|
||||
total_difficulty: update.info.total_difficulty,
|
||||
timestamp: update.timestamp,
|
||||
block: update.block.to_vec(),
|
||||
});
|
||||
}
|
||||
if is_best && update.info.location != BlockLocation::Branch {
|
||||
batch.put(db::COL_EXTRA, b"best", &update.info.hash);
|
||||
*best_block = Some(BestBlock {
|
||||
hash: update.info.hash,
|
||||
number: update.info.number,
|
||||
total_difficulty: update.info.total_difficulty,
|
||||
timestamp: update.timestamp,
|
||||
block: update.block.to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut write_hashes = self.pending_block_hashes.write();
|
||||
@@ -1216,6 +1274,59 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// This functions returns modified blocks blooms.
|
||||
///
|
||||
/// To accelerate blooms lookups, blomms are stored in multiple
|
||||
/// layers (BLOOM_LEVELS, currently 3).
|
||||
/// ChainFilter is responsible for building and rebuilding these layers.
|
||||
/// It returns them in HashMap, where values are Blooms and
|
||||
/// keys are BloomIndexes. BloomIndex represents bloom location on one
|
||||
/// of these layers.
|
||||
///
|
||||
/// To reduce number of queries to databse, block blooms are stored
|
||||
/// in BlocksBlooms structure which contains info about several
|
||||
/// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms.
|
||||
///
|
||||
/// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex)
|
||||
/// to bloom location in database (BlocksBloomLocation).
|
||||
///
|
||||
fn prepare_block_blooms_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<GroupPosition, BloomGroup> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header_view();
|
||||
|
||||
let log_blooms = match info.location {
|
||||
BlockLocation::Branch => HashMap::new(),
|
||||
BlockLocation::CanonChain => {
|
||||
let log_bloom = header.log_bloom();
|
||||
if log_bloom.is_zero() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
chain.insert(info.number as bc::Number, log_bloom)
|
||||
}
|
||||
},
|
||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||
let ancestor_number = self.block_number(&data.ancestor).unwrap();
|
||||
let start_number = ancestor_number + 1;
|
||||
let range = start_number as bc::Number..self.best_block_number() as bc::Number;
|
||||
|
||||
let mut blooms: Vec<Bloom> = data.enacted.iter()
|
||||
.map(|hash| self.block_header_data(hash).unwrap())
|
||||
.map(|h| h.log_bloom())
|
||||
.collect();
|
||||
|
||||
blooms.push(header.log_bloom());
|
||||
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
chain.replace(&range, blooms)
|
||||
}
|
||||
};
|
||||
|
||||
log_blooms.into_iter()
|
||||
.map(|p| (From::from(p.0), From::from(p.1)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get best block hash.
|
||||
pub fn best_block_hash(&self) -> H256 {
|
||||
self.best_block.read().hash
|
||||
@@ -1249,6 +1360,7 @@ impl BlockChain {
|
||||
blocks: self.block_headers.read().heap_size_of_children() + self.block_bodies.read().heap_size_of_children(),
|
||||
block_details: self.block_details.read().heap_size_of_children(),
|
||||
transaction_addresses: self.transaction_addresses.read().heap_size_of_children(),
|
||||
blocks_blooms: self.blocks_blooms.read().heap_size_of_children(),
|
||||
block_receipts: self.block_receipts.read().heap_size_of_children(),
|
||||
}
|
||||
}
|
||||
@@ -1262,6 +1374,7 @@ impl BlockChain {
|
||||
let mut block_details = self.block_details.write();
|
||||
let mut block_hashes = self.block_hashes.write();
|
||||
let mut transaction_addresses = self.transaction_addresses.write();
|
||||
let mut blocks_blooms = self.blocks_blooms.write();
|
||||
let mut block_receipts = self.block_receipts.write();
|
||||
|
||||
let mut cache_man = self.cache_man.lock();
|
||||
@@ -1273,6 +1386,7 @@ impl BlockChain {
|
||||
CacheId::BlockDetails(ref h) => { block_details.remove(h); }
|
||||
CacheId::BlockHashes(ref h) => { block_hashes.remove(h); }
|
||||
CacheId::TransactionAddresses(ref h) => { transaction_addresses.remove(h); }
|
||||
CacheId::BlocksBlooms(ref h) => { blocks_blooms.remove(h); }
|
||||
CacheId::BlockReceipts(ref h) => { block_receipts.remove(h); }
|
||||
}
|
||||
}
|
||||
@@ -1282,6 +1396,7 @@ impl BlockChain {
|
||||
block_details.shrink_to_fit();
|
||||
block_hashes.shrink_to_fit();
|
||||
transaction_addresses.shrink_to_fit();
|
||||
blocks_blooms.shrink_to_fit();
|
||||
block_receipts.shrink_to_fit();
|
||||
|
||||
block_headers.heap_size_of_children() +
|
||||
@@ -1289,6 +1404,7 @@ impl BlockChain {
|
||||
block_details.heap_size_of_children() +
|
||||
block_hashes.heap_size_of_children() +
|
||||
transaction_addresses.heap_size_of_children() +
|
||||
blocks_blooms.heap_size_of_children() +
|
||||
block_receipts.heap_size_of_children()
|
||||
});
|
||||
}
|
||||
@@ -1971,46 +2087,46 @@ mod tests {
|
||||
let db = new_db();
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
assert!(blocks_b1.is_empty());
|
||||
assert!(blocks_b2.is_empty());
|
||||
|
||||
insert_block(&db, &bc, &b1.last().encoded(), vec![]);
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
assert_eq!(blocks_b1, vec![1]);
|
||||
assert!(blocks_b2.is_empty());
|
||||
|
||||
insert_block(&db, &bc, &b2.last().encoded(), vec![]);
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
assert_eq!(blocks_b1, vec![1]);
|
||||
assert_eq!(blocks_b2, vec![2]);
|
||||
|
||||
// hasn't been forked yet
|
||||
insert_block(&db, &bc, &b1a.last().encoded(), vec![]);
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_ba = bc.blocks_with_blooms(&[bloom_ba], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
let blocks_ba = bc.blocks_with_bloom(&bloom_ba, 0, 5);
|
||||
assert_eq!(blocks_b1, vec![1]);
|
||||
assert_eq!(blocks_b2, vec![2]);
|
||||
assert!(blocks_ba.is_empty());
|
||||
|
||||
// fork has happend
|
||||
insert_block(&db, &bc, &b2a.last().encoded(), vec![]);
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_ba = bc.blocks_with_blooms(&[bloom_ba], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
let blocks_ba = bc.blocks_with_bloom(&bloom_ba, 0, 5);
|
||||
assert!(blocks_b1.is_empty());
|
||||
assert!(blocks_b2.is_empty());
|
||||
assert_eq!(blocks_ba, vec![1, 2]);
|
||||
|
||||
// fork back
|
||||
insert_block(&db, &bc, &b3.last().encoded(), vec![]);
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 5);
|
||||
let blocks_ba = bc.blocks_with_blooms(&[bloom_ba], 0, 5);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
let blocks_ba = bc.blocks_with_bloom(&bloom_ba, 0, 5);
|
||||
assert_eq!(blocks_b1, vec![1]);
|
||||
assert_eq!(blocks_b2, vec![2]);
|
||||
assert_eq!(blocks_ba, vec![3]);
|
||||
@@ -2046,9 +2162,9 @@ mod tests {
|
||||
assert_eq!(bc.block_hash(2).unwrap(), b2.last().hash());
|
||||
assert_eq!(bc.block_hash(3).unwrap(), b3.last().hash());
|
||||
|
||||
let blocks_b1 = bc.blocks_with_blooms(&[bloom_b1], 0, 3);
|
||||
let blocks_b2 = bc.blocks_with_blooms(&[bloom_b2], 0, 3);
|
||||
let blocks_b3 = bc.blocks_with_blooms(&[bloom_b3], 0, 3);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 3);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 3);
|
||||
let blocks_b3 = bc.blocks_with_bloom(&bloom_b3, 0, 3);
|
||||
|
||||
assert_eq!(blocks_b1, vec![1]);
|
||||
assert_eq!(blocks_b2, vec![2]);
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct CacheSize {
|
||||
pub block_details: usize,
|
||||
/// Transaction addresses cache size.
|
||||
pub transaction_addresses: usize,
|
||||
/// Blooms cache size.
|
||||
pub blocks_blooms: usize,
|
||||
/// Block receipts size.
|
||||
pub block_receipts: usize,
|
||||
}
|
||||
@@ -30,6 +32,6 @@ pub struct CacheSize {
|
||||
impl CacheSize {
|
||||
/// Total amount used by the cache.
|
||||
pub fn total(&self) -> usize {
|
||||
self.blocks + self.block_details + self.transaction_addresses + self.block_receipts
|
||||
self.blocks + self.block_details + self.transaction_addresses + self.blocks_blooms + self.block_receipts
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use std::ops;
|
||||
use std::io::Write;
|
||||
use blooms::{GroupPosition, BloomGroup};
|
||||
use db::Key;
|
||||
use engines::epoch::{Transition as EpochTransition};
|
||||
use header::BlockNumber;
|
||||
@@ -37,6 +38,8 @@ pub enum ExtrasIndex {
|
||||
BlockHash = 1,
|
||||
/// Transaction address index
|
||||
TransactionAddress = 2,
|
||||
/// Block blooms index
|
||||
BlocksBlooms = 3,
|
||||
/// Block receipts index
|
||||
BlockReceipts = 4,
|
||||
/// Epoch transition data index.
|
||||
@@ -84,6 +87,31 @@ impl Key<BlockDetails> for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LogGroupKey([u8; 6]);
|
||||
|
||||
impl ops::Deref for LogGroupKey {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<BloomGroup> for GroupPosition {
|
||||
type Target = LogGroupKey;
|
||||
|
||||
fn key(&self) -> Self::Target {
|
||||
let mut result = [0u8; 6];
|
||||
result[0] = ExtrasIndex::BlocksBlooms as u8;
|
||||
result[1] = self.level;
|
||||
result[2] = (self.index >> 24) as u8;
|
||||
result[3] = (self.index >> 16) as u8;
|
||||
result[4] = (self.index >> 8) as u8;
|
||||
result[5] = self.index as u8;
|
||||
LogGroupKey(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<TransactionAddress> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
//! Blockchain generator for tests.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use bigint::prelude::{U256, H256, H2048 as Bloom};
|
||||
use bigint::prelude::{U256, H256};
|
||||
use bloomchain::Bloom;
|
||||
|
||||
use bytes::Bytes;
|
||||
use header::Header;
|
||||
|
||||
@@ -3,6 +3,7 @@ use bigint::hash::H256;
|
||||
use header::BlockNumber;
|
||||
use blockchain::block_info::BlockInfo;
|
||||
use blockchain::extras::{BlockDetails, BlockReceipts, TransactionAddress};
|
||||
use blooms::{BloomGroup, GroupPosition};
|
||||
|
||||
/// Block extras update info.
|
||||
pub struct ExtrasUpdate<'a> {
|
||||
@@ -18,6 +19,8 @@ pub struct ExtrasUpdate<'a> {
|
||||
pub block_details: HashMap<H256, BlockDetails>,
|
||||
/// Modified block receipts.
|
||||
pub block_receipts: HashMap<H256, BlockReceipts>,
|
||||
/// Modified blocks blooms.
|
||||
pub blocks_blooms: HashMap<GroupPosition, BloomGroup>,
|
||||
/// Modified transaction addresses (None signifies removed transactions).
|
||||
pub transactions_addresses: HashMap<H256, Option<TransactionAddress>>,
|
||||
}
|
||||
|
||||
@@ -28,13 +28,6 @@ impl From<LogBloom> for Bloom {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bc::Bloom> for Bloom {
|
||||
fn from(bloom: bc::Bloom) -> Self {
|
||||
let bytes: [u8; 256] = bloom.into();
|
||||
Bloom(LogBloom::from(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<bc::Bloom> for Bloom {
|
||||
fn into(self) -> bc::Bloom {
|
||||
let log = self.0;
|
||||
|
||||
55
ethcore/src/blooms/bloom_group.rs
Normal file
55
ethcore/src/blooms/bloom_group.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use bloomchain::group as bc;
|
||||
use bloomchain::{Bloom, BloomCompat};
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
/// Represents group of X consecutive blooms.
|
||||
#[derive(Debug, Clone, RlpEncodableWrapper, RlpDecodableWrapper)]
|
||||
pub struct BloomGroup {
|
||||
blooms: Vec<Bloom>,
|
||||
}
|
||||
|
||||
impl BloomGroup {
|
||||
pub fn accrue_bloom_group(&mut self, group: &BloomGroup) {
|
||||
for (bloom, other) in self.blooms.iter_mut().zip(group.blooms.iter()) {
|
||||
bloom.accrue_bloom(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bc::BloomGroup> for BloomGroup {
|
||||
fn from(group: bc::BloomGroup) -> Self {
|
||||
BloomGroup {
|
||||
blooms: group.blooms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<bc::BloomGroup> for BloomGroup {
|
||||
fn into(self) -> bc::BloomGroup {
|
||||
bc::BloomGroup {
|
||||
blooms: self.blooms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for BloomGroup {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.blooms.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
42
ethcore/src/blooms/group_position.rs
Normal file
42
ethcore/src/blooms/group_position.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use bloomchain::group as bc;
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
/// Represents `BloomGroup` position in database.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct GroupPosition {
|
||||
/// Bloom level.
|
||||
pub level: u8,
|
||||
/// Group index.
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
impl From<bc::GroupPosition> for GroupPosition {
|
||||
fn from(p: bc::GroupPosition) -> Self {
|
||||
GroupPosition {
|
||||
level: p.level as u8,
|
||||
index: p.index as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for GroupPosition {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1673,8 +1673,17 @@ impl BlockChainClient for Client {
|
||||
};
|
||||
|
||||
let chain = self.chain.read();
|
||||
let blocks = chain.blocks_with_blooms(&filter.bloom_possibilities(), from, to);
|
||||
chain.logs(blocks, |entry| filter.matches(entry), filter.limit)
|
||||
let blocks = filter.bloom_possibilities().iter()
|
||||
.map(move |bloom| {
|
||||
chain.blocks_with_bloom(bloom, from, to)
|
||||
})
|
||||
.flat_map(|m| m)
|
||||
// remove duplicate elements
|
||||
.collect::<HashSet<u64>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<u64>>();
|
||||
|
||||
self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)
|
||||
}
|
||||
|
||||
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
//! cargo build --release
|
||||
//! ```
|
||||
|
||||
extern crate bloomchain;
|
||||
extern crate bn;
|
||||
extern crate byteorder;
|
||||
extern crate crossbeam;
|
||||
@@ -155,6 +156,7 @@ pub mod views;
|
||||
|
||||
mod cache_manager;
|
||||
mod basic_types;
|
||||
mod blooms;
|
||||
mod pod_account;
|
||||
mod state_db;
|
||||
mod account_db;
|
||||
|
||||
@@ -264,15 +264,9 @@ impl EthereumMachine {
|
||||
} else if block_number < ext.eip150_transition {
|
||||
Schedule::new_homestead()
|
||||
} else {
|
||||
// There's no max_code_size transition so we tie it to eip161abc
|
||||
let max_code_size = if block_number >= ext.eip161abc_transition {
|
||||
self.params.max_code_size as usize
|
||||
} else {
|
||||
usize::max_value()
|
||||
};
|
||||
|
||||
let max_code_size = self.params.max_code_size(block_number);
|
||||
let mut schedule = Schedule::new_post_eip150(
|
||||
max_code_size,
|
||||
max_code_size as _,
|
||||
block_number >= ext.eip160_transition,
|
||||
block_number >= ext.eip161abc_transition,
|
||||
block_number >= ext.eip161d_transition
|
||||
|
||||
@@ -120,6 +120,8 @@ pub struct CommonParams {
|
||||
pub node_permission_contract: Option<Address>,
|
||||
/// Maximum contract code size that can be deployed.
|
||||
pub max_code_size: u64,
|
||||
/// Number of first block where max code size limit is active.
|
||||
pub max_code_size_transition: BlockNumber,
|
||||
/// Transaction permission managing contract address.
|
||||
pub transaction_permission_contract: Option<Address>,
|
||||
}
|
||||
@@ -127,11 +129,20 @@ pub struct CommonParams {
|
||||
impl CommonParams {
|
||||
/// Schedule for an EVM in the post-EIP-150-era of the Ethereum main net.
|
||||
pub fn schedule(&self, block_number: u64) -> ::vm::Schedule {
|
||||
let mut schedule = ::vm::Schedule::new_post_eip150(self.max_code_size as _, true, true, true);
|
||||
let mut schedule = ::vm::Schedule::new_post_eip150(self.max_code_size(block_number) as _, true, true, true);
|
||||
self.update_schedule(block_number, &mut schedule);
|
||||
schedule
|
||||
}
|
||||
|
||||
/// Returns max code size at given block.
|
||||
pub fn max_code_size(&self, block_number: u64) -> u64 {
|
||||
if block_number >= self.max_code_size_transition {
|
||||
self.max_code_size
|
||||
} else {
|
||||
u64::max_value()
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply common spec config parameters to the schedule.
|
||||
pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) {
|
||||
schedule.have_create2 = block_number >= self.eip86_transition;
|
||||
@@ -228,6 +239,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
node_permission_contract: p.node_permission_contract.map(Into::into),
|
||||
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
||||
max_code_size_transition: p.max_code_size_transition.map_or(0, Into::into),
|
||||
transaction_permission_contract: p.transaction_permission_contract.map(Into::into),
|
||||
wasm_activation_transition: p.wasm_activation_transition.map_or(
|
||||
BlockNumber::max_value(),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Traces config.
|
||||
use bloomchain::Config as BloomConfig;
|
||||
|
||||
/// Traces config.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -22,6 +23,8 @@ pub struct Config {
|
||||
/// Indicates if tracing should be enabled or not.
|
||||
/// If it's None, it will be automatically configured.
|
||||
pub enabled: bool,
|
||||
/// Traces blooms configuration.
|
||||
pub blooms: BloomConfig,
|
||||
/// Preferef cache-size.
|
||||
pub pref_cache_size: usize,
|
||||
/// Max cache-size.
|
||||
@@ -32,6 +35,10 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
enabled: false,
|
||||
blooms: BloomConfig {
|
||||
levels: 3,
|
||||
elements_per_index: 16,
|
||||
},
|
||||
pref_cache_size: 15 * 1024 * 1024,
|
||||
max_cache_size: 20 * 1024 * 1024,
|
||||
}
|
||||
|
||||
@@ -15,15 +15,19 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Trace database.
|
||||
use std::ops::Deref;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::Arc;
|
||||
use bloomchain::{Number, Config as BloomConfig};
|
||||
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
|
||||
use heapsize::HeapSizeOf;
|
||||
use bigint::hash::{H256, H264, H2048 as Bloom};
|
||||
use bigint::hash::{H256, H264};
|
||||
use kvdb::{KeyValueDB, DBTransaction};
|
||||
use parking_lot::RwLock;
|
||||
use header::BlockNumber;
|
||||
use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras};
|
||||
use db::{self, Key, Writable, Readable, CacheUpdatePolicy};
|
||||
use blooms;
|
||||
use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
|
||||
use cache_manager::CacheManager;
|
||||
|
||||
@@ -33,8 +37,8 @@ const TRACE_DB_VER: &'static [u8] = b"1.0";
|
||||
enum TraceDBIndex {
|
||||
/// Block traces index.
|
||||
BlockTraces = 0,
|
||||
/// Blooms index.
|
||||
Blooms = 2,
|
||||
/// Trace bloom group index.
|
||||
BloomGroups = 1,
|
||||
}
|
||||
|
||||
impl Key<FlatBlockTraces> for H256 {
|
||||
@@ -48,37 +52,80 @@ impl Key<FlatBlockTraces> for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<Bloom> for H256 {
|
||||
type Target = H264;
|
||||
/// Wrapper around `blooms::GroupPosition` so it could be
|
||||
/// uniquely identified in the database.
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
struct TraceGroupPosition(blooms::GroupPosition);
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
let mut result = H264::default();
|
||||
result[0] = TraceDBIndex::Blooms as u8;
|
||||
result[1..33].copy_from_slice(self);
|
||||
result
|
||||
impl From<GroupPosition> for TraceGroupPosition {
|
||||
fn from(position: GroupPosition) -> Self {
|
||||
TraceGroupPosition(From::from(position))
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for TraceGroupPosition {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper data structure created cause [u8; 6] does not implement Deref to &[u8].
|
||||
pub struct TraceGroupKey([u8; 6]);
|
||||
|
||||
impl Deref for TraceGroupKey {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<blooms::BloomGroup> for TraceGroupPosition {
|
||||
type Target = TraceGroupKey;
|
||||
|
||||
fn key(&self) -> Self::Target {
|
||||
let mut result = [0u8; 6];
|
||||
result[0] = TraceDBIndex::BloomGroups as u8;
|
||||
result[1] = self.0.level;
|
||||
result[2] = self.0.index as u8;
|
||||
result[3] = (self.0.index >> 8) as u8;
|
||||
result[4] = (self.0.index >> 16) as u8;
|
||||
result[5] = (self.0.index >> 24) as u8;
|
||||
TraceGroupKey(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
enum CacheId {
|
||||
Trace(H256),
|
||||
Bloom(H256),
|
||||
Bloom(TraceGroupPosition),
|
||||
}
|
||||
|
||||
/// Trace database.
|
||||
pub struct TraceDB<T> where T: DatabaseExtras {
|
||||
// cache
|
||||
traces: RwLock<HashMap<H256, FlatBlockTraces>>,
|
||||
blooms: RwLock<HashMap<H256, Bloom>>,
|
||||
blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>,
|
||||
cache_manager: RwLock<CacheManager<CacheId>>,
|
||||
// db
|
||||
tracesdb: Arc<KeyValueDB>,
|
||||
// config,
|
||||
bloom_config: BloomConfig,
|
||||
// tracing enabled
|
||||
enabled: bool,
|
||||
// extras
|
||||
extras: Arc<T>,
|
||||
}
|
||||
|
||||
impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
fn blooms_at(&self, position: &GroupPosition) -> Option<BloomGroup> {
|
||||
let position = TraceGroupPosition::from(position.clone());
|
||||
let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.blooms, &position).map(Into::into);
|
||||
self.note_used(CacheId::Bloom(position));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TraceDB<T> where T: DatabaseExtras {
|
||||
/// Creates new instance of `TraceDB`.
|
||||
pub fn new(config: Config, tracesdb: Arc<KeyValueDB>, extras: Arc<T>) -> Self {
|
||||
@@ -90,12 +137,13 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
||||
tracesdb.write(batch).expect("failed to update version");
|
||||
|
||||
TraceDB {
|
||||
traces: RwLock::new(HashMap::new()),
|
||||
blooms: RwLock::new(HashMap::new()),
|
||||
cache_manager: RwLock::new(CacheManager::new(config.pref_cache_size, config.max_cache_size, 10 * 1024)),
|
||||
tracesdb,
|
||||
tracesdb: tracesdb,
|
||||
bloom_config: config.blooms,
|
||||
enabled: config.enabled,
|
||||
extras,
|
||||
traces: RwLock::default(),
|
||||
blooms: RwLock::default(),
|
||||
extras: extras,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,12 +188,6 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
||||
result
|
||||
}
|
||||
|
||||
fn bloom(&self, block_hash: &H256) -> Option<Bloom> {
|
||||
let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.blooms, block_hash);
|
||||
self.note_used(CacheId::Bloom(block_hash.clone()));
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns vector of transaction traces for given block.
|
||||
fn transactions_traces(&self, block_hash: &H256) -> Option<Vec<FlatTransactionTraces>> {
|
||||
self.traces(block_hash).map(Into::into)
|
||||
@@ -222,16 +264,48 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
return;
|
||||
}
|
||||
|
||||
// now let's rebuild the blooms
|
||||
if !request.enacted.is_empty() {
|
||||
let range_start = request.block_number as Number + 1 - request.enacted.len();
|
||||
let range_end = range_start + request.retracted;
|
||||
let replaced_range = range_start..range_end;
|
||||
let enacted_blooms = request.enacted
|
||||
.iter()
|
||||
// all traces are expected to be found here. That's why `expect` has been used
|
||||
// instead of `filter_map`. If some traces haven't been found, it meens that
|
||||
// traces database is corrupted or incomplete.
|
||||
.map(|block_hash| if block_hash == &request.block_hash {
|
||||
request.traces.bloom()
|
||||
} else {
|
||||
self.traces(block_hash).expect("Traces database is incomplete.").bloom()
|
||||
})
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||
let trace_blooms = chain.replace(&replaced_range, enacted_blooms);
|
||||
let blooms_to_insert = trace_blooms.into_iter()
|
||||
.map(|p| (From::from(p.0), From::from(p.1)))
|
||||
.collect::<HashMap<TraceGroupPosition, blooms::BloomGroup>>();
|
||||
|
||||
let blooms_keys: Vec<_> = blooms_to_insert.keys().cloned().collect();
|
||||
let mut blooms = self.blooms.write();
|
||||
batch.extend_with_cache(db::COL_TRACE, &mut *blooms, blooms_to_insert, CacheUpdatePolicy::Remove);
|
||||
// note_used must be called after locking blooms to avoid cache/traces deadlock on garbage collection
|
||||
for key in blooms_keys {
|
||||
self.note_used(CacheId::Bloom(key));
|
||||
}
|
||||
}
|
||||
|
||||
// insert new block traces into the cache and the database
|
||||
let mut traces = self.traces.write();
|
||||
let mut blooms = self.blooms.write();
|
||||
// it's important to use overwrite here,
|
||||
// cause this value might be queried by hash later
|
||||
batch.write_with_cache(db::COL_TRACE, &mut *blooms, request.block_hash, request.traces.bloom(), CacheUpdatePolicy::Overwrite);
|
||||
batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
||||
self.note_used(CacheId::Trace(request.block_hash));
|
||||
self.note_used(CacheId::Bloom(request.block_hash));
|
||||
{
|
||||
let mut traces = self.traces.write();
|
||||
// it's important to use overwrite here,
|
||||
// cause this value might be queried by hash later
|
||||
batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
||||
self.note_used(CacheId::Trace(request.block_hash.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
||||
@@ -318,17 +392,15 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
}
|
||||
|
||||
fn filter(&self, filter: &Filter) -> Vec<LocalizedTrace> {
|
||||
let possibilities = filter.bloom_possibilities();
|
||||
// + 1, cause filters are inclusive
|
||||
(filter.range.start..filter.range.end + 1).into_iter()
|
||||
.map(|n| n as BlockNumber)
|
||||
.filter_map(|n| self.extras.block_hash(n).map(|hash| (n, hash)))
|
||||
.filter(|&(_,ref hash)| {
|
||||
let bloom = self.bloom(hash).expect("hash exists; qed");
|
||||
possibilities.iter().any(|p| bloom.contains(p))
|
||||
})
|
||||
.flat_map(|(number, hash)| {
|
||||
let traces = self.traces(&hash).expect("hash exists; qed");
|
||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||
let numbers = chain.filter(filter);
|
||||
numbers.into_iter()
|
||||
.flat_map(|n| {
|
||||
let number = n as BlockNumber;
|
||||
let hash = self.extras.block_hash(number)
|
||||
.expect("Expected to find block hash. Extras db is probably corrupted");
|
||||
let traces = self.traces(&hash)
|
||||
.expect("Expected to find a trace. Db is probably corrupted.");
|
||||
self.matching_block_traces(filter, traces, hash, number)
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -17,12 +17,13 @@
|
||||
//! Trace filters type definitions
|
||||
|
||||
use std::ops::Range;
|
||||
use hash::keccak;
|
||||
use util::Address;
|
||||
use bloomable::Bloomable;
|
||||
use bigint::prelude::H2048 as Bloom;
|
||||
use basic_types::LogBloom;
|
||||
use bloomable::Bloomable;
|
||||
use bloomchain::{Filter as BloomFilter, Number, Bloom};
|
||||
use hash::keccak;
|
||||
use trace::flat::FlatTrace;
|
||||
use util::Address;
|
||||
|
||||
use super::trace::{Action, Res};
|
||||
|
||||
/// Addresses filter.
|
||||
@@ -87,9 +88,19 @@ pub struct Filter {
|
||||
pub to_address: AddressesFilter,
|
||||
}
|
||||
|
||||
impl BloomFilter for Filter {
|
||||
fn bloom_possibilities(&self) -> Vec<Bloom> {
|
||||
self.bloom_possibilities()
|
||||
}
|
||||
|
||||
fn range(&self) -> Range<Number> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
/// Returns combinations of each address.
|
||||
pub fn bloom_possibilities(&self) -> Vec<Bloom> {
|
||||
fn bloom_possibilities(&self) -> Vec<LogBloom> {
|
||||
self.to_address.with_blooms(self.from_address.blooms())
|
||||
}
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ mod tests {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn blocks_with_blooms(&self, _blooms: &[H2048], _from_block: BlockNumber, _to_block: BlockNumber) -> Vec<BlockNumber> {
|
||||
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec<BlockNumber> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ ethcore-util = { path = "../../util" }
|
||||
ethcore-bigint = { path = "../../util/bigint" }
|
||||
ethjson = { path = "../../json" }
|
||||
bloomable = { path = "../../util/bloomable" }
|
||||
ethbloom = "0.2.5"
|
||||
keccak-hash = { path = "../../util/hash" }
|
||||
heapsize = "0.4"
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Blockchain filter
|
||||
|
||||
use util::Address;
|
||||
use bigint::hash::{H256, H2048};
|
||||
use bigint::hash::{H256, H2048 as Bloom};
|
||||
use bloomable::Bloomable;
|
||||
use ids::BlockId;
|
||||
use log_entry::LogEntry;
|
||||
@@ -75,15 +75,15 @@ impl Clone for Filter {
|
||||
|
||||
impl Filter {
|
||||
/// Returns combinations of each address and topic.
|
||||
pub fn bloom_possibilities(&self) -> Vec<H2048> {
|
||||
pub fn bloom_possibilities(&self) -> Vec<Bloom> {
|
||||
let blooms = match self.address {
|
||||
Some(ref addresses) if !addresses.is_empty() =>
|
||||
addresses.iter().map(|ref address| {
|
||||
let mut bloom = H2048::default();
|
||||
let mut bloom = Bloom::default();
|
||||
bloom.shift_bloomed(&keccak(address));
|
||||
bloom
|
||||
}).collect(),
|
||||
_ => vec![H2048::default()]
|
||||
_ => vec![Bloom::default()]
|
||||
};
|
||||
|
||||
self.topics.iter().fold(blooms, |bs, topic| match *topic {
|
||||
@@ -93,7 +93,7 @@ impl Filter {
|
||||
let mut b = bloom.clone();
|
||||
b.shift_bloomed(&keccak(topic));
|
||||
b
|
||||
}).collect::<Vec<H2048>>()
|
||||
}).collect::<Vec<Bloom>>()
|
||||
}).collect()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ extern crate rlp;
|
||||
#[macro_use]
|
||||
extern crate rlp_derive;
|
||||
extern crate bloomable;
|
||||
extern crate ethbloom;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate heapsize;
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ pub struct Schedule {
|
||||
|
||||
/// Wasm cost table
|
||||
pub struct WasmCosts {
|
||||
/// Arena allocator cost, per byte
|
||||
pub alloc: u32,
|
||||
/// Default opcode cost
|
||||
pub regular: u32,
|
||||
/// Div operations multiplier.
|
||||
pub div: u32,
|
||||
/// Div operations multiplier.
|
||||
@@ -135,17 +135,20 @@ pub struct WasmCosts {
|
||||
pub initial_mem: u32,
|
||||
/// Grow memory cost, per page (64kb)
|
||||
pub grow_mem: u32,
|
||||
/// Memory copy cost, per byte
|
||||
pub memcpy: u32,
|
||||
/// Max stack height (native WebAssembly stack limiter)
|
||||
pub max_stack_height: u32,
|
||||
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
|
||||
pub opcodes_mul: u32,
|
||||
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
|
||||
pub opcodes_div: u32,
|
||||
|
||||
}
|
||||
|
||||
impl Default for WasmCosts {
|
||||
fn default() -> Self {
|
||||
WasmCosts {
|
||||
alloc: 2,
|
||||
regular: 1,
|
||||
div: 16,
|
||||
mul: 4,
|
||||
mem: 2,
|
||||
@@ -153,6 +156,8 @@ impl Default for WasmCosts {
|
||||
static_address: 40,
|
||||
initial_mem: 4096,
|
||||
grow_mem: 8192,
|
||||
memcpy: 1,
|
||||
max_stack_height: 64*1024,
|
||||
opcodes_mul: 3,
|
||||
opcodes_div: 8,
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ byteorder = "1.0"
|
||||
ethcore-util = { path = "../../util" }
|
||||
ethcore-bigint = { path = "../../util/bigint" }
|
||||
log = "0.3"
|
||||
parity-wasm = "0.23"
|
||||
parity-wasm = "0.27"
|
||||
libc = "0.2"
|
||||
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
||||
vm = { path = "../vm" }
|
||||
ethcore-logger = { path = "../../logger" }
|
||||
wasmi = { git = "https://github.com/pepyakin/wasmi" }
|
||||
wasmi = { git = "https://github.com/paritytech/wasmi" }
|
||||
@@ -19,7 +19,7 @@
|
||||
use std::cell::RefCell;
|
||||
use wasmi::{
|
||||
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
|
||||
MemoryRef, MemoryInstance,
|
||||
MemoryRef, MemoryInstance, memory_units,
|
||||
};
|
||||
|
||||
/// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter
|
||||
@@ -219,7 +219,10 @@ impl ImportResolver {
|
||||
let mut mem_ref = self.memory.borrow_mut();
|
||||
if mem_ref.is_none() {
|
||||
*mem_ref = Some(
|
||||
MemoryInstance::alloc(0, Some(0)).expect("Memory allocation (0, 0) should not fail; qed")
|
||||
MemoryInstance::alloc(
|
||||
memory_units::Pages(0),
|
||||
Some(memory_units::Pages(0)),
|
||||
).expect("Memory allocation (0, 0) should not fail; qed")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -229,7 +232,7 @@ impl ImportResolver {
|
||||
|
||||
/// Returns memory size module initially requested
|
||||
pub fn memory_size(&self) -> Result<u32, Error> {
|
||||
Ok(self.memory_ref().size())
|
||||
Ok(self.memory_ref().current_size().0 as u32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +284,10 @@ impl wasmi::ModuleImportResolver for ImportResolver {
|
||||
{
|
||||
Err(Error::Instantiation("Module requested too much memory".to_owned()))
|
||||
} else {
|
||||
let mem = MemoryInstance::alloc(descriptor.initial(), descriptor.maximum())?;
|
||||
let mem = MemoryInstance::alloc(
|
||||
memory_units::Pages(descriptor.initial() as usize),
|
||||
descriptor.maximum().map(|x| memory_units::Pages(x as usize)),
|
||||
)?;
|
||||
*self.memory.borrow_mut() = Some(mem.clone());
|
||||
Ok(mem)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ mod panic_payload;
|
||||
mod parser;
|
||||
|
||||
use vm::{GasLeft, ReturnData, ActionParams};
|
||||
use wasmi::Error as InterpreterError;
|
||||
use wasmi::{Error as InterpreterError, Trap};
|
||||
|
||||
use runtime::{Runtime, RuntimeContext};
|
||||
|
||||
@@ -43,17 +43,29 @@ use bigint::uint::U256;
|
||||
|
||||
/// Wrapped interpreter error
|
||||
#[derive(Debug)]
|
||||
pub struct Error(InterpreterError);
|
||||
pub enum Error {
|
||||
Interpreter(InterpreterError),
|
||||
Trap(Trap),
|
||||
}
|
||||
|
||||
impl From<InterpreterError> for Error {
|
||||
fn from(e: InterpreterError) -> Self {
|
||||
Error(e)
|
||||
Error::Interpreter(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Trap> for Error {
|
||||
fn from(e: Trap) -> Self {
|
||||
Error::Trap(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for vm::Error {
|
||||
fn from(e: Error) -> Self {
|
||||
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e.0))
|
||||
match e {
|
||||
Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
|
||||
Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,19 +78,25 @@ impl From<runtime::Error> for vm::Error {
|
||||
}
|
||||
}
|
||||
|
||||
enum ExecutionOutcome {
|
||||
Suicide,
|
||||
Return,
|
||||
NotSpecial,
|
||||
}
|
||||
|
||||
impl vm::Vm for WasmInterpreter {
|
||||
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
|
||||
let (module, data) = parser::payload(¶ms, ext.schedule().wasm())?;
|
||||
|
||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error)?;
|
||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;
|
||||
|
||||
let instantiation_resolover = env::ImportResolver::with_limit(16);
|
||||
let instantiation_resolver = env::ImportResolver::with_limit(16);
|
||||
|
||||
let module_instance = wasmi::ModuleInstance::new(
|
||||
&loaded_module,
|
||||
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolover)
|
||||
).map_err(Error)?;
|
||||
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver)
|
||||
).map_err(Error::Interpreter)?;
|
||||
|
||||
let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) /
|
||||
U256::from(ext.schedule().wasm().opcodes_mul);
|
||||
@@ -88,13 +106,13 @@ impl vm::Vm for WasmInterpreter {
|
||||
return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned()));
|
||||
}
|
||||
|
||||
let initial_memory = instantiation_resolover.memory_size().map_err(Error)?;
|
||||
let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?;
|
||||
trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory);
|
||||
|
||||
let (gas_left, result) = {
|
||||
let mut runtime = Runtime::with_params(
|
||||
ext,
|
||||
instantiation_resolover.memory_ref(),
|
||||
instantiation_resolver.memory_ref(),
|
||||
// cannot overflow, checked above
|
||||
adjusted_gas.low_u64(),
|
||||
data.to_vec(),
|
||||
@@ -115,33 +133,29 @@ impl vm::Vm for WasmInterpreter {
|
||||
assert!(runtime.schedule().wasm().initial_mem < 1 << 16);
|
||||
runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?;
|
||||
|
||||
let module_instance = module_instance.run_start(&mut runtime).map_err(Error)?;
|
||||
let module_instance = module_instance.run_start(&mut runtime).map_err(Error::Trap)?;
|
||||
|
||||
match module_instance.invoke_export("call", &[], &mut runtime) {
|
||||
Ok(_) => { },
|
||||
Err(InterpreterError::Host(boxed)) => {
|
||||
match boxed.downcast_ref::<runtime::Error>() {
|
||||
None => {
|
||||
return Err(vm::Error::Wasm("Invalid user error used in interpreter".to_owned()));
|
||||
}
|
||||
Some(runtime_err) => {
|
||||
match *runtime_err {
|
||||
runtime::Error::Suicide => {
|
||||
// Suicide uses trap to break execution
|
||||
}
|
||||
ref any_err => {
|
||||
trace!(target: "wasm", "Error executing contract: {:?}", boxed);
|
||||
return Err(vm::Error::from(Error::from(InterpreterError::Host(Box::new(any_err.clone())))));
|
||||
}
|
||||
}
|
||||
}
|
||||
let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
|
||||
|
||||
let mut execution_outcome = ExecutionOutcome::NotSpecial;
|
||||
if let Err(InterpreterError::Trap(ref trap)) = invoke_result {
|
||||
if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() {
|
||||
let ref runtime_err = boxed.downcast_ref::<runtime::Error>()
|
||||
.expect("Host errors other than runtime::Error never produced; qed");
|
||||
|
||||
match **runtime_err {
|
||||
runtime::Error::Suicide => { execution_outcome = ExecutionOutcome::Suicide; },
|
||||
runtime::Error::Return => { execution_outcome = ExecutionOutcome::Return; },
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
trace!(target: "wasm", "Error executing contract: {:?}", err);
|
||||
return Err(vm::Error::from(Error::from(err)))
|
||||
}
|
||||
}
|
||||
|
||||
if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) {
|
||||
trace!(target: "wasm", "Error executing contract: {:?}", e);
|
||||
return Err(vm::Error::from(Error::from(e)));
|
||||
}
|
||||
|
||||
(
|
||||
runtime.gas_left().expect("Cannot fail since it was not updated since last charge"),
|
||||
runtime.into_result(),
|
||||
|
||||
@@ -22,14 +22,18 @@ use parity_wasm::elements::{self, Deserialize};
|
||||
use parity_wasm::peek_size;
|
||||
|
||||
fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
|
||||
rules::Set::new({
|
||||
let mut vals = ::std::collections::HashMap::with_capacity(4);
|
||||
vals.insert(rules::InstructionType::Load, wasm_costs.mem as u32);
|
||||
vals.insert(rules::InstructionType::Store, wasm_costs.mem as u32);
|
||||
vals.insert(rules::InstructionType::Div, wasm_costs.div as u32);
|
||||
vals.insert(rules::InstructionType::Mul, wasm_costs.mul as u32);
|
||||
vals
|
||||
}).with_grow_cost(wasm_costs.grow_mem)
|
||||
rules::Set::new(
|
||||
wasm_costs.regular,
|
||||
{
|
||||
let mut vals = ::std::collections::HashMap::with_capacity(8);
|
||||
vals.insert(rules::InstructionType::Load, rules::Metering::Fixed(wasm_costs.mem as u32));
|
||||
vals.insert(rules::InstructionType::Store, rules::Metering::Fixed(wasm_costs.mem as u32));
|
||||
vals.insert(rules::InstructionType::Div, rules::Metering::Fixed(wasm_costs.div as u32));
|
||||
vals.insert(rules::InstructionType::Mul, rules::Metering::Fixed(wasm_costs.mul as u32));
|
||||
vals
|
||||
})
|
||||
.with_grow_cost(wasm_costs.grow_mem)
|
||||
.with_forbidden_floats()
|
||||
}
|
||||
|
||||
/// Splits payload to code and data according to params.params_type, also
|
||||
@@ -71,7 +75,12 @@ pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts)
|
||||
let contract_module = wasm_utils::inject_gas_counter(
|
||||
deserialized_module,
|
||||
&gas_rules(wasm_costs),
|
||||
);
|
||||
).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?;
|
||||
|
||||
let contract_module = wasm_utils::stack_height::inject_limiter(
|
||||
contract_module,
|
||||
wasm_costs.max_stack_height,
|
||||
).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?;
|
||||
|
||||
let data = match params.params_type {
|
||||
vm::ParamsType::Embedded => {
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::Address;
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H256;
|
||||
use vm::{self, CallType};
|
||||
use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError};
|
||||
use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError, Trap, TrapKind};
|
||||
use super::panic_payload;
|
||||
|
||||
pub struct RuntimeContext {
|
||||
@@ -52,6 +52,8 @@ pub enum Error {
|
||||
MemoryAccessViolation,
|
||||
/// Native code resulted in suicide
|
||||
Suicide,
|
||||
/// Native code requested execution to finish
|
||||
Return,
|
||||
/// Suicide was requested but coudn't complete
|
||||
SuicideAbort,
|
||||
/// Invalid gas state inside interpreter
|
||||
@@ -72,15 +74,40 @@ pub enum Error {
|
||||
Other,
|
||||
/// Syscall signature mismatch
|
||||
InvalidSyscall,
|
||||
/// Unreachable instruction encountered
|
||||
Unreachable,
|
||||
/// Invalid virtual call
|
||||
InvalidVirtualCall,
|
||||
/// Division by zero
|
||||
DivisionByZero,
|
||||
/// Invalid conversion to integer
|
||||
InvalidConversionToInt,
|
||||
/// Stack overflow
|
||||
StackOverflow,
|
||||
/// Panic with message
|
||||
Panic(String),
|
||||
}
|
||||
|
||||
impl wasmi::HostError for Error { }
|
||||
|
||||
impl From<Trap> for Error {
|
||||
fn from(trap: Trap) -> Self {
|
||||
match *trap.kind() {
|
||||
TrapKind::Unreachable => Error::Unreachable,
|
||||
TrapKind::MemoryAccessOutOfBounds => Error::MemoryAccessViolation,
|
||||
TrapKind::TableAccessOutOfBounds | TrapKind::ElemUninitialized => Error::InvalidVirtualCall,
|
||||
TrapKind::DivisionByZero => Error::DivisionByZero,
|
||||
TrapKind::InvalidConversionToInt => Error::InvalidConversionToInt,
|
||||
TrapKind::UnexpectedSignature => Error::InvalidVirtualCall,
|
||||
TrapKind::StackOverflow => Error::StackOverflow,
|
||||
TrapKind::Host(_) => Error::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InterpreterError> for Error {
|
||||
fn from(interpreter_err: InterpreterError) -> Self {
|
||||
match interpreter_err {
|
||||
fn from(err: InterpreterError) -> Self {
|
||||
match err {
|
||||
InterpreterError::Value(_) => Error::InvalidSyscall,
|
||||
InterpreterError::Memory(_) => Error::MemoryAccessViolation,
|
||||
_ => Error::Other,
|
||||
@@ -98,6 +125,7 @@ impl ::std::fmt::Display for Error {
|
||||
Error::InvalidGasState => write!(f, "Invalid gas state"),
|
||||
Error::BalanceQueryError => write!(f, "Balance query resulted in an error"),
|
||||
Error::Suicide => write!(f, "Suicide result"),
|
||||
Error::Return => write!(f, "Return result"),
|
||||
Error::Unknown => write!(f, "Unknown runtime function invoked"),
|
||||
Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
|
||||
Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
|
||||
@@ -105,6 +133,11 @@ impl ::std::fmt::Display for Error {
|
||||
Error::Log => write!(f, "Error occured while logging an event"),
|
||||
Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"),
|
||||
Error::Other => write!(f, "Other unspecified error"),
|
||||
Error::Unreachable => write!(f, "Unreachable instruction encountered"),
|
||||
Error::InvalidVirtualCall => write!(f, "Invalid virtual call"),
|
||||
Error::DivisionByZero => write!(f, "Division by zero"),
|
||||
Error::StackOverflow => write!(f, "Stack overflow"),
|
||||
Error::InvalidConversionToInt => write!(f, "Invalid conversion to integer"),
|
||||
Error::Panic(ref msg) => write!(f, "Panic: {}", msg),
|
||||
}
|
||||
}
|
||||
@@ -161,12 +194,14 @@ impl<'a> Runtime<'a> {
|
||||
/// Intuition about the return value sense is to aswer the question 'are we allowed to continue?'
|
||||
fn charge_gas(&mut self, amount: u64) -> bool {
|
||||
let prev = self.gas_counter;
|
||||
if prev + amount > self.gas_limit {
|
||||
// exceeds gas
|
||||
false
|
||||
} else {
|
||||
self.gas_counter = prev + amount;
|
||||
true
|
||||
match prev.checked_add(amount) {
|
||||
// gas charge overflow protection
|
||||
None => false,
|
||||
Some(val) if val > self.gas_limit => false,
|
||||
Some(_) => {
|
||||
self.gas_counter = prev + amount;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,8 +255,8 @@ impl<'a> Runtime<'a> {
|
||||
/// Read from the storage to wasm memory.
|
||||
pub fn storage_read(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
let key = self.h256_at(args.nth(0)?)?;
|
||||
let val_ptr: u32 = args.nth(1)?;
|
||||
let key = self.h256_at(args.nth_checked(0)?)?;
|
||||
let val_ptr: u32 = args.nth_checked(1)?;
|
||||
|
||||
let val = self.ext.storage_at(&key).map_err(|_| Error::StorageReadError)?;
|
||||
|
||||
@@ -235,8 +270,8 @@ impl<'a> Runtime<'a> {
|
||||
/// Write to storage from wasm memory.
|
||||
pub fn storage_write(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
let key = self.h256_at(args.nth(0)?)?;
|
||||
let val_ptr: u32 = args.nth(1)?;
|
||||
let key = self.h256_at(args.nth_checked(0)?)?;
|
||||
let val_ptr: u32 = args.nth_checked(1)?;
|
||||
|
||||
let val = self.h256_at(val_ptr)?;
|
||||
let former_val = self.ext.storage_at(&key).map_err(|_| Error::StorageUpdateError)?;
|
||||
@@ -264,14 +299,14 @@ impl<'a> Runtime<'a> {
|
||||
/// Return. Syscall takes 2 arguments - pointer in sandboxed memory where result is and
|
||||
/// the length of the result.
|
||||
pub fn ret(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let ptr: u32 = args.nth(0)?;
|
||||
let len: u32 = args.nth(1)?;
|
||||
let ptr: u32 = args.nth_checked(0)?;
|
||||
let len: u32 = args.nth_checked(1)?;
|
||||
|
||||
trace!(target: "wasm", "Contract ret: {} bytes @ {}", len, ptr);
|
||||
|
||||
self.result = self.memory.get(ptr, len as usize)?;
|
||||
|
||||
Ok(())
|
||||
Err(Error::Return)
|
||||
}
|
||||
|
||||
/// Destroy the runtime, returning currently recorded result of the execution.
|
||||
@@ -287,7 +322,7 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
/// Report gas cost with the params passed in wasm stack
|
||||
fn gas(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let amount: u32 = args.nth(0)?;
|
||||
let amount: u32 = args.nth_checked(0)?;
|
||||
if self.charge_gas(amount as u64) {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -302,7 +337,10 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
/// Write input bytes to the memory location using the passed pointer
|
||||
fn fetch_input(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let ptr: u32 = args.nth(0)?;
|
||||
let args_len = self.args.len() as u64;
|
||||
self.charge(|s| args_len * s.wasm().memcpy as u64)?;
|
||||
|
||||
let ptr: u32 = args.nth_checked(0)?;
|
||||
self.memory.set(ptr, &self.args[..])?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -310,8 +348,8 @@ impl<'a> Runtime<'a> {
|
||||
/// User panic. Contract can invoke this when he encounters unrecoverable error.
|
||||
fn panic(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
let payload_ptr: u32 = args.nth(0)?;
|
||||
let payload_len: u32 = args.nth(1)?;
|
||||
let payload_ptr: u32 = args.nth_checked(0)?;
|
||||
let payload_len: u32 = args.nth_checked(1)?;
|
||||
|
||||
let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?;
|
||||
let payload = panic_payload::decode(&raw_payload);
|
||||
@@ -345,26 +383,26 @@ impl<'a> Runtime<'a> {
|
||||
{
|
||||
trace!(target: "wasm", "runtime: CALL({:?})", call_type);
|
||||
|
||||
let gas: u64 = args.nth(0)?;
|
||||
let gas: u64 = args.nth_checked(0)?;
|
||||
trace!(target: "wasm", " gas: {:?}", gas);
|
||||
|
||||
let address = self.address_at(args.nth(1)?)?;
|
||||
let address = self.address_at(args.nth_checked(1)?)?;
|
||||
trace!(target: "wasm", " address: {:?}", address);
|
||||
|
||||
let vofs = if use_val { 1 } else { 0 };
|
||||
let val = if use_val { Some(self.u256_at(args.nth(2)?)?) } else { None };
|
||||
let val = if use_val { Some(self.u256_at(args.nth_checked(2)?)?) } else { None };
|
||||
trace!(target: "wasm", " val: {:?}", val);
|
||||
|
||||
let input_ptr: u32 = args.nth(2 + vofs)?;
|
||||
let input_ptr: u32 = args.nth_checked(2 + vofs)?;
|
||||
trace!(target: "wasm", " input_ptr: {:?}", input_ptr);
|
||||
|
||||
let input_len: u32 = args.nth(3 + vofs)?;
|
||||
let input_len: u32 = args.nth_checked(3 + vofs)?;
|
||||
trace!(target: "wasm", " input_len: {:?}", input_len);
|
||||
|
||||
let result_ptr: u32 = args.nth(4 + vofs)?;
|
||||
let result_ptr: u32 = args.nth_checked(4 + vofs)?;
|
||||
trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
|
||||
|
||||
let result_alloc_len: u32 = args.nth(5 + vofs)?;
|
||||
let result_alloc_len: u32 = args.nth_checked(5 + vofs)?;
|
||||
trace!(target: "wasm", " result_len: {:?}", result_alloc_len);
|
||||
|
||||
if let Some(ref val) = val {
|
||||
@@ -464,7 +502,7 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
pub fn value(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let val = self.context.value;
|
||||
self.return_u256_ptr(args.nth(0)?, val)
|
||||
self.return_u256_ptr(args.nth_checked(0)?, val)
|
||||
}
|
||||
|
||||
pub fn create(&mut self, args: RuntimeArgs) -> Result<RuntimeValue>
|
||||
@@ -474,13 +512,13 @@ impl<'a> Runtime<'a> {
|
||||
// fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
|
||||
//
|
||||
trace!(target: "wasm", "runtime: CREATE");
|
||||
let endowment = self.u256_at(args.nth(0)?)?;
|
||||
let endowment = self.u256_at(args.nth_checked(0)?)?;
|
||||
trace!(target: "wasm", " val: {:?}", endowment);
|
||||
let code_ptr: u32 = args.nth(1)?;
|
||||
let code_ptr: u32 = args.nth_checked(1)?;
|
||||
trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
|
||||
let code_len: u32 = args.nth(2)?;
|
||||
let code_len: u32 = args.nth_checked(2)?;
|
||||
trace!(target: "wasm", " code_len: {:?}", code_len);
|
||||
let result_ptr: u32 = args.nth(3)?;
|
||||
let result_ptr: u32 = args.nth_checked(3)?;
|
||||
trace!(target: "wasm", "result_ptr: {:?}", result_ptr);
|
||||
|
||||
let code = self.memory.get(code_ptr, code_len as usize)?;
|
||||
@@ -522,13 +560,13 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
fn debug(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
let msg_ptr: u32 = args.nth(0)?;
|
||||
let msg_len: u32 = args.nth(1)?;
|
||||
trace!(target: "wasm", "Contract debug message: {}", {
|
||||
let msg_ptr: u32 = args.nth_checked(0)?;
|
||||
let msg_len: u32 = args.nth_checked(1)?;
|
||||
|
||||
let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
|
||||
.map_err(|_| Error::BadUtf8)?;
|
||||
|
||||
trace!(target: "wasm", "Contract debug message: {}", msg);
|
||||
String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
|
||||
.map_err(|_| Error::BadUtf8)?
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -536,7 +574,7 @@ impl<'a> Runtime<'a> {
|
||||
/// Pass suicide to state runtime
|
||||
pub fn suicide(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
let refund_address = self.address_at(args.nth(0)?)?;
|
||||
let refund_address = self.address_at(args.nth_checked(0)?)?;
|
||||
|
||||
if self.ext.exists(&refund_address).map_err(|_| Error::SuicideAbort)? {
|
||||
trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address);
|
||||
@@ -554,8 +592,8 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?;
|
||||
let hash = self.ext.blockhash(&U256::from(args.nth::<u64>(0)?));
|
||||
self.memory.set(args.nth(1)?, &*hash)?;
|
||||
let hash = self.ext.blockhash(&U256::from(args.nth_checked::<u64>(0)?));
|
||||
self.memory.set(args.nth_checked(1)?, &*hash)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -566,32 +604,32 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
pub fn coinbase(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let coinbase = self.ext.env_info().author;
|
||||
self.return_address_ptr(args.nth(0)?, coinbase)
|
||||
self.return_address_ptr(args.nth_checked(0)?, coinbase)
|
||||
}
|
||||
|
||||
pub fn difficulty(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let difficulty = self.ext.env_info().difficulty;
|
||||
self.return_u256_ptr(args.nth(0)?, difficulty)
|
||||
self.return_u256_ptr(args.nth_checked(0)?, difficulty)
|
||||
}
|
||||
|
||||
pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let gas_limit = self.ext.env_info().gas_limit;
|
||||
self.return_u256_ptr(args.nth(0)?, gas_limit)
|
||||
self.return_u256_ptr(args.nth_checked(0)?, gas_limit)
|
||||
}
|
||||
|
||||
pub fn address(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let address = self.context.address;
|
||||
self.return_address_ptr(args.nth(0)?, address)
|
||||
self.return_address_ptr(args.nth_checked(0)?, address)
|
||||
}
|
||||
|
||||
pub fn sender(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let sender = self.context.sender;
|
||||
self.return_address_ptr(args.nth(0)?, sender)
|
||||
self.return_address_ptr(args.nth_checked(0)?, sender)
|
||||
}
|
||||
|
||||
pub fn origin(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||
let origin = self.context.origin;
|
||||
self.return_address_ptr(args.nth(0)?, origin)
|
||||
self.return_address_ptr(args.nth_checked(0)?, origin)
|
||||
}
|
||||
|
||||
pub fn timestamp(&mut self) -> Result<RuntimeValue> {
|
||||
@@ -601,12 +639,10 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
pub fn elog(&mut self, args: RuntimeArgs) -> Result<()>
|
||||
{
|
||||
// signature is:
|
||||
// pub fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32);
|
||||
let topic_ptr: u32 = args.nth(0)?;
|
||||
let topic_count: u32 = args.nth(1)?;
|
||||
let data_ptr: u32 = args.nth(2)?;
|
||||
let data_len: u32 = args.nth(3)?;
|
||||
let topic_ptr: u32 = args.nth_checked(0)?;
|
||||
let topic_count: u32 = args.nth_checked(1)?;
|
||||
let data_ptr: u32 = args.nth_checked(2)?;
|
||||
let data_len: u32 = args.nth_checked(3)?;
|
||||
|
||||
if topic_count > 4 {
|
||||
return Err(Error::Log.into());
|
||||
@@ -639,7 +675,7 @@ impl<'a> Runtime<'a> {
|
||||
|
||||
mod ext_impl {
|
||||
|
||||
use wasmi::{Externals, RuntimeArgs, RuntimeValue, Error};
|
||||
use wasmi::{Externals, RuntimeArgs, RuntimeValue, Trap};
|
||||
use env::ids::*;
|
||||
|
||||
macro_rules! void {
|
||||
@@ -659,7 +695,7 @@ mod ext_impl {
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: RuntimeArgs,
|
||||
) -> Result<Option<RuntimeValue>, Error> {
|
||||
) -> Result<Option<RuntimeValue>, Trap> {
|
||||
match index {
|
||||
STORAGE_WRITE_FUNC => void!(self.storage_write(args)),
|
||||
STORAGE_READ_FUNC => void!(self.storage_read(args)),
|
||||
|
||||
@@ -209,7 +209,7 @@ fn dispersion() {
|
||||
result,
|
||||
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(93_972));
|
||||
assert_eq!(gas_left, U256::from(94_013));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -237,7 +237,7 @@ fn suicide_not() {
|
||||
result,
|
||||
vec![0u8]
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(94_970));
|
||||
assert_eq!(gas_left, U256::from(94_984));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -269,7 +269,7 @@ fn suicide() {
|
||||
};
|
||||
|
||||
assert!(ext.suicides.contains(&refund));
|
||||
assert_eq!(gas_left, U256::from(94_933));
|
||||
assert_eq!(gas_left, U256::from(94_925));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -299,7 +299,7 @@ fn create() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Create,
|
||||
gas: U256::from(60_917),
|
||||
gas: U256::from(60_914),
|
||||
sender_address: None,
|
||||
receive_address: None,
|
||||
value: Some(1_000_000_000.into()),
|
||||
@@ -307,7 +307,7 @@ fn create() {
|
||||
code_address: None,
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(60_903));
|
||||
assert_eq!(gas_left, U256::from(60_900));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -467,7 +467,7 @@ fn realloc() {
|
||||
}
|
||||
};
|
||||
assert_eq!(result, vec![0u8; 2]);
|
||||
assert_eq!(gas_left, U256::from(94_352));
|
||||
assert_eq!(gas_left, U256::from(94_372));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -543,7 +543,7 @@ fn keccak() {
|
||||
};
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
assert_eq!(gas_left, U256::from(84_223));
|
||||
assert_eq!(gas_left, U256::from(84_240));
|
||||
}
|
||||
|
||||
// math_* tests check the ability of wasm contract to perform big integer operations
|
||||
@@ -572,7 +572,7 @@ fn math_add() {
|
||||
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(93_818));
|
||||
assert_eq!(gas_left, U256::from(93_814));
|
||||
}
|
||||
|
||||
// multiplication
|
||||
@@ -594,7 +594,7 @@ fn math_mul() {
|
||||
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(93_304));
|
||||
assert_eq!(gas_left, U256::from(93_300));
|
||||
}
|
||||
|
||||
// subtraction
|
||||
@@ -616,7 +616,7 @@ fn math_sub() {
|
||||
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(93_831));
|
||||
assert_eq!(gas_left, U256::from(93_826));
|
||||
}
|
||||
|
||||
// subtraction with overflow
|
||||
@@ -658,7 +658,7 @@ fn math_div() {
|
||||
U256::from_dec_str("1125000").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(90_607));
|
||||
assert_eq!(gas_left, U256::from(90_603));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -686,7 +686,7 @@ fn storage_metering() {
|
||||
};
|
||||
|
||||
// 0 -> not 0
|
||||
assert_eq!(gas_left, U256::from(74_410));
|
||||
assert_eq!(gas_left, U256::from(74_338));
|
||||
|
||||
// #2
|
||||
|
||||
@@ -705,7 +705,7 @@ fn storage_metering() {
|
||||
};
|
||||
|
||||
// not 0 -> not 0
|
||||
assert_eq!(gas_left, U256::from(89_410));
|
||||
assert_eq!(gas_left, U256::from(89_338));
|
||||
}
|
||||
|
||||
// This test checks the ability of wasm contract to invoke
|
||||
@@ -793,7 +793,7 @@ fn externs() {
|
||||
"Gas limit requested and returned does not match"
|
||||
);
|
||||
|
||||
assert_eq!(gas_left, U256::from(92_089));
|
||||
assert_eq!(gas_left, U256::from(92_110));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -819,7 +819,7 @@ fn embedded_keccak() {
|
||||
};
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
assert_eq!(gas_left, U256::from(84_223));
|
||||
assert_eq!(gas_left, U256::from(84_240));
|
||||
}
|
||||
|
||||
/// This test checks the correctness of log extern
|
||||
@@ -854,5 +854,5 @@ fn events() {
|
||||
assert_eq!(&log_entry.data, b"gnihtemos");
|
||||
|
||||
assert_eq!(&result, b"gnihtemos");
|
||||
assert_eq!(gas_left, U256::from(81_235));
|
||||
assert_eq!(gas_left, U256::from(81_292));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user