diff --git a/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs b/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs index 34c076c0e..ded7d5b48 100644 --- a/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs +++ b/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs @@ -398,7 +398,9 @@ pub struct FailureCollection { inner: Vec>, } -/// +/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:2197-2210 +/// Commit: bfd1009abdbee78979ff0343cc73a3480e862f58 +/// Last verified: 2025-10-16 impl FailureCollection { fn empty() -> Self { Self { @@ -440,17 +442,63 @@ impl FailureCollection { } } -/// Structure of the failure status: -/// I. No fee transfer and coinbase transfer fails: `[[failure]]` -/// II. With fee transfer- -/// Both fee transfer and coinbase fails: -/// `[[failure-of-fee-transfer]; [failure-of-coinbase]]` -/// Fee transfer succeeds and coinbase fails: -/// `[[];[failure-of-coinbase]]` -/// Fee transfer fails and coinbase succeeds: -/// `[[failure-of-fee-transfer];[]]` +/// Applies a coinbase transaction to the ledger. +/// +/// Processes the coinbase by first applying the optional fee transfer (if present), +/// then applying the coinbase reward to the receiver. Updates account balances and +/// timing, creates accounts if needed, and handles permission checks. +/// +/// # Implementation Notes +/// +/// - When `coinbase.fee_transfer` is `Some`, processes fee transfer first, then +/// coinbase receiver gets `coinbase.amount - fee_transfer.fee` +/// - When `coinbase.fee_transfer` is `None`, coinbase receiver gets full +/// `coinbase.amount` +/// - Calls `has_permission_to_receive` for each recipient +/// - Calls `sub_account_creation_fee` when creating new accounts +/// - Calls `update_timing_when_no_deduction` for timing validation +/// - Only updates coinbase receiver timing when `fee_transfer` is `None` +/// - Uses `FailureCollection` to track which operations succeeded/failed +/// +/// # Parameters +/// +/// - `constraint_constants`: Protocol constants including account creation fees +/// - `txn_global_slot`: Current global slot for timing validation +/// - `ledger`: Mutable ledger to update +/// - `coinbase`: Coinbase transaction containing receiver, amount, and optional +/// fee transfer +/// +/// # Returns +/// +/// Returns [`CoinbaseApplied`] containing: +/// - `coinbase`: The input coinbase with transaction status +/// - `new_accounts`: Vector of newly created account IDs +/// - `burned_tokens`: Amount of tokens burned from failed transfers +/// +/// # Errors +/// +/// Returns `Err` if: +/// - `fee_transfer.fee` exceeds `coinbase.amount` (checked subtraction fails) +/// - Burned tokens overflow when summing across transfers +/// +/// For protocol-level documentation and behavioral specification, see: +/// +/// +/// # Tests +/// +/// Test coverage (in `ledger/tests/test_transaction_logic_first_pass_coinbase.rs`): +/// +/// - `test_apply_coinbase_without_fee_transfer` +/// - `test_apply_coinbase_with_fee_transfer` +/// - `test_apply_coinbase_with_fee_transfer_creates_account` +/// - `test_apply_coinbase_with_fee_transfer_to_same_account` +/// - `test_apply_coinbase_creates_account` +/// +/// # OCaml Reference /// -/// +/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:2074-2178 +/// Commit: 0063f0196d046d9d2fc8af0cea76ff30f51b49b7 +/// Last verified: 2025-10-16 pub fn apply_coinbase( constraint_constants: &ConstraintConstants, txn_global_slot: &Slot, @@ -649,7 +697,9 @@ where }) } -/// +/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:607 +/// Commit: 2ee6e004ba8c6a0541056076aab22ea162f7eb3a +/// Last verified: 2025-10-16 fn sub_account_creation_fee( constraint_constants: &ConstraintConstants, action: AccountState, @@ -862,7 +912,9 @@ pub enum AccountState { #[derive(Debug)] struct HasPermissionToReceive(bool); -/// +/// OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:1852 +/// Commit: 2ee6e004ba8c6a0541056076aab22ea162f7eb3a +/// Last verified: 2025-10-16 fn has_permission_to_receive( ledger: &mut L, receiver_account_id: &AccountId, @@ -1151,7 +1203,9 @@ where }), Err(failure) => { // This case occurs when an exception is throwned in OCaml - // + // OCaml reference: src/lib/transaction_logic/mina_transaction_logic.ml L:964 + // Commit: 3753a8593cc1577bcf4da16620daf9946d88e8e5 + // Last verified: 2025-10-16 assert!(reject_command); Err(failure.to_string()) } diff --git a/ledger/tests/test_transaction_logic_first_pass.rs b/ledger/tests/test_transaction_logic_first_pass.rs index a28355148..0964ffc23 100644 --- a/ledger/tests/test_transaction_logic_first_pass.rs +++ b/ledger/tests/test_transaction_logic_first_pass.rs @@ -4,6 +4,7 @@ //! //! Tests the first pass of two-phase transaction application, covering: //! - Successful payment transactions +//! - Payment creating receiver account //! - Insufficient balance errors //! - Invalid nonce errors //! - Nonexistent fee payer errors @@ -439,3 +440,127 @@ fn test_apply_payment_nonexistent_fee_payer() { "Alice's account should still not exist after transaction error" ); } + +/// Test payment that creates a new receiver account. +/// +/// When the receiver account doesn't exist, a new account is created +/// automatically. The account creation fee is deducted from the payment amount, +/// not from the sender's balance. +/// +/// Ledger state: Sender's balance decreased by amount + fee, receiver account +/// created with balance = amount - account_creation_fee. +#[test] +fn test_apply_payment_creates_receiver_account() { + let db = Database::create(15); + let mut ledger = Mask::new_root(db); + + let alice_pk = mina_signer::PubKey::from_address( + "B62qmnY6m4c6bdgSPnQGZriSaj9vuSjsfh6qkveGTsFX3yGA5ywRaja", + ) + .unwrap() + .into_compressed(); + let bob_pk = mina_signer::PubKey::from_address( + "B62qjVQLxt9nYMWGn45mkgwYfcz8e8jvjNCBo11VKJb7vxDNwv5QLPS", + ) + .unwrap() + .into_compressed(); + + // Create only Alice's account + let alice_id = AccountId::new(alice_pk.clone(), Default::default()); + let alice_account = Account::create_with(alice_id.clone(), Balance::from_u64(5_000_000_000)); + ledger + .get_or_create_account(alice_id.clone(), alice_account) + .unwrap(); + + let bob_id = AccountId::new(bob_pk.clone(), Default::default()); + + // Verify Bob's account does not exist before the transaction + assert!( + ledger.location_of_account(&bob_id).is_none(), + "Bob's account should not exist before transaction" + ); + + // Record initial state + let alice_location = ledger.location_of_account(&alice_id).unwrap(); + let alice_before = ledger.get(alice_location).unwrap(); + let initial_alice_balance = alice_before.balance; + let initial_alice_nonce = alice_before.nonce; + let initial_alice_receipt_hash = alice_before.receipt_chain_hash; + + let amount = 2_000_000_000; // 2 MINA + let fee = 10_000_000; // 0.01 MINA + let nonce = 0; + let payment = create_payment(&alice_pk, &bob_pk, amount, fee, nonce); + + let constraint_constants = &test_constraint_constants(); + let account_creation_fee = constraint_constants.account_creation_fee; // 1 MINA + + let state_view = ProtocolStateView { + snarked_ledger_hash: Fp::zero(), + blockchain_length: Length::from_u32(0), + min_window_density: Length::from_u32(0), + total_currency: Amount::zero(), + global_slot_since_genesis: Slot::from_u32(0), + staking_epoch_data: dummy_epoch_data(), + next_epoch_data: dummy_epoch_data(), + }; + let result = apply_transaction_first_pass( + constraint_constants, + Slot::from_u32(0), + &state_view, + &mut ledger, + &Transaction::Command(UserCommand::SignedCommand(Box::new(payment))), + ); + + assert!(result.is_ok()); + + // Verify Alice's balance decreased by fee + payment amount + let alice_location = ledger.location_of_account(&alice_id).unwrap(); + let alice_after = ledger.get(alice_location).unwrap(); + let expected_alice_balance = initial_alice_balance + .sub_amount(Amount::from_u64(fee)) + .unwrap() + .sub_amount(Amount::from_u64(amount)) + .unwrap(); + assert_eq!( + alice_after.balance, expected_alice_balance, + "Alice's balance should decrease by fee + payment amount" + ); + + // Verify Alice's nonce incremented + assert_eq!( + alice_after.nonce, + initial_alice_nonce.incr(), + "Alice's nonce should be incremented" + ); + + // Verify Alice's receipt chain hash updated + assert_ne!( + alice_after.receipt_chain_hash, initial_alice_receipt_hash, + "Alice's receipt chain hash should be updated" + ); + + // Verify Bob's account was created + let bob_location = ledger.location_of_account(&bob_id); + assert!( + bob_location.is_some(), + "Bob's account should now exist after transaction" + ); + + // Verify Bob's balance is payment amount minus account creation fee + let bob_location = bob_location.unwrap(); + let bob_after = ledger.get(bob_location).unwrap(); + let expected_bob_balance = + Balance::from_u64(amount - account_creation_fee); + assert_eq!( + bob_after.balance, expected_bob_balance, + "Bob's balance should be payment amount minus account creation fee" + ); + + // Verify Bob's nonce is 0 (new account) + assert_eq!( + bob_after.nonce, + Nonce::zero(), + "Bob's nonce should be 0 for new account" + ); +} diff --git a/ledger/tests/test_transaction_logic_first_pass_coinbase.rs b/ledger/tests/test_transaction_logic_first_pass_coinbase.rs index c2754517b..11d881f51 100644 --- a/ledger/tests/test_transaction_logic_first_pass_coinbase.rs +++ b/ledger/tests/test_transaction_logic_first_pass_coinbase.rs @@ -1,11 +1,12 @@ //! Tests for apply_transaction_first_pass with coinbase transactions //! -//! Run with: cargo test --test test_transaction_logic_first_pass_coinbase +//! Run with: cargo test --test test_transaction_logic_first_pass_coinbase --release //! //! Tests the first pass of two-phase transaction application for coinbase //! rewards, covering: //! - Successful coinbase without fee transfer //! - Successful coinbase with fee transfer to different account +//! - Coinbase with fee transfer to nonexistent account (creates account) //! - Coinbase with fee transfer to same account (fee transfer should be //! removed) //! - Coinbase creating a new account @@ -228,6 +229,120 @@ fn test_apply_coinbase_with_fee_transfer() { ); } +/// Test coinbase with fee transfer to a nonexistent account. +/// +/// The coinbase receiver exists, but the fee transfer receiver doesn't exist. +/// The fee transfer should create the receiver account, deducting the account +/// creation fee from the fee transfer amount. +/// +/// Ledger state: +/// - Coinbase receiver gets coinbase_amount - fee_transfer_amount +/// - Fee transfer receiver account created with fee_transfer_amount - +/// account_creation_fee +#[test] +fn test_apply_coinbase_with_fee_transfer_creates_account() { + let mut ledger = create_test_ledger(); + + let alice_pk = mina_signer::PubKey::from_address( + "B62qmnY6m4c6bdgSPnQGZriSaj9vuSjsfh6qkveGTsFX3yGA5ywRaja", + ) + .unwrap() + .into_compressed(); + let bob_pk = mina_signer::PubKey::from_address( + "B62qjVQLxt9nYMWGn45mkgwYfcz8e8jvjNCBo11VKJb7vxDNwv5QLPS", + ) + .unwrap() + .into_compressed(); + + let alice_id = AccountId::new(alice_pk.clone(), Default::default()); + let bob_id = AccountId::new(bob_pk.clone(), Default::default()); + + // Verify Bob's account does not exist before the transaction + assert!( + ledger.location_of_account(&bob_id).is_none(), + "Bob's account should not exist before transaction" + ); + + // Record Alice's initial state + let alice_location = ledger.location_of_account(&alice_id).unwrap(); + let alice_before = ledger.get(alice_location).unwrap(); + let initial_alice_balance = alice_before.balance; + + // Create a coinbase of 720 MINA to Alice with a 10 MINA fee transfer to Bob + // (who doesn't exist yet) + let coinbase_amount = Amount::from_u64(720_000_000_000); + let fee_transfer_amount = Fee::from_u64(10_000_000_000); + let fee_transfer = CoinbaseFeeTransfer::create(bob_pk.clone(), fee_transfer_amount); + let coinbase = Coinbase::create(coinbase_amount, alice_pk.clone(), Some(fee_transfer)).unwrap(); + + let constraint_constants = &test_constraint_constants(); + let state_view = ProtocolStateView { + snarked_ledger_hash: Fp::zero(), + blockchain_length: Length::from_u32(0), + min_window_density: Length::from_u32(0), + total_currency: Amount::zero(), + global_slot_since_genesis: Slot::from_u32(0), + staking_epoch_data: dummy_epoch_data(), + next_epoch_data: dummy_epoch_data(), + }; + let result = apply_transaction_first_pass( + constraint_constants, + Slot::from_u32(0), + &state_view, + &mut ledger, + &Transaction::Coinbase(coinbase), + ); + + assert!(result.is_ok()); + + // Verify Bob's account was created + let bob_location = ledger.location_of_account(&bob_id); + assert!( + bob_location.is_some(), + "Bob's account should exist after transaction" + ); + + // Verify ledger state changes + let alice_location = ledger.location_of_account(&alice_id).unwrap(); + let alice_after = ledger.get(alice_location).unwrap(); + let bob_account = ledger.get(bob_location.unwrap()).unwrap(); + + // Verify Alice's balance increased by (coinbase amount - fee transfer amount) + let coinbase_after_fee_transfer = coinbase_amount + .checked_sub(&Amount::of_fee(&fee_transfer_amount)) + .unwrap(); + let expected_alice_balance = initial_alice_balance + .add_amount(coinbase_after_fee_transfer) + .unwrap(); + assert_eq!( + alice_after.balance, expected_alice_balance, + "Alice's balance should increase by coinbase minus fee transfer" + ); + + // Verify Bob's balance equals fee transfer amount minus account creation fee + let account_creation_fee = constraint_constants.account_creation_fee; + let expected_bob_balance = Balance::from_u64( + Amount::of_fee(&fee_transfer_amount) + .as_u64() + .saturating_sub(account_creation_fee), + ); + assert_eq!( + bob_account.balance, expected_bob_balance, + "Bob's balance should equal fee transfer minus account creation fee" + ); + + // Verify nonces + assert_eq!( + alice_after.nonce, alice_before.nonce, + "Alice's nonce should remain unchanged" + ); + assert_eq!( + bob_account.nonce, + Nonce::zero(), + "Bob's nonce should be 0 for new account" + ); +} + /// Test coinbase with fee transfer to the same account. /// /// When the coinbase receiver and fee transfer receiver are the same, the fee diff --git a/website/docs/developers/transactions.md b/website/docs/developers/transactions.md new file mode 100644 index 000000000..a55b1bc78 --- /dev/null +++ b/website/docs/developers/transactions.md @@ -0,0 +1,184 @@ +--- +sidebar_position: 5 +title: Transactions +description: Understanding transaction types in the Mina protocol implementation +slug: /developers/transactions +--- + +# Transactions + +## Overview + +Transactions in Mina represent state changes to the ledger. The protocol +supports both user-initiated transactions and protocol-generated transactions, +each serving distinct purposes in the blockchain's operation. + +This document provides an entry point for developers to understand how +transactions are structured and processed in the Rust codebase. + +## Transaction Types + +The top-level `Transaction` enum represents all possible transactions in the +Mina protocol: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L1093-L1100 +``` + +### User-Initiated Transactions + +**[User Commands](./transactions/user-commands)** are transactions submitted and +signed by users: + +- **[Payment transactions](./transactions/payments)** - Transfer MINA tokens + between accounts +- **[Stake delegation](./transactions/delegations)** - Delegate stake to block + producers +- **[zkApp commands](./transactions/zkapps)** - Execute complex multi-account + zero-knowledge operations + +### Protocol Transactions + +**Protocol transactions** (also called **system transactions**) are generated +automatically by the system: + +- **[Fee transfers](./transactions/fee-transfers)** - Distribute collected + transaction fees to block producers +- **[Coinbase rewards](./transactions/coinbase)** - Issue block production + rewards + +## Transaction Application Model + +Transactions in Mina are applied in two phases: + +### First Pass (apply_transaction_first_pass) + +The first pass validates preconditions and begins transaction application: + +- Validates account existence and permissions +- Checks nonces and balances +- Applies fee payments +- Performs initial state changes +- Returns `TransactionPartiallyApplied` containing transaction state + +**Function:** +[`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L190) + +### Second Pass (apply_transaction_second_pass) + +The second pass completes the transaction after SNARK work: + +- Finalizes account updates +- Applies protocol-specific logic +- Updates receipt chain hashes +- Returns final `TransactionApplied` status + +**Function:** +[`ledger/src/scan_state/transaction_logic/transaction_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_applied.rs) + +## Common Transaction Components + +### Fees + +All user-initiated transactions require fees. The `Fee` type is generated by a +macro along with other currency types in +[`ledger/src/scan_state/currency.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/currency.rs#L575-L578). + +Fees serve two purposes: they compensate block producers for including +transactions and prevent network spam by making it economically infeasible to +flood the network with transactions. Fees are paid by the transaction sender (or +fee payer in zkApp commands) and distributed to block producers through fee +transfers. + +### Nonces + +User accounts maintain nonces to prevent replay attacks. The `Nonce` type is +generated by a macro along with other currency types in +[`ledger/src/scan_state/currency.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/currency.rs#L575-L578). + +Each user-initiated transaction must specify the correct nonce, which increments +after successful application. + +### Memos + +Transactions can include optional 32-byte memos: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L548-L548 +``` + +Memos provide auxiliary information and are included in transaction commitments. + +### Slots + +A slot is a fixed time interval in the consensus protocol during which a block +can be produced. Slots are used to measure time in the blockchain and determine +block production opportunities. + +Transactions include validity windows using slot numbers. The `Slot` type is +generated by a macro along with other currency types in +[`ledger/src/scan_state/currency.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/currency.rs#L575-L578). + +The `valid_until` field specifies when a transaction expires. + +## Account Creation + +Creating new accounts requires an account creation fee (1 MINA by default): + +```rust +ConstraintConstants { + account_creation_fee: 1_000_000_000, // 1 MINA in nanomina + // ... +} +``` + +The fee is deducted from the amount being transferred to the new account: + +- **Payments**: Sender pays `amount + fee`, receiver gets + `amount - account_creation_fee` (if creating account) +- **Fee transfers**: Receiver gets `fee_amount - account_creation_fee` (if + creating account) +- **Coinbase**: Receiver gets `coinbase_amount - account_creation_fee` (if + creating account) + +## Transaction Status + +After application, transactions have a status: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L240-L243 +``` + +Failed transactions may still consume fees in some cases (zkApp commands with +fee payer failures). + +## Further Reading + +- [Payment Transactions](./transactions/payments) - Transferring tokens between + accounts +- [Stake Delegation](./transactions/delegations) - Delegating stake to block + producers +- [Fee Transfers](./transactions/fee-transfers) - Protocol fee distribution +- [Coinbase Rewards](./transactions/coinbase) - Block production rewards +- [zkApp Commands](./transactions/zkapps) - Zero-knowledge applications + +## Related Files + +- [`ledger/src/scan_state/transaction_logic/mod.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs) - + Transaction type definitions +- [`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs) - + First pass application +- [`ledger/src/scan_state/transaction_logic/transaction_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_applied.rs) - + Second pass application +- [`ledger/src/scan_state/transaction_logic/signed_command.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs) - + Signed command types +- [`ledger/src/scan_state/transaction_logic/zkapp_command/`](https://github.com/o1-labs/mina-rust/tree/develop/ledger/src/scan_state/transaction_logic/zkapp_command) - + zkApp command implementation +- [`ledger/src/scan_state/currency.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/currency.rs) - + Currency types (Fee, Nonce, Slot, Amount, Balance) diff --git a/website/docs/developers/transactions/coinbase.md b/website/docs/developers/transactions/coinbase.md new file mode 100644 index 000000000..0b6e2499a --- /dev/null +++ b/website/docs/developers/transactions/coinbase.md @@ -0,0 +1,182 @@ +--- +sidebar_position: 4 +title: Coinbase rewards +description: Understanding coinbase reward transactions in the Mina protocol +slug: /developers/transactions/coinbase +--- + +# Coinbase rewards + +## Overview + +Coinbase transactions are protocol-generated rewards issued to block producers +for successfully producing a block. They represent new MINA tokens entering +circulation as compensation for maintaining the network. + +A coinbase consists of: + +- **Receiver**: The block producer receiving the reward +- **Amount**: The coinbase reward amount (720 MINA by default) +- **Fee transfer**: Optional fee transfer to SNARK worker + +## Transaction structure + +Coinbase transactions have a simple structure with an optional fee transfer: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L438-L442 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L416-L419 +``` + +### Creating coinbase + +Coinbase transactions are constructed with validation logic: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L452-L476 +``` + +## Transaction application + +### First pass + +During the first pass +([`apply_coinbase`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L454)), +the coinbase: + +1. **Applies coinbase reward** + - Adds coinbase amount to receiver's balance + - Creates receiver account if it doesn't exist + - Deducts account creation fee if creating account + +2. **Applies fee transfer (if present)** + - Adds fee to fee transfer receiver's balance + - Deducts fee from coinbase receiver's reward + - Creates fee transfer receiver account if needed + +### No fee payment + +Like fee transfers, coinbase transactions do not pay fees: + +- No nonce increment +- No fee payer +- No signature required +- Cannot be submitted by users + +## Coinbase amount + +The coinbase amount is configured in +[`ConstraintConstants`](https://github.com/o1-labs/mina-rust/blob/develop/core/src/constants.rs#L39-L42) +(`core/src/constants.rs`), which includes the base `coinbase_amount` and the +`supercharged_coinbase_factor` for rewards when SNARK work is included. The +specific values are defined in the network configurations: +[devnet](https://github.com/o1-labs/mina-rust/blob/develop/core/src/network.rs#L145-L146) +sets `coinbase_amount` to 720,000,000,000 nanomina (720 MINA) and +`supercharged_coinbase_factor` to 1, while +[mainnet](https://github.com/o1-labs/mina-rust/blob/develop/core/src/network.rs#L223-L224) +uses the same values. + +### Supercharged coinbase + +Supercharged rewards were designed to provide double block rewards (factor of 2) +to block producers staking with unlocked tokens during the early mainnet period +following the 2021 launch. This mechanism incentivized participation and orderly +markets after mainnet launch. + +**Historical values**: + +- Original mainnet: 2 (double rewards for unlocked tokens) +- Berkeley hardfork (June 2024): 1 (supercharged rewards removed via + [MIP1](https://github.com/MinaProtocol/MIPs/blob/main/MIPS/mip-0001-remove-supercharged-rewards.md)) + +The removal was decided by community vote on January 1, 2023, as proposed by +community member Gareth Davies. This change ensures uniform rewards for all +tokens and reduces inflation, promoting a sustainable economic model. + +**References**: + +- [Berkeley Upgrade](https://minaprotocol.com/blog/minas-berkeley-upgrade-what-to-expect) +- [Supercharged Rewards Removal](https://minaprotocol.com/blog/update-on-minas-supercharged-rewards-schedule) +- [Original Proposal](https://github.com/MinaProtocol/mina/issues/5753) + +## Fee transfer interaction + +### Splitting the reward + +When a coinbase includes a fee transfer, the reward is split +([implementation](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L496-L498)): + +- **Coinbase receiver**: Gets `coinbase_amount - fee_transfer_amount` +- **Fee transfer receiver**: Gets `fee_transfer_amount` + +This mechanism allows block producers to share rewards with SNARK workers. + +### Same receiver + +If the fee transfer receiver equals the coinbase receiver, the fee transfer is +removed +([`Coinbase::create`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L452)): + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L464-L472 +``` + +## Account creation + +If receiver accounts don't exist, they are created automatically: + +### Coinbase receiver creation + +When creating a new coinbase receiver account, the account creation fee is +deducted from the reward: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L559-L561 +``` + +The +[`sub_account_creation_fee`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L653) +function handles the fee deduction logic. + +### Fee transfer receiver creation + +The fee transfer receiver follows standard account creation rules, and the fee +transfer must be sufficient to cover the account creation fee. + +## Testing + +Comprehensive tests are available in +[`ledger/tests/test_transaction_logic_first_pass_coinbase.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs): + +- [`test_apply_coinbase_without_fee_transfer`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs#L76) - + Basic coinbase reward +- [`test_apply_coinbase_with_fee_transfer`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs#L135) - + Coinbase with SNARK work payment to existing account +- [`test_apply_coinbase_with_fee_transfer_creates_account`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs#L242) - + Fee transfer creating new account +- [`test_apply_coinbase_with_fee_transfer_to_same_account`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs#L353) - + Same receiver optimization +- [`test_apply_coinbase_creates_account`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs#L428) - + Coinbase creating new account + +## Related files + +- [`ledger/src/scan_state/transaction_logic/mod.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs) - + Coinbase type definitions +- [`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs) - + Coinbase application logic +- [`ledger/tests/test_transaction_logic_first_pass_coinbase.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_coinbase.rs) - + Coinbase tests diff --git a/website/docs/developers/transactions/delegations.md b/website/docs/developers/transactions/delegations.md new file mode 100644 index 000000000..79162c8c3 --- /dev/null +++ b/website/docs/developers/transactions/delegations.md @@ -0,0 +1,288 @@ +--- +sidebar_position: 2 +title: Stake delegation +description: Understanding stake delegation transactions in the Mina protocol +slug: /developers/transactions/delegations +--- + +# Stake delegation + +## Overview + +Stake delegation transactions allow token holders to delegate their stake to +block producers without transferring token ownership. This enables users to +participate in consensus and earn staking rewards while maintaining control of +their funds. + +A delegation consists of: + +- **Delegator**: The account delegating stake (pays the fee) +- **Delegate**: The block producer receiving the delegation +- **Fee**: The transaction fee paid to block producers +- **Nonce**: The delegator's transaction counter + +## Transaction structure + +Delegation transactions are a type of `SignedCommand` with a `StakeDelegation` +body: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L152-L158 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L82-L87 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L49-L55 +``` + +### Common fields + +Like payments, delegations use the `Common` struct for fee, nonce, and other +shared fields (see Payment transactions for details). + +## Transaction application + +### First pass + +During the first pass (`apply_transaction_first_pass`), the delegation +transaction: + +1. **Validates the fee payer account** + - Checks account exists + - Verifies sufficient balance for fee + - Validates nonce matches + +2. **Applies the fee payment** + - Deducts fee from delegator's balance + - Increments delegator's nonce + - Updates receipt chain hash + +3. **Sets the delegate** + - Updates the delegator's `delegate` field + - No funds are transferred + - Delegate account doesn't need to exist + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` + +### Second pass + +The second pass finalizes the transaction after SNARK proof verification. + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_applied.rs` + +## Key characteristics + +### No fund transfer + +Unlike payments, delegations do not transfer any tokens. Only the delegation +pointer is updated: + +- Delegator retains full ownership of their tokens +- Delegator can spend tokens freely +- Delegator can change delegation at any time (after nonce increment) + +### Delegate account + +The delegate account: + +- Does not need to exist at delegation time +- Does not receive any notification of the delegation +- Cannot reject the delegation +- Receives staking weight from all delegators + +### Balance requirements + +The delegator must only have sufficient balance for the fee: + +``` +delegator_balance >= fee +``` + +No account creation fee is charged since no new accounts are created. + +## Staking weight + +After delegation, the delegate's staking weight for consensus includes: + +- The delegate's own balance +- All balances delegated to them + +This affects their probability of winning block production slots. + +## Examples + +### Successful delegation + +From the test suite +(`tests/test_transaction_logic_first_pass_delegation.rs:114`): + +```rust +// Alice delegates to Bob with 0.01 MINA fee +let delegation = create_delegation(&alice_pk, &bob_pk, 10_000_000, 0); + +let result = apply_transaction_first_pass( + constraint_constants, + Slot::from_u32(0), + &state_view, + &mut ledger, + &Transaction::Command(UserCommand::SignedCommand(Box::new(delegation))), +); + +assert!(result.is_ok()); + +// Verify state changes: +// - Alice's balance decreased by fee only (no amount transferred) +// - Alice's nonce incremented +// - Alice's delegate set to Bob +// - Bob's balance unchanged +``` + +### Insufficient balance for fee + +From the test suite +(`tests/test_transaction_logic_first_pass_delegation.rs:228`): + +```rust +// Alice has 0.001 MINA but fee is 0.01 MINA +let delegation = create_delegation(&alice_pk, &bob_pk, 10_000_000, 0); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!(result.unwrap_err(), "insufficient funds"); + +// Ledger state unchanged: +// - No fee charged +// - Nonce not incremented +// - Delegate not changed +``` + +### Invalid nonce + +From the test suite +(`tests/test_transaction_logic_first_pass_delegation.rs:312`): + +```rust +// Alice's nonce is 0, but transaction specifies nonce 5 +let delegation = create_delegation(&alice_pk, &bob_pk, 10_000_000, 5); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!( + result.unwrap_err(), + "Nonce in account Nonce(0) different from nonce in transaction Nonce(5)" +); + +// Ledger state unchanged +``` + +### Nonexistent fee payer + +From the test suite +(`tests/test_transaction_logic_first_pass_delegation.rs:387`): + +```rust +// Alice's account doesn't exist +let delegation = create_delegation(&alice_pk, &bob_pk, 10_000_000, 0); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!(result.unwrap_err(), "The fee-payer account does not exist"); + +// No account created +``` + +## Delegation lifecycle + +### Initial state + +By default, accounts delegate to themselves: + +```rust +pub struct Account { + pub delegate: Option, + // When None, delegates to self (public_key) +} +``` + +### Changing delegation + +Users can change their delegation by submitting a new delegation transaction: + +1. Verify balance covers new transaction fee +2. Wait for previous transaction to be confirmed (nonce must increment) +3. Submit new delegation with incremented nonce +4. New delegation takes effect immediately upon inclusion + +### Removing delegation + +To remove delegation (return to self-delegation), delegate to your own public +key: + +```rust +let delegation = create_delegation(&alice_pk, &alice_pk, fee, nonce); +``` + +## Consensus implications + +### Epoch boundaries + +Delegations affect staking weight at epoch boundaries: + +- **Staking epoch**: Determines who can produce blocks +- **Next epoch**: Determines delegation snapshots for future epochs + +The exact timing depends on the current slot and epoch progression. + +### Snarked ledger + +Delegations use the snarked ledger state for computing staking distributions. +Recent delegations may not affect current epoch block production. + +## Fee calculation + +Users should ensure sufficient balance: + +```rust +let required_balance = fee; + +if delegator_balance < required_balance { + return Err("insufficient funds"); +} +``` + +## Testing + +Comprehensive tests are available in +`tests/test_transaction_logic_first_pass_delegation.rs`: + +- `test_apply_delegation_success` - Successful delegation to block producer +- `test_apply_delegation_insufficient_balance` - Insufficient funds for fee +- `test_apply_delegation_invalid_nonce` - Nonce mismatch error +- `test_apply_delegation_nonexistent_fee_payer` - Nonexistent delegator error + +## Related files + +- `ledger/src/scan_state/transaction_logic/signed_command.rs` - Delegation type + definitions +- `ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` - + Delegation application logic +- `tests/test_transaction_logic_first_pass_delegation.rs` - Delegation tests + +## See also + +- [Transactions overview](../transactions) +- [Payment transactions](./payments) diff --git a/website/docs/developers/transactions/fee-transfers.md b/website/docs/developers/transactions/fee-transfers.md new file mode 100644 index 000000000..6cd19a479 --- /dev/null +++ b/website/docs/developers/transactions/fee-transfers.md @@ -0,0 +1,164 @@ +--- +sidebar_position: 3 +title: Fee transfers +description: Understanding fee transfer transactions in the Mina protocol +slug: /developers/transactions/fee-transfers +--- + +# Fee transfers + +## Overview + +Fee transfers are protocol-generated transactions that distribute collected +transaction fees to block producers. Unlike user commands, fee transfers are +created automatically during block production and do not require user +signatures. + +A fee transfer consists of: + +- **Receiver(s)**: One or two block producers receiving fees +- **Fee amount(s)**: The fee amount(s) to distribute +- **Token**: Must be the default token (MINA) + +## Transaction structure + +Fee transfers use a specialized structure that supports one or two receivers: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L357-L357 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L330-L334 +``` + +### Creating fee transfers + +Fee transfers are constructed using the `of_singles` method which validates +token compatibility: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L396-L412 +``` + +## Transaction application + +### First pass + +During the first pass (`apply_transaction_first_pass`), the fee transfer: + +1. **Validates token compatibility** + - All fees must be in the default token (MINA) + - Rejects non-default tokens + +2. **Processes single or double transfers** + - For single transfers: adds fee to receiver's balance + - For double transfers: adds respective fees to both receivers + - Creates receiver accounts if they don't exist + +3. **Deducts account creation fees** + - If receiver account doesn't exist, charges account creation fee + - Receiver gets: `fee_amount - account_creation_fee` + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` + +### No fee payment + +Fee transfers do not pay fees themselves - they are protocol transactions: + +- No nonce increment +- No fee payer +- No signature required +- Cannot be submitted by users + +## Account creation + +If a receiver account doesn't exist, it is created automatically. The account +creation fee (1 MINA by default) is deducted from the fee being transferred. + +## Single vs. double transfers + +### Single transfer + +Distributes fees to one block producer. See +[test_apply_single_fee_transfer_success](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L94-L95) +for an example: + + + +```rust reference title="ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L94-L95 +``` + +### Double transfer + +Distributes fees to two block producers (e.g., coinbase producer and snark work +producer). See +[test_apply_double_fee_transfer_success](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L169-L174) +for an example: + + + +```rust reference title="ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L169-L174 +``` + +## Token constraints + +Fee transfers must use the default token. For double transfers, both tokens must +match. The validation is implemented in +[FeeTransfer::of_singles](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L400): + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L400-L408 +``` + +See +[test_apply_fee_transfer_incompatible_tokens](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L333-L337) +for an example creating fee transfers with incompatible tokens: + + + +```rust reference title="ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L333-L337 +``` + +The validation fails with an error: + + + +```rust reference title="ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L344-L346 +``` + +## Testing + +Comprehensive tests are available in +[`ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs): + +- [`test_apply_single_fee_transfer_success`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L75) - + Single fee transfer to existing account +- [`test_apply_double_fee_transfer_success`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L137) - + Double fee transfer to two receivers +- [`test_apply_fee_transfer_creates_account`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L237) - + Fee transfer creating new account +- [`test_apply_fee_transfer_incompatible_tokens`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs#L316) - + Token compatibility validation + +## Related files + +- [`ledger/src/scan_state/transaction_logic/mod.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs) - + Fee transfer type definitions +- [`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs) - + Fee transfer application logic +- [`ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs`](https://github.com/o1-labs/mina-rust/blob/develop/ledger/tests/test_transaction_logic_first_pass_fee_transfer.rs) - + Fee transfer tests diff --git a/website/docs/developers/transactions/payments.md b/website/docs/developers/transactions/payments.md new file mode 100644 index 000000000..9440928ad --- /dev/null +++ b/website/docs/developers/transactions/payments.md @@ -0,0 +1,149 @@ +--- +sidebar_position: 1 +title: Payment transactions +description: Understanding payment transactions in the Mina protocol +slug: /developers/transactions/payments +--- + +# Payment transactions + +## Overview + +Payment transactions transfer MINA tokens from one account to another. They are +the most common type of user-initiated transaction in the Mina blockchain. + +A payment consists of: + +- **Sender**: The account sending tokens (pays the fee) +- **Receiver**: The account receiving tokens +- **Amount**: The quantity of tokens to transfer +- **Fee**: The transaction fee paid to block producers +- **Nonce**: The sender's transaction counter (prevents replay attacks) + +## Transaction structure + +Payment transactions are a type of `SignedCommand` with a `Payment` body: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L152-L158 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L95-L100 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L82-L87 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L36-L41 +``` + +### Common fields + +The `Common` struct contains fields shared by all signed commands: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/signed_command.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/signed_command.rs#L21-L32 +``` + +## Transaction application + +### First pass + +During the first pass (`apply_transaction_first_pass`), the payment transaction: + +1. **Validates the fee payer account** + - Checks account exists (transaction rejected if it doesn't) + - Verifies sufficient balance for fee + - Validates nonce matches + +2. **Applies the fee payment** + - Deducts fee from sender's balance + - Increments sender's nonce + - Updates receipt chain hash + +3. **Applies the payment** + - Deducts amount from sender's balance + - Adds amount to receiver's balance + - Creates receiver account if it doesn't exist (charges account creation fee) + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` + +### Second pass + +The second pass finalizes the transaction after SNARK proof verification. + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_applied.rs` + +## Account creation + +### Fee payer account must exist + +The fee payer (sender) account **must already exist** in the ledger. If it +doesn't exist, the transaction is rejected with error "The fee-payer account +does not exist". + + + +```rust reference title="ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs#L1341-L1343 +``` + +### Receiver account creation + +If the receiver account doesn't exist, a new account is created automatically. +The account creation fee (1 MINA by default) is deducted from the amount being +transferred to the receiver: + +```rust +// Constraint constants +account_creation_fee: 1_000_000_000 // 1 MINA in nanomina +``` + +**Example:** + +- Sender sends 10 MINA to a new account with 1 MINA fee +- Sender pays: `10 MINA + 1 MINA (fee) = 11 MINA` +- Receiver gets: `10 MINA - 1 MINA (account creation) = 9 MINA` + +## Token support + +While the payment structure supports custom tokens via `TokenId`, currently only +the default token (MINA) can be used for fee payments. Custom token payments +must use zkApp commands. + +## Testing + +Comprehensive tests are available in +`tests/test_transaction_logic_first_pass.rs`: + +- `test_apply_payment_success` - Successful payment between existing accounts +- `test_apply_payment_creates_receiver_account` - Payment creating new receiver + account +- `test_apply_payment_insufficient_balance` - Insufficient funds for payment + (fee charged) +- `test_apply_payment_invalid_nonce` - Nonce mismatch error +- `test_apply_payment_nonexistent_fee_payer` - Nonexistent sender error + (transaction rejected) + +## Related files + +- `ledger/src/scan_state/transaction_logic/signed_command.rs` - Payment type + definitions +- `ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` - + Payment application logic +- `tests/test_transaction_logic_first_pass.rs` - Payment transaction tests diff --git a/website/docs/developers/transactions/user-commands.md b/website/docs/developers/transactions/user-commands.md new file mode 100644 index 000000000..3dc40b1ad --- /dev/null +++ b/website/docs/developers/transactions/user-commands.md @@ -0,0 +1,264 @@ +--- +sidebar_position: 0 +title: User commands +description: Understanding user-initiated transactions in the Mina protocol +slug: /developers/transactions/user-commands +--- + +# User commands + +## Overview + +User commands are transactions initiated and signed by users to interact with +the Mina blockchain. Unlike protocol transactions (fee transfers and coinbase), +user commands require user signatures and pay transaction fees. + +The Mina protocol supports two categories of user commands: + +- **Signed commands**: Simple transactions (payments and stake delegations) +- **zkApp commands**: Complex multi-account zero-knowledge operations + +## Transaction structure + +User commands are represented by the `UserCommand` enum: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/mod.rs#L701-L704 +``` + +## Signed commands + +Signed commands are straightforward transactions signed by a single user. The +structure includes a payload with common fields and a body containing either a +payment or stake delegation. + +**Structure details:** See +[Payment transactions](./payments#transaction-structure) for the full type +definitions of `SignedCommand`, `SignedCommandPayload`, `Body`, and `Common`. + +### Payment transactions + +Transfer MINA tokens between accounts. + +**Details:** [Payment transactions](./payments) + +### Stake delegation + +Delegate stake to block producers. + +**Details:** [Stake delegation](./delegations) + +## zkApp commands + +zkApp commands enable complex multi-account operations with zero-knowledge proof +authorization: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2708-L2712 +``` + +zkApp commands can: + +- Update multiple accounts atomically +- Use zero-knowledge proofs for authorization +- Specify complex preconditions +- Emit events and actions +- Create and manage custom tokens + +**Details:** [zkApp commands](./zkapps) + +## Transaction fees + +All user commands require transaction fees: + +### Fee payment + +Fees are paid by: + +- **Signed commands**: The fee payer (usually the sender) +- **zkApp commands**: The fee payer specified in the command + +### Fee distribution + +Transaction fees are: + +1. Deducted from the fee payer's balance +2. Collected by the block producer +3. Distributed via fee transfer transactions + +### Fee requirements + +Minimum fee requirements: + +- Sufficient to incentivize block producers +- Cover account creation fees if creating new accounts +- Scale with transaction complexity (zkApp commands) + +## Nonce management + +User accounts maintain nonces to prevent replay attacks. + +### Nonce rules + +1. **Starts at zero**: New accounts have nonce 0 +2. **Must match**: Transaction nonce must equal current account nonce +3. **Increments**: Successful transactions increment the nonce +4. **Sequential**: Nonces must be used in order + +### Example + +```rust +// Account nonce: 0 +let tx1 = create_payment(&alice_pk, &bob_pk, amount, fee, 0); // Valid +let tx2 = create_payment(&alice_pk, &bob_pk, amount, fee, 1); // Valid after tx1 +let tx3 = create_payment(&alice_pk, &bob_pk, amount, fee, 3); // Invalid - skips nonce 2 +``` + +## Signatures + +### Signing signed commands + +Signed commands use a single signature. The signature covers: + +- Transaction payload (common fields + body) +- Network identifier (mainnet/testnet) + +### Signing zkApp commands + +zkApp commands support multiple authorization types: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1769-L1777 +``` + +Each account update can have different authorization. + +## Valid until + +Transactions specify an expiration slot in the `Common` struct. + +### Expiration behavior + +- **Before expiration**: Transaction can be included in blocks +- **After expiration**: Transaction becomes invalid +- **Set to max**: Transaction never expires (not recommended) + +This prevents old transactions from being included indefinitely. + +## Memos + +Transactions can include optional 32-byte memos for auxiliary data. + +### Use cases + +- Payment references +- Application identifiers +- Arbitrary data +- Empty (all zeros) + +Memos are included in transaction commitments and affect signatures. + +## Transaction application + +### Two-phase processing + +User commands are applied in two phases: + +1. **First pass**: Validates preconditions, applies fee payment, initiates state + changes +2. **Second pass**: Verifies proofs (zkApps), finalizes state changes + +### Status + +After application, transactions have a status (see +[Transaction Status](../transactions#transaction-status)). + +**Signed commands**: Either fully succeed or fully fail + +**zkApp commands**: Can partially succeed (some account updates fail) + +## Account creation + +User commands can create new accounts: + +### Automatic creation + +- **Payments**: Create receiver account if it doesn't exist +- **zkApp commands**: Create accounts specified with + `implicit_account_creation_fee` + +### Account creation fee + +Creating accounts costs 1 MINA (by default) as specified in constraint +constants. The fee is deducted from: + +- **Payments**: The amount being transferred +- **zkApp commands**: The balance change of the created account + +## Token support + +### Default token (MINA) + +All user commands support the default token (MINA), represented by `TokenId`. + +### Custom tokens + +Custom tokens are only supported via zkApp commands: + +- Create new tokens +- Transfer custom tokens +- Manage token permissions + +Signed commands (payments, delegations) only support the default token. + +## Comparison + +| Feature | Signed commands | zkApp commands | +| --------------- | ---------------- | ------------------------- | +| Signature | Single signature | Multiple authorizations | +| Complexity | Simple | Complex | +| Account updates | 1-2 accounts | Multiple accounts | +| Authorization | Signature only | Signature, proof, or none | +| Preconditions | Basic | Advanced | +| Token support | Default only | Custom tokens | +| Partial success | No | Yes | + +## Testing + +User command tests are available in multiple files: + +### Signed commands + +- `tests/test_transaction_logic_first_pass.rs` - Payment tests +- `tests/test_transaction_logic_first_pass_delegation.rs` - Delegation tests + +### zkApp commands + +- `tests/test_transaction_logic_first_pass_zkapp.rs` - zkApp command tests +- `tests/test_zkapp.rs` - Additional zkApp tests + +## Related files + +- `ledger/src/scan_state/transaction_logic/mod.rs` - User command type + definitions +- `ledger/src/scan_state/transaction_logic/signed_command.rs` - Signed command + implementation +- `ledger/src/scan_state/transaction_logic/zkapp_command/` - zkApp command + implementation +- `ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` - + Transaction application logic + +## See also + +- [Transactions overview](../transactions) +- [Payment transactions](./payments) +- [Stake delegation](./delegations) +- [zkApp commands](./zkapps) +- [Fee transfers](./fee-transfers) +- [Coinbase rewards](./coinbase) diff --git a/website/docs/developers/transactions/zkapps.md b/website/docs/developers/transactions/zkapps.md new file mode 100644 index 000000000..4017dc5fb --- /dev/null +++ b/website/docs/developers/transactions/zkapps.md @@ -0,0 +1,381 @@ +--- +sidebar_position: 5 +title: zkApp commands +description: Understanding zkApp command transactions in the Mina protocol +slug: /developers/transactions/zkapps +--- + +# zkApp commands + +## Overview + +zkApp commands are transactions that interact with zkApps (zero-knowledge +applications) - smart contracts on the Mina blockchain that leverage +zero-knowledge proofs for private, verifiable computation. Unlike traditional +smart contracts that execute on-chain, zkApp logic executes off-chain and +produces a zero-knowledge proof that the computation was performed correctly. + +A zkApp command consists of: + +- **Fee payer**: The account paying transaction fees +- **Account updates**: A forest of account modifications +- **Memo**: Optional 32-byte data field + +## What is a zkApp? + +A zkApp is a smart contract on Mina that: + +- **Stores state**: 8 field elements of app state per account +- **Executes off-chain**: Logic verified by zero-knowledge proofs +- **Multi-account**: Can interact with multiple accounts atomically +- **Flexible permissions**: Custom authorization rules and verification keys + +This architecture keeps the blockchain lightweight by only verifying proofs +on-chain, not executing the full computation. + +## Transaction structure + +zkApp commands have a complex structure supporting multiple account updates: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2708-L2712 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2701-L2704 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2692-L2697 +``` + +### Account updates + +Each account update specifies modifications to an account: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1881-L1887 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1573-L1587 +``` + +### Call forest structure + +Account updates are organized in a tree structure (forest): + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2305-L2305 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L2262-L2266 +``` + +This allows nested account update calls, similar to contract call chains. + +## Authorization methods + +Account updates can be authorized in different ways: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1769-L1777 +``` + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1509-L1513 +``` + +- **Proof**: Zero-knowledge proof of correct execution +- **Signature**: Traditional signature authorization +- **NoneGiven**: No authorization (for accounts with no permissions) + +## Transaction application + +### First pass + +During the first pass (`apply_transaction_first_pass`), the zkApp command: + +1. **Validates the fee payer** + - Checks account exists + - Verifies sufficient balance for fee + - Validates nonce matches + +2. **Applies the fee payment** + - Deducts fee from fee payer's balance + - Increments fee payer's nonce + +3. **Processes account updates** + - Validates preconditions for each update + - Applies balance changes + - Updates account state + - Verifies authorizations (signatures/proofs) + - Handles account creation fees + +4. **Collects failures** + - zkApp commands can partially succeed + - Failed account updates are collected + - Transaction marked as failed if any updates fail + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` + +### Second pass + +The second pass verifies zero-knowledge proofs and finalizes state changes. + +**Implementation:** +`ledger/src/scan_state/transaction_logic/transaction_applied.rs` + +## Preconditions + +Account updates can specify preconditions that must be satisfied. The +`Preconditions` struct contains network conditions, account conditions, and +validity windows. + +### Network preconditions + +Conditions on network state: + +- Snarked ledger hash +- Blockchain length +- Global slot +- Staking epoch data + +### Account preconditions + +Conditions on account state: + +- Balance constraints +- Nonce requirements +- State field values +- Delegate settings + +## Balance changes + +Account updates specify balance changes with sign (positive for addition, +negative for subtraction). Balance changes must sum to zero across all account +updates (conservation of funds). + +## Account creation + +zkApp commands can create accounts. When the `implicit_account_creation_fee` +field in the account update body is true: + +- Account creation fee is automatically deducted +- New account is created with specified state +- Balance changes account for creation fee + +## Examples + +### Simple zkApp command + +From the test suite (`tests/test_transaction_logic_first_pass_zkapp.rs:169`): + +```rust +// Create zkApp command with Alice as fee payer +// and Bob's account as the update target (no balance change) +let fee = 10_000_000; // 0.01 MINA +let nonce = 0; +let zkapp_command = create_simple_zkapp_command(&alice_pk, &bob_pk, fee, nonce); + +let result = apply_transaction_first_pass( + constraint_constants, + Slot::from_u32(0), + &state_view, + &mut ledger, + &Transaction::Command(UserCommand::ZkAppCommand(Box::new(zkapp_command))), +); + +assert!(result.is_ok()); + +// Verify state changes: +// - Alice's balance decreased by fee +// - Alice's nonce incremented +// - Bob's account unchanged (no balance change in this example) +``` + +### Insufficient balance + +From the test suite (`tests/test_transaction_logic_first_pass_zkapp.rs:243`): + +```rust +// Alice has 0.001 MINA but fee is 0.01 MINA +let zkapp_command = create_simple_zkapp_command(&alice_pk, &bob_pk, 10_000_000, 0); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!(result.unwrap_err(), "[[Overflow]]"); + +// Ledger state unchanged +``` + +### Invalid nonce + +From the test suite (`tests/test_transaction_logic_first_pass_zkapp.rs:322`): + +```rust +// Alice's nonce is 0, but transaction specifies nonce 5 +let zkapp_command = create_simple_zkapp_command(&alice_pk, &bob_pk, 10_000_000, 5); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!(result.unwrap_err(), "[[AccountNoncePreconditionUnsatisfied]]"); + +// Ledger state unchanged +``` + +### Nonexistent fee payer + +From the test suite (`tests/test_transaction_logic_first_pass_zkapp.rs:392`): + +```rust +// Alice's account doesn't exist +let zkapp_command = create_simple_zkapp_command(&alice_pk, &bob_pk, 10_000_000, 0); + +let result = apply_transaction_first_pass(/* ... */); + +assert!(result.is_err()); +assert_eq!( + result.unwrap_err(), + "[[Overflow, AmountInsufficientToCreateAccount]]" +); + +// No account created +``` + +## Error handling + +zkApp commands use a list-based error format: + +```rust +"[[ErrorType1, ErrorType2, ...]]" +``` + +Common error types: + +- **Overflow**: Balance overflow or underflow +- **AccountNoncePreconditionUnsatisfied**: Nonce mismatch +- **AmountInsufficientToCreateAccount**: Cannot afford account creation +- **PreconditionUnsatisfied**: Other precondition failures + +## Transaction commitments + +zkApp commands use two types of commitments for signing: + +### Partial commitment + +Hash of account updates only (used for account update signatures): + +```rust +pub fn compute_account_update_digest(account_update: &AccountUpdate) -> Fp { + // Hash account update fields +} +``` + +### Full commitment + +Includes memo and fee payer (used for fee payer signature): + +```rust +pub fn compute_full_commitment(zkapp_command: &ZkAppCommand) -> Fp { + // Hash fee payer + account updates + memo +} +``` + +Account updates specify which commitment to use via `use_full_commitment`. + +## State updates + +zkApp accounts can store up to 8 field elements of app state. The `Update` +struct allows updating app state, delegate, verification key, permissions, zkApp +URI, token symbol, timing, and voting fields. Each field can be updated +independently through account updates. + +## Permissions + +zkApp accounts have granular permissions controlling which operations require +which authorizations. The `Permissions` struct specifies permission levels +(None, Either, Proof, Signature, or Impossible) for operations like editing +state, sending/receiving funds, setting delegate, modifying permissions, +updating verification keys, and managing zkApp metadata. + +## Events and actions + +zkApp commands can emit events and actions. Events are emitted for off-chain +indexing, while actions are used for reducer pattern state management. Both are +represented as vectors of field elements. + +## Token support + +zkApp commands support custom tokens: + + + +```rust reference title="ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs" +https://github.com/o1-labs/mina-rust/blob/develop/ledger/src/scan_state/transaction_logic/zkapp_command/mod.rs#L1813-L1823 +``` + +This enables: + +- Creating new tokens +- Transferring custom tokens +- Token permission management + +## Testing + +Comprehensive tests are available in +`tests/test_transaction_logic_first_pass_zkapp.rs`: + +- `test_apply_zkapp_command_success` - Successful zkApp command +- `test_apply_zkapp_command_insufficient_balance` - Insufficient fee payer + balance +- `test_apply_zkapp_command_invalid_nonce` - Nonce mismatch error +- `test_apply_zkapp_command_nonexistent_fee_payer` - Nonexistent fee payer error + +## Related files + +- `ledger/src/scan_state/transaction_logic/zkapp_command/` - zkApp command + implementation +- `ledger/src/scan_state/transaction_logic/transaction_partially_applied.rs` - + zkApp command application +- `ledger/src/generators/zkapp_command_builder.rs` - zkApp command construction +- `tests/test_transaction_logic_first_pass_zkapp.rs` - zkApp command tests +- `tests/test_zkapp.rs` - Additional zkApp tests + +## Further reading + +For detailed zkApp development: + +- [o1js Documentation](https://docs.minaprotocol.com/zkapps) - High-level zkApp + development +- [zkApp Protocol Specification](https://docs.minaprotocol.com/zkapps/advanced) + - Detailed protocol specification +- [Zero-Knowledge Proofs](https://docs.minaprotocol.com/zkapps/tutorials) - + Understanding ZK proofs + +## See also + +- [Transactions overview](../transactions) +- [Payment transactions](./payments) diff --git a/website/docs/developers/wallet.md b/website/docs/developers/wallet.md deleted file mode 100644 index 424fea83b..000000000 --- a/website/docs/developers/wallet.md +++ /dev/null @@ -1,463 +0,0 @@ ---- -title: Wallet operations -description: CLI wallet commands for managing accounts and sending transactions -sidebar_position: 15 ---- - -# Wallet operations - -The Mina CLI provides wallet functionality for sending transactions and managing -accounts. All wallet operations use encrypted key files and interact with a -running Mina node via GraphQL. - -## Prerequisites - -Before using wallet commands, you need: - -- An encrypted private key file -- The password to decrypt the key -- A running Mina node (local or remote) - only required for sending transactions - and checking balances - -## Generate key pair - -Generate a new encrypted key pair for use with Mina. - -### Basic usage - -```bash -mina wallet generate --output /path/to/key -``` - -### Arguments - -**Required:** - -- `--output ` - Path where the encrypted key file will be saved - -**Optional:** - -- `[PASSWORD]` - Password to encrypt the key. Can be provided as an argument or - via the `MINA_PRIVKEY_PASS` environment variable (recommended for security) - -### Example - -```bash -# Generate new key with environment variable for password -export MINA_PRIVKEY_PASS="my-secret-password" -mina wallet generate --output ./keys/my-new-wallet -``` - -This command generates a new random keypair, encrypts the private key with the -provided password, and saves it to the specified path. It also creates a `.pub` -file containing the public key. - -## Get address from key file - -Extract the public address from an encrypted key file. - -### Basic usage - -```bash -mina wallet address --from /path/to/encrypted/key -``` - -### Arguments - -**Required:** - -- `--from ` - Path to encrypted key file - -**Optional:** - -- `[PASSWORD]` - Password to decrypt the key. Can be provided as an argument or - via the `MINA_PRIVKEY_PASS` environment variable (recommended for security) - -### Example - -```bash -# Get address from encrypted key -mina wallet address --from ./keys/my-wallet - -# Using environment variable for password -export MINA_PRIVKEY_PASS="my-secret-password" -mina wallet address --from ./keys/my-wallet -``` - -This command simply decrypts the key file and displays the associated public -address. It does not require a connection to a node. - -## Check account balance - -Query the balance of an account using GraphQL. - -### Basic usage - -```bash -# Check balance using key file -mina wallet balance --from /path/to/encrypted/key - -# Check balance using public address -mina wallet balance --address -``` - -### Arguments - -**Required (one of):** - -- `--from ` - Path to encrypted key file -- `--address ` - Public key to query directly - -**Optional:** - -- `[PASSWORD]` - Password to decrypt the key (only required when using - `--from`). Can be provided as an argument or via the `MINA_PRIVKEY_PASS` - environment variable (recommended for security) -- `--endpoint ` - GraphQL endpoint URL (default: - `http://localhost:3000/graphql`) -- `--format ` - Output format: `text` (default) or `json` - -### Examples - -#### Check balance using key file - -```bash -export MINA_PRIVKEY_PASS="my-secret-password" -mina wallet balance --from ./keys/my-wallet -``` - -#### Check balance using public address - -```bash -mina wallet balance \ - --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy -``` - -#### Check balance on remote node - -```bash -mina wallet balance \ - --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --endpoint https://node.example.com:3000/graphql -``` - -#### Get balance in JSON format - -```bash -mina wallet balance \ - --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --format json -``` - -### Output - -The balance command displays: - -- **Total balance** - Total amount of MINA in the account (both nanomina and - MINA) -- **Liquid balance** - Amount available for spending -- **Locked balance** - Amount locked due to vesting schedule -- **Nonce** - Current account nonce -- **Delegate** - Public key of the delegate (if set) - -#### Text format (default) - -``` -Account: B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy - -Balance: - Total: 1000.000000000 MINA - Liquid: 800.000000000 MINA - Locked: 200.000000000 MINA - -Nonce: 5 - -Delegate: B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG -``` - -#### JSON format - -```json -{ - "account": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy", - "balance": { - "total": "1000000000000", - "total_mina": "1000.000000000", - "liquid": "800000000000", - "liquid_mina": "800.000000000", - "locked": "200000000000", - "locked_mina": "200.000000000" - }, - "nonce": "5", - "delegate": "B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG" -} -``` - -The JSON format includes both nanomina (raw values) and formatted MINA values -for convenience. - -## Send payment - -Send a payment transaction to the network. - -### Basic usage - -```bash -mina wallet send \ - --from /path/to/encrypted/key \ - --to \ - --amount \ - --fee -``` - -### Arguments - -**Required:** - -- `--from ` - Path to encrypted sender key file -- `--to ` - Receiver's public key (Base58Check encoded) -- `--amount ` - Amount in nanomina (1 MINA = 1,000,000,000 nanomina) -- `--fee ` - Transaction fee in nanomina - -**Optional:** - -- `[PASSWORD]` - Password to decrypt the sender key. Can be provided as an - argument or via the `MINA_PRIVKEY_PASS` environment variable (recommended for - security) -- `--memo ` - Transaction memo (max 32 bytes, default: empty) -- `--nonce ` - Transaction nonce (default: fetched from node) -- `--valid-until ` - Slot until which transaction is valid (default: never - expires) -- `--fee-payer ` - Optional fee payer public key (default: sender - pays) -- `--network ` - Network for signing: `mainnet` or `devnet` (default: - `devnet`) -- `--node ` - Node GraphQL endpoint (default: `http://localhost:3000`) - -### Environment variables - -You can set the following environment variables: - -```bash -export MINA_PRIVKEY_PASS="your-password" -export MINA_NETWORK="mainnet" -``` - -### Examples - -#### Send payment on devnet - -```bash -mina wallet send \ - --from ./keys/my-wallet \ - --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --amount 1000000000 \ - --fee 10000000 \ - --network devnet -``` - -#### Send payment on mainnet with memo - -```bash -mina wallet send \ - --from ./keys/my-wallet \ - --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --amount 5000000000 \ - --fee 10000000 \ - --memo "Payment for services" \ - --network mainnet -``` - -#### Send payment with separate fee payer - -```bash -mina wallet send \ - --from ./keys/sender-wallet \ - --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --amount 1000000000 \ - --fee 10000000 \ - --fee-payer B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG -``` - -#### Send payment to remote node - -```bash -mina wallet send \ - --from ./keys/my-wallet \ - --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --amount 1000000000 \ - --fee 10000000 \ - --node https://node.example.com:3000 -``` - -#### Use environment variable for password - -```bash -export MINA_PRIVKEY_PASS="my-secret-password" - -mina wallet send \ - --from ./keys/my-wallet \ - --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ - --amount 1000000000 \ - --fee 10000000 -``` - -## Understanding amounts - -All amounts in the CLI are specified in **nanomina**: - -- 1 MINA = 1,000,000,000 nanomina -- 0.1 MINA = 100,000,000 nanomina -- 0.01 MINA = 10,000,000 nanomina (common minimum fee) - -## How it works - -When you send a payment, the CLI: - -1. **Decrypts your key** - Uses the provided password to decrypt your private - key -2. **Fetches nonce** - Queries the node via GraphQL to get your current account - nonce (if not specified) -3. **Creates payload** - Builds the payment transaction payload with all details -4. **Signs transaction** - Signs the transaction using your private key and the - correct network ID -5. **Submits to node** - Sends the signed transaction to the node via GraphQL - `sendPayment` mutation -6. **Returns hash** - Displays the transaction hash, which can be used with - `mina wallet status` to track the transaction - -## Network selection - -The `--network` flag controls which network the transaction is signed for: - -- `devnet` - For development and testing (default) -- `mainnet` - For production transactions - -**Important:** Make sure to use the correct network flag. A transaction signed -for devnet will not be valid on mainnet and vice versa. - -## Fee payer - -By default, the sender pays the transaction fee. However, you can specify a -different fee payer using the `--fee-payer` option: - -- The sender's key is used to sign the payment -- The fee payer's public key is included in the transaction -- The fee payer must also sign the transaction (currently requires manual - coordination) - -This is useful for sponsored transactions where another party pays the fees. - -## Check transaction status - -Check the status of a submitted transaction using its hash. - -### Basic usage - -```bash -mina wallet status --hash -``` - -### Arguments - -**Required:** - -- `--hash ` - Transaction hash to check - -**Optional:** - -- `--node ` - Node GraphQL endpoint (default: `http://localhost:3000`) -- `--check-mempool` - Force checking the mempool even if transaction is found in - blockchain - -### Examples - -#### Check transaction on local node - -```bash -mina wallet status \ - --hash 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew -``` - -#### Check transaction on remote node - -```bash -mina wallet status \ - --hash 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew \ - --node https://devnet-plain-1.gcp.o1test.net -``` - -### Output - -The status command will: - -1. First attempt to query the blockchain for the transaction status -2. If not found in the blockchain, automatically check the mempool (pending - transactions) -3. Display transaction details if found in the mempool - -#### Transaction found in mempool - -``` -Checking transaction status... -Transaction hash: 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew - -Transaction not found in blockchain, checking mempool... - -✓ Transaction found in mempool! - -Transaction Details: - Hash: 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew - From: B62qjtpVAMr7knjLxRLU887QgT7GPk3JYCg8NGdZsfMuaykAJ9C2Rem - To: B62qjtpVAMr7knjLxRLU887QgT7GPk3JYCg8NGdZsfMuaykAJ9C2Rem - Amount: 1000000000 nanomina - Fee: 1000000 nanomina - Nonce: 0 - -Status: PENDING (waiting to be included in a block) -``` - -#### Transaction not found - -``` -Checking transaction status... -Transaction hash: 5Ju6ku4DY5McpfqPvduQyQASjv1iAF12Xn75W3f3kGL1wsgSRKBA - -Transaction not found in blockchain, checking mempool... - -✗ Transaction not found in mempool - -The transaction may have: - - Already been included in a block - - Been rejected by the network - - Not yet propagated to this node -``` - -### How it works - -The status command automatically: - -1. **Queries blockchain** - Attempts to query `transactionStatus` via GraphQL -2. **Falls back to mempool** - If not found or if the query fails, checks - `pooledUserCommands` for pending transactions -3. **Displays results** - Shows transaction details if found, or helpful - messages if not found - -This is particularly useful immediately after sending a transaction to verify it -has been accepted into the mempool. - -## GraphQL integration - -The wallet commands use the node's GraphQL API: - -- **Account query** - Fetches current nonce and account information (`balance` - command) -- **sendPayment mutation** - Submits signed transactions to the network (`send` - command) -- **transactionStatus query** - Checks if a transaction is included in the - blockchain (`status` command) -- **pooledUserCommands query** - Lists pending transactions in the mempool - (`status` command) - -For more details on the GraphQL API, see the [GraphQL API](./graphql-api.md) -documentation. diff --git a/website/docs/developers/wallet/address.md b/website/docs/developers/wallet/address.md new file mode 100644 index 000000000..2e2936c17 --- /dev/null +++ b/website/docs/developers/wallet/address.md @@ -0,0 +1,40 @@ +--- +title: address +description: Get the public address from an encrypted key file +sidebar_position: 1 +--- + +# address + +Get the public address from an encrypted key file. + +## Basic usage + +```bash +mina wallet address --from /path/to/encrypted/key +``` + +## Arguments + +**Required:** + +- `--from ` - Path to encrypted key file + +**Optional:** + +- `[PASSWORD]` - Password to decrypt the key. Can be provided as an argument or + via the `MINA_PRIVKEY_PASS` environment variable (recommended for security) + +## Example + +```bash +# Get address from encrypted key +mina wallet address --from ./keys/my-wallet + +# Using environment variable for password +export MINA_PRIVKEY_PASS="my-secret-password" +mina wallet address --from ./keys/my-wallet +``` + +This command simply decrypts the key file and displays the associated public +address. It does not require a connection to a node. diff --git a/website/docs/developers/wallet/balance.md b/website/docs/developers/wallet/balance.md new file mode 100644 index 000000000..06808ba3e --- /dev/null +++ b/website/docs/developers/wallet/balance.md @@ -0,0 +1,114 @@ +--- +title: balance +description: Query account balance and details using GraphQL +sidebar_position: 2 +--- + +# balance + +Query the balance and details of an account using GraphQL. + +## Basic usage + +```bash +# Check balance using key file +mina wallet balance --from /path/to/encrypted/key + +# Check balance using public address +mina wallet balance --address +``` + +## Arguments + +**Required (one of):** + +- `--from ` - Path to encrypted key file +- `--address ` - Public key to query directly + +**Optional:** + +- `[PASSWORD]` - Password to decrypt the key (only required when using + `--from`). Can be provided as an argument or via the `MINA_PRIVKEY_PASS` + environment variable (recommended for security) +- `--endpoint ` - GraphQL endpoint URL (default: + `http://localhost:3000/graphql`) +- `--format ` - Output format: `text` (default) or `json` + +## Examples + +### Check balance using key file + +```bash +export MINA_PRIVKEY_PASS="my-secret-password" +mina wallet balance --from ./keys/my-wallet +``` + +### Check balance using public address + +```bash +mina wallet balance \ + --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy +``` + +### Check balance on remote node + +```bash +mina wallet balance \ + --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --endpoint https://node.example.com:3000/graphql +``` + +### Get balance in JSON format + +```bash +mina wallet balance \ + --address B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --format json +``` + +## Output + +The balance command displays: + +- **Total balance** - Total amount of MINA in the account (both nanomina and + MINA) +- **Liquid balance** - Amount available for spending +- **Locked balance** - Amount locked due to vesting schedule +- **Nonce** - Current account nonce +- **Delegate** - Public key of the delegate (if set) + +### Text format (default) + +``` +Account: B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy + +Balance: + Total: 1000.000000000 MINA + Liquid: 800.000000000 MINA + Locked: 200.000000000 MINA + +Nonce: 5 + +Delegate: B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG +``` + +### JSON format + +```json +{ + "account": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy", + "balance": { + "total": "1000000000000", + "total_mina": "1000.000000000", + "liquid": "800000000000", + "liquid_mina": "800.000000000", + "locked": "200000000000", + "locked_mina": "200.000000000" + }, + "nonce": "5", + "delegate": "B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG" +} +``` + +The JSON format includes both nanomina (raw values) and formatted MINA values +for convenience. diff --git a/website/docs/developers/wallet/generate.md b/website/docs/developers/wallet/generate.md new file mode 100644 index 000000000..bd19778c7 --- /dev/null +++ b/website/docs/developers/wallet/generate.md @@ -0,0 +1,38 @@ +--- +title: generate +description: Generate a new encrypted key pair +sidebar_position: 3 +--- + +# generate + +Generate a new encrypted key pair for use with Mina. + +## Basic usage + +```bash +mina wallet generate --output /path/to/key +``` + +## Arguments + +**Required:** + +- `--output ` - Path where the encrypted key file will be saved + +**Optional:** + +- `[PASSWORD]` - Password to encrypt the key. Can be provided as an argument or + via the `MINA_PRIVKEY_PASS` environment variable (recommended for security) + +## Example + +```bash +# Generate new key with environment variable for password +export MINA_PRIVKEY_PASS="my-secret-password" +mina wallet generate --output ./keys/my-new-wallet +``` + +This command generates a new random keypair, encrypts the private key with the +provided password, and saves it to the specified path. It also creates a `.pub` +file containing the public key. diff --git a/website/docs/developers/wallet/index.md b/website/docs/developers/wallet/index.md new file mode 100644 index 000000000..c195a370c --- /dev/null +++ b/website/docs/developers/wallet/index.md @@ -0,0 +1,71 @@ +--- +title: Introduction +description: CLI wallet commands for managing accounts and sending transactions +sidebar_position: 15 +--- + +# Wallet operations + +The Mina CLI provides wallet functionality for sending transactions and managing +accounts. All wallet operations use encrypted key files and interact with a +running Mina node via GraphQL. + +## Introduction + +The wallet commands provide a complete set of tools for managing Mina accounts +and sending transactions directly from the command line. These commands handle +key generation, account queries, and transaction submission. + +### Prerequisites + +Before using wallet commands, you need: + +- An encrypted private key file +- The password to decrypt the key +- A running Mina node (local or remote) - only required for sending transactions + and checking balances + +### Environment variables + +You can set the following environment variables for convenience: + +```bash +export MINA_PRIVKEY_PASS="your-password" +export MINA_NETWORK="mainnet" # or "devnet" +``` + +## Commands + +The wallet CLI provides the following commands, each documented in detail in +their respective sections: + +- **[address](./address.md)** - Get the public address from an encrypted key + file +- **[balance](./balance.md)** - Query account balance and details using GraphQL +- **[generate](./generate.md)** - Generate a new encrypted key pair +- **[send](./send.md)** - Send a payment transaction to the network +- **[status](./status.md)** - Check the status of a submitted transaction + +## Understanding amounts + +All amounts in the CLI are specified in **nanomina**: + +- 1 MINA = 1,000,000,000 nanomina +- 0.1 MINA = 100,000,000 nanomina +- 0.01 MINA = 10,000,000 nanomina (common minimum fee) + +## GraphQL integration + +The wallet commands use the node's GraphQL API: + +- **Account query** - Fetches current nonce and account information (`balance` + command) +- **sendPayment mutation** - Submits signed transactions to the network (`send` + command) +- **transactionStatus query** - Checks if a transaction is included in the + blockchain (`status` command) +- **pooledUserCommands query** - Lists pending transactions in the mempool + (`status` command) + +For more details on the GraphQL API, see the [GraphQL API](../graphql-api.md) +documentation. diff --git a/website/docs/developers/wallet/send.md b/website/docs/developers/wallet/send.md new file mode 100644 index 000000000..03af21f98 --- /dev/null +++ b/website/docs/developers/wallet/send.md @@ -0,0 +1,140 @@ +--- +title: send +description: Send a payment transaction to the network +sidebar_position: 4 +--- + +# send + +Send a payment transaction to the network. + +## Basic usage + +```bash +mina wallet send \ + --from /path/to/encrypted/key \ + --to \ + --amount \ + --fee +``` + +## Arguments + +**Required:** + +- `--from ` - Path to encrypted sender key file +- `--to ` - Receiver's public key (Base58Check encoded) +- `--amount ` - Amount in nanomina (1 MINA = 1,000,000,000 nanomina) +- `--fee ` - Transaction fee in nanomina + +**Optional:** + +- `[PASSWORD]` - Password to decrypt the sender key. Can be provided as an + argument or via the `MINA_PRIVKEY_PASS` environment variable (recommended for + security) +- `--memo ` - Transaction memo (max 32 bytes, default: empty) +- `--nonce ` - Transaction nonce (default: fetched from node) +- `--valid-until ` - Slot until which transaction is valid (default: never + expires) +- `--fee-payer ` - Optional fee payer public key (default: sender + pays) +- `--network ` - Network for signing: `mainnet` or `devnet` (default: + `devnet`) +- `--node ` - Node GraphQL endpoint (default: `http://localhost:3000`) + +## Examples + +### Send payment on devnet + +```bash +mina wallet send \ + --from ./keys/my-wallet \ + --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --amount 1000000000 \ + --fee 10000000 \ + --network devnet +``` + +### Send payment on mainnet with memo + +```bash +mina wallet send \ + --from ./keys/my-wallet \ + --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --amount 5000000000 \ + --fee 10000000 \ + --memo "Payment for services" \ + --network mainnet +``` + +### Send payment with separate fee payer + +```bash +mina wallet send \ + --from ./keys/sender-wallet \ + --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --amount 1000000000 \ + --fee 10000000 \ + --fee-payer B62qkfHpLpELqpMK6ZvUTJ5wRqKDRF3UHyJ4Kv3FU79Sgs4qpBnx5RG +``` + +### Send payment to remote node + +```bash +mina wallet send \ + --from ./keys/my-wallet \ + --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --amount 1000000000 \ + --fee 10000000 \ + --node https://node.example.com:3000 +``` + +### Use environment variable for password + +```bash +export MINA_PRIVKEY_PASS="my-secret-password" + +mina wallet send \ + --from ./keys/my-wallet \ + --to B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy \ + --amount 1000000000 \ + --fee 10000000 +``` + +## How it works + +When you send a payment, the CLI: + +1. **Decrypts your key** - Uses the provided password to decrypt your private + key +2. **Fetches nonce** - Queries the node via GraphQL to get your current account + nonce (if not specified) +3. **Creates payload** - Builds the payment transaction payload with all details +4. **Signs transaction** - Signs the transaction using your private key and the + correct network ID +5. **Submits to node** - Sends the signed transaction to the node via GraphQL + `sendPayment` mutation +6. **Returns hash** - Displays the transaction hash, which can be used with + `mina wallet status` to track the transaction + +## Network selection + +The `--network` flag controls which network the transaction is signed for: + +- `devnet` - For development and testing (default) +- `mainnet` - For production transactions + +**Important:** Make sure to use the correct network flag. A transaction signed +for devnet will not be valid on mainnet and vice versa. + +## Fee payer + +By default, the sender pays the transaction fee. However, you can specify a +different fee payer using the `--fee-payer` option: + +- The sender's key is used to sign the payment +- The fee payer's public key is included in the transaction +- The fee payer must also sign the transaction (currently requires manual + coordination) + +This is useful for sponsored transactions where another party pays the fees. diff --git a/website/docs/developers/wallet/status.md b/website/docs/developers/wallet/status.md new file mode 100644 index 000000000..27eeef0f7 --- /dev/null +++ b/website/docs/developers/wallet/status.md @@ -0,0 +1,103 @@ +--- +title: status +description: Check the status of a submitted transaction +sidebar_position: 5 +--- + +# status + +Check the status of a submitted transaction using its hash. + +## Basic usage + +```bash +mina wallet status --hash +``` + +## Arguments + +**Required:** + +- `--hash ` - Transaction hash to check + +**Optional:** + +- `--node ` - Node GraphQL endpoint (default: `http://localhost:3000`) +- `--check-mempool` - Force checking the mempool even if transaction is found in + blockchain + +## Examples + +### Check transaction on local node + +```bash +mina wallet status \ + --hash 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew +``` + +### Check transaction on remote node + +```bash +mina wallet status \ + --hash 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew \ + --node https://devnet-plain-1.gcp.o1test.net +``` + +## Output + +The status command will: + +1. First attempt to query the blockchain for the transaction status +2. If not found in the blockchain, automatically check the mempool (pending + transactions) +3. Display transaction details if found in the mempool + +### Transaction found in mempool + +``` +Checking transaction status... +Transaction hash: 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew + +Transaction not found in blockchain, checking mempool... + +✓ Transaction found in mempool! + +Transaction Details: + Hash: 5Ju4H4DTE1zkwrnLrQ8vb2sZR19b7eSMiAVbb4wQh4bfhh4aQNew + From: B62qjtpVAMr7knjLxRLU887QgT7GPk3JYCg8NGdZsfMuaykAJ9C2Rem + To: B62qjtpVAMr7knjLxRLU887QgT7GPk3JYCg8NGdZsfMuaykAJ9C2Rem + Amount: 1000000000 nanomina + Fee: 1000000 nanomina + Nonce: 0 + +Status: PENDING (waiting to be included in a block) +``` + +### Transaction not found + +``` +Checking transaction status... +Transaction hash: 5Ju6ku4DY5McpfqPvduQyQASjv1iAF12Xn75W3f3kGL1wsgSRKBA + +Transaction not found in blockchain, checking mempool... + +✗ Transaction not found in mempool + +The transaction may have: + - Already been included in a block + - Been rejected by the network + - Not yet propagated to this node +``` + +## How it works + +The status command automatically: + +1. **Queries blockchain** - Attempts to query `transactionStatus` via GraphQL +2. **Falls back to mempool** - If not found or if the query fails, checks + `pooledUserCommands` for pending transactions +3. **Displays results** - Shows transaction details if found, or helpful + messages if not found + +This is particularly useful immediately after sending a transaction to verify it +has been accepted into the mempool. diff --git a/website/sidebars.ts b/website/sidebars.ts index 38d56200f..90aee575b 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -85,15 +85,45 @@ const sidebars: SidebarsConfig = { 'developers/ledger-crate', ], }, + { + type: 'category', + label: 'Transactions', + items: [ + 'developers/transactions', + { + type: 'category', + label: 'Transaction types', + items: [ + 'developers/transactions/coinbase', + 'developers/transactions/fee-transfers', + 'developers/transactions/payments', + 'developers/transactions/user-commands', + 'developers/transactions/delegations', + 'developers/transactions/zkapps', + ], + }, + ], + }, { type: 'category', label: 'APIs and Data', items: [ 'developers/graphql-api', - 'developers/wallet', 'developers/archive-database-queries', ], }, + { + type: 'category', + label: 'Wallet operations', + items: [ + 'developers/wallet/index', + 'developers/wallet/address', + 'developers/wallet/balance', + 'developers/wallet/generate', + 'developers/wallet/send', + 'developers/wallet/status', + ], + }, { type: 'category', label: 'Frontend',