1. 术语

  • lamport:1 lamport = 10−910^{-9}10−9 SOL
  • shred:A fraction of a block; the smallest unit sent between validators。为block的一部分,validators之间传输的最小单元。
  • ledger entry:账本上的entry要么为tick要么为transactions entry。其中tick用于:estimates wallclock duration;而transactions entry 是指:A set of transactions that may be executed in parallel。
  • slot:The period of time for which each leader ingests transactions and produces a block.【A leader produces at most one block per slot. 即每个slot最多只有一个区块。Solana中,设置1个slot对应64个ticks。】
    Collectively, slots create a logical clock. Slots are ordered sequentially and non-overlapping, comprising roughly equal real-world time as per PoH.
    ···
    /// Slot is a unit of time given to a leader for encoding,
    /// is some some number of Ticks long.
    pub type Slot = u64;
    ···
  • skipped slot:A past slot that did not produce a block, because the leader was offline or the fork containing the slot was abandoned for a better alternative by cluster consensus. A skipped slot will not appear as an ancestor for blocks at subsequent slots, nor increment the block height, nor expire the oldest recent_blockhash.
    Whether a slot has been skipped can only be determined when it becomes older than the latest rooted (thus not-skipped) slot.
  • transaction:One or more instructions signed by the client using one or more keypairs and executed atomically with only two possible outcomes: success or failure.
    Solana的交易中会封装recent_hash,可通过getRecentBlockhash rpc 接口获得:
$ curl https://api.testnet.solana.com -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}
'
{"jsonrpc":"2.0","result":{"context":{"slot":91170278},"value":{"blockhash":"78WXFoVnDdBcqFVQp3peAKzma3igxBXhpat1CGpRUPX4","feeCalculator":{"lamportsPerSignature":5000}}},"id":1}
  • transaction id:The first signature in a transaction, which can be used to uniquely identify the transaction across the complete ledger.
  • transaction confirmations:The number of confirmed blocks since the transaction was accepted onto the ledger. A transaction is finalized when its block becomes a root.
  • transactions entry:A set of transactions that may be executed in parallel.
  • epoch:目前Solana一个epoch对应43.2万个slot。【若一个slot对应400ms,则一个epoch对应48小时。】
  • blockhash:A preimage resistant hash of the ledger at a given block height. Taken from the last entry id in the slot。Slot中的最后一个entry id作为block hash。即:【粒度为:一个slot内若没有交易,则对应为64个tick;若有交易,一个slot对应有多个entry,每个entry对应多笔交易,每个entry会切分为多个shreds便于传输,会对每个shred进行签名sign_shred()。】
let new_extra_entry = Entry::new(&prev_entry_hash, 1, vec![extra_tx]);
// 实际即为 `hashv(prev_entry_hash, hash_transactions(transactions))`
    /// Tell the bank which Entry IDs exist on the ledger. This function/// assumes subsequent calls correspond to later entries, and will boot/// the oldest ones once its internal cache is full. Once boot, the/// bank will reject transactions using that `hash`.pub fn register_tick(&self, hash: &Hash) {assert!(!self.freeze_started(),"register_tick() working on a bank that is already frozen or is undergoing freezing!");inc_new_counter_debug!("bank-register_tick-registered", 1);let mut w_blockhash_queue = self.blockhash_queue.write().unwrap();if self.is_block_boundary(self.tick_height.load(Relaxed) + 1) {w_blockhash_queue.register_hash(hash, &self.fee_calculator);self.update_recent_blockhashes_locked(&w_blockhash_queue);}// ReplayStage will start computing the accounts delta hash when it// detects the tick height has reached the boundary, so the system// needs to guarantee all account updates for the slot have been// committed before this tick height is incremented (like the blockhash// sysvar above)self.tick_height.fetch_add(1, Relaxed);}pub fn is_complete(&self) -> bool {self.tick_height() == self.max_tick_height()}pub fn is_block_boundary(&self, tick_height: u64) -> bool {tick_height % self.ticks_per_slot == 0}

Validator节点可同时运行2个pipelined processes,TPU对应为leader mode,TVU对应为validator mode。在TPU和TVU这2种情况下,硬件的pipeline顺序是一样的:the network input -> the GPU cards -> the CPU cores -> writes to disk -> the network output。但是,TPU主要用来创建ledger entries,而TVU用于validate ledger entries。
根据validator/src/main.rscore/src/validator.rs:启动Validator时,会同时配置TPU和TVU资源。

  • Fetch stage:将(<=)1024笔交易打包为packet,发送给Sigverify stage。

  • Sigverify stage:分4个线程接收fetch stage发来的packet,调用ed25519_verify(借助GPU/CPU)进行批量验签,对验签不通过和签名不合格的交易,将其中的meta.discard标记为true。将所有交易发给Banking stage。

  • Banking stage:分3个线程(verified_receiver)接收sigverify stage发来的packet,有1个线程(verified_vote_receiver)用于接收投票信息。timeout时长为(10ms或100ms)来接收packet。
    支持最多buffer 50万笔交易(若超过,则会pop_front,然后再push_back。),每次batch处理128笔交易。

  • Broadcast stage:会通过entry_receiver接收Banking stage中process_and_record_transactions_locked()通过PohRecorder.record() 发送的已成功执行的交易,在processe_receive_results()将entries转换为shreds(会sign_shred()签名,为可传输block的最小单位),会按质押权重通过broadcast_shreds() 将shreds发送给相应的nodes,同时将shreds通过record()存入rocksdb数据库中。

2. TPU

为Validator作为leader角色运行的Transaction Processing Unit(TPU),用于create ledger entries。

  • fetch_stage:batches input from a UDP socket and sends it to a channel.
  • sigverify_stage:implements the signature verification stage of the TPU. It receives a list of lists of packets and outputs the same list, but tags each top-level list with a list of booleans, telling the next stage whether the signature in that packet is valid. It assumes each packet contains one transaction. All processing is done on the CPU by default and on a GPU if perf-libs are available.
  • banking_stage:processes Transaction messages. It is intended to be used to construct a software pipeline. The stage uses all available CPU cores and can do its processing in parallel with signature verification on the GPU.
  • broadcast_state:A stage to broadcast data from a leader node to validators.
  • core/src/consensus.rs:包含了Tower BFT的实现以及相关测试用例。
  • core/src/cluster_info_vote_listener.rs:定义了SlotVoteTracker来map投票情况。
  • core/src/cost_model.rs:用于评估交易开销,提供的维度有writable_accounts、account_access_cost和execution_cost。在banking_stage会调用could_fit()函数评估交易开销是否超过limit。
pub struct TransactionCost {pub writable_accounts: Vec<Pubkey>,pub account_access_cost: u64,pub execution_cost: u64,
}/*
self.would_fit(&tx_cost.writable_accounts,&(tx_cost.account_access_cost + tx_cost.execution_cost),)
*/
fn would_fit(&self, keys: &[Pubkey], cost: &u64) -> Result<(), CostModelError> {// check against the total package costif self.block_cost + cost > self.block_cost_limit {return Err(CostModelError::WouldExceedBlockMaxLimit);}// check if the transaction itself is more costly than the account_cost_limitif *cost > self.account_cost_limit {return Err(CostModelError::WouldExceedAccountMaxLimit);}// check each account against account_cost_limit,for account_key in keys.iter() {match self.cost_by_writable_accounts.get(account_key) {Some(chained_cost) => {if chained_cost + cost > self.account_cost_limit {return Err(CostModelError::WouldExceedAccountMaxLimit);} else {continue;}}None => continue,}}Ok(())}
// Guestimated from mainnet-beta data, sigver averages 1us, average read 7us and average write 25us
const SIGVER_COST: u64 = 1;
const NON_SIGNED_READONLY_ACCOUNT_ACCESS_COST: u64 = 7;
const NON_SIGNED_WRITABLE_ACCOUNT_ACCESS_COST: u64 = 25;
const SIGNED_READONLY_ACCOUNT_ACCESS_COST: u64 =SIGVER_COST + NON_SIGNED_READONLY_ACCOUNT_ACCESS_COST;
const SIGNED_WRITABLE_ACCOUNT_ACCESS_COST: u64 =SIGVER_COST + NON_SIGNED_WRITABLE_ACCOUNT_ACCESS_COST;
pub struct SlotVoteTracker {// Maps pubkeys that have voted for this slot// to whether or not we've seen the vote on gossip.// True if seen on gossip, false if only seen in replay.voted: HashMap<Pubkey, bool>,optimistic_votes_tracker: HashMap<Hash, VoteStakeTracker>,voted_slot_updates: Option<Vec<Pubkey>>,gossip_only_stake: u64,
}
                                 .------------.| Upstream   || Validators |`----+-------`||.-----------------------------------.| Validator              |          ||                        v          ||  .-----------.    .------------.  |.--------.  |  | Fetch     |    | Repair     |  || Client +---->| Stage     |    | Stage      |  |`--------`  |  `---+-------`    `----+-------`  ||      |                 |          ||      v                 v          ||  .-----------.    .------------.  ||  | TPU       |<-->| Blockstore |  ||  |           |    |            |  ||  `-----------`    `----+-------`  ||                        |          ||                        v          ||                   .------------.  ||                   | Multicast  |  ||                   | Stage      |  ||                   `----+-------`  ||                        |          |`-----------------------------------`|v.------------.| Downstream || Validators |`------------`.------------.| PoH        || Service    |`-------+----`^   ||   |.-----------------------------------.| TPU                   |   |       ||                       |   v       |.-------.  |  .-----------.    .---+--------.  |  .------------.| Fetch +---->| SigVerify +--->| Banking    |<--->| Blockstore || Stage |  |  | Stage     |    | Stage      |  |  |            |`-------`  |  `-----------`    `-----+------`  |  `------------`|                         |         ||                         |         |`-----------------------------------`|v.------------.| Banktree   ||            |`------------`

对应的TPU:

Self {fetch_stage,sigverify_stage,banking_stage,cluster_info_vote_listener,broadcast_stage,
}

3. TVU

为Validator作为validator 角色运行的Transaction Verification Unit(TVU),用于validate ledger entries。

                                                                         +------------+|   Gossip   ||   Service  |+-------------+                         |            ||Child        |    +------------+       +-+-----+----+|Validators   |    |Neighborhood|         |     ^|             |    |Validators  |         |     |+-------+-----+    |            |         |     |^          +---------+--+     Peer|     |Votes|                    ^        List|     |+-----------------+  |            |     |shreds(forward=true)|  |            |     |+-----------------------------------------------------------------------------------------+|                                 |  |            |     |                                 ||  TVU                            |  |   +--------+     |                                 ||                                 |  |   v              |                                 |
+------------+           |  +-------+  +------------+    +-+--+---+---+      +---+--------------+      +---------+ |
|            |    Repair |  |       |  |            |    | Retransmit |      | Replay           |      | Storage | |
| Upstream   +------------->+       +->+ Shred      +--->+ Stage      +----->+ Stage            +----->+ Stage   | |
| Validators |     TVU   |  | Shred |  | Verify     |    |            |      | +--------------+ |      |         | |
|            +------------->+ Fetch |  | Leader Sig |    +------+-----+      | | PoH Verify   | |      +---------+ |
|            |           |  | Stage |  | Stage      |           ^            | | TX Sig Verify| |                  |
|            +------------->+       |  |            |           |            | |              | |                  |
|            |   TVU     |  |       |  +--+---------+           |            | +-+------------+ |                  |
+------------+   Forwards|  +-------+     ^                     |            |   |              |                  ||                |                     |            +------------------+                  ||                |                     |                |                                 ||                |                     |                |                                 ||                |                     |                |                                 |+-----------------------------------------------------------------------------------------+|                     |                ||                     |Validator       v|                     |Stakes      +---+-----+|                     +------------+         |+----------------------------------+ Bank    |Leader                       |         |Schedule                     +---------+

批量验签过程中做了判断,若有GPU则走GPU验签,否则走CPU验签:

pub fn verify_shreds_gpu(batches: &[Packets],slot_leaders: &HashMap<u64, [u8; 32]>,recycler_cache: &RecyclerCache,
) -> Vec<Vec<u8>> {let api = perf_libs::api();if api.is_none() {return verify_shreds_cpu(batches, slot_leaders);}let api = api.unwrap();let mut elems = Vec::new();let mut rvs = Vec::new();let count = batch_size(batches);let (pubkeys, pubkey_offsets, mut num_packets) =slot_key_data_for_gpu(0, batches, slot_leaders, recycler_cache);//HACK: Pubkeys vector is passed along as a `Packets` buffer to the GPU//TODO: GPU needs a more opaque interface, which can handle variable sized structures for datalet pubkeys_len = num_packets * size_of::<Packet>();trace!("num_packets: {}", num_packets);trace!("pubkeys_len: {}", pubkeys_len);let (signature_offsets, msg_start_offsets, msg_sizes, v_sig_lens) =shred_gpu_offsets(pubkeys_len, batches, recycler_cache);let mut out = recycler_cache.buffer().allocate("out_buffer");out.set_pinnable();elems.push(perf_libs::Elems {#[allow(clippy::cast_ptr_alignment)]elems: pubkeys.as_ptr() as *const solana_sdk::packet::Packet,num: num_packets as u32,});for p in batches {elems.push(perf_libs::Elems {elems: p.packets.as_ptr(),num: p.packets.len() as u32,});let mut v = Vec::new();v.resize(p.packets.len(), 0);rvs.push(v);num_packets += p.packets.len();}out.resize(signature_offsets.len(), 0);trace!("Starting verify num packets: {}", num_packets);trace!("elem len: {}", elems.len() as u32);trace!("packet sizeof: {}", size_of::<Packet>() as u32);const USE_NON_DEFAULT_STREAM: u8 = 1;unsafe {let res = (api.ed25519_verify_many)(elems.as_ptr(),elems.len() as u32,size_of::<Packet>() as u32,num_packets as u32,signature_offsets.len() as u32,msg_sizes.as_ptr(),pubkey_offsets.as_ptr(),signature_offsets.as_ptr(),msg_start_offsets.as_ptr(),out.as_mut_ptr(),USE_NON_DEFAULT_STREAM,);if res != 0 {trace!("RETURN!!!: {}", res);}}trace!("done verify");trace!("out buf {:?}", out);sigverify::copy_return_values(&v_sig_lens, &out, &mut rvs);inc_new_counter_debug!("ed25519_shred_verify_gpu", count);rvs
}

4. 交易处理

交易由一系列签名和消息组成:

/// An atomic transaction
#[frozen_abi(digest = "AAeVxvWiiotwxDLxKLxsfgkA6ndW74nVbaAEb6cwJYqR")]
#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize, AbiExample)]
pub struct Transaction {/// A set of digital signatures of `account_keys`, `program_ids`, `recent_blockhash`, and `instructions`, signed by the first/// signatures.len() keys of account_keys/// NOTE: Serialization-related changes must be paired with the direct read at sigverify.#[serde(with = "short_vec")]pub signatures: Vec<Signature>,/// The message to sign.pub message: Message,
}

4.1 锁定交易相关账号

借助MutexGuard,锁定交易相关账号,防止多线程同时对同一账号状态进行修改:

// Once accounts are locked, other threads cannot encode transactions that will modify the// same account statelet batch = bank.prepare_sanitized_batch(txs);/// This function will prevent multiple threads from modifying the same account state at the/// same time#[must_use]#[allow(clippy::needless_collect)]pub fn lock_accounts<'a>(&self, txs: impl Iterator<Item = &'a Transaction>) -> Vec<Result<()>> {let keys: Vec<_> = txs.map(|tx| tx.message().get_account_keys_by_lock_type()).collect();let mut account_locks = &mut self.account_locks.lock().unwrap();keys.into_iter().map(|(writable_keys, readonly_keys)| {self.lock_account(&mut account_locks, writable_keys, readonly_keys)}).collect()}
/// Once accounts are unlocked, new transactions that modify that state can enter the pipeline#[allow(clippy::needless_collect)]pub fn unlock_accounts<'a>(&self,txs: impl Iterator<Item = &'a Transaction>,results: &[Result<()>],) pub fn prepare_sanitized_batch<'a, 'b>(&'a self,sanitized_txs: &'b [SanitizedTransaction],) -> TransactionBatch<'a, 'b> {let lock_results = self.rc.accounts.lock_accounts(sanitized_txs.as_transactions_iter());TransactionBatch::new(lock_results, self, Cow::Borrowed(sanitized_txs))}

4.2 标记交易有效性

vec![1u8, 0u8, 1u8] 可有效标记交易的有效性,具体体现在discard字段,无效的交易标记为1。

pub fn convert_from_old_verified(mut with_vers: Vec<(Packets, Vec<u8>)>) -> Vec<Packets> {with_vers.iter_mut().for_each(|(b, v)| {b.packets.iter_mut().zip(v).for_each(|(p, f)| p.meta.discard = *f == 0)});with_vers.into_iter().map(|(b, _)| b).collect()}fn test_banking_stage_entries_only() {........// fund another account so we can send 2 good transactions in a single batch.let keypair = Keypair::new();let fund_tx =system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 2, start_hash);bank.process_transaction(&fund_tx).unwrap();// good txlet to = solana_sdk::pubkey::new_rand();let tx = system_transaction::transfer(&mint_keypair, &to, 1, start_hash);// good tx, but no verifylet to2 = solana_sdk::pubkey::new_rand();let tx_no_ver = system_transaction::transfer(&keypair, &to2, 2, start_hash);// bad tx, AccountNotFoundlet keypair = Keypair::new();let to3 = solana_sdk::pubkey::new_rand();let tx_anf = system_transaction::transfer(&keypair, &to3, 1, start_hash);// send 'em overlet packets = to_packets_chunked(&[tx_no_ver, tx_anf, tx], 3);// glad they all fitassert_eq!(packets.len(), 1);let packets = packets.into_iter().map(|packets| (packets, vec![1u8, 0u8, 1u8])).collect();........assert_eq!(bank.get_balance(&to), 1);assert_eq!(bank.get_balance(&to2), 2);drop(entry_receiver);}Blockstore::destroy(&ledger_path).unwrap();}

sdk/src/transaction.rs 中定义了交易错误类型有:

pub enum TransactionError {/// An account is already being processed in another transaction in a way/// that does not support parallelism#[error("Account in use")]AccountInUse,/// A `Pubkey` appears twice in the transaction's `account_keys`.  Instructions can reference/// `Pubkey`s more than once but the message must contain a list with no duplicate keys#[error("Account loaded twice")]AccountLoadedTwice,/// Attempt to debit an account but found no record of a prior credit.#[error("Attempt to debit an account but found no record of a prior credit.")]AccountNotFound,/// Attempt to load a program that does not exist#[error("Attempt to load a program that does not exist")]ProgramAccountNotFound,/// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction#[error("Insufficient funds for fee")]InsufficientFundsForFee,/// This account may not be used to pay transaction fees#[error("This account may not be used to pay transaction fees")]InvalidAccountForFee,/// The bank has seen this transaction before. This can occur under normal operation/// when a UDP packet is duplicated, as a user error from a client not updating/// its `recent_blockhash`, or as a double-spend attack.#[error("This transaction has already been processed")]AlreadyProcessed,/// The bank has not seen the given `recent_blockhash` or the transaction is too old and/// the `recent_blockhash` has been discarded.#[error("Blockhash not found")]BlockhashNotFound,/// An error occurred while processing an instruction. The first element of the tuple/// indicates the instruction index in which the error occurred.#[error("Error processing Instruction {0}: {1}")]InstructionError(u8, InstructionError),/// Loader call chain is too deep#[error("Loader call chain is too deep")]CallChainTooDeep,/// Transaction requires a fee but has no signature present#[error("Transaction requires a fee but has no signature present")]MissingSignatureForFee,/// Transaction contains an invalid account reference#[error("Transaction contains an invalid account reference")]InvalidAccountIndex,/// Transaction did not pass signature verification#[error("Transaction did not pass signature verification")]SignatureFailure,/// This program may not be used for executing instructions#[error("This program may not be used for executing instructions")]InvalidProgramForExecution,/// Transaction failed to sanitize accounts offsets correctly/// implies that account locks are not taken for this TX, and should/// not be unlocked.#[error("Transaction failed to sanitize accounts offsets correctly")]SanitizeFailure,#[error("Transactions are currently disabled due to cluster maintenance")]ClusterMaintenance,/// Transaction processing left an account with an outstanding borrowed reference#[error("Transaction processing left an account with an outstanding borrowed reference")]AccountBorrowOutstanding,
}

sdk/src/banking.rsload_and_execute_transactions()函数会对净化交易进一步处理,在banking_stage中会调用该函数。
transactions_from_packets() 中借助verify_precompiles()对交易进行净化,排除AccountNotFoundInvalidAccountIndex的错误交易。
process_loop()-> 【process_buffered_packets()->consume_buffered_packets()process_packets()->process_packets_transactions()】 ->process_transactions()->process_and_record_transactions()->process_and_record_transactions_locked()->load_and_execute_transactions()

    /// This function will prevent multiple threads from modifying the same account state at the/// same time#[must_use]#[allow(clippy::needless_collect)]pub fn lock_accounts<'a>(&self, txs: impl Iterator<Item = &'a Transaction>) -> Vec<Result<()>> {let keys: Vec<_> = txs.map(|tx| tx.message().get_account_keys_by_lock_type()).collect();let mut account_locks = &mut self.account_locks.lock().unwrap();keys.into_iter().map(|(writable_keys, readonly_keys)| {self.lock_account(&mut account_locks, writable_keys, readonly_keys)}).collect()}pub fn prepare_sanitized_batch<'a, 'b>(&'a self,sanitized_txs: &'b [SanitizedTransaction],) -> TransactionBatch<'a, 'b> {let lock_results = self.rc.accounts.lock_accounts(sanitized_txs.as_transactions_iter());TransactionBatch::new(lock_results, self, Cow::Borrowed(sanitized_txs))}
    pub fn load_and_execute_transactions(&self,batch: &TransactionBatch,max_age: usize,enable_cpi_recording: bool,enable_log_recording: bool,timings: &mut ExecuteTimings,) -> (Vec<TransactionLoadResult>,Vec<TransactionExecutionResult>,Vec<Option<InnerInstructionsList>>,Vec<Option<TransactionLogMessages>>,Vec<usize>,u64,u64,) {let sanitized_txs = batch.sanitized_transactions();debug!("processing transactions: {}", sanitized_txs.len());inc_new_counter_info!("bank-process_transactions", sanitized_txs.len());let mut error_counters = ErrorCounters::default();let retryable_txs: Vec<_> = batch.lock_results().iter().enumerate().filter_map(|(index, res)| match res {Err(TransactionError::AccountInUse) => {error_counters.account_in_use += 1;Some(index)}Err(_) => None,Ok(_) => None,}).collect();let mut check_time = Measure::start("check_transactions");let check_results = self.check_transactions(sanitized_txs,batch.lock_results(),max_age,&mut error_counters,);check_time.stop();let mut load_time = Measure::start("accounts_load");let mut loaded_txs = self.rc.accounts.load_accounts(&self.ancestors,sanitized_txs.as_transactions_iter(),check_results,&self.blockhash_queue.read().unwrap(),&mut error_counters,&self.rent_collector,&self.feature_set,);load_time.stop();let mut execution_time = Measure::start("execution_time");let mut signature_count: u64 = 0;let mut inner_instructions: Vec<Option<InnerInstructionsList>> =Vec::with_capacity(sanitized_txs.len());let mut transaction_log_messages: Vec<Option<Vec<String>>> =Vec::with_capacity(sanitized_txs.len());let executed: Vec<TransactionExecutionResult> = loaded_txs.iter_mut().zip(sanitized_txs.as_transactions_iter()).map(|(accs, tx)| match accs {(Err(e), _nonce_rollback) => {transaction_log_messages.push(None);inner_instructions.push(None);(Err(e.clone()), None)}(Ok(loaded_transaction), nonce_rollback) => {let feature_set = self.feature_set.clone();signature_count += u64::from(tx.message().header.num_required_signatures);let mut compute_budget = self.compute_budget.unwrap_or_else(ComputeBudget::new);let mut process_result = if feature_set.is_active(&tx_wide_compute_cap::id()) {compute_budget.process_transaction(tx)} else {Ok(())};if process_result.is_ok() {let executors =self.get_executors(&tx.message, &loaded_transaction.loaders);let (account_refcells, loader_refcells) = Self::accounts_to_refcells(&mut loaded_transaction.accounts,&mut loaded_transaction.loaders,);let instruction_recorders = if enable_cpi_recording {let ix_count = tx.message.instructions.len();let mut recorders = Vec::with_capacity(ix_count);recorders.resize_with(ix_count, InstructionRecorder::default);Some(recorders)} else {None};let log_collector = if enable_log_recording {Some(Rc::new(LogCollector::default()))} else {None};let compute_meter = Rc::new(RefCell::new(TransactionComputeMeter::new(compute_budget.max_units,)));process_result = self.message_processor.process_message(tx.message(),&loader_refcells,&account_refcells,&self.rent_collector,log_collector.clone(),executors.clone(),instruction_recorders.as_deref(),feature_set,compute_budget,compute_meter,&mut timings.details,self.rc.accounts.clone(),&self.ancestors,);transaction_log_messages.push(Self::collect_log_messages(log_collector));inner_instructions.push(Self::compile_recorded_instructions(instruction_recorders,&tx.message,));if let Err(e) = Self::refcells_to_accounts(&mut loaded_transaction.accounts,&mut loaded_transaction.loaders,account_refcells,loader_refcells,) {warn!("Account lifetime mismanagement");process_result = Err(e);}if process_result.is_ok() {self.update_executors(executors);}} else {transaction_log_messages.push(None);inner_instructions.push(None);}let nonce_rollback =if let Err(TransactionError::InstructionError(_, _)) = &process_result {error_counters.instruction_error += 1;nonce_rollback.clone()} else if process_result.is_err() {None} else {nonce_rollback.clone()};(process_result, nonce_rollback)}}).collect();execution_time.stop();debug!("check: {}us load: {}us execute: {}us txs_len={}",check_time.as_us(),load_time.as_us(),execution_time.as_us(),sanitized_txs.len(),);timings.check_us = timings.check_us.saturating_add(check_time.as_us());timings.load_us = timings.load_us.saturating_add(load_time.as_us());timings.execute_us = timings.execute_us.saturating_add(execution_time.as_us());let mut tx_count: u64 = 0;let err_count = &mut error_counters.total;let transaction_log_collector_config =self.transaction_log_collector_config.read().unwrap();for (i, ((r, _nonce_rollback), tx)) in executed.iter().zip(sanitized_txs).enumerate() {if let Some(debug_keys) = &self.transaction_debug_keys {for key in &tx.message.account_keys {if debug_keys.contains(key) {info!("slot: {} result: {:?} tx: {:?}", self.slot, r, tx);break;}}}if Self::can_commit(r) // Skip log collection for unprocessed transactions&& transaction_log_collector_config.filter != TransactionLogCollectorFilter::None{let mut transaction_log_collector = self.transaction_log_collector.write().unwrap();let transaction_log_index = transaction_log_collector.logs.len();let mut mentioned_address = false;if !transaction_log_collector_config.mentioned_addresses.is_empty(){for key in &tx.message.account_keys {if transaction_log_collector_config.mentioned_addresses.contains(key){transaction_log_collector.mentioned_address_map.entry(*key).or_default().push(transaction_log_index);mentioned_address = true;}}}let is_vote = is_simple_vote_transaction(tx);let store = match transaction_log_collector_config.filter {TransactionLogCollectorFilter::All => !is_vote || mentioned_address,TransactionLogCollectorFilter::AllWithVotes => true,TransactionLogCollectorFilter::None => false,TransactionLogCollectorFilter::OnlyMentionedAddresses => mentioned_address,};if store {if let Some(log_messages) = transaction_log_messages.get(i).cloned().flatten() {transaction_log_collector.logs.push(TransactionLogInfo {signature: tx.signatures[0],result: r.clone(),is_vote,log_messages,});}}}if r.is_ok() {tx_count += 1;} else {if *err_count == 0 {debug!("tx error: {:?} {:?}", r, tx);}*err_count += 1;}}if *err_count > 0 {debug!("{} errors of {} txs",*err_count,*err_count as u64 + tx_count);}Self::update_error_counters(&error_counters);(loaded_txs,executed,inner_instructions,transaction_log_messages,retryable_txs,tx_count,signature_count,)}

5. PoH

其中一种PoH为:

pub struct TransactionRecorder {// shared by all users of PohRecorderpub record_sender: CrossbeamSender<Record>,pub is_exited: Arc<AtomicBool>,
}
// 有:
poh: &TransactionRecorder,

以及另一种PoH为:

poh: &Arc<Mutex<PohRecorder>>,pub struct PohRecorder {pub poh: Arc<Mutex<Poh>>,tick_height: u64,clear_bank_signal: Option<SyncSender<bool>>,start_slot: Slot,              // parent slotstart_tick_height: u64,        // first tick_height this recorder will observetick_cache: Vec<(Entry, u64)>, // cache of entry and its tick_heightworking_bank: Option<WorkingBank>,sender: Sender<WorkingBankEntry>,leader_first_tick_height: Option<u64>,leader_last_tick_height: u64, // zero if nonegrace_ticks: u64,id: Pubkey,blockstore: Arc<Blockstore>,leader_schedule_cache: Arc<LeaderScheduleCache>,poh_config: Arc<PohConfig>,ticks_per_slot: u64,target_ns_per_tick: u64,record_lock_contention_us: u64,flush_cache_no_tick_us: u64,flush_cache_tick_us: u64,prepare_send_us: u64,send_us: u64,tick_lock_contention_us: u64,tick_overhead_us: u64,total_sleep_us: u64,record_us: u64,ticks_from_record: u64,last_metric: Instant,record_sender: CrossbeamSender<Record>,pub is_exited: Arc<AtomicBool>,
}pub struct Poh {pub hash: Hash,num_hashes: u64,hashes_per_tick: u64,remaining_hashes: u64,ticks_per_slot: u64,tick_number: u64,slot_start_time: Instant,
}

6. concurrent database

Cloudbreak:Horizontally-Scaled Accounts Database
对应实现在:runtime/src/accounts_db.rs

//! Persistent accounts are stored in below path location:
//!  <path>/<pid>/data/
//!
//! The persistent store would allow for this mode of operation:
//!  - Concurrent single thread append with many concurrent readers.
//!
//! The underlying memory is memory mapped to a file. The accounts would be
//! stored across multiple files and the mappings of file and offset of a
//! particular account would be stored in a shared index. This will allow for
//! concurrent commits without blocking reads, which will sequentially write
//! to memory, ssd or disk, and should be as fast as the hardware allow for.
//! The only required in memory data structure with a write lock is the index,
//! which should be fast to update.
//!
//! AppendVec's only store accounts for single slots.  To bootstrap the
//! index from a persistent store of AppendVec's, the entries include
//! a "write_version".  A single global atomic `AccountsDb::write_version`
//! tracks the number of commits to the entire data store. So the latest
//! commit for each slot entry would be indexed.

7. 投票

Solana中的 Native合约 中有:

Vote Program#
Create and manage accounts that track validator voting state and rewards.Program id: Vote111111111111111111111111111111111111111
Instructions: VoteInstruction

Vote Account Management 中描述了Vote账号的相关管理规则。

Secure Vote Signing 中指出:
Validator会从当前leader中接收到entries,并提交votes来确认那些entries是valid的。提交vote的过程中存在相关安全风险,因为伪造的投票违背了共识,将会slash掉该Validator的stake。

Solana代码解析相关推荐

  1. Wormhole资产跨链项目代码解析

    1. 引言 Wormhole支持基于Solana与多个链进行资产转移,开源代码为: https://github.com/certusone/wormhole/tree/main 实际部署配置信息参见 ...

  2. matrix_multiply代码解析

    matrix_multiply代码解析 关于matrix_multiply 程序执行代码里两个矩阵的乘法,并将相乘结果打印在屏幕上. 示例的主要目的是展现怎么实现一个自定义CPU计算任务. 参考:ht ...

  3. CornerNet代码解析——损失函数

    CornerNet代码解析--损失函数 文章目录 CornerNet代码解析--损失函数 前言 总体损失 1.Heatmap的损失 2.Embedding的损失 3.Offset的损失 前言 今天要解 ...

  4. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析

    来源:深蓝学院,文稿整理者:何常鑫,审核&修改:刘国庆 本文总结于上交感知与导航研究所科研助理--刘国庆关于[视觉SLAM开源算法ORB-SLAM3 原理与代码解析]的公开课. ORB-SLA ...

  5. java获取object属性值_java反射获取一个object属性值代码解析

    有些时候你明明知道这个object里面是什么,但是因为种种原因,你不能将它转化成一个对象,只是想单纯地提取出这个object里的一些东西,这个时候就需要用反射了. 假如你这个类是这样的: privat ...

  6. python中的doc_基于Python获取docx/doc文件内容代码解析

    这篇文章主要介绍了基于Python获取docx/doc文件内容代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 整体思路: 下载文件并修改后缀 ...

  7. mongoose框架示例代码解析(一)

    mongoose框架示例代码解析(一) 参考: Mongoose Networking Library Documentation(Server) Mongoose Networking Librar ...

  8. ViBe算法原理和代码解析

    ViBe - a powerful technique for background detection and subtraction in video sequences 算法官网:http:// ...

  9. 【Android 逆向】使用 Python 代码解析 ELF 文件 ( PyCharm 中进行断点调试 | ELFFile 实例对象分析 )

    文章目录 一.PyCharm 中进行断点调试 二.ELFFile 实例对象分析 一.PyCharm 中进行断点调试 在上一篇博客 [Android 逆向]使用 Python 代码解析 ELF 文件 ( ...

  10. 密码算法中iv值是什么_?标检测中的?极?值抑制算法(nms):python代码解析

    ⾮极⼤值抑制(Non-Maximum Suppression)原理 ⾮极⼤值抑制,顾名思义,找出极⼤值,抑制⾮极⼤值.这种思路和算法在各个领域中应⽤⼴泛,⽐如边缘检测算法canny算⼦中就使⽤了该⽅法 ...

最新文章

  1. 怎么装python解析器_linux 下安装 python 解析器
  2. PAT甲级1083 List Grades:[C++题解]结构体、排序
  3. Markdown如何插入LaTeX公式?(在线LaTeX公式编辑器)
  4. 当你真的去买菜的时候,你就知道为什么奶奶姥姥对于价格那么敏感,为什么喜欢打折?
  5. python 判断div 之间的内容是否为空_python实现单向链表数据结构及其基本方法
  6. nginx输出日志_ingressnginx持久化日志
  7. 通过Dapr实现一个简单的基于.net的微服务电商系统
  8. java none怎么用tomcat_使用tomcat做java中间件
  9. C# 计算代码执行时间
  10. window常用的『运行』命令
  11. [C/C++] ccpuid:CPUID信息模块 V1.02版,支持Mac OS X,支持纯C,增加CPUF常数
  12. WebService-WSDL报文解析
  13. ant中修改a-switch的大小、修改a-checkbox的大小
  14. 罗技G603鼠标欧姆龙D2FC-F-7N微动开关拆解修复双击问题要点解析
  15. PPT基础(三十)图片的特殊效果
  16. EasyDSS部署在C盘,录像回看无法正常播放该如何解决?
  17. Python 数据可视化基础教程
  18. 小工具开发笔记—IE自动填表器—第一篇:你好,世界
  19. MAPI错误0x80040107
  20. 程序员配电脑(只要三千元!)

热门文章

  1. 【Pyecharts50例】添加背景图片/Logo
  2. 北京邮电大学计算机学院考研夏令营,北京邮电大学计算机学院(专业学位)计算机技术保研夏令营...
  3. Intro.js 简介和用法
  4. 【小墨mysql】mysql系列之三---事务
  5. Java中除法运算符简介说明
  6. JAVA多线程任务详解笔记
  7. 图灵计算机模型意义,图灵机有什么意义_学习图灵机模型中遇到的问题 - 人工智能 - 电子发烧友网...
  8. zigbee 4:协调器/路由器/终端 建立/加入 网络
  9. word里画的流程图怎么全选_流程图怎么做?用Word制作流程图超方便!
  10. 深入解析J.U.C并发包(二)——AtomicInteger类