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:
Andreas Fackler
2019-12-17 11:34:14 +01:00
committed by David
parent f6909d8243
commit 2b1d148ceb
26 changed files with 1293 additions and 69 deletions

View File

@@ -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));

View File

@@ -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();

View File

@@ -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;

View File

@@ -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())
}
}