diff --git a/cli-output/src/display.rs b/cli-output/src/display.rs index 720a3f161d77fb..26947a346fc749 100644 --- a/cli-output/src/display.rs +++ b/cli-output/src/display.rs @@ -262,6 +262,7 @@ fn write_transaction( write_status(w, &transaction_status.status, prefix)?; write_fees(w, transaction_status.fee, prefix)?; write_balances(w, transaction_status, prefix)?; + write_compute_units_consumed(w, transaction_status.compute_units_consumed, prefix)?; write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?; write_return_data(w, transaction_status.return_data.as_ref(), prefix)?; write_rewards(w, transaction_status.rewards.as_ref(), prefix)?; @@ -613,6 +614,17 @@ fn write_return_data( Ok(()) } +fn write_compute_units_consumed( + w: &mut W, + compute_units_consumed: Option, + prefix: &str, +) -> io::Result<()> { + if let Some(cus) = compute_units_consumed { + writeln!(w, "{}Compute Units Consumed: {}", prefix, cus)?; + } + Ok(()) +} + fn write_log_messages( w: &mut W, log_messages: Option<&Vec>, @@ -794,6 +806,7 @@ mod test { program_id: Pubkey::new_from_array([2u8; 32]), data: vec![1, 2, 3], }), + compute_units_consumed: Some(1234u64), }; let output = { @@ -828,6 +841,7 @@ Status: Ok Fee: ◎0.000005 Account 0 balance: ◎0.000005 -> ◎0 Account 1 balance: ◎0.00001 -> ◎0.0000099 +Compute Units Consumed: 1234 Log Messages: Test message Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR: @@ -871,6 +885,7 @@ Rewards: program_id: Pubkey::new_from_array([2u8; 32]), data: vec![1, 2, 3], }), + compute_units_consumed: Some(2345u64), }; let output = { @@ -914,6 +929,7 @@ Status: Ok Account 1 balance: ◎0.00001 Account 2 balance: ◎0.000015 -> ◎0.0000149 Account 3 balance: ◎0.00002 +Compute Units Consumed: 2345 Log Messages: Test message Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR: diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index bfef4350404458..8ab244a2996df9 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -230,6 +230,7 @@ impl RpcSender for MockSender { rewards: None, loaded_addresses: None, return_data: None, + compute_units_consumed: None, }), }, block_time: Some(1628633791), diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index a4161003adf6b2..2547c00f94e5ca 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -3883,6 +3883,7 @@ mod tests { post_token_balances: Some(vec![]), rewards: Some(vec![]), loaded_addresses: sanitized_tx.get_loaded_addresses(), + compute_units_consumed: Some(0), ..TransactionStatusMeta::default() } ); diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 93423fd0f5fdee..403f6105b5e3a6 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -6485,6 +6485,7 @@ pub mod tests { pre_balances.push(i as u64 * 10); post_balances.push(i as u64 * 11); } + let compute_units_consumed = Some(12345); let signature = transaction.signatures[0]; let status = TransactionStatusMeta { status: Ok(()), @@ -6498,6 +6499,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6516,6 +6518,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6534,6 +6537,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6554,6 +6558,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, }, } }) @@ -6670,6 +6675,8 @@ pub mod tests { program_id: Pubkey::new_unique(), data: vec![1, 2, 3], }; + let compute_units_consumed_1 = Some(3812649u64); + let compute_units_consumed_2 = Some(42u64); // result not found assert!(transaction_status_cf @@ -6690,6 +6697,7 @@ pub mod tests { rewards: Some(rewards_vec.clone()), loaded_addresses: test_loaded_addresses.clone(), return_data: Some(test_return_data.clone()), + compute_units_consumed: compute_units_consumed_1, } .into(); assert!(transaction_status_cf @@ -6709,6 +6717,7 @@ pub mod tests { rewards, loaded_addresses, return_data, + compute_units_consumed, } = transaction_status_cf .get_protobuf_or_bincode::((0, Signature::default(), 0)) .unwrap() @@ -6726,6 +6735,7 @@ pub mod tests { assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(return_data.unwrap(), test_return_data); + assert_eq!(compute_units_consumed, compute_units_consumed_1); // insert value let status = TransactionStatusMeta { @@ -6740,6 +6750,7 @@ pub mod tests { rewards: Some(rewards_vec.clone()), loaded_addresses: test_loaded_addresses.clone(), return_data: Some(test_return_data.clone()), + compute_units_consumed: compute_units_consumed_2, } .into(); assert!(transaction_status_cf @@ -6759,6 +6770,7 @@ pub mod tests { rewards, loaded_addresses, return_data, + compute_units_consumed, } = transaction_status_cf .get_protobuf_or_bincode::(( 0, @@ -6782,6 +6794,7 @@ pub mod tests { assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(return_data.unwrap(), test_return_data); + assert_eq!(compute_units_consumed, compute_units_consumed_2); } #[test] @@ -7011,6 +7024,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(42u64), } .into(); @@ -7207,6 +7221,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(42u64), } .into(); @@ -7394,6 +7409,7 @@ pub mod tests { rewards: rewards.clone(), loaded_addresses: LoadedAddresses::default(), return_data: return_data.clone(), + compute_units_consumed: Some(42), } .into(); blockstore @@ -7414,6 +7430,7 @@ pub mod tests { rewards, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(42), }, } }) @@ -7502,6 +7519,7 @@ pub mod tests { rewards: rewards.clone(), loaded_addresses: LoadedAddresses::default(), return_data: return_data.clone(), + compute_units_consumed: Some(42u64), } .into(); blockstore @@ -7522,6 +7540,7 @@ pub mod tests { rewards, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(42u64), }, } }) @@ -8282,6 +8301,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: None, } .into(); transaction_status_cf @@ -8889,6 +8909,7 @@ pub mod tests { program_id: Pubkey::new_unique(), data: vec![1, 2, 3], }), + compute_units_consumed: Some(23456), }; let deprecated_status: StoredTransactionStatusMeta = status.clone().try_into().unwrap(); let protobuf_status: generated::TransactionStatusMeta = status.into(); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index ea0f9edf37de81..5410c20f25c824 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -440,6 +440,7 @@ fn execute_transactions( inner_instructions, durable_nonce_fee, return_data, + executed_units, .. } = details; @@ -482,6 +483,7 @@ fn execute_transactions( rewards: None, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(executed_units), }; Ok(ConfirmedTransactionWithStatusMeta { diff --git a/rpc/src/transaction_status_service.rs b/rpc/src/transaction_status_service.rs index 53493780345a3f..b9fdfb9bf90b49 100644 --- a/rpc/src/transaction_status_service.rs +++ b/rpc/src/transaction_status_service.rs @@ -103,6 +103,7 @@ impl TransactionStatusService { inner_instructions, durable_nonce_fee, return_data, + executed_units, .. } = details; let lamports_per_signature = match durable_nonce_fee { @@ -160,6 +161,7 @@ impl TransactionStatusService { rewards, loaded_addresses, return_data, + compute_units_consumed: Some(executed_units), }; if let Some(transaction_notifier) = transaction_notifier.as_ref() { diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index 629b1eab387474..d74c50bb5eb2fe 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -897,6 +897,7 @@ mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(1234), }, }); let expected_block = ConfirmedBlock { @@ -955,6 +956,7 @@ mod tests { meta.post_token_balances = None; // Legacy bincode implementation does not support token balances meta.rewards = None; // Legacy bincode implementation does not support rewards meta.return_data = None; // Legacy bincode implementation does not support return data + meta.compute_units_consumed = None; // Legacy bincode implementation does not support CU consumed } assert_eq!(block, bincode_block.into()); } else { diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index ba8e8c0e39dc4b..4e1cb401d1e0c4 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -244,6 +244,7 @@ impl From for TransactionStatusMeta { rewards: None, loaded_addresses: LoadedAddresses::default(), return_data: None, + compute_units_consumed: None, } } } diff --git a/storage-proto/proto/confirmed_block.proto b/storage-proto/proto/confirmed_block.proto index 5d69f0e9c2e70b..ef8dc0fb215f54 100644 --- a/storage-proto/proto/confirmed_block.proto +++ b/storage-proto/proto/confirmed_block.proto @@ -59,6 +59,11 @@ message TransactionStatusMeta { repeated bytes loaded_readonly_addresses = 13; ReturnData return_data = 14; bool return_data_none = 15; + + // Sum of compute units consumed by all instructions. + // Available since Solana v1.10.35 / v1.11.6. + // Set to `None` for txs executed on earlier versions. + optional uint64 compute_units_consumed = 16; } message TransactionError { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 4998d5f9a206a2..6580095403cf4f 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -365,6 +365,7 @@ impl From for generated::TransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, } = value; let err = match status { Ok(()) => None, @@ -424,6 +425,7 @@ impl From for generated::TransactionStatusMeta { loaded_readonly_addresses, return_data, return_data_none, + compute_units_consumed, } } } @@ -455,6 +457,7 @@ impl TryFrom for TransactionStatusMeta { loaded_readonly_addresses, return_data, return_data_none, + compute_units_consumed, } = value; let status = match &err { None => Ok(()), @@ -515,6 +518,7 @@ impl TryFrom for TransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, }) } } diff --git a/storage-proto/src/lib.rs b/storage-proto/src/lib.rs index 90cb01903362d3..0832690f1e2b88 100644 --- a/storage-proto/src/lib.rs +++ b/storage-proto/src/lib.rs @@ -176,6 +176,8 @@ pub struct StoredTransactionStatusMeta { pub rewards: Option>, #[serde(deserialize_with = "default_on_eof")] pub return_data: Option, + #[serde(deserialize_with = "default_on_eof")] + pub compute_units_consumed: Option, } impl From for TransactionStatusMeta { @@ -191,6 +193,7 @@ impl From for TransactionStatusMeta { post_token_balances, rewards, return_data, + compute_units_consumed, } = value; Self { status, @@ -207,6 +210,7 @@ impl From for TransactionStatusMeta { .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed, } } } @@ -226,6 +230,7 @@ impl TryFrom for StoredTransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, } = value; if !loaded_addresses.is_empty() { @@ -250,6 +255,7 @@ impl TryFrom for StoredTransactionStatusMeta { rewards: rewards .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), return_data, + compute_units_consumed, }) } } diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index d594af806bcec9..4a677606fcad51 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -288,6 +288,7 @@ pub struct TransactionStatusMeta { pub rewards: Option, pub loaded_addresses: LoadedAddresses, pub return_data: Option, + pub compute_units_consumed: Option, } impl Default for TransactionStatusMeta { @@ -304,6 +305,7 @@ impl Default for TransactionStatusMeta { rewards: None, loaded_addresses: LoadedAddresses::default(), return_data: None, + compute_units_consumed: None, } } } @@ -325,6 +327,8 @@ pub struct UiTransactionStatusMeta { #[serde(default, skip_serializing_if = "Option::is_none")] pub loaded_addresses: Option, pub return_data: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub compute_units_consumed: Option, } /// A duplicate representation of LoadedAddresses @@ -376,6 +380,7 @@ impl UiTransactionStatusMeta { rewards: meta.rewards, loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), return_data: meta.return_data, + compute_units_consumed: meta.compute_units_consumed, } } } @@ -401,6 +406,7 @@ impl From for UiTransactionStatusMeta { rewards: meta.rewards, loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), return_data: meta.return_data, + compute_units_consumed: meta.compute_units_consumed, } } }