前序博客有:
- Mina的支付流程
Mina中目前的交易类型主要有:
- Coinbase交易:给产块者激励和手续费的交易,为内部交易。
- Fee_transfer交易:给snark worker手续费的交易,为内部交易。
- Payment交易:支付交易,为用户交易。
- Stake_delegation交易:委托交易,为用户交易。
- Parties交易:为支持zkApps智能合约创建的交易类型。
在Mina实际代码实现中,Payment交易和Stake_delegation交易共用Signed_command
结构。
- Payment交易示例为:
{
"data": [
"Signed_command",
{
"payload": {
"common": {
"fee": "0.03",
"fee_token": "1",
"fee_payer_pk": "B62qoiyAqMVg4hnWFa3mBLsWVJycekKeHLjZi7KdKUDrvdk2o5hyuAe",
"nonce": "0",
"valid_until": "4294967295",
"memo": "E4YVe5YCtgSZuaBo1RiwHFWqtPzV6Eur8xG6JnbzEigit5nZKobQG"
},
"body": [
"Payment",
{
"source_pk": "B62qoiyAqMVg4hnWFa3mBLsWVJycekKeHLjZi7KdKUDrvdk2o5hyuAe",
"receiver_pk": "B62qpWaQoQoPL5AGta7Hz2DgJ9CJonpunjzCGTdw8KiCCD1hX8fNHuR",
"token_id": "1",
"amount": "15270000000"
}
]
},
"signer": "B62qoiyAqMVg4hnWFa3mBLsWVJycekKeHLjZi7KdKUDrvdk2o5hyuAe",
"signature": "7mXTZ6UJj2ZwGzF39ZvU9wnLeDTmHLitwDz7WfVtaXmwToDoVspgauzNKgrgwSzvxxusWsRMgFXyEZ4cTDHKDxmndDYuGLGM"
}
],
"status": [
"Applied",
{
"fee_payer_account_creation_fee_paid": null,
"receiver_account_creation_fee_paid": null,
"created_token": null
},
{
"fee_payer_balance": "0",
"source_balance": "0",
"receiver_balance": "18032234944156559"
}
]
}
- Stake_delegation交易示例为:
{
"data": [
"Signed_command",
{
"payload": {
"common": {
"fee": "0.0101",
"fee_token": "1",
"fee_payer_pk": "B62qp5MgMnCrd2bB8pGpPVmAntym3Qfx3vu7wBWwJ5p9e6eU9srYx9v",
"nonce": "0",
"valid_until": "4294967295",
"memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH"
},
"body": [
"Stake_delegation",
[
"Set_delegate",
{
"delegator": "B62qp5MgMnCrd2bB8pGpPVmAntym3Qfx3vu7wBWwJ5p9e6eU9srYx9v",
"new_delegate": "B62qns9cPvDwckhJXHpWZZ8b8T8oUgoF4Enpax5zNVBYYMtQwHf4Cmp"
}
]
]
},
"signer": "B62qp5MgMnCrd2bB8pGpPVmAntym3Qfx3vu7wBWwJ5p9e6eU9srYx9v",
"signature": "7mX2tu8ZxQbYkYWCCUKK6CpWhsAtPtiBWYkaa2BuJLJt4p934TfspRi7End8RKm5MBWXpqXGP4WxLv2NNjUPC1UiPM6KvnRn"
}
],
"status": [
"Applied",
{
"fee_payer_account_creation_fee_paid": null,
"receiver_account_creation_fee_paid": null,
"created_token": null
},
{
"fee_payer_balance": "90989900000",
"source_balance": "90989900000",
"receiver_balance": "66002906100000"
}
]
}
区块内成功的交易标记为“Applied”,失败的交易标记为“Failed”,并会附上相应的失败原因,如“Amount_insufficient_to_create_account”:
{
"data": [
"Signed_command",
{
"payload": {
"common": {
"fee": "0.2001",
"fee_token": "1",
"fee_payer_pk": "B62qqscHMyaJrYW938bUEEWKGJRz7yzbd9HbWj6Ja1Aep2y75RwnnBi",
"nonce": "0",
"valid_until": "4294967295",
"memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH"
},
"body": [
"Payment",
{
"source_pk": "B62qqscHMyaJrYW938bUEEWKGJRz7yzbd9HbWj6Ja1Aep2y75RwnnBi",
"receiver_pk": "B62qrby8tq1SQGMzjwHiHJYupC1XtSy9XfwEELbJzuWrFWG1YNZXsC1",
"token_id": "1",
"amount": "100000000"
}
]
},
"signer": "B62qqscHMyaJrYW938bUEEWKGJRz7yzbd9HbWj6Ja1Aep2y75RwnnBi",
"signature": "7mXXAdE3L4kYfcDgA17cKp85wu2BQgdiEPgaALZ5PQTb9zvhr81vbRsn2h4YUoQY888Cbez2NBU2rEaq1eV6BDrd8o2WYANG"
}
],
"status": [
"Failed",
[
"Amount_insufficient_to_create_account"
],
{
"fee_payer_balance": "25370955635",
"source_balance": "25370955635",
"receiver_balance": null
}
]
}
本文重点关注Payment支付交易的 snark流程。
在Mina中,引入了snark worker来对交易进行snark:
Mina的实际代码实现中,其snark_worker模块主要提供了2个异步RPC接口:
- get_work:snark worker通过
get_work
RPC接口获取待生成snark的work。 - submit_work:完成snark的work通过
submit_work
RPC接口广播到网络。
Mina给新账号转账需要从收款方扣除1MINA的费用,具体逻辑为:apply_transaction/apply_user_command
->apply_user_command_unchecked
中有:
(* Charge the account creation fee. *)
let%bind receiver_amount =
match receiver_location with
| `Existing _ ->
return amount
| `New ->
(* Subtract the creation fee from the transaction amount. *)
sub_account_creation_fee ~constraint_constants `Added amount
|> Result.map_error ~f:(fun _ ->
Transaction_status.Failure
.Amount_insufficient_to_create_account )
in
let%map receiver_account =
incr_balance receiver_account receiver_amount
in ........
2. Signed_command基本结构
Signed_command
基本结构为:
( Payload.Stable.V2.t
, Public_key.Stable.V1.t
, Signature.Stable.V1.t )
Poly.Stable.V1.t
type ('payload, 'pk, 'signature) t =
{ payload : 'payload; signer : 'pk; signature : 'signature }
(* payload结构为: *)
type t = (Common.Stable.V2.t, Body.Stable.V2.t) Poly.Stable.V1.t
type ('common, 'body) t = { common : 'common; body : 'body }
(* payload.common结构为: *)
type t =
( Currency.Fee.Stable.V1.t (* 为Unsigned.UInt64.t *)
, Public_key.Compressed.Stable.V1.t
, Account_nonce.Stable.V1.t
, Global_slot.Stable.V1.t
, Memo.Stable.V1.t )
Poly.Stable.V2.t
type ('fee, 'public_key, 'nonce, 'global_slot, 'memo) t =
{ fee : 'fee (* 为Unsigned.UInt64.t *)
; fee_payer_pk : 'public_key
; nonce : 'nonce
; valid_until : 'global_slot
; memo : 'memo
}
(* payload.body结构为: *)
type t = Binable_arg.Stable.V2.t =
| Payment of Payment_payload.Stable.V2.t
| Stake_delegation of Stake_delegation.Stable.V1.t
(* payload.body为Payment时的结构为: *)
type t =
(Public_key.Compressed.Stable.V1.t, Amount.Stable.V1.t) Poly.Stable.V2.t
type ('public_key, 'amount) t =
{ source_pk : 'public_key; receiver_pk : 'public_key; amount : 'amount }
3. Snark worker生成交易snark流程
3.1 Snark worker生成交易snark关键结构
-
1)
of_non_parties_transaction
函数中的transaction_in_block参数,为Transaction.Valid.t Transaction_protocol_state.t类型:Transaction.Valid.t Transaction_protocol_state.t type 'a t = { transaction : 'a; block_data : Block_data.Stable.V2.t } (* transaction类型,Transaction.Valid.t为: *) type t = User_command.Valid.Stable.V2.t Poly.Stable.V2.t type 'command t = | Command of 'command | Fee_transfer of Fee_transfer.Stable.V2.t | Coinbase of Coinbase.Stable.V1.t (* 本文只考虑Command类型,User_command.Valid.Stable.V2.t : *) type t = ( Signed_command.With_valid_signature.Stable.V2.t , Parties.Valid.Stable.V1.t ) Poly.Stable.V2.t type ('u, 's) t = Signed_command of 'u | Parties of 's (* 本文只考虑Signed_command类型, Signed_command.With_valid_signature.Stable.V2.t: *) type t = Stable.V2.t (* 对应为上面第2节的Signed_command结构。 *) (* block_data类型, Block_data.Stable.V2.t为:*) type t = Mina_state.Protocol_state.Body.Value.Stable.V2.t type t = (* 为protocol_state结构 *) ( State_hash.Stable.V1.t , Blockchain_state.Value.Stable.V2.t , Consensus.Data.Consensus_state.Value.Stable.V1.t , Protocol_constants_checked.Value.Stable.V1.t ) Poly.Stable.V1.t type ('state_hash, 'blockchain_state, 'consensus_state, 'constants) t = { genesis_state_hash : 'state_hash ; blockchain_state : 'blockchain_state ; consensus_state : 'consensus_state ; constants : 'constants }
-
2)
perform
函数中有参数({ instances; fee } as spec : Work.Spec.t)
:【perform_single
中有一个隐含参数“single: single_spec”,为Work.Single.Spec.t类型。】(* Work.Spec.t结构为: *) type t = Single.Spec.t Work.Spec.t type 'single t = 'single Stable.Latest.t = { instances : 'single One_or_two.t; fee : Currency.Fee.t } (* fee为 Unsigned.UInt64.t *) (* 其中instances类型,One_or_two.t为: *) type 'a t = [ `One of 'a | `Two of 'a * 'a ] (* Single.Spec.t结构为: *) type t = (Transaction_witness.t, Ledger_proof.t) Work.Single.Spec.t (* Work.Single.Spec.t类型为: *) type ('witness, 'ledger_proof) t = ('witness, 'ledger_proof) Stable.Latest.t = | Transition of Transaction_snark.Statement.t * 'witness | Merge of Transaction_snark.Statement.t * 'ledger_proof * 'ledger_proof
本文只考虑“Transition”情况,其中
Transaction_witness.t
结构为:(* 本文只考虑“Transition”情况: *) ( * Transition_witness.t结构为: *) type t = { transaction : Mina_transaction.Transaction.Stable.V2.t (* 本文只考虑Signed_command类型交易 *) ; ledger : Mina_ledger.Sparse_ledger.Stable.V2.t (* 包含indexes(由account_id和int组成的数组)、depth(int结构)、tree(由ledger_hash和account组成的数结构)三个字段。 *) ; protocol_state_body : Mina_state.Protocol_state.Body.Value.Stable.V2.t (* 包含genesis_state_hash/blockchain_state/consensus_state/constants这4个字段。 *) ; init_stack : Mina_base.Pending_coinbase.Stack_versioned.Stable.V1.t (* data为Field.t,state(包含init/curr)也均为Field.t *) ; status : Mina_base.Transaction_status.Stable.V2.t (* 表示交易状态,为Appplied或Failed枚举类型。 *) } (*《1》 transaction类型,Mina_transaction.Transaction.Stable.V2.t结构为: *) type t = User_command.Stable.V2.t Poly.Stable.V2.t (* 见上面的1),本文只考虑Signed_command类型,见上面第2节。 *) (* 《2》ledger类型,Mina_ledger.Sparse_ledger.Stable.V2.t 结构为:*) type t = ( Ledger_hash.Stable.V1.t (* 为哈希值,Field.t *) , Account_id.Stable.V2.t (* 为压缩公钥与Pickles.Backend.Tick.Field.Stable.V1.t组合,Public_key.Compressed.Stable.V1.t * Digest.Stable.V1.t *) , Account.Stable.V2.t ) (* Account结构中包含了:public_key/token_id/token_permissions/token_symbol/balance/nonce/receipt_chain_hash/delegate/voting_for/timing/permissions/zkapp/zkapp_uri字段 *) Sparse_ledger_lib.Sparse_ledger.T.Stable.V2.t type ('hash, 'key, 'account) t = { indexes : ('key * int) list (* indexes为由account_id和int组成的数组 *) ; depth : int ; tree : ('hash, 'account) Tree.Stable.V1.t (* 由ledger_hash和account组成的树结构。 *) } (* tree结构,('hash, 'account) Tree.Stable.V1.t 为: *) type ('hash, 'account) t = (* 为树结构,节点递归 *) | Account of 'account | Hash of 'hash | Node of 'hash * ('hash, 'account) t * ('hash, 'account) t (* Account.Stable.V2.t结构为: *) type t = ( Public_key.Compressed.Stable.V1.t (* 为压缩公钥 *) , Token_id.Stable.V1.t (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) , Token_permissions.Stable.V1.t (* 分为Token_owned和Not_owned两种枚举类型,分别有disable_new_accounts和account_disabled bool字段。 *) , Token_symbol.Stable.V1.t (* 为string *) , Balance.Stable.V1.t (* 为Unsigned.UInt64.t *) , Nonce.Stable.V1.t (* 为Unsigned_extended.UInt32.t *) , Receipt.Chain_hash.Stable.V1.t (* 为哈希值,Field.t *) , Public_key.Compressed.Stable.V1.t option (* 为option,压缩公钥 *) , State_hash.Stable.V1.t (* 为哈希值,Field.t *) , Timing.Stable.V1.t (* 分为Untimed和Timed两种类型,其中Timed包含:initial_minimum_balance(为Unsigned.UInt64.t类型)、cliff_time(为global_slot,Unsigned_extended.UInt32.t类型)、cliff_amount(为Unsigned.UInt64.t类型)、vesting_period(为global_slot,Unsigned_extended.UInt32.t类型)、vesting_increment(为Unsigned.UInt64.t类型)这5个字段。 *) , Permissions.Stable.V2.t (* 包含edit_state、send、receive、set_delegate、set_permissions、set_verification_key、set_zkapp_uri、edit_sequence_state、set_token_symbol、increment_nonce、set_voting_for这11个字段,均为Auth_required枚举类型——None | Either | Proof | Signature | Impossible。 *) , Zkapp_account.Stable.V2.t option (* 为option,Zkapp_account包含:app_state(为Zkapp_basic.F.Stable.V1.t Vector.Vector_8.Stable.V1.t类型,为8 fields of 32 bytes each of arbitrary storage。)、verification_key(为option,包含data和hash2个字段,其中data为Side_loaded_verification_key类型)、zkapp_version(版本信息,为Unsigned_extended.UInt32.t类型)、sequence_state(为Pickles_types.Vector.Vector_5.Stable.V1.t为5 fields of 32 bytes each of arbitrary storage。)、last_sequqnece_slot(为global_slot,Unsigned_extended.UInt32.t类型)、proved_state(为bool值)这6个字段。 *) , string ) (* TODO: Cache the digest of this? *) Poly.Stable.V2.t type ( 'pk (* 为压缩公钥 *) , 'id (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) , 'token_permissions (* 分为Token_owned和Not_owned两种枚举类型,分别有disable_new_accounts和account_disabled bool字段。 *) , 'token_symbol (* 为string *) , 'amount (* 为Unsigned.UInt64.t *) , 'nonce (* 为Unsigned_extended.UInt32.t *) , 'receipt_chain_hash (* 为哈希值,Field.t *) , 'delegate (* 为option,压缩公钥 *) , 'state_hash (* 为哈希值,Field.t *) , 'timing (* 分为Untimed和Timed两种类型,其中Timed包含:initial_minimum_balance(为Unsigned.UInt64.t类型)、cliff_time(为global_slot,Unsigned_extended.UInt32.t类型)、cliff_amount(为Unsigned.UInt64.t类型)、vesting_period(为global_slot,Unsigned_extended.UInt32.t类型)、vesting_increment(为Unsigned.UInt64.t类型)这5个字段。 *) , 'permissions (* 包含edit_state、send、receive、set_delegate、set_permissions、set_verification_key、set_zkapp_uri、edit_sequence_state、set_token_symbol、increment_nonce、set_voting_for这11个字段,均为Auth_required枚举类型——None | Either | Proof | Signature | Impossible。 *) , 'zkapp_opt (* 为option,Zkapp_account包含:app_state(为Zkapp_basic.F.Stable.V1.t Vector.Vector_8.Stable.V1.t类型,为8 fields of 32 bytes each of arbitrary storage。)、verification_key(为option,包含data和hash2个字段,其中data为Side_loaded_verification_key类型)、zkapp_version(版本信息,为Unsigned_extended.UInt32.t类型)、sequence_state(为Pickles_types.Vector.Vector_5.Stable.V1.t为5 fields of 32 bytes each of arbitrary storage.。)、last_sequqnece_slot(为global_slot,Unsigned_extended.UInt32.t类型)、proved_state(为bool值)这6个字段。 *) , 'zkapp_uri ) (* 为string *) t = { public_key : 'pk (* 为压缩公钥 *) ; token_id : 'id (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) ; token_permissions : 'token_permissions (* 分为Token_owned和Not_owned两种枚举类型,分别有disable_new_accounts和account_disabled bool字段。 *) ; token_symbol : 'token_symbol (* 为string *) ; balance : 'amount (* 为Unsigned.UInt64.t *) ; nonce : 'nonce (* 为Unsigned_extended.UInt32.t *) ; receipt_chain_hash : 'receipt_chain_hash (* 为哈希值,Field.t *) ; delegate : 'delegate (* 为option,压缩公钥 *) ; voting_for : 'state_hash (* 为哈希值,Field.t *) ; timing : 'timing (* 分为Untimed和Timed两种类型,其中Timed包含:initial_minimum_balance(为Unsigned.UInt64.t类型)、cliff_time(为global_slot,Unsigned_extended.UInt32.t类型)、cliff_amount(为Unsigned.UInt64.t类型)、vesting_period(为global_slot,Unsigned_extended.UInt32.t类型)、vesting_increment(为Unsigned.UInt64.t类型)这5个字段。 *) ; permissions : 'permissions (* 包含edit_state、send、receive、set_delegate、set_permissions、set_verification_key、set_zkapp_uri、edit_sequence_state、set_token_symbol、increment_nonce、set_voting_for这11个字段,均为Auth_required枚举类型——None | Either | Proof | Signature | Impossible。 *) ; zkapp : 'zkapp_opt (* 为option,Zkapp_account包含:app_state(为Zkapp_basic.F.Stable.V1.t Vector.Vector_8.Stable.V1.t类型,为8 fields of 32 bytes each of arbitrary storage。)、verification_key(为option,包含data和hash2个字段,其中data为Side_loaded_verification_key类型)、zkapp_version(版本信息,为Unsigned_extended.UInt32.t类型)、sequence_state(为Pickles_types.Vector.Vector_5.Stable.V1.t为5 fields of 32 bytes each of arbitrary storage.。)、last_sequqnece_slot(为global_slot,Unsigned_extended.UInt32.t类型)、proved_state(为bool值)这6个字段。 *) ; zkapp_uri : 'zkapp_uri (* 为string *) } (* 《3》protocol_state_body结构,Mina_state.Protocol_state.Body.Value.Stable.V2.t为: *) type t = ( State_hash.Stable.V1.t (* 为哈希值,Field.t *) , Blockchain_state.Value.Stable.V2.t (* 包含staged_ledger_hash/genesis_ledger_hash/registers/timestamp四个字段。 *) , Consensus.Data.Consensus_state.Value.Stable.V1.t (* 包含blockchain_length/epoch_count/min_window_density/sub_window_densities/last_vrf_output/total_currency/curr_global_slot/global_slot_since_genesis/staking_epoch_data/next_epoch_data//has_ancestor_in_same_checkpoint_window/block_stake_winner/block_creator/coinbase_receiver/supercharge_coinbase这15个字段。 *) , Protocol_constants_checked.Value.Stable.V1.t ) (* 包含k/slots_per_epoch/slots_per_sub_window/delta/genesis_state_timestamp这5个字段。 *) Poly.Stable.V1.t type ('state_hash, 'blockchain_state, 'consensus_state, 'constants) t = { genesis_state_hash : 'state_hash (* 为哈希值,Field.t *) ; blockchain_state : 'blockchain_state (* 包含staged_ledger_hash/genesis_ledger_hash/registers/timestamp四个字段。 *) ; consensus_state : 'consensus_state (* 包含blockchain_length/epoch_count/min_window_density/sub_window_densities/last_vrf_output/total_currency/curr_global_slot/global_slot_since_genesis/staking_epoch_data/next_epoch_data//has_ancestor_in_same_checkpoint_window/block_stake_winner/block_creator/coinbase_receiver/supercharge_coinbase这15个字段。 *) ; constants : 'constants (* 包含k/slots_per_epoch/slots_per_sub_window/delta/genesis_state_timestamp这5个字段。 *) } (* 其中protocol_state_body.blockchain_state字段为Blockchain_state.Value.Stable.V2.t结构: *) type t = ( Staged_ledger_hash.Stable.V1.t (*staged_ledger_hash由2部分组成:pending_coinbase_hash和non_snark(再包含ledger_hash/aux_hash/pending_coinbase_aux三个字段)。*) , Frozen_ledger_hash.Stable.V1.t (* 为Field.t *) , Local_state.Stable.V1.t (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) , Block_time.Stable.V1.t ) (* 为Unsigned.UInt64.t *) Poly.Stable.V2.t type ('staged_ledger_hash, 'snarked_ledger_hash, 'local_state, 'time) t = { staged_ledger_hash : 'staged_ledger_hash (*staged_ledger_hash由2部分组成:pending_coinbase_hash和non_snark(再包含ledger_hash/aux_hash/pending_coinbase_aux三个字段)。*) ; genesis_ledger_hash : 'snarked_ledger_hash (* 为Field.t *) ; registers : ('snarked_ledger_hash, unit, 'local_state) Registers.Stable.V1.t (* 包含了ledger(为哈希值)、pending_coinbase_stack(为int)、local_state三个字段。 *) ; timestamp : 'time (* 为Unsigned.UInt64.t *) } (* Registers.Stable.V1.t结构为: *) type ('ledger, 'pending_coinbase_stack, 'local_state) t = { ledger : 'ledger (* 为哈希值。 *) ; pending_coinbase_stack : 'pending_coinbase_stack (* 为uint。 *) ; local_state : 'local_state (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) } (* protocol_state_body.blockchain_state.staged_ledger_hash字段为,Staged_ledger_hash.Stable.V1.t结构:*) (** Staged ledger hash has two parts 1) merkle root of the pending coinbases 2) ledger hash, aux hash, and the FIFO order of the coinbase stacks(Non snark). Only part 1 is required for blockchain snark computation and therefore the remaining fields of the staged ledger are grouped together as "Non_snark" *) type t = ( Non_snark.Stable.V1.t (* 包含ledger_hahs、aux_hash和pending_coinbase_aux 三个字段。 *) , Pending_coinbase.Hash_versioned.Stable.V1.t ) (* 为哈希值,Field.t。 *) Poly.Stable.V1.t type ('non_snark, 'pending_coinbase_hash) t = { non_snark : 'non_snark (* 包含ledger_hahs、aux_hash和pending_coinbase_aux 三个字段。 *) ; pending_coinbase_hash : 'pending_coinbase_hash (* 为哈希值,Field.t。 *) } (* non_snark, Non_snark.Stable.V1.t结构为:*) type t = { ledger_hash : Ledger_hash.Stable.V1.t (* 为哈希值,Field.t *) ; aux_hash : Aux_hash.Stable.V1.t (* 为哈希值,string *) ; pending_coinbase_aux : Pending_coinbase_aux.Stable.V1.t (* 为string *) } (* consensus_state字段为,Consensus.Data.Consensus_state.Value.Stable.V1.t: *) type t = ( Length.Stable.V1.t , Vrf.Output.Truncated.Stable.V1.t , Amount.Stable.V1.t , Global_slot.Stable.V1.t , Mina_numbers.Global_slot.Stable.V1.t , Epoch_data.Staking_value_versioned.Value.Stable.V1.t , Epoch_data.Next_value_versioned.Value.Stable.V1.t , bool , Public_key.Compressed.Stable.V1.t ) Poly.Stable.V1.t type ( 'length , 'vrf_output , 'amount , 'global_slot , 'global_slot_since_genesis , 'staking_epoch_data , 'next_epoch_data , 'bool , 'pk ) t = { blockchain_length : 'length ; epoch_count : 'length ; min_window_density : 'length ; sub_window_densities : 'length list ; last_vrf_output : 'vrf_output ; total_currency : 'amount ; curr_global_slot : 'global_slot ; global_slot_since_genesis : 'global_slot_since_genesis ; staking_epoch_data : 'staking_epoch_data ; next_epoch_data : 'next_epoch_data ; has_ancestor_in_same_checkpoint_window : 'bool ; block_stake_winner : 'pk ; block_creator : 'pk ; coinbase_receiver : 'pk ; supercharge_coinbase : 'bool } (* 《4》 init_stack 字段,为 Mina_base.Pending_coinbase.Stack_versioned.Stable.V1.t结构: *) type t = (Coinbase_stack.Stable.V1.t, State_stack.Stable.V1.t) Poly.Stable.V1.t type ('data_stack, 'state_stack) t = { data : 'data_stack; state : 'state_stack } (* data为Field.t,state(包含init/curr)也均为Field.t *)
本文只考虑“Transition”情况,其中
Transaction_snark.Statement.t
结构为:type t = ( Frozen_ledger_hash.Stable.V1.t (* 为Field.t,即为哈希值。 *) , Currency.Amount.Stable.V1.t (* 为Unsigned.UInt64.t *) , Pending_coinbase.Stack_versioned.Stable.V1.t (* data为Field.t,state中的init和curr均为哈希值。 *) , Fee_excess.Stable.V1.t (* 包含fee_token_l/fee_excess_l/fee_token_r/fee_excess_r 四个字段。 *) , unit , Local_state.Stable.V1.t ) (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) Poly.Stable.V2.t (* Poly.Stable.V2.t结构为: *) type ( 'ledger_hash (* 为哈希值。 *) , 'amount (* 为Unsigned.UInt64.t *) , 'pending_coinbase (* data为Field.t,state中的init和curr均为哈希值。 *) , 'fee_excess (* 包含fee_token_l/fee_excess_l/fee_token_r/fee_excess_r 四个字段。 *) , 'sok_digest (* 为uint。 *) , 'local_state ) (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) t = { source : ( 'ledger_hash (* 为哈希值。 *) , 'pending_coinbase (* data为Field.t,state中的init和curr均为哈希值。 *) , 'local_state ) (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) Registers.Stable.V1.t ; target : ( 'ledger_hash (* 为哈希值。 *) , 'pending_coinbase (* data为Field.t,state中的init和curr均为哈希值。 *) , 'local_state ) (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) Registers.Stable.V1.t ; supply_increase : 'amount (* 为Unsigned.UInt64.t *) ; fee_excess : 'fee_excess (* 包含fee_token_l/fee_excess_l/fee_token_r/fee_excess_r 四个字段。 *) ; sok_digest : 'sok_digest (* 为uint。 *) } (* Registers.Stable.V1.t结构为: *) type ('ledger, 'pending_coinbase_stack, 'local_state) t = { ledger : 'ledger (* 为哈希值。 *) ; pending_coinbase_stack : 'pending_coinbase_stack (* data为Field.t,state中的init和curr均为哈希值。 *) ; local_state : 'local_state (* 包含stack_frame/call_stack/transaction_commitment/full_transaction_commitment/token_id/excess/ledger/success/failure_status_tbl字段。 *) }
其中
Pending_coinbase.Stack_versioned.Stable.V1.t
结构为:(* Pending_coinbase.Stack_versioned.Stable.V1.t结构为: *) type t = (Coinbase_stack.Stable.V1.t, State_stack.Stable.V1.t) Poly.Stable.V1.t type ('data_stack, 'state_stack) t = { data : 'data_stack; state : 'state_stack } (* data类型,Coinbase_stack.Stable.V1.t为:Field.t *) (* state类型, State_stack.Stable.V1.t为: *) type t = Stack_hash.Stable.V1.t Poly.Stable.V1.t type 'stack_hash t = { init : 'stack_hash; curr : 'stack_hash } (* 其中init/curr均为 Stack_hash.Stable.V1.t ,本质即为Filed.t,即为哈希值 *)
其中
Fee_excess.Stable.V1.t
结构为:(* Fee_excess.Stable.V1.t 结构为: *) type t = ( Token_id.Stable.V1.t (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) , (Fee.Stable.V1.t, Sgn.Stable.V1.t) Signed_poly.Stable.V1.t ) (* 其中的magnitude字段为Unsigned.UInt64.t,sgn字段为枚举类型Pos|Neg *) Poly.Stable.V1.t (* 包含fee_token_l/fee_excess_l/fee_token_r/fee_excess_r 四个字段。 *) (* Poly.Stable.V1.t结构为: *) type ('token, 'fee) t = { fee_token_l : 'token ; fee_excess_l : 'fee ; fee_token_r : 'token ; fee_excess_r : 'fee } (* Signed_poly.Stable.V1.t 结构为: *) type ('magnitude, 'sgn) t = { magnitude : 'magnitude; sgn : 'sgn } (* 其中magnitude类型,Fee.Stable.V1.t为:Unsigned.UInt64.t *) (* 其中sgn类型, Sgn.Stable.V1.t枚举类型Pos|Neg:*) type t = Sgn_type.Sgn.Stable.V1.t = Pos | Neg
其中
Local_state.Stable.V1.t
结构为:【src/lib/transaction_logic/parties_logic.ml中的Local_state模块有:】type t = ( Mina_base.Stack_frame.Digest.Stable.V1.t (* 为哈希值,Kimchi_backend.Pasta.Basic.Fp.Stable.V1.t *) , Mina_base.Call_stack_digest.Stable.V1.t (* 为哈希值,Zkapp_basic.F.t *) , Token_id.Stable.V1.t (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) , ( Currency.Amount.Stable.V1.t (* 为Unsigned.UInt64.t *) , Sgn.Stable.V1.t ) (* 为枚举类型Pos|Neg *) Currency.Signed_poly.Stable.V1.t (* 其中的magnitude字段为Unsigned.UInt64.t,sgn字段为枚举类型Pos|Neg *) , Ledger_hash.Stable.V1.t (* 为哈希值,Field.t *) , bool , Parties.Transaction_commitment.Stable.V1.t (* 为F.t *) , Transaction_status.Failure.Collection.Stable.V1.t ) (* 为表示交易失败原因的二维数组。 *) Stable.V1.t type ( 'stack_frame (* 为哈希值,Kimchi_backend.Pasta.Basic.Fp.Stable.V1.t *) , 'call_stack (* 为哈希值,Zkapp_basic.F.t *) , 'token_id (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) , 'excess (* 其中的magnitude字段为Unsigned.UInt64.t,sgn字段为枚举类型Pos|Neg *) , 'ledger (* 为哈希值,Field.t *) , 'bool , 'comm (* 为F.t *) , 'failure_status_tbl ) (* 为表示交易失败原因的二维数组。 *) t = { stack_frame : 'stack_frame (* 为哈希值,Kimchi_backend.Pasta.Basic.Fp.Stable.V1.t *) ; call_stack : 'call_stack (* 为哈希值,Zkapp_basic.F.t *) ; transaction_commitment : 'comm (* 为F.t *) ; full_transaction_commitment : 'comm (* 为F.t *) ; token_id : 'token_id (* 为Pickles.Backend.Tick.Field.Stable.V1.t,对应Tick曲线的scalar域 *) ; excess : 'excess (* 其中的magnitude字段为Unsigned.UInt64.t,sgn字段为枚举类型Pos|Neg *) ; ledger : 'ledger (* 为哈希值,Field.t *) ; success : 'bool ; failure_status_tbl : 'failure_status_tbl (* 为表示交易失败原因的二维数组。 *) }
-
3)
perform_single
中的参数message:Mina_base.Sok_message.t
:【其中的“fee”表示snark worker对其提供的snark work的定价;“prover”为snark worker用于生成交易snark的公钥。】(* Sok_message.t类型为: *) type t = { fee : Currency.Fee.Stable.V1.t (* 为Unsigned.UInt64.t *) ; prover : Public_key.Compressed.Stable.V1.t )(* 为snark worker用于生成交易snark的公钥。 *) }
-
4)
perform_single
中参数({ m; cache; proof_level } : Worker_state.t)
,为Worker_state.t类型:module type S = Transaction_snark.S type t = { m : (module S) option (* 为调用Transaction_snark.Make返回的module *) ; cache : Cache.t (* cache缓存,key为statement,value为调用`Transaction_snark.S相应函数`返回的相应proof *) ; proof_level : Genesis_constants.Proof_level.t (* 只考虑为Full的情况 *) }
Snark worker生成交易snark流程为:
- 1)
src/lib/snark_worker/prod.ml
中的perform_single
|| \/let perform (s : Worker_state.t) public_key ({ instances; fee } as spec : Work.Spec.t) = One_or_two.Deferred_result.map instances ~f:(fun w -> let open Deferred.Or_error.Let_syntax in let%map proof, time = perform_single s ~message:(Mina_base.Sok_message.create ~fee ~prover:public_key) w in ( proof , (time, match w with Transition _ -> `Transition | Merge _ -> `Merge) ) ) val perform_single : Worker_state.t -> message:Mina_base.Sok_message.t -> (Transaction_witness.t, Ledger_proof.t) Work.Single.Spec.t (* 对应隐含参数“single: signle_spec” *) -> (Ledger_proof.t * Time.Span.t) Deferred.Or_error.t let perform_single ({ m; cache; proof_level } : Worker_state.t) ~message = (* 其中还有一个隐含参数“single: single_spec”,为Work.Single.Spec.t类型。 *)
- 2)只考虑“Work.Single.Spec.Transition”类型,不考虑“Merge”类型。 || \/
- 3)若交易是“Parties”类型,则额外处理;否则,对于“Signed_commnad”/“Fee_transfer”/"Coinbase"类型的交易,同一调用Transaction_snark的
of_non_parties_transaction
函数来生成相应的proof,会将proof添加到cache中。【其中statement
和input
为Transaction_snark.Statement.t类型(包含source、target、supply_increase、fee_excess、sok_digest五个字段),message
为Sok_message.t类型(包含snark worker定价的fee和作为prover的公钥),sok_digest
为对Sok_message类型message的哈希值。w
为Transaction_witness.t类型(包含transaction、ledger、protocol_state_body、init_stack和status五个字段)。init_stack
为 Mina_base.Pending_coinbase.Stack_versioned.Stable.V1.t 类型(其中data字段为Field.t,state字段(包含init/curr)也均为Field.t)。】
取M.of_non_parties_transaction ~statement:{ input with sok_digest } { Transaction_protocol_state.Poly.transaction = t ; block_data = w.protocol_state_body } (* 为transaction_in_block参数,为Transaction.Valid.t Transaction_protocol_state.t类型 *) ~init_stack:w.init_stack (unstage (Mina_ledger.Sparse_ledger.handler w.ledger) ) val of_non_parties_transaction : statement:Statement.With_sok.t -> init_stack:Pending_coinbase.Stack.t -> Transaction.Valid.t Transaction_protocol_state.t -> Tick.Handler.t -> t Async.Deferred.t let of_non_parties_transaction ~statement ~init_stack transaction_in_block handler =
transaction_in_block.transaction
为transaction
(为Mina_transaction.Transaction.Stable.V2.t类型(本文只考虑Signed_command类型交易)),取transaction_in_block.block_data
为state_body
(为Mina_state.Protocol_state.Body.Value.Stable.V2.t类型 (包含genesis_state_hash/blockchain_state/consensus_state/constants这4个字段。))。 || \/ - 4)对
transaction
仍然调用Transaction_union.of_transaction
,结果仍命名为transaction
——目的是:恢复Signed_command结构,会在common内插入fee_token = Token_id.default
字段,在body内插入token_id = Token_id.default
字段。 || \/ - 5)调用
of_transation_union
,返回{statement; proof}
(为Ledger_proof.t结构(等价为Transaction_snark.t结构),详细见Mina中的ledger proof)。其中proof是调用Transaction_snark compile时返回的base函数。let%map proof = base [] (* 对应pickles.compile时的wrap函数的prevs参数。 *) ~handler: (Base.transaction_union_handler handler transaction state_body init_stack ) statement (* 对应pickles.compile时的wrap函数的next_state参数。 *)
- 6)Base.rule的约束内容为:【即生成交易snark的约束系统】
其中(* Someday: write the following soundness tests: - apply a transaction where the signature is incorrect - apply a transaction where the sender does not have enough money in their account - apply a transaction and stuff in the wrong target hash *) (* spec for [main statement]: constraints pass iff there exists t : Tagged_transaction.t such that - applying [t] to ledger with merkle hash [l1] results in ledger with merkle hash [l2]. - applying [t] to [pc.source] with results in pending coinbase stack [pc.target] - t has fee excess equal to [fee_excess] - t has supply increase equal to [supply_increase] where statement includes l1 : Frozen_ledger_hash.t, l2 : Frozen_ledger_hash.t, fee_excess : Amount.Signed.t, supply_increase : Amount.t pc: Pending_coinbase_stack_state.t *) let%snarkydef main ~constraint_constants (statement : Statement.With_sok.Checked.t) = let%bind () = dummy_constraints () in let%bind (module Shifted) = Tick.Inner_curve.Checked.Shifted.create () in let%bind t = (* 为transaction,Signed_command结构 *) with_label __LOC__ (exists Transaction_union.typ ~request:(As_prover.return Transaction)) in let%bind pending_coinbase_init = (* 为init_stack,为 Mina_base.Pending_coinbase.Stack_versioned.Stable.V1.t 类型(其中data字段为Field.t,state字段(包含init/curr)也均为Field.t) *) exists Pending_coinbase.Stack.typ ~request:(As_prover.return Init_stack) in let%bind state_body =(* 为state_body,为Mina_state.Protocol_state.Body.Value.Stable.V2.t类型 (包含genesis_state_hash/blockchain_state/consensus_state/constants这4个字段。*) exists (Mina_state.Protocol_state.Body.typ ~constraint_constants) ~request:(As_prover.return State_body) in let%bind root_after, fee_excess, supply_increase = apply_tagged_transaction ~constraint_constants (module Shifted) statement.source.ledger pending_coinbase_init statement.source.pending_coinbase_stack statement.target.pending_coinbase_stack state_body t in let%bind fee_excess = (* Use the default token for the fee excess if it is zero. This matches the behaviour of [Fee_excess.rebalance], which allows [verify_complete_merge] to verify a proof without knowledge of the particular fee tokens used. *) let%bind fee_excess_zero = Amount.Signed.Checked.equal fee_excess Amount.Signed.(Checked.constant zero) in let%map fee_token_l = make_checked (fun () -> Token_id.Checked.if_ fee_excess_zero ~then_:Token_id.(Checked.constant default) ~else_:t.payload.common.fee_token ) in { Fee_excess.fee_token_l ; fee_excess_l = Amount.Signed.Checked.to_fee fee_excess ; fee_token_r = Token_id.(Checked.constant default) ; fee_excess_r = Fee.Signed.(Checked.constant zero) } in let%bind () = [%with_label "local state check"] (make_checked (fun () -> Local_state.Checked.assert_equal statement.source.local_state statement.target.local_state ) ) in Checked.all_unit [ [%with_label "equal roots"] (Frozen_ledger_hash.assert_equal root_after statement.target.ledger) ; [%with_label "equal supply_increases"] (Currency.Amount.Checked.assert_equal supply_increase statement.supply_increase ) ; [%with_label "equal fee excesses"] (Fee_excess.assert_equal_checked fee_excess statement.fee_excess) ]
apply_tagged_transaction
子约束系统为:【返回(final_root(又名root_after), fee_excess, supply_increase),fee_excess
的目的是为了不泄露具体使用哪种token来支付的手续费。】 【- root:为statement.source.ledger,为哈希值;
- pending_coinbase_stack_init:为Transaction_witness.t中的init_stack,为 Mina_base.Pending_coinbase.Stack_versioned.Stable.V1.t 类型(其中data字段为Field.t,state字段(包含init/curr)也均为Field.t)。
- pending_coinbase_stack_before:为statement.source.pending_coinbase_stack,为Pending_coinbase.Stack_versioned.Stable.V1.t类型,其中data字段为Field.t,state字段中的init和curr均为哈希值。
- pending_coinbase_after:为statement.target.pending_coinbase_stack,为Pending_coinbase.Stack_versioned.Stable.V1.t类型,其中data字段为Field.t,state字段中的init和curr均为哈希值。
- state_body:为Mina_state.Protocol_state.Body.Value.Stable.V2.t类型 (包含genesis_state_hash/blockchain_state/consensus_state/constants这4个字段。
- 将transaction(txn,为Signed_command类型)解析为signer; signature和payload。 】 基本流程为:
- 1)校验交易签名;
- 2)校验交易签名者公钥与交易payload.common中的fee_payer_pk一致;
- 3)fee_token_default:校验交易payload.common中的fee_token是否为Token_id.default,约束其必须为true;
- 4)token_default:校验交易payload.body中的token_id是否为Token_id.default,约束其必须为true;
- 5)若为非create_account交易,payload.body.token_locked必须为false;(支付交易payload.body.token_locked为false)
- 6)调用
compute_as_prover
,当支付交易的receiver account不存在时,是否有足够的费用来支付创建新账号。若失败,原因需为User_command_failure之一。 - 7) 取state_body.consensus_state.global_slot_since_genesis为current_global_slot,要求其小于等于交易payload.common.valid_until,即约束交易未过期。
- 8)约束pending_coinbase_stack_init、pending_coinbase_stack_before、pending_coinbase_after和state_body之间的关系。
- 9)约束若非user_command,user_command_fails必须为false;
- 10)约束要么为create_account交易,否则交易payload.common.fee_payer_pk必须等于payload.body.source_pk;
- 11)调用
modify_account_send
约束系统来更新扣除手续费余额及tree root; - 12)调用
modify_account_recv
约束系统来进行receiver account金额更新以及tree root更新; - 13)调用
modify_account_send
更新发送者账号余额以及tree root; - 14)约束statement.source.local_state与statement.source.local_state相等;
- 15)约束
apply_tagged_transaction
返回的supply_increase与statement.supply_increase相等; - 16)约束
apply_tagged_transaction
返回的fee_excess经组装后,与statement.fee_excess相等; - 17)约束束
apply_tagged_transaction
返回的final_root(又名root_after),与statement.target.ledger相等。
let%snarkydef apply_tagged_transaction ~(constraint_constants : Genesis_constants.Constraint_constants.t) (type shifted) (shifted : (module Inner_curve.Checked.Shifted.S with type t = shifted)) root pending_coinbase_stack_init pending_coinbase_stack_before pending_coinbase_after state_body ({ signer; signature; payload } as txn : Transaction_union.var) = let tag = payload.body.tag in let is_user_command = Transaction_union.Tag.Unpacked.is_user_command tag in (* 对于支付交易来说,此为true *) let%bind () = (* 1)校验交易签名; *) [%with_label "Check transaction signature"] (check_signature shifted ~payload ~is_user_command ~signer ~signature) in let%bind signer_pk = Public_key.compress_var signer in let%bind () = (* 2)校验交易签名者公钥与交易中的fee_payer_pk一致; *) [%with_label "Fee-payer must sign the transaction"] ((* TODO: Enable multi-sig. *) Public_key.Compressed.Checked.Assert.equal signer_pk payload.common.fee_payer_pk ) in (* Compute transaction kind. *) let is_payment = Transaction_union.Tag.Unpacked.is_payment tag in (* 对于支付交易来说,此为true。其它为false。 *) let is_mint_tokens = Transaction_union.Tag.Unpacked.is_mint_tokens tag in let is_stake_delegation = Transaction_union.Tag.Unpacked.is_stake_delegation tag in let is_create_account = Transaction_union.Tag.Unpacked.is_create_account tag in let is_fee_transfer = Transaction_union.Tag.Unpacked.is_fee_transfer tag in let is_coinbase = Transaction_union.Tag.Unpacked.is_coinbase tag in let fee_token = payload.common.fee_token in (* 支付交易手续费的token类型。 *) let%bind fee_token_default = (* 3)fee_token_default:校验交易payload.common中的fee_token是否为Token_id.default; *) make_checked (fun () -> Token_id.(Checked.equal fee_token (Checked.constant default)) ) in let token = payload.body.token_id in let%bind token_default = (* 4)token_default:校验交易payload.body中的token_id是否为Token_id.default; *) make_checked (fun () -> Token_id.(Checked.equal token (Checked.constant default)) ) in let%bind () = (* 5)若为非create_account交易,payload.body.token_locked必须为false; *) Checked.all_unit [ [%with_label "Token_locked value is compatible with the transaction kind"] (Boolean.Assert.any [ Boolean.not payload.body.token_locked; is_create_account ] ) ; [%with_label "Token_locked cannot be used with the default token"] (Boolean.Assert.any [ Boolean.not payload.body.token_locked ; Boolean.not token_default ] ) ] in let%bind () = Boolean.Assert.is_true token_default in (* 4)token_default:校验交易payload.body中的token_id是否为Token_id.default,约束其必须为true; *) let%bind () = [%with_label "Validate tokens"] (Checked.all_unit [ [%with_label "Fee token is default or command allows non-default fee"] (Boolean.Assert.any [ fee_token_default ; is_payment ; is_stake_delegation ; is_fee_transfer ] ) ; (* TODO: Remove this check and update the transaction snark once we have an exchange rate mechanism. See issue #4447. *) [%with_label "Fees in tokens disabled"] (Boolean.Assert.is_true fee_token_default) (* 3)fee_token_default:校验交易payload.common中的fee_token是否为Token_id.default,约束其必须为true; *) ; [%with_label "Command allows default token"] Boolean.( Assert.any [ is_payment ; is_stake_delegation ; is_create_account ; is_fee_transfer ; is_coinbase ]) ] ) in let current_global_slot = (* 取state_body.consensus_state.global_slot_since_genesis为current_global_slot。 *) Mina_state.Protocol_state.Body.consensus_state state_body |> Consensus.Data.Consensus_state.global_slot_since_genesis_var in (* Query user command predicted failure/success. *) let%bind user_command_failure = (* 6)调用`compute_as_prover`,当支付交易的receiver account不存在时,是否有足够的费用来支付创建新账号。若失败,原因需为User_command_failure之一。 *) User_command_failure.compute_as_prover ~constraint_constants ~txn_global_slot:current_global_slot txn in let%bind user_command_fails = User_command_failure.any user_command_failure in let fee = payload.common.fee in (* 交易手续费 *) let receiver = Account_id.Checked.create payload.body.receiver_pk token in (* 取交易payload.body.token_id为token,与payload.body.receiver_pk一起构成receiver account_id *) let source = Account_id.Checked.create payload.body.source_pk token in (* 取交易payload.body.token_id为token,与payload.body.source_pk一起构成source account_id *) (* Information for the fee-payer. *) let nonce = payload.common.nonce in let fee_payer = (* 取交易payload.common.fee_token为fee_token,与payload.body.fee_payer_pk一起构成fee_payer account_id *) Account_id.Checked.create payload.common.fee_payer_pk fee_token in let%bind () = [%with_label "Check slot validity"] ( Global_slot.Checked.(current_global_slot >= Boolean.Assert.is_true ) in (* Check coinbase stack. Protocol state body is pushed into the Pending coinbase stack once per block. For example, consider any two transactions in a block. Their pending coinbase stacks would be: transaction1: s1 -> t1 = s1+ protocol_state_body + maybe_coinbase transaction2: t1 -> t1 + maybe_another_coinbase (Note: protocol_state_body is not pushed again) However, for each transaction, we need to constrain the protocol state body. This is done is by using the stack ([init_stack]) without the current protocol state body, pushing the state body to it in every transaction snark and checking if it matches the target. We also need to constrain the source for the merges to work correctly. Basically, init_stack + protocol_state_body + maybe_coinbase = target AND init_stack = source || init_stack + protocol_state_body = source *) (* These are all the possible cases: Init_stack Source Target -------------------------------------------------------------- i i i + state i i i + state + coinbase i i + state i + state i i + state i + state + coinbase i + coinbase i + state + coinbase i + state + coinbase *) let%bind () = (* 8)约束pending_coinbase_stack_init、pending_coinbase_stack_before、pending_coinbase_after和state_body之间的关系。 *) [%with_label "Compute coinbase stack"] (let%bind state_body_hash = Mina_state.Protocol_state.Body.hash_checked state_body in let%bind pending_coinbase_stack_with_state = Pending_coinbase.Stack.Checked.push_state state_body_hash pending_coinbase_stack_init in let%bind computed_pending_coinbase_stack_after = let coinbase = (Account_id.Checked.public_key receiver, payload.body.amount) in let%bind stack' = Pending_coinbase.Stack.Checked.push_coinbase coinbase pending_coinbase_stack_with_state in Pending_coinbase.Stack.Checked.if_ is_coinbase ~then_:stack' ~else_:pending_coinbase_stack_with_state in [%with_label "Check coinbase stack"] (let%bind correct_coinbase_target_stack = Pending_coinbase.Stack.equal_var computed_pending_coinbase_stack_after pending_coinbase_after in let%bind valid_init_state = let%bind equal_source = Pending_coinbase.Stack.equal_var pending_coinbase_stack_init pending_coinbase_stack_before in let%bind equal_source_with_state = Pending_coinbase.Stack.equal_var pending_coinbase_stack_with_state pending_coinbase_stack_before in Boolean.(equal_source ||| equal_source_with_state) in [%with_label "target stack and valid init state"] (Boolean.Assert.all [ correct_coinbase_target_stack; valid_init_state ] ) ) ) in (* Interrogate failure cases. This value is created without constraints; the failures should be checked against potential failures to ensure consistency. *) let%bind () = (* 9)约束若非user_command,user_command_fails必须为false; *) [%with_label "A failing user command is a user command"] Boolean.(Assert.any [ is_user_command; not user_command_fails ]) in let predicate_deferred = (* Account_precondition check is to be performed later if this is true. *) is_create_account in let%bind predicate_result = (* 10)约束要么为create_account交易,否则交易payload.common.fee_payer_pk必须等于payload.body.source_pk; *) let%bind is_own_account = Public_key.Compressed.Checked.equal payload.common.fee_payer_pk payload.body.source_pk in let predicate_result = (* TODO: Predicates. *) Boolean.false_ in Boolean.(is_own_account ||| predicate_result) in let%bind () = [%with_label "Check account_precondition failure against predicted"] (let%bind predicate_failed = Boolean.((not predicate_result) &&& not predicate_deferred) in assert_r1cs (predicate_failed :> Field.Var.t) (is_user_command :> Field.Var.t) (user_command_failure.predicate_failed :> Field.Var.t) ) in let account_creation_amount = (* 从constraints.account_creation_fee获取创建新账号的费用。 *) Amount.Checked.of_fee Fee.(var_of_t constraint_constants.account_creation_fee) in let%bind is_zero_fee = Fee.(equal_var fee (var_of_t zero)) in (* 判断创建新账号费用是否为零。is_zero_fee为false。 *) let is_coinbase_or_fee_transfer = Boolean.not is_user_command in (* 对于paymeng交易,其为false。 *) let%bind can_create_fee_payer_account = (* Fee transfers and coinbases may create an account. We check the normal invariants to ensure that the account creation fee is paid. *) let%bind fee_may_be_charged = (* If the fee is zero, we do not create the account at all, so we allow this through. Otherwise, the fee must be the default. *) Boolean.(token_default ||| is_zero_fee) in Boolean.(is_coinbase_or_fee_transfer &&& fee_may_be_charged) in let%bind root_after_fee_payer_update = (* 11)调用`modify_account_send`约束系统来更新扣除手续费余额及tree root; *) [%with_label "Update fee payer"] (* [modify_account_send t aid ~f] implements the following spec: - finds an account [account] in [t] at path [addr] whose account id is [aid] OR it is a fee transfer and is an empty account - returns a root [t'] of a tree of depth [depth] which is [t] but with the account [f account] at path [addr]. *) (Frozen_ledger_hash.modify_account_send ~depth:constraint_constants.ledger_depth root ~is_writeable:can_create_fee_payer_account fee_payer ~f:(fun ~is_empty_and_writeable account -> (* this account is: - the fee-payer for payments - the fee-payer for stake delegation - the fee-payer for account creation - the fee-payer for token minting - the fee-receiver for a coinbase - the second receiver for a fee transfer *) let%bind next_nonce = Account.Nonce.Checked.succ_if account.nonce is_user_command in let%bind () = [%with_label "Check fee nonce"] (let%bind nonce_matches = Account.Nonce.Checked.equal nonce account.nonce in Boolean.Assert.any [ Boolean.not is_user_command; nonce_matches ] ) in let%bind receipt_chain_hash = let current = account.receipt_chain_hash in let%bind r = Receipt.Chain_hash.Checked.cons (Signed_command payload) current in Receipt.Chain_hash.Checked.if_ is_user_command ~then_:r ~else_:current in let%bind is_empty_and_writeable = (* If this is a coinbase with zero fee, do not create the account, since the fee amount won't be enough to pay for it. *) Boolean.(is_empty_and_writeable &&& not is_zero_fee) in let%bind should_pay_to_create = (* Coinbases and fee transfers may create, or we may be creating a new token account. These are mutually exclusive, so we can encode this as a boolean. *) let%bind is_create_account = Boolean.(is_create_account &&& not user_command_fails) in Boolean.(is_empty_and_writeable ||| is_create_account) in let%bind amount = [%with_label "Compute fee payer amount"] (let fee_payer_amount = let sgn = Sgn.Checked.neg_if_true is_user_command in Amount.Signed.create_var ~magnitude:(Amount.Checked.of_fee fee) ~sgn in (* Account creation fee for fee transfers/coinbases. *) let%bind account_creation_fee = let%map magnitude = Amount.Checked.if_ should_pay_to_create ~then_:account_creation_amount ~else_:Amount.(var_of_t zero) in Amount.Signed.create_var ~magnitude ~sgn:Sgn.Checked.neg in Amount.Signed.Checked.( add fee_payer_amount account_creation_fee) ) in let txn_global_slot = current_global_slot in let%bind `Min_balance _, timing = [%with_label "Check fee payer timing"] (let%bind txn_amount = let%bind sgn = Amount.Signed.Checked.sgn amount in let%bind magnitude = Amount.Signed.Checked.magnitude amount in Amount.Checked.if_ (Sgn.Checked.is_neg sgn) ~then_:magnitude ~else_:Amount.(var_of_t zero) in let balance_check ok = [%with_label "Check fee payer balance"] (Boolean.Assert.is_true ok) in let timed_balance_check ok = [%with_label "Check fee payer timed balance"] (Boolean.Assert.is_true ok) in check_timing ~balance_check ~timed_balance_check ~account ~txn_amount:(Some txn_amount) ~txn_global_slot ) in let%bind balance = [%with_label "Check payer balance"] (Balance.Checked.add_signed_amount account.balance amount) in let%map public_key = Public_key.Compressed.Checked.if_ is_empty_and_writeable ~then_:(Account_id.Checked.public_key fee_payer) ~else_:account.public_key and token_id = make_checked (fun () -> Token_id.Checked.if_ is_empty_and_writeable ~then_:(Account_id.Checked.token_id fee_payer) ~else_:account.token_id ) and delegate = Public_key.Compressed.Checked.if_ is_empty_and_writeable ~then_:(Account_id.Checked.public_key fee_payer) ~else_:account.delegate in { Account.Poly.balance ; public_key ; token_id ; token_permissions = account.token_permissions ; token_symbol = account.token_symbol ; nonce = next_nonce ; receipt_chain_hash ; delegate ; voting_for = account.voting_for ; timing ; permissions = account.permissions ; zkapp = account.zkapp ; zkapp_uri = account.zkapp_uri } ) ) in let%bind receiver_increase = (* 计算receiver接收的金额。 *) (* - payments: payload.body.amount - stake delegation: 0 - account creation: 0 - token minting: payload.body.amount - coinbase: payload.body.amount - payload.common.fee - fee transfer: payload.body.amount *) [%with_label "Compute receiver increase"] (let%bind base_amount = let%bind zero_transfer = Boolean.any [ is_stake_delegation; is_create_account ] in Amount.Checked.if_ zero_transfer ~then_:(Amount.var_of_t Amount.zero) ~else_:payload.body.amount in (* The fee for entering the coinbase transaction is paid up front. *) let%bind coinbase_receiver_fee = Amount.Checked.if_ is_coinbase ~then_:(Amount.Checked.of_fee fee) ~else_:(Amount.var_of_t Amount.zero) in Amount.Checked.sub base_amount coinbase_receiver_fee ) in let receiver_overflow = ref Boolean.false_ in let%bind root_after_receiver_update = (* 12)调用`modify_account_recv`约束系统来进行receiver account金额更新以及tree root更新; *) [%with_label "Update receiver"] (* [modify_account_recv t aid ~f] implements the following spec: - finds an account [account] in [t] at path [addr] whose account id is [aid] OR which is an empty account - returns a root [t'] of a tree of depth [depth] which is [t] but with the account [f account] at path [addr]. *) (Frozen_ledger_hash.modify_account_recv ~depth:constraint_constants.ledger_depth root_after_fee_payer_update receiver ~f:(fun ~is_empty_and_writeable account -> (* this account is: - the receiver for payments - the delegated-to account for stake delegation - the created account for an account creation - the receiver for minted tokens - the receiver for a coinbase - the first receiver for a fee transfer *) let%bind is_empty_failure = let%bind must_not_be_empty = Boolean.(is_stake_delegation ||| is_mint_tokens) in Boolean.(is_empty_and_writeable &&& must_not_be_empty) in let%bind () = [%with_label "Receiver existence failure matches predicted"] (Boolean.Assert.( = ) is_empty_failure user_command_failure.receiver_not_present ) in let is_empty_and_writeable = (* is_empty_and_writable && not is_empty_failure *) Boolean.Unsafe.of_cvar @@ Field.Var.( sub (is_empty_and_writeable :> t) (is_empty_failure :> t)) in let%bind should_pay_to_create = Boolean.(is_empty_and_writeable &&& not is_create_account) in let%bind () = [%with_label "Check whether creation fails due to a non-default token"] (let%bind token_should_not_create = Boolean.(should_pay_to_create &&& Boolean.not token_default) in let%bind token_cannot_create = Boolean.(token_should_not_create &&& is_user_command) in let%bind () = [%with_label "Check that account creation is paid in the default \ token for non-user-commands"] ((* This expands to [token_should_not_create = token_should_not_create && is_user_command] which is - [token_should_not_create = token_should_not_create] (ie. always satisfied) for user commands - [token_should_not_create = false] for coinbases/fee transfers. *) Boolean.Assert.( = ) token_should_not_create token_cannot_create ) in [%with_label "equal token_cannot_create"] (Boolean.Assert.( = ) token_cannot_create user_command_failure.token_cannot_create ) ) in let%bind balance = (* [receiver_increase] will be zero in the stake delegation case. *) let%bind receiver_amount = let%bind account_creation_amount = Amount.Checked.if_ should_pay_to_create ~then_:account_creation_amount ~else_:Amount.(var_of_t zero) in let%bind amount_for_new_account, `Underflow underflow = Amount.Checked.sub_flagged receiver_increase account_creation_amount in let%bind () = [%with_label "Receiver creation fee failure matches predicted"] (Boolean.Assert.( = ) underflow user_command_failure.amount_insufficient_to_create ) in Currency.Amount.Checked.if_ user_command_fails ~then_:Amount.(var_of_t zero) ~else_:amount_for_new_account in (* NOTE: Instead of capturing this as part of the user command failures, we capture it inline here and bubble it out to a reference. This behavior is still in line with the out-of-snark transaction logic. Updating [user_command_fails] to include this value from here onwards will ensure that we do not update the source or receiver accounts. The only places where [user_command_fails] may have already affected behaviour are * when the fee-payer is paying the account creation fee, and * when a new token is created. In both of these, this account is new, and will have a balance of 0, so we can guarantee that there is no overflow. *) let%bind balance, `Overflow overflow = Balance.Checked.add_amount_flagged account.balance receiver_amount in let%bind () = [%with_label "Overflow error only occurs in user commands"] Boolean.(Assert.any [ is_user_command; not overflow ]) in receiver_overflow := overflow ; Balance.Checked.if_ overflow ~then_:account.balance ~else_:balance in let%bind user_command_fails = Boolean.(!receiver_overflow ||| user_command_fails) in let%bind is_empty_and_writeable = (* Do not create a new account if the user command will fail. *) Boolean.(is_empty_and_writeable &&& not user_command_fails) in let%bind may_delegate = (* Only default tokens may participate in delegation. *) Boolean.(is_empty_and_writeable &&& token_default) in let%map delegate = Public_key.Compressed.Checked.if_ may_delegate ~then_:(Account_id.Checked.public_key receiver) ~else_:account.delegate and public_key = Public_key.Compressed.Checked.if_ is_empty_and_writeable ~then_:(Account_id.Checked.public_key receiver) ~else_:account.public_key and token_id = make_checked (fun () -> Token_id.Checked.if_ is_empty_and_writeable ~then_:token ~else_:account.token_id ) and token_owner = (* TODO: Delete token permissions *) Boolean.if_ is_empty_and_writeable ~then_:Boolean.false_ ~else_:account.token_permissions.token_owner and token_locked = Boolean.if_ is_empty_and_writeable ~then_:payload.body.token_locked ~else_:account.token_permissions.token_locked in { Account.Poly.balance ; public_key ; token_id ; token_permissions = { Token_permissions.token_owner; token_locked } ; token_symbol = account.token_symbol ; nonce = account.nonce ; receipt_chain_hash = account.receipt_chain_hash ; delegate ; voting_for = account.voting_for ; timing = account.timing ; permissions = account.permissions ; zkapp = account.zkapp ; zkapp_uri = account.zkapp_uri } ) ) in let%bind user_command_fails = (* 若receiver_overflow,则交易也失败。 *) Boolean.(!receiver_overflow ||| user_command_fails) in let%bind fee_payer_is_source = Account_id.Checked.equal fee_payer source in (* 判断fee_payer是否等于source *) let%bind root_after_source_update = (* 13)调用`modify_account_send`更新发送者账号余额以及tree root; *) [%with_label "Update source"] (Frozen_ledger_hash.modify_account_send ~depth:constraint_constants.ledger_depth ~is_writeable: (* [modify_account_send] does this failure check for us. *) user_command_failure.source_not_present root_after_receiver_update source ~f:(fun ~is_empty_and_writeable account -> (* this account is: - the source for payments - the delegator for stake delegation - the token owner for account creation - the token owner for token minting - the fee-receiver for a coinbase - the second receiver for a fee transfer *) let%bind () = [%with_label "Check source presence failure matches predicted"] (Boolean.Assert.( = ) is_empty_and_writeable user_command_failure.source_not_present ) in let%bind () = [%with_label "Check source failure cases do not apply when fee-payer is \ source"] (let num_failures = let open Field.Var in add (user_command_failure.source_insufficient_balance :> t) (user_command_failure.source_bad_timing :> t) in let not_fee_payer_is_source = (Boolean.not fee_payer_is_source :> Field.Var.t) in (* Equivalent to: if fee_payer_is_source then num_failures = 0 else num_failures = num_failures *) [%with_label "Check num_failures"] (assert_r1cs not_fee_payer_is_source num_failures num_failures ) ) in let%bind amount = (* Only payments should affect the balance at this stage. *) if_ is_payment ~typ:Amount.typ ~then_:payload.body.amount ~else_:Amount.(var_of_t zero) in let txn_global_slot = current_global_slot in let%bind `Min_balance _, timing = [%with_label "Check source timing"] (let balance_check ok = [%with_label "Check source balance failure matches predicted"] (Boolean.Assert.( = ) ok (Boolean.not user_command_failure.source_insufficient_balance ) ) in let timed_balance_check ok = [%with_label "Check source timed balance failure matches predicted"] (let%bind not_ok = Boolean.( (not ok) &&& not user_command_failure .source_insufficient_balance) in Boolean.Assert.( = ) not_ok user_command_failure.source_bad_timing ) in check_timing ~balance_check ~timed_balance_check ~account ~txn_amount:(Some amount) ~txn_global_slot ) in let%bind balance, `Underflow underflow = Balance.Checked.sub_amount_flagged account.balance amount in let%bind () = (* TODO: Remove the redundancy in balance calculation between here and [check_timing]. *) [%with_label "Check source balance failure matches predicted"] (Boolean.Assert.( = ) underflow user_command_failure.source_insufficient_balance ) in let%map delegate = Public_key.Compressed.Checked.if_ is_stake_delegation ~then_:(Account_id.Checked.public_key receiver) ~else_:account.delegate in (* NOTE: Technically we update the account here even in the case of [user_command_fails], but we throw the resulting hash away in [final_root] below, so it shouldn't matter. *) { Account.Poly.balance ; public_key = account.public_key ; token_id = account.token_id ; token_permissions = account.token_permissions ; token_symbol = account.token_symbol ; nonce = account.nonce ; receipt_chain_hash = account.receipt_chain_hash ; delegate ; voting_for = account.voting_for ; timing ; permissions = account.permissions ; zkapp = account.zkapp ; zkapp_uri = account.zkapp_uri } ) ) in let%bind fee_excess = (* 获取fee_access *) (* - payments: payload.common.fee - stake delegation: payload.common.fee - account creation: payload.common.fee - token minting: payload.common.fee - coinbase: 0 (fee already paid above) - fee transfer: - payload.body.amount - payload.common.fee *) let open Amount in chain Signed.Checked.if_ is_coinbase ~then_:(return (Signed.Checked.of_unsigned (var_of_t zero))) ~else_: (let user_command_excess = Signed.Checked.of_unsigned (Checked.of_fee payload.common.fee) in let%bind fee_transfer_excess, fee_transfer_excess_overflowed = let%map magnitude, `Overflow overflowed = Checked.( add_flagged payload.body.amount (of_fee payload.common.fee)) in (Signed.create_var ~magnitude ~sgn:Sgn.Checked.neg, overflowed) in let%bind () = (* TODO: Reject this in txn pool before fees-in-tokens. *) [%with_label "Fee excess does not overflow"] Boolean.( Assert.any [ not is_fee_transfer; not fee_transfer_excess_overflowed ]) in Signed.Checked.if_ is_fee_transfer ~then_:fee_transfer_excess ~else_:user_command_excess ) in let%bind supply_increase = (* 若为coinbase交易,则supply_increase为交易的payload.body.amount,否则为0。 *) Amount.Checked.if_ is_coinbase ~then_:payload.body.amount ~else_:Amount.(var_of_t zero) in let%map final_root = (* 若交易失败,则手续费仍然需要支付,否则成功,各个账号相应余额均更新。 *) (* Ensure that only the fee-payer was charged if this was an invalid user command. *) Frozen_ledger_hash.if_ user_command_fails ~then_:root_after_fee_payer_update ~else_:root_after_source_update in (final_root, fee_excess, supply_increase)
[1] Guide to Snark Work
附录1. Mina系列博客Mina系列博客有:
- Mina概览
- Mina的支付流程
- Mina的zkApp
- Mina中的Pasta(Pallas和Vesta)曲线
- Mina中的Schnorr signature
- Mina中的Pickles SNARK
- Mina中的Kimchi SNARK
- Mina Kimchi SNARK 代码解析
- Mina Berkeley QANet测试网zkApp初体验
- Mina中的Poseidon hash
- Mina中的多项式承诺方案
- Recursive SNARKs总览
- Mina技术白皮书
- Mina代码解析
- Mina中的Snark Worker
- Mina中的Scan State
- Mina中的VRF
- Mina中的delta_transition_chain_proof/delta_block_chain_proof
- Mina中的stake delegation
- Mina如何实现22KB?
- Mina中的stake_proof
- Mina中的genesis_proof
- Mina中的交易及经济白皮书
- Mina中的ledger proof
- Mina中的基于DLG的Plonk polynomial commitment scheme代码解析
- Mina中的约束系统代码解析
- Mina中的scan state代码解析
- Mina中的区块证明
- Mina中的wrap snark