Skip to content

Commit 63a7865

Browse files
committed
Ledger: test applying coinbase tx when fee payer does not exist
1 parent 7c3f549 commit 63a7865

File tree

1 file changed

+116
-1
lines changed

1 file changed

+116
-1
lines changed

ledger/tests/test_transaction_logic_first_pass_coinbase.rs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Tests for apply_transaction_first_pass with coinbase transactions
22
//!
3-
//! Run with: cargo test --test test_transaction_logic_first_pass_coinbase
3+
//! Run with: cargo test --test test_transaction_logic_first_pass_coinbase --release
44
//!
55
//! Tests the first pass of two-phase transaction application for coinbase
66
//! rewards, covering:
77
//! - Successful coinbase without fee transfer
88
//! - Successful coinbase with fee transfer to different account
9+
//! - Coinbase with fee transfer to nonexistent account (creates account)
910
//! - Coinbase with fee transfer to same account (fee transfer should be
1011
//! removed)
1112
//! - Coinbase creating a new account
@@ -228,6 +229,120 @@ fn test_apply_coinbase_with_fee_transfer() {
228229
);
229230
}
230231

232+
/// Test coinbase with fee transfer to a nonexistent account.
233+
///
234+
/// The coinbase receiver exists, but the fee transfer receiver doesn't exist.
235+
/// The fee transfer should create the receiver account, deducting the account
236+
/// creation fee from the fee transfer amount.
237+
///
238+
/// Ledger state:
239+
/// - Coinbase receiver gets coinbase_amount - fee_transfer_amount
240+
/// - Fee transfer receiver account created with fee_transfer_amount -
241+
/// account_creation_fee
242+
#[test]
243+
fn test_apply_coinbase_with_fee_transfer_creates_account() {
244+
let mut ledger = create_test_ledger();
245+
246+
let alice_pk = mina_signer::PubKey::from_address(
247+
"B62qmnY6m4c6bdgSPnQGZriSaj9vuSjsfh6qkveGTsFX3yGA5ywRaja",
248+
)
249+
.unwrap()
250+
.into_compressed();
251+
let bob_pk = mina_signer::PubKey::from_address(
252+
"B62qjVQLxt9nYMWGn45mkgwYfcz8e8jvjNCBo11VKJb7vxDNwv5QLPS",
253+
)
254+
.unwrap()
255+
.into_compressed();
256+
257+
let alice_id = AccountId::new(alice_pk.clone(), Default::default());
258+
let bob_id = AccountId::new(bob_pk.clone(), Default::default());
259+
260+
// Verify Bob's account does not exist before the transaction
261+
assert!(
262+
ledger.location_of_account(&bob_id).is_none(),
263+
"Bob's account should not exist before transaction"
264+
);
265+
266+
// Record Alice's initial state
267+
let alice_location = ledger.location_of_account(&alice_id).unwrap();
268+
let alice_before = ledger.get(alice_location).unwrap();
269+
let initial_alice_balance = alice_before.balance;
270+
271+
// Create a coinbase of 720 MINA to Alice with a 10 MINA fee transfer to Bob
272+
// (who doesn't exist yet)
273+
let coinbase_amount = Amount::from_u64(720_000_000_000);
274+
let fee_transfer_amount = Fee::from_u64(10_000_000_000);
275+
let fee_transfer = CoinbaseFeeTransfer::create(bob_pk.clone(), fee_transfer_amount);
276+
let coinbase = Coinbase::create(coinbase_amount, alice_pk.clone(), Some(fee_transfer)).unwrap();
277+
278+
let constraint_constants = &test_constraint_constants();
279+
let state_view = ProtocolStateView {
280+
snarked_ledger_hash: Fp::zero(),
281+
blockchain_length: Length::from_u32(0),
282+
min_window_density: Length::from_u32(0),
283+
total_currency: Amount::zero(),
284+
global_slot_since_genesis: Slot::from_u32(0),
285+
staking_epoch_data: dummy_epoch_data(),
286+
next_epoch_data: dummy_epoch_data(),
287+
};
288+
let result = apply_transaction_first_pass(
289+
constraint_constants,
290+
Slot::from_u32(0),
291+
&state_view,
292+
&mut ledger,
293+
&Transaction::Coinbase(coinbase),
294+
);
295+
296+
assert!(result.is_ok());
297+
298+
// Verify Bob's account was created
299+
let bob_location = ledger.location_of_account(&bob_id);
300+
assert!(
301+
bob_location.is_some(),
302+
"Bob's account should exist after transaction"
303+
);
304+
305+
// Verify ledger state changes
306+
let alice_location = ledger.location_of_account(&alice_id).unwrap();
307+
let alice_after = ledger.get(alice_location).unwrap();
308+
let bob_account = ledger.get(bob_location.unwrap()).unwrap();
309+
310+
// Verify Alice's balance increased by (coinbase amount - fee transfer amount)
311+
let coinbase_after_fee_transfer = coinbase_amount
312+
.checked_sub(&Amount::of_fee(&fee_transfer_amount))
313+
.unwrap();
314+
let expected_alice_balance = initial_alice_balance
315+
.add_amount(coinbase_after_fee_transfer)
316+
.unwrap();
317+
assert_eq!(
318+
alice_after.balance, expected_alice_balance,
319+
"Alice's balance should increase by coinbase minus fee transfer"
320+
);
321+
322+
// Verify Bob's balance equals fee transfer amount minus account creation fee
323+
let account_creation_fee = constraint_constants.account_creation_fee;
324+
let expected_bob_balance = Balance::from_u64(
325+
Amount::of_fee(&fee_transfer_amount)
326+
.as_u64()
327+
.saturating_sub(account_creation_fee),
328+
);
329+
assert_eq!(
330+
bob_account.balance, expected_bob_balance,
331+
"Bob's balance should equal fee transfer minus account creation fee"
332+
);
333+
334+
// Verify nonces
335+
assert_eq!(
336+
alice_after.nonce, alice_before.nonce,
337+
"Alice's nonce should remain unchanged"
338+
);
339+
assert_eq!(
340+
bob_account.nonce,
341+
Nonce::zero(),
342+
"Bob's nonce should be 0 for new account"
343+
);
344+
}
345+
231346
/// Test coinbase with fee transfer to the same account.
232347
///
233348
/// When the coinbase receiver and fee transfer receiver are the same, the fee

0 commit comments

Comments
 (0)