Add program and runtime support for Durable Transaction Nonces (#6845)

* Rework transaction processing result forwarding

Durable nonce prereq

* Add Durable Nonce program API

* Add runtime changes for Durable Nonce program

* Register Durable Nonce program

* Concise comments and bad math

* Fix c/p error

* Add rent sysvar to withdraw ix

* Remove rent exempt required balance from Meta struct

* Use the helper
This commit is contained in:
Trent Nelson
2019-12-07 12:54:10 -07:00
committed by GitHub
parent 6469606baf
commit 1ffd6b4b4d
18 changed files with 1832 additions and 143 deletions

View File

@ -20,7 +20,11 @@ use solana_ledger::{
use solana_measure::measure::Measure;
use solana_metrics::{inc_new_counter_debug, inc_new_counter_info, inc_new_counter_warn};
use solana_perf::{cuda_runtime::PinnedVec, perf_libs};
use solana_runtime::{accounts_db::ErrorCounters, bank::Bank, transaction_batch::TransactionBatch};
use solana_runtime::{
accounts_db::ErrorCounters,
bank::{Bank, TransactionProcessResult},
transaction_batch::TransactionBatch,
};
use solana_sdk::{
clock::{
Slot, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE,
@ -443,7 +447,7 @@ impl BankingStage {
fn record_transactions(
bank_slot: Slot,
txs: &[Transaction],
results: &[transaction::Result<()>],
results: &[TransactionProcessResult],
poh: &Arc<Mutex<PohRecorder>>,
) -> (Result<usize>, Vec<usize>) {
let mut processed_generation = Measure::start("record::process_generation");
@ -451,7 +455,7 @@ impl BankingStage {
.iter()
.zip(txs.iter())
.enumerate()
.filter_map(|(i, (r, x))| {
.filter_map(|(i, ((r, _h), x))| {
if Bank::can_commit(r) {
Some((x.clone(), i))
} else {
@ -678,13 +682,13 @@ impl BankingStage {
// This function returns a vector containing index of all valid transactions. A valid
// transaction has result Ok() as the value
fn filter_valid_transaction_indexes(
valid_txs: &[transaction::Result<()>],
valid_txs: &[TransactionProcessResult],
transaction_indexes: &[usize],
) -> Vec<usize> {
let valid_transactions = valid_txs
.iter()
.enumerate()
.filter_map(|(index, x)| if x.is_ok() { Some(index) } else { None })
.filter_map(|(index, (x, _h))| if x.is_ok() { Some(index) } else { None })
.collect_vec();
valid_transactions
@ -1022,6 +1026,7 @@ mod tests {
entry::{next_entry, Entry, EntrySlice},
get_tmp_ledger_path,
};
use solana_runtime::bank::HashAgeKind;
use solana_sdk::{
instruction::InstructionError,
signature::{Keypair, KeypairUtil},
@ -1369,7 +1374,10 @@ mod tests {
system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()),
];
let mut results = vec![Ok(()), Ok(())];
let mut results = vec![
(Ok(()), Some(HashAgeKind::Extant)),
(Ok(()), Some(HashAgeKind::Extant)),
];
let _ = BankingStage::record_transactions(
bank.slot(),
&transactions,
@ -1380,10 +1388,13 @@ mod tests {
assert_eq!(entry.transactions.len(), transactions.len());
// InstructionErrors should still be recorded
results[0] = Err(TransactionError::InstructionError(
1,
InstructionError::new_result_with_negative_lamports(),
));
results[0] = (
Err(TransactionError::InstructionError(
1,
InstructionError::new_result_with_negative_lamports(),
)),
Some(HashAgeKind::Extant),
);
let (res, retryable) = BankingStage::record_transactions(
bank.slot(),
&transactions,
@ -1396,7 +1407,7 @@ mod tests {
assert_eq!(entry.transactions.len(), transactions.len());
// Other TransactionErrors should not be recorded
results[0] = Err(TransactionError::AccountNotFound);
results[0] = (Err(TransactionError::AccountNotFound), None);
let (res, retryable) = BankingStage::record_transactions(
bank.slot(),
&transactions,
@ -1559,12 +1570,12 @@ mod tests {
assert_eq!(
BankingStage::filter_valid_transaction_indexes(
&vec![
Err(TransactionError::BlockhashNotFound),
Err(TransactionError::BlockhashNotFound),
Ok(()),
Err(TransactionError::BlockhashNotFound),
Ok(()),
Ok(())
(Err(TransactionError::BlockhashNotFound), None),
(Err(TransactionError::BlockhashNotFound), None),
(Ok(()), Some(HashAgeKind::Extant)),
(Err(TransactionError::BlockhashNotFound), None),
(Ok(()), Some(HashAgeKind::Extant)),
(Ok(()), Some(HashAgeKind::Extant)),
],
&vec![2, 4, 5, 9, 11, 13]
),
@ -1574,12 +1585,12 @@ mod tests {
assert_eq!(
BankingStage::filter_valid_transaction_indexes(
&vec![
Ok(()),
Err(TransactionError::BlockhashNotFound),
Err(TransactionError::BlockhashNotFound),
Ok(()),
Ok(()),
Ok(())
(Ok(()), Some(HashAgeKind::Extant)),
(Err(TransactionError::BlockhashNotFound), None),
(Err(TransactionError::BlockhashNotFound), None),
(Ok(()), Some(HashAgeKind::Extant)),
(Ok(()), Some(HashAgeKind::Extant)),
(Ok(()), Some(HashAgeKind::Extant)),
],
&vec![1, 6, 7, 9, 31, 43]
),

View File

@ -2,7 +2,7 @@ use crate::result::{Error, Result};
use crossbeam_channel::{Receiver, RecvTimeoutError};
use solana_client::rpc_request::RpcTransactionStatus;
use solana_ledger::{blocktree::Blocktree, blocktree_processor::TransactionStatusBatch};
use solana_runtime::bank::Bank;
use solana_runtime::bank::{Bank, HashAgeKind};
use std::{
sync::{
atomic::{AtomicBool, Ordering},
@ -56,10 +56,15 @@ impl TransactionStatusService {
} = write_transaction_status_receiver.recv_timeout(Duration::from_secs(1))?;
let slot = bank.slot();
for (transaction, status) in transactions.iter().zip(statuses) {
for (transaction, (status, hash_age_kind)) in transactions.iter().zip(statuses) {
if Bank::can_commit(&status) && !transaction.signatures.is_empty() {
let fee_hash = if let Some(HashAgeKind::DurableNonce) = hash_age_kind {
bank.last_blockhash()
} else {
transaction.message().recent_blockhash
};
let fee_calculator = bank
.get_fee_calculator(&transaction.message().recent_blockhash)
.get_fee_calculator(&fee_hash)
.expect("FeeCalculator must exist");
let fee = fee_calculator.calculate_fee(transaction.message());
blocktree