Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/lib/transaction_logic/mina_transaction_logic.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,9 @@ module Make (L : Ledger_intf.S) : S with type ledger := L.t = struct
~f:Party.Update.Timing_info.to_account_timing timing
}

let is_timed (a : t) =
match a.timing with Account_timing.Untimed -> false | _ -> true

let set_token_id (a : t) (id : Token_id.t) : t = { a with token_id = id }

let balance (a : t) : Balance.t = a.balance
Expand Down
7 changes: 6 additions & 1 deletion src/lib/transaction_logic/parties_logic.ml
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ module type Account_intf = sig

val set_timing : t -> timing -> t

val is_timed : t -> bool

val set_token_id : t -> token_id -> t

type balance
Expand Down Expand Up @@ -1071,13 +1073,16 @@ module Make (Inputs : Inputs_intf) = struct
let a = Account.set_token_id a (Party.token_id party) in
let party_token = Party.token_id party in
let party_token_is_default = Token_id.(equal default) party_token in
let account_is_untimed = Bool.not (Account.is_timed a) in
(* Set account timing for new accounts, if specified. *)
let a, local_state =
let timing = Party.Update.timing party in
let local_state =
Local_state.add_check local_state
Update_not_permitted_timing_existing_account
Bool.(account_is_new ||| Set_or_keep.is_keep timing)
Bool.(
Set_or_keep.is_keep timing
||| (account_is_untimed &&& signature_verifies))
in
let timing =
Set_or_keep.set_or_keep ~if_:Timing.if_ timing (Account.timing a)
Expand Down
233 changes: 233 additions & 0 deletions src/lib/transaction_snark/test/account_timing/account_timing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1563,4 +1563,237 @@ let%test_module "account timing check" =
~state_view ledger parties
in
check_zkapp_failure Transaction_status.Failure.Overflow result ) )

let%test_unit "zkApp command, create timed account with wrong authorization"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you write a test for correct authorization and a test for the new case (fails when the account is already timed)?

=
let ledger_init_state =
List.map keypairs ~f:(fun keypair ->
let balance = Currency.Amount.of_int 100_000_000_000_000 in
let nonce = Mina_numbers.Account_nonce.zero in
(keypair, balance, nonce, Account_timing.Untimed) )
|> Array.of_list
in
let sender_keypair = List.hd_exn keypairs in
let zkapp_keypair = Signature_lib.Keypair.create () in
let (create_timed_account_spec : Transaction_snark.For_tests.Spec.t) =
{ sender = (sender_keypair, Account.Nonce.zero)
; fee = Currency.Fee.of_int 1_000_000
; fee_payer = None
; receivers = []
; amount = Currency.Amount.of_int 50_000_000_000_000
; zkapp_account_keypairs = [ zkapp_keypair ]
; memo =
Signed_command_memo.create_from_string_exn
"zkApp create timed account"
; new_zkapp_account = true
; snapp_update =
(let timing =
Zkapp_basic.Set_or_keep.Set
( { initial_minimum_balance =
Currency.Balance.of_int 1_000_000_000
; cliff_time = Mina_numbers.Global_slot.of_int 10
; cliff_amount = Currency.Amount.of_int 1_000_000_000
; vesting_period = Mina_numbers.Global_slot.of_int 10
; vesting_increment = Currency.Amount.of_int 1_000_000_000
}
: Party.Update.Timing_info.value )
in
{ Party.Update.dummy with timing } )
; current_auth = Permissions.Auth_required.Proof
; call_data = Snark_params.Tick.Field.zero
; events = []
; sequence_events = []
; protocol_state_precondition = None
; account_precondition = None
}
in
let timing_account_id =
Account_id.create
(zkapp_keypair.public_key |> Signature_lib.Public_key.compress)
Token_id.default
in
let create_timed_account_parties, _, _, _ =
( Transaction_snark.For_tests.deploy_snapp ~no_auth:true
~constraint_constants create_timed_account_spec
, timing_account_id
, create_timed_account_spec.snapp_update
, zkapp_keypair )
in
let gen =
Quickcheck.Generator.return
(ledger_init_state, create_timed_account_parties)
in
Async.Thread_safe.block_on_async_exn (fun () ->
Async.Quickcheck.async_test
~seed:
(`Deterministic
"zkapp command, create timed account with wrong authorization"
)
~sexp_of:[%sexp_of: Mina_ledger.Ledger.init_state * Parties.t]
~trials:1 gen
~f:(fun (ledger_init_state, create_timed_account_parties) ->
let ledger =
Mina_ledger.Ledger.create
~depth:constraint_constants.ledger_depth ()
in
Mina_ledger.Ledger.apply_initial_ledger_state ledger
ledger_init_state ;
Transaction_snark_tests.Util.check_parties_with_merges_exn
~expected_failure:
Transaction_status.Failure
.Update_not_permitted_timing_existing_account ledger
[ create_timed_account_parties ] ) )

let%test_unit "zkApp command, change untimed account to timed" =
Async.Thread_safe.block_on_async_exn (fun () ->
Backtrace.elide := false ;
let ledger_init_state =
List.map keypairs ~f:(fun keypair ->
let balance = Currency.Amount.of_int 100_000_000_000_000 in
let nonce = Mina_numbers.Account_nonce.zero in
(keypair, balance, nonce, Account_timing.Untimed) )
|> Array.of_list
in
let sender_keypair = List.hd_exn keypairs in
let zkapp_keypair = List.nth_exn keypairs 1 in
let (update_timing_spec : Transaction_snark.For_tests.Spec.t) =
{ sender = (sender_keypair, Account.Nonce.zero)
; fee = Currency.Fee.of_int 1_000_000
; fee_payer = None
; receivers = []
; amount = Currency.Amount.zero
; zkapp_account_keypairs = [ zkapp_keypair ]
; memo =
Signed_command_memo.create_from_string_exn "zkApp update timing"
; new_zkapp_account = false
; snapp_update =
(let timing =
Zkapp_basic.Set_or_keep.Set
( { initial_minimum_balance =
Currency.Balance.of_int 1_000_000_000
; cliff_time = Mina_numbers.Global_slot.of_int 10
; cliff_amount = Currency.Amount.of_int 1_000_000_000
; vesting_period = Mina_numbers.Global_slot.of_int 10
; vesting_increment =
Currency.Amount.of_int 1_000_000_000
}
: Party.Update.Timing_info.value )
in
{ Party.Update.dummy with timing } )
; current_auth = Permissions.Auth_required.Signature
; call_data = Snark_params.Tick.Field.zero
; events = []
; sequence_events = []
; protocol_state_precondition = None
; account_precondition = None
}
in
let open Async.Deferred.Let_syntax in
let%bind update_timing_parties =
Transaction_snark.For_tests.update_states ~constraint_constants
update_timing_spec
in
let gen =
Quickcheck.Generator.return
(ledger_init_state, update_timing_parties)
in
Async.Quickcheck.async_test
~seed:
(`Deterministic
"zkapp command, change untimed account to timed account" )
~sexp_of:[%sexp_of: Mina_ledger.Ledger.init_state * Parties.t]
~trials:1 gen ~f:(fun (ledger_init_state, update_timing_parties) ->
let ledger =
Mina_ledger.Ledger.create
~depth:constraint_constants.ledger_depth ()
in
Mina_ledger.Ledger.apply_initial_ledger_state ledger
ledger_init_state ;
Transaction_snark_tests.Util.check_parties_with_merges_exn ledger
[ update_timing_parties ] ) )

let%test_unit "zkApp command, invalid update for timed account" =
Async.Thread_safe.block_on_async_exn (fun () ->
let ledger_init_state =
List.mapi keypairs ~f:(fun i keypair ->
let balance = Currency.Amount.of_int 100_000_000_000_000 in
let nonce = Mina_numbers.Account_nonce.zero in
( keypair
, balance
, nonce
, if i = 1 then
Account_timing.Timed
{ initial_minimum_balance =
Currency.Balance.of_int 10_000_000_000
; cliff_time = Mina_numbers.Global_slot.of_int 10_000
; cliff_amount = Currency.Amount.zero
; vesting_period = Mina_numbers.Global_slot.of_int 1
; vesting_increment = Currency.Amount.of_int 100_000
}
else Account_timing.Untimed ) )
|> Array.of_list
in
let sender_keypair = List.hd_exn keypairs in
let zkapp_keypair = List.nth_exn keypairs 1 in
let (update_timing_spec : Transaction_snark.For_tests.Spec.t) =
{ sender = (sender_keypair, Account.Nonce.zero)
; fee = Currency.Fee.of_int 1_000_000
; fee_payer = None
; receivers = []
; amount = Currency.Amount.zero
; zkapp_account_keypairs = [ zkapp_keypair ]
; memo =
Signed_command_memo.create_from_string_exn "zkApp update timing"
; new_zkapp_account = false
; snapp_update =
(let timing =
Zkapp_basic.Set_or_keep.Set
( { initial_minimum_balance =
Currency.Balance.of_int 1_000_000_000
; cliff_time = Mina_numbers.Global_slot.of_int 10
; cliff_amount = Currency.Amount.of_int 1_000_000_000
; vesting_period = Mina_numbers.Global_slot.of_int 10
; vesting_increment =
Currency.Amount.of_int 1_000_000_000
}
: Party.Update.Timing_info.value )
in
{ Party.Update.dummy with timing } )
; current_auth = Permissions.Auth_required.Signature
; call_data = Snark_params.Tick.Field.zero
; events = []
; sequence_events = []
; protocol_state_precondition = None
; account_precondition = None
}
in
let open Async.Deferred.Let_syntax in
let%map update_timing_parties =
Transaction_snark.For_tests.update_states ~constraint_constants
update_timing_spec
in
let gen =
Quickcheck.Generator.return
(ledger_init_state, update_timing_parties)
in
Async.Thread_safe.block_on_async_exn (fun () ->
Async.Quickcheck.async_test
~seed:
(`Deterministic
"zkapp command, invalid update for timed account" )
~sexp_of:[%sexp_of: Mina_ledger.Ledger.init_state * Parties.t]
~trials:1 gen
~f:(fun (ledger_init_state, update_timing_parties) ->
let ledger =
Mina_ledger.Ledger.create
~depth:constraint_constants.ledger_depth ()
in
Mina_ledger.Ledger.apply_initial_ledger_state ledger
ledger_init_state ;

Transaction_snark_tests.Util.check_parties_with_merges_exn
~expected_failure:
Transaction_status.Failure
.Update_not_permitted_timing_existing_account ledger
[ update_timing_parties ] ) ) )
end )
52 changes: 32 additions & 20 deletions src/lib/transaction_snark/transaction_snark.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,12 @@ module Base = struct
let set_timing (account : t) (timing : timing) : t =
{ account with data = { account.data with timing } }

let is_timed ({ data = account; _ } : t) =
let open Account.Poly in
let open Account.Timing.As_record in
let { is_timed; _ } = account.timing in
is_timed

let set_token_id (account : t) (token_id : Token_id.Checked.t) : t =
account_with_hash { account.data with token_id }

Expand Down Expand Up @@ -4455,7 +4461,7 @@ module For_tests = struct
, `Txn_commitment commitment
, `Full_txn_commitment full_commitment )

let deploy_snapp ~constraint_constants (spec : Spec.t) =
let deploy_snapp ?(no_auth = false) ~constraint_constants (spec : Spec.t) =
let `VK vk, `Prover _trivial_prover =
create_trivial_snapp ~constraint_constants ()
in
Expand All @@ -4467,15 +4473,17 @@ module For_tests = struct
|| (spec.new_zkapp_account && List.length spec.zkapp_account_keypairs = 1) ) ;
let update_vk =
let update = spec.snapp_update in
{ update with
verification_key = Zkapp_basic.Set_or_keep.Set vk
; permissions =
Zkapp_basic.Set_or_keep.Set
{ Permissions.user_default with
edit_state = Permissions.Auth_required.Proof
; edit_sequence_state = Proof
}
}
if no_auth then update
else
{ update with
verification_key = Zkapp_basic.Set_or_keep.Set vk
; permissions =
Zkapp_basic.Set_or_keep.Set
{ Permissions.user_default with
edit_state = Permissions.Auth_required.Proof
; edit_sequence_state = Proof
}
}
in
let ( `Parties { Parties.fee_payer; other_parties; memo }
, `Sender_party sender_party
Expand All @@ -4491,16 +4499,20 @@ module For_tests = struct
in
let snapp_parties =
List.map snapp_parties_keypairs ~f:(fun (snapp_party, keypair) ->
let commitment =
if snapp_party.body.use_full_commitment then full_commitment
else commitment
in
let signature =
Signature_lib.Schnorr.Chunked.sign keypair.private_key
(Random_oracle.Input.Chunked.field commitment)
in
( { body = snapp_party.body; authorization = Signature signature }
: Party.Wire.t ) )
if no_auth then
( { body = snapp_party.body; authorization = None_given }
: Party.Wire.t )
else
let commitment =
if snapp_party.body.use_full_commitment then full_commitment
else commitment
in
let signature =
Signature_lib.Schnorr.Chunked.sign keypair.private_key
(Random_oracle.Input.Chunked.field commitment)
in
( { body = snapp_party.body; authorization = Signature signature }
: Party.Wire.t ) )
in
let other_parties = Option.to_list sender_party @ snapp_parties in
let parties : Parties.t =
Expand Down
3 changes: 2 additions & 1 deletion src/lib/transaction_snark/transaction_snark.mli
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,8 @@ module For_tests : sig
end

val deploy_snapp :
constraint_constants:Genesis_constants.Constraint_constants.t
?no_auth:bool
-> constraint_constants:Genesis_constants.Constraint_constants.t
-> Spec.t
-> Parties.t

Expand Down