Add randomness contract support to AuthorityRound. (#10946)
* Add randomness contract support to Authority Round. Changes have been cherry-picked from poanetwork's aura-pos branch. Most of the work has been done by @mbr. * Address review comments for randomness contract. Co-Authored-By: David <dvdplm@gmail.com> * Rename revealSecret to revealNumber * Update Randomness contract bytecode * Use H256, rename secret to random number. * Use get_commit_and_cipher * Clean up Miner::prepare_block. * Remove is_reveal_phase call. * Add more comments, require randomness contract map. * Simplify run_randomness_phase * Address review comments. * Remove Client::transact_contract.
This commit is contained in:
@@ -79,6 +79,7 @@ use client_traits::{
|
||||
StateOrBlock,
|
||||
Tick,
|
||||
TransactionInfo,
|
||||
TransactionRequest,
|
||||
ForceUpdateSealing
|
||||
};
|
||||
use db::{keys::BlockDetails, Readable, Writable};
|
||||
@@ -2154,29 +2155,35 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
|
||||
fn create_transaction(&self, TransactionRequest { action, data, gas, gas_price, nonce }: TransactionRequest)
|
||||
-> Result<SignedTransaction, transaction::Error>
|
||||
{
|
||||
let authoring_params = self.importer.miner.authoring_params();
|
||||
let service_transaction_checker = self.importer.miner.service_transaction_checker();
|
||||
let gas_price = if let Some(checker) = service_transaction_checker {
|
||||
match checker.check_address(self, authoring_params.author) {
|
||||
Ok(true) => U256::zero(),
|
||||
_ => self.importer.miner.sensible_gas_price(),
|
||||
_ => gas_price.unwrap_or_else(|| self.importer.miner.sensible_gas_price()),
|
||||
}
|
||||
} else {
|
||||
self.importer.miner.sensible_gas_price()
|
||||
};
|
||||
let transaction = transaction::Transaction {
|
||||
nonce: self.latest_nonce(&authoring_params.author),
|
||||
action: Action::Call(address),
|
||||
gas: self.importer.miner.sensible_gas_limit(),
|
||||
nonce: nonce.unwrap_or_else(|| self.latest_nonce(&authoring_params.author)),
|
||||
action,
|
||||
gas: gas.unwrap_or_else(|| self.importer.miner.sensible_gas_limit()),
|
||||
gas_price,
|
||||
value: U256::zero(),
|
||||
data: data,
|
||||
data,
|
||||
};
|
||||
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
|
||||
let signature = self.engine.sign(transaction.hash(chain_id))
|
||||
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
|
||||
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
|
||||
Ok(SignedTransaction::new(transaction.with_signature(signature, chain_id))?)
|
||||
}
|
||||
|
||||
fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
|
||||
let signed = self.create_transaction(tx_request)?;
|
||||
self.importer.miner.import_own_transaction(self, signed.into())
|
||||
}
|
||||
}
|
||||
@@ -2978,7 +2985,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_mark_finalization_correctly_for_parent() {
|
||||
let client = generate_dummy_client_with_spec_and_data(spec::new_test_with_finality, 2, 0, &[]);
|
||||
let client = generate_dummy_client_with_spec_and_data(spec::new_test_with_finality, 2, 0, &[], false);
|
||||
let chain = client.chain();
|
||||
|
||||
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));
|
||||
|
||||
@@ -327,6 +327,13 @@ impl Miner {
|
||||
///
|
||||
/// NOTE This should be only used for tests.
|
||||
pub fn new_for_tests(spec: &Spec, accounts: Option<HashSet<Address>>) -> Miner {
|
||||
Miner::new_for_tests_force_sealing(spec, accounts, false)
|
||||
}
|
||||
|
||||
/// Creates new instance of miner with given spec and accounts.
|
||||
///
|
||||
/// NOTE This should be only used for tests.
|
||||
pub fn new_for_tests_force_sealing(spec: &Spec, accounts: Option<HashSet<Address>>, force_sealing: bool) -> Miner {
|
||||
let minimal_gas_price = 0.into();
|
||||
Miner::new(MinerOptions {
|
||||
pool_verification_options: pool::verifier::Options {
|
||||
@@ -336,6 +343,7 @@ impl Miner {
|
||||
no_early_reject: false,
|
||||
},
|
||||
reseal_min_period: Duration::from_secs(0),
|
||||
force_sealing,
|
||||
..Default::default()
|
||||
}, GasPricer::new_fixed(minimal_gas_price), spec, accounts.unwrap_or_default())
|
||||
}
|
||||
@@ -422,7 +430,8 @@ impl Miner {
|
||||
let chain_info = chain.chain_info();
|
||||
|
||||
// Open block
|
||||
let (mut open_block, original_work_hash) = {
|
||||
// Some engines add transactions to the block for their own purposes, e.g. AuthorityRound RANDAO.
|
||||
let (mut open_block, original_work_hash, engine_txs) = {
|
||||
let mut sealing = self.sealing.lock();
|
||||
let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash());
|
||||
let best_hash = chain_info.best_block_hash;
|
||||
@@ -433,38 +442,48 @@ impl Miner {
|
||||
// if at least one was pushed successfully, close and enqueue new ClosedBlock;
|
||||
// otherwise, leave everything alone.
|
||||
// otherwise, author a fresh block.
|
||||
let mut open_block = match sealing.queue.get_pending_if(|b| b.header.parent_hash() == &best_hash) {
|
||||
match sealing.queue.get_pending_if(|b| b.header.parent_hash() == &best_hash) {
|
||||
Some(old_block) => {
|
||||
trace!(target: "miner", "prepare_block: Already have previous work; updating and returning");
|
||||
// add transactions to old_block
|
||||
chain.reopen_block(old_block)
|
||||
(chain.reopen_block(old_block), last_work_hash, Vec::new())
|
||||
}
|
||||
None => {
|
||||
// block not found - create it.
|
||||
trace!(target: "miner", "prepare_block: No existing work - making new block");
|
||||
let params = self.params.read().clone();
|
||||
|
||||
match chain.prepare_open_block(
|
||||
let block = match chain.prepare_open_block(
|
||||
params.author,
|
||||
params.gas_range_target,
|
||||
params.extra_data,
|
||||
) {
|
||||
Ok(block) => block,
|
||||
Err(err) => {
|
||||
warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err);
|
||||
warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in \
|
||||
chain specification or on-chain consensus smart contracts.", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Before adding from the queue to the new block, give the engine a chance to add transactions.
|
||||
match self.engine.generate_engine_transactions(&block) {
|
||||
Ok(transactions) => (block, last_work_hash, transactions),
|
||||
Err(err) => {
|
||||
error!(target: "miner", "Failed to prepare engine transactions for new block: {:?}. \
|
||||
This is likely an error in chain specification or on-chain consensus smart \
|
||||
contracts.", err);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if self.options.infinite_pending_block {
|
||||
open_block.remove_gas_limit();
|
||||
}
|
||||
|
||||
(open_block, last_work_hash)
|
||||
};
|
||||
|
||||
if self.options.infinite_pending_block {
|
||||
open_block.remove_gas_limit();
|
||||
}
|
||||
|
||||
let mut invalid_transactions = HashSet::new();
|
||||
let mut not_allowed_transactions = HashSet::new();
|
||||
let mut senders_to_penalize = HashSet::new();
|
||||
@@ -488,13 +507,13 @@ impl Miner {
|
||||
MAX_SKIPPED_TRANSACTIONS.saturating_add(cmp::min(*open_block.header.gas_limit() / min_tx_gas, u64::max_value().into()).as_u64() as usize)
|
||||
};
|
||||
|
||||
let pending: Vec<Arc<_>> = self.transaction_queue.pending(
|
||||
let queue_txs: Vec<Arc<_>> = self.transaction_queue.pending(
|
||||
client.clone(),
|
||||
pool::PendingSettings {
|
||||
block_number: chain_info.best_block_number,
|
||||
current_timestamp: chain_info.best_block_timestamp,
|
||||
nonce_cap,
|
||||
max_len: max_transactions,
|
||||
max_len: max_transactions.saturating_sub(engine_txs.len()),
|
||||
ordering: miner::PendingOrdering::Priority,
|
||||
}
|
||||
);
|
||||
@@ -504,12 +523,11 @@ impl Miner {
|
||||
};
|
||||
|
||||
let block_start = Instant::now();
|
||||
debug!(target: "miner", "Attempting to push {} transactions.", pending.len());
|
||||
debug!(target: "miner", "Attempting to push {} transactions.", engine_txs.len() + queue_txs.len());
|
||||
|
||||
for tx in pending {
|
||||
for transaction in engine_txs.into_iter().chain(queue_txs.into_iter().map(|tx| tx.signed().clone())) {
|
||||
let start = Instant::now();
|
||||
|
||||
let transaction = tx.signed().clone();
|
||||
let hash = transaction.hash();
|
||||
let sender = transaction.sender();
|
||||
|
||||
|
||||
@@ -118,31 +118,35 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransa
|
||||
|
||||
/// Generates dummy client (not test client) with corresponding amount of blocks
|
||||
pub fn generate_dummy_client(block_number: u32) -> Arc<Client> {
|
||||
generate_dummy_client_with_spec_and_data(spec::new_test, block_number, 0, &[])
|
||||
generate_dummy_client_with_spec_and_data(spec::new_test, block_number, 0, &[], false)
|
||||
}
|
||||
|
||||
/// Generates dummy client (not test client) with corresponding amount of blocks and txs per every block
|
||||
pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc<Client> {
|
||||
generate_dummy_client_with_spec_and_data(spec::new_null, block_number, txs_per_block, tx_gas_prices)
|
||||
generate_dummy_client_with_spec_and_data(spec::new_null, block_number, txs_per_block, tx_gas_prices, false)
|
||||
}
|
||||
|
||||
/// Generates dummy client (not test client) with corresponding spec and accounts
|
||||
pub fn generate_dummy_client_with_spec<F>(test_spec: F) -> Arc<Client> where F: Fn() -> Spec {
|
||||
generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[])
|
||||
generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[], false)
|
||||
}
|
||||
|
||||
/// Generates dummy client (not test client) with corresponding amount of blocks, txs per block and spec
|
||||
pub fn generate_dummy_client_with_spec_and_data<F>(test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> Arc<Client> where
|
||||
pub fn generate_dummy_client_with_spec_and_data<F>(
|
||||
test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256], force_sealing: bool,
|
||||
) -> Arc<Client> where
|
||||
F: Fn() -> Spec
|
||||
{
|
||||
let test_spec = test_spec();
|
||||
let client_db = new_db();
|
||||
|
||||
let miner = Miner::new_for_tests_force_sealing(&test_spec, None, force_sealing);
|
||||
|
||||
let client = Client::new(
|
||||
ClientConfig::default(),
|
||||
&test_spec,
|
||||
client_db,
|
||||
Arc::new(Miner::new_for_tests(&test_spec, None)),
|
||||
Arc::new(miner),
|
||||
IoChannel::disconnected(),
|
||||
).unwrap();
|
||||
let test_engine = &*test_spec.engine;
|
||||
|
||||
@@ -72,7 +72,7 @@ use client::{
|
||||
use client_traits::{
|
||||
BlockInfo, Nonce, Balance, ChainInfo, TransactionInfo, BlockChainClient, ImportBlock,
|
||||
AccountData, BlockChain, IoClient, BadBlocks, ScheduleInfo, StateClient, ProvingBlockChainClient,
|
||||
StateOrBlock, ForceUpdateSealing
|
||||
StateOrBlock, ForceUpdateSealing, TransactionRequest
|
||||
};
|
||||
use engine::Engine;
|
||||
use machine::executed::Executed;
|
||||
@@ -912,18 +912,24 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
|
||||
fn create_transaction(&self, TransactionRequest { action, data, gas, gas_price, nonce }: TransactionRequest)
|
||||
-> Result<SignedTransaction, transaction::Error>
|
||||
{
|
||||
let transaction = Transaction {
|
||||
nonce: self.latest_nonce(&self.miner.authoring_params().author),
|
||||
action: Action::Call(address),
|
||||
gas: self.spec.gas_limit,
|
||||
gas_price: U256::zero(),
|
||||
nonce: nonce.unwrap_or_else(|| self.latest_nonce(&self.miner.authoring_params().author)),
|
||||
action,
|
||||
gas: gas.unwrap_or(self.spec.gas_limit),
|
||||
gas_price: gas_price.unwrap_or_else(U256::zero),
|
||||
value: U256::default(),
|
||||
data: data,
|
||||
};
|
||||
let chain_id = Some(self.spec.chain_id());
|
||||
let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap();
|
||||
let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap();
|
||||
Ok(SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap())
|
||||
}
|
||||
|
||||
fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
|
||||
let signed = self.create_transaction(tx_request)?;
|
||||
self.miner.import_own_transaction(self, signed.into())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user