Mina中的delta_transition_chain_proof/delta_block_chain_proof

1. 引言

Mina区块中的delta_transition_chain_proof/delta_block_chain_proof字段的主要目的是:Proof that the block was produced within the allotted slot time。

(* header.ml中有: *)
type t =
      { protocol_state : Protocol_state.Value.Stable.V2.t
      ; protocol_state_proof : Proof.Stable.V2.t [@sexp.opaque]
      ; delta_block_chain_proof :
          (* TODO: abstract *)
          State_hash.Stable.V1.t * State_body_hash.Stable.V1.t list
      ; current_protocol_version : Protocol_version.Stable.V1.t
      ; proposed_protocol_version_opt : Protocol_version.Stable.V1.t option
      ; body_reference : Body_reference.Stable.V1.t
      }

(* external_transition.ml(指节点收到的由外部产块者产的区块)中有: *)
type t =
          { protocol_state : Protocol_state.Value.Stable.V2.t
          ; protocol_state_proof : Proof.Stable.V2.t [@sexp.opaque]
          ; staged_ledger_diff : Staged_ledger_diff.Stable.V2.t
          ; delta_transition_chain_proof :
              State_hash.Stable.V1.t * State_body_hash.Stable.V1.t list
          ; current_protocol_version : Protocol_version.Stable.V1.t
          ; proposed_protocol_version_opt : Protocol_version.Stable.V1.t option
          ; mutable validation_callback : Validate_content.t
          }

生成该proof的代码为:

				let delta_block_chain_proof =
                      Transition_chain_prover.prove
                        ~length:
                          (Mina_numbers.Length.to_int consensus_constants.delta)
                        ~frontier previous_state_hash
                      |> Option.value_exn

其中:

  • consensus_constants.delta:为Mina共识参数中delta值,当前Mina主网设置为0。
  • previous_state_hash:为commitment to previous block (hash of previous protocol state hash and body hash)。可将protocol_state看成是传统的区块头,包含了区块中最重要的信息。

protocol_state结构为:

Field Type Description
version u8 (= 0x01) Block structure version
previous_state_hash State_hash.Stable.V1.t Commitment to previous block (hash of previous protocol state hash and body hash)
body Protocol_state.Body.Value.Stable.V1 The body of the protocol state

其它基础术语有:

  • Transition:等同于block区块。
  • Transition Frontier:为本地data store中包含的网络中的最新 k k k个区块。为rose tree-type数据结构,该tree中的每个节点可能有多个children,即分叉。该tree中的每个节点都可称为breadcrumb。【Mina主网共识中设置 k = 290 k=290 k=290 k k k表示:Depth of finality (number of confirmations)】
    When a node receives a block from a peer, it is first validated, applied to the existing state, and added to the node’s transition frontier. If, according to the consensus rules, it results in increasing the length of the blockchain, the node’s best tip is updated, and the root of the transition frontier is moved up to only maintain k blocks in the transition frontier.【产块者自己产的块称为internal transition,接收其它产块者的区块称为external transition。】
(* breadcrumb结构体定义为: *)
type t =
    { validated_transition : External_transition.Validated.t
    ; staged_ledger : Staged_ledger.t [@sexp.opaque]
    ; just_emitted_a_proof : bool
    ; transition_receipt_time : Time.t option
    }

(* transition_frontier结构体定义为: *)
type t =
  { logger : Logger.t
  ; verifier : Verifier.t
  ; consensus_local_state : Consensus.Data.Local_state.t
  ; catchup_tree : Catchup_tree.t
  ; full_frontier : Full_frontier.t
  ; persistent_root : Persistent_root.t
  ; persistent_root_instance : Persistent_root.Instance.t
  ; persistent_frontier : Persistent_frontier.t
  ; persistent_frontier_instance : Persistent_frontier.Instance.t
  ; extensions : Extensions.t
  ; genesis_state_hash : State_hash.t
  ; closed : unit Ivar.t
  }

 (* Extensionts结构体定义为: *)
  type t =
  { root_history : Root_history.Broadcasted.t
  ; snark_pool_refcount : Snark_pool_refcount.Broadcasted.t
  ; best_tip_diff : Best_tip_diff.Broadcasted.t
  ; transition_registry : Transition_registry.Broadcasted.t
  ; ledger_table : Ledger_table.Broadcasted.t
  ; identity : Identity.Broadcasted.t
  ; new_breadcrumbs : New_breadcrumbs.Broadcasted.t
  }
(* Transition_chain_prover: *)
let prove ?length ~frontier state_hash =
    let open Option.Let_syntax in
    let%map requested_transition =
      Option.merge
        Transition_frontier.(
          find frontier state_hash >>| Breadcrumb.validated_transition)
        (find_in_root_history frontier state_hash)
        ~f:Fn.const
    in
    let first_transition, merkle_list =
      Merkle_list.prove ?length ~context:frontier requested_transition
    in
    ( (External_transition.Validated.state_hashes first_transition).state_hash
    , merkle_list )

附录A. 获取Mina最新区块API

可通过如下API获取Mina canonical chain上的最近区块信息,默认返回最新区块信息,最多可返回最近的10个区块新。For full block data use the GraphQL API.

$ curl --request GET \
>  --url https://api.minaexplorer.com/blocks?limit=1
{
    
    
  "blocks": [
    {
    
    
      "blockHeight": 131444, 
      "canonical": true, 
      "creator": "B62qphpEdBwSycpN67XFjcXSEY9j18chmttFTXYerhTPMX4JkXubWkd", 
      "creatorAccount": {
    
    
        "publicKey": "B62qphpEdBwSycpN67XFjcXSEY9j18chmttFTXYerhTPMX4JkXubWkd"
      }, 
      "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
      "protocolState": {
    
    
        "blockchainState": {
    
    
          "date": 1650874680000, 
          "snarkedLedgerHash": "jxdqjqb13YoWnqmcYLsLHzcufdu7WHhM5DPZcz77R6KNTUaHU7o", 
          "stagedLedgerHash": "jwJ9SunhJxpAvTJHrskGnoXG6oD5A1cKV9CJpBy49iMsZnHmVhE", 
          "utcDate": 1650874680000
        }, 
        "consensusState": {
    
    
          "blockHeight": 131444, 
          "blockchainLength": 131444, 
          "epoch": 27, 
          "epochCount": 27, 
          "hasAncestorInSameCheckpointWindow": true, 
          "lastVrfOutput": "EiRtbPoEfWdYFSmLFvXsybucaXdtuw4KHrzqU1JhgEePTmvGKV2RC", 
          "minWindowDensity": 14, 
          "nextEpochData": {
    
    
            "epochLength": 854, 
            "ledger": {
    
    
              "hash": "jx29wpTRDF8tuMFXgqT8inkJhb5chPjtZiwgTHzs6GxsvAy5KiH", 
              "totalCurrency": 911458252840039233
            }, 
            "lockCheckpoint": "3NK7scXTiS5huxuaZZWhiDJLPu8aAWZmPau4q2RLE1wu3YppsSb5", 
            "seed": "2vaMPjFYKDJpeBbyZeBkTXzuPBRLXA6PZpcKe1QrBkmdCmRAqmP5", 
            "startCheckpoint": "3NKsqyz5gDjjf3rKjzNUFTPXbnth3ZULGfBzhP47uXUvSqkmrjvp"
          }, 
          "slot": 1306, 
          "slotSinceGenesis": 194086, 
          "stakingEpochData": {
    
    
            "epochLength": 4924, 
            "ledger": {
    
    
              "hash": "jxsdc9d3AkKmVSWZQExucepfuLwfzQHtZpiCFArGqtfVe5jveiZ", 
              "totalCurrency": 907174972840039233
            }, 
            "lockCheckpoint": "3NKkU6PNVgY6RNSgXpQ5V3mKdjNuwFWPB5DSsEG5tTnAcyscvJhz", 
            "seed": "2vakgmuzxwow8WB6EBGggsTBZyRcbDTgBXJWJkWwvs5athQ1WeGk", 
            "startCheckpoint": "3NK86sb3HTArDahaSLWbsMihC49N2rC72sYP2vTVEr6mq8gLNvqH"
          }, 
          "totalCurrency": 912225772840039233
        }, 
        "previousStateHash": "3NK7scXTiS5huxuaZZWhiDJLPu8aAWZmPau4q2RLE1wu3YppsSb5"
      }, 
      "receivedTime": "Mon, 25 Apr 2022 08:19:19 GMT", 
      "snarkJobs": [], 
      "stateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
      "stateHashField": "13610166132023585511305744971845126343390668980628345075948931205879504829316", 
      "transactions": {
    
    
        "coinbase": 720000000000, 
        "coinbaseReceiverAccount": {
    
    
          "publicKey": "B62qmybys2vViMnedZSheEZ6ZSBjNwXHr9uBDy8kMUEJx8UsJ53r11D"
        }, 
        "feeTransfer": [
          {
    
    
            "fee": 91100000, 
            "recipient": "B62qmybys2vViMnedZSheEZ6ZSBjNwXHr9uBDy8kMUEJx8UsJ53r11D", 
            "type": "Fee_transfer"
          }
        ], 
        "userCommands": [
          {
    
    
            "amount": 1000, 
            "blockHeight": 131444, 
            "blockStateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
            "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
            "fee": 1000000, 
            "feePayer": {
    
    
              "publicKey": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy", 
              "token": 1
            }, 
            "feeToken": 1, 
            "from": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy", 
            "fromAccount": {
    
    
              "publicKey": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy", 
              "token": 1
            }, 
            "hash": "Ckpa1G9q2VATS1kHGqyoUjZUtMtNw7UiSBZJwMrRM5riopodrA9ji", 
            "id": "PSJ6tbskxprEcucuCKvoyV3ujHYFFvF3crYB37cjvPWsFzwkTTem7pXZRuwMJWSgr6JRDZhj2Gt1K3KokxsQ1diisxyESSyQEWZ2DoqF7Yaaox45aJcPHQFFza5es2wjLwyDRXYT6fujqbxfYgiwN8dxCnCBwyx3TND9FSKg3a7bzSFhGd2npF7XjBaC86wsJgtrKg7yNcUj8BvtkpHvpJBPG8QfxHoRc3qr2z1MbZVu3LQK7G2n12W6sTDNBJuxNEAZiV3RPTVE6VmsfHfPhtXDTgd3dai2xBc6WP7Hj3V5hXu6WrScCbWhoku8BF6mxV8ahPePo8oQUhUHtDcmwXVH7gsN1UhprJRKzvnvmferyLcubwti1ZDKgUgH15UVxjsCtTp3k4mWP", 
            "isDelegation": false, 
            "kind": "PAYMENT", 
            "memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH", 
            "nonce": 184335, 
            "receiver": {
    
    
              "publicKey": "B62qjYanmV7y9njVeH5UHkz3GYBm7xKir1rAnoY4KsEYUGLMiU45FSM"
            }, 
            "source": {
    
    
              "publicKey": "B62qre3erTHfzQckNuibViWQGyyKwZseztqrjPZBv6SQF384Rg6ESAy"
            }, 
            "to": "B62qjYanmV7y9njVeH5UHkz3GYBm7xKir1rAnoY4KsEYUGLMiU45FSM", 
            "toAccount": {
    
    
              "publicKey": "B62qjYanmV7y9njVeH5UHkz3GYBm7xKir1rAnoY4KsEYUGLMiU45FSM", 
              "token": 1
            }, 
            "token": 1
          }, 
          {
    
    
            "amount": 1905000, 
            "blockHeight": 131444, 
            "blockStateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
            "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
            "fee": 10000000, 
            "feePayer": {
    
    
              "publicKey": "B62qqa9g4CFfkSuX2j22S52z6UfcDcS9tMTgQrFKZ21v7GrEP6Zu5Tc", 
              "token": 1
            }, 
            "feeToken": 1, 
            "from": "B62qqa9g4CFfkSuX2j22S52z6UfcDcS9tMTgQrFKZ21v7GrEP6Zu5Tc", 
            "fromAccount": {
    
    
              "publicKey": "B62qqa9g4CFfkSuX2j22S52z6UfcDcS9tMTgQrFKZ21v7GrEP6Zu5Tc", 
              "token": 1
            }, 
            "hash": "CkpYtQ48qS3CaHkaVB7okZwKLq4twFg56ofN5yKe37SZmtTP29kPj", 
            "id": "PSJ6tbskxprEcue6aySgF6jSQg4w7tC2a2QekB6rDZbAsLEVPoXdyHSt46774q9KkPyeSsQuKAsoByhMgAz3jU7iC6rbfuhHShgNvCDZbbDzrJpGLxxEWbH3BiHXd83WbY1axTsCQge8mmKAvixUADTQmhS5Va8ayoYCt87DHkAMEqscoqVn8ufdgB23EHX5G5gyttrsfhahL8pUBznSeAB3Lvu5Z673VoT3ahra5efhZt34iUVJiyK9ATY7J1FBfF1m22xsmeYsfcoN7DXTgwv9VqxmKKJDQaBY1EQtvRws7QX4BVeQfawTRPmmivEQCNMeFZYxUdwuEanw93LEAkqLdgbPe3t9K7aqLiL6aasFSQ1dXAjLqGgbEpYCdnPzGTFdxfGCTX337", 
            "isDelegation": false, 
            "kind": "PAYMENT", 
            "memo": "E4Yd7qwaRCHR6t7i6ToM98eSUy5eKKadQUPZX7Vpw4CWBvWyd8fzK", 
            "nonce": 4187, 
            "receiver": {
    
    
              "publicKey": "B62qq3fsYyjPWfhLSfoXDd2dxopsrzCPaQnvfdQm3wtmHT9UarBkh3f"
            }, 
            "source": {
    
    
              "publicKey": "B62qqa9g4CFfkSuX2j22S52z6UfcDcS9tMTgQrFKZ21v7GrEP6Zu5Tc"
            }, 
            "to": "B62qq3fsYyjPWfhLSfoXDd2dxopsrzCPaQnvfdQm3wtmHT9UarBkh3f", 
            "toAccount": {
    
    
              "publicKey": "B62qq3fsYyjPWfhLSfoXDd2dxopsrzCPaQnvfdQm3wtmHT9UarBkh3f", 
              "token": 1
            }, 
            "token": 1
          }, 
          {
    
    
            "amount": 253303898779, 
            "blockHeight": 131444, 
            "blockStateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
            "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
            "fee": 10100000, 
            "feePayer": {
    
    
              "publicKey": "B62qopdvPkbF8Gp3Uafs5zPgmTnqCPe3arBoT5xCZ6CjenYVuDBrzjX", 
              "token": 1
            }, 
            "feeToken": 1, 
            "from": "B62qopdvPkbF8Gp3Uafs5zPgmTnqCPe3arBoT5xCZ6CjenYVuDBrzjX", 
            "fromAccount": {
    
    
              "publicKey": "B62qopdvPkbF8Gp3Uafs5zPgmTnqCPe3arBoT5xCZ6CjenYVuDBrzjX", 
              "token": 1
            }, 
            "hash": "CkpYYo8BCAsr7eY7UVbqUswPKUsFXXvPL3JKZPabrpAiWhA2jckNb", 
            "id": "8Y6GSZvuW1bNvwdV9zWpr1LrtTAJmAgyskzbbvaNBVayaa9CHDTskBPCcgkwaTcfcYTSgoex6KgRw4FYtN9ZLo7uA9gd8djS4TbShP53seShLg3KXfBUdeWHmwqUeJqymta2iZSaWjzksLBYimCAfecsoer4CTePLmKhLRPuUZDA5nrtNqZVCaNx5Kudv9PNGKbm5Z8fMcoRamLNBbyq9uDKzmdvgShnLDoYVfE4PLYrPbEJMcpa24fXGa8J1eXbvKLoHtpUfSgGaxBH6YSKhPnsBP6bW35vjjns5eCNnDeBArfjzGK9WPmfNPeYbZ78HGXNimddBcBzcYyCFsgD4Vj7n2SqaqwLcBNkB6Hz5xaV1LF8qVM8XYiTk9R9apGy7ENXHgy67nBW53oZ", 
            "isDelegation": false, 
            "kind": "PAYMENT", 
            "memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH", 
            "nonce": 1, 
            "receiver": {
    
    
              "publicKey": "B62qpEva8DE1Z6KPSi3MpZEKgrrNnj7ZGVF5PzKD6RXVgU5cxmD4JM9"
            }, 
            "source": {
    
    
              "publicKey": "B62qopdvPkbF8Gp3Uafs5zPgmTnqCPe3arBoT5xCZ6CjenYVuDBrzjX"
            }, 
            "to": "B62qpEva8DE1Z6KPSi3MpZEKgrrNnj7ZGVF5PzKD6RXVgU5cxmD4JM9", 
            "toAccount": {
    
    
              "publicKey": "B62qpEva8DE1Z6KPSi3MpZEKgrrNnj7ZGVF5PzKD6RXVgU5cxmD4JM9", 
              "token": 1
            }, 
            "token": 1
          }, 
          {
    
    
            "amount": 38951000000, 
            "blockHeight": 131444, 
            "blockStateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
            "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
            "fee": 20000000, 
            "feePayer": {
    
    
              "publicKey": "B62qp2BETsTNsFg1TXcbwt4zYH4SWgjvGtNB1SjKWhb1m6xjYaW3Us4", 
              "token": 1
            }, 
            "feeToken": 1, 
            "from": "B62qp2BETsTNsFg1TXcbwt4zYH4SWgjvGtNB1SjKWhb1m6xjYaW3Us4", 
            "fromAccount": {
    
    
              "publicKey": "B62qp2BETsTNsFg1TXcbwt4zYH4SWgjvGtNB1SjKWhb1m6xjYaW3Us4", 
              "token": 1
            }, 
            "hash": "CkpZZJ3LZUnGMCDSnNCXEDfXnDFrcwUWAtz2huy3eSFFRYuo17LGN", 
            "id": "8Y6GSZvuW1bNvwdHapkk5X5MbRx2ikTCaYmKsZBVCedhH9RM8B9gQEgT3VVm8Crs6u5vHWHsHqytYDQf9XbRmawUfVdThyka2AgHMgq4Hj82Mdr3ti4L8PWffFwXNhnmHuZRKyH4iuK5Qf9eWbEhuusgeEoJwseU5fXkgufHLMC9FEuNCu2vv43EQmxmkJa84n9PMLA1WCxcHqMQ8hAw3rGeVDPjYys57YfG7c6DjcTB4H1UdDMybrUUjPWFn4xtkMMNpRmPLcKaheoyLvxM6qy84rTEUuhzvxUREFzCDaWBp2CRn1VcN2fTE1kprtoTo5hJBdk4TYUssHHeCTwUBF3Dk5VH2233fdhQezYQBu4K7Px9cX7i3kwnaEybnCtQrN9uEuPCghUNZD9j", 
            "isDelegation": false, 
            "kind": "PAYMENT", 
            "memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH", 
            "nonce": 2, 
            "receiver": {
    
    
              "publicKey": "B62qpq3jk9tAb52DTEZaLhgM7j8EgXX9U6Xkm5n9KRXVdgLwi4ad8tL"
            }, 
            "source": {
    
    
              "publicKey": "B62qp2BETsTNsFg1TXcbwt4zYH4SWgjvGtNB1SjKWhb1m6xjYaW3Us4"
            }, 
            "to": "B62qpq3jk9tAb52DTEZaLhgM7j8EgXX9U6Xkm5n9KRXVdgLwi4ad8tL", 
            "toAccount": {
    
    
              "publicKey": "B62qpq3jk9tAb52DTEZaLhgM7j8EgXX9U6Xkm5n9KRXVdgLwi4ad8tL", 
              "token": 1
            }, 
            "token": 1
          }, 
          {
    
    
            "amount": 730458000000, 
            "blockHeight": 131444, 
            "blockStateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
            "dateTime": "Mon, 25 Apr 2022 08:18:00 GMT", 
            "fee": 50000000, 
            "feePayer": {
    
    
              "publicKey": "B62qmLLkkx3y22DXfNACbF24dSiBVjvAQVLup9RBXhNvgDVQkkvLbmT", 
              "token": 1
            }, 
            "feeToken": 1, 
            "from": "B62qmLLkkx3y22DXfNACbF24dSiBVjvAQVLup9RBXhNvgDVQkkvLbmT", 
            "fromAccount": {
    
    
              "publicKey": "B62qmLLkkx3y22DXfNACbF24dSiBVjvAQVLup9RBXhNvgDVQkkvLbmT", 
              "token": 1
            }, 
            "hash": "CkpZ3MA7iLm8dDtwv7nDDYdmEFFmvRkxuts7qoZLxTxgPP4DmcTw6", 
            "id": "CB1PypTjpLT56HzVoH38qjS2Fth7PNdUviE7xKgsnE8T63PKz2UybbM46HC7V5wJUUhtcdADpV7hKdmUcRXt7Hm7CaP9qnDtfH9gvAU6jTym4jWBa4TCztHfh86aou7ZW3eEFzM5CrMCV3rD2zSzfHkGVNvBHvmTbvxgddj33Cb7JqXSbY8Fem1XxnWwaSu4wnXgQiz2fAHc9xSAi1K2EB9yh5sbhCE3FBMYJeP12nSa6q6J4k3H5Q3WVrVwvLmsznXsSqJam5ya9df4TmFoVswL94EQk14oHhDtVjkheg4j5kr33majEs25soMQeBt6ZRWWiZ7JG5dw7cJWjeUo2DxVfwFgFE2YWKtXEUqPWAMk3qGienVNYxw4mUGWzMfS8eZPxBD2xTJPaRduxrDi", 
            "isDelegation": false, 
            "kind": "PAYMENT", 
            "memo": "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH", 
            "nonce": 0, 
            "receiver": {
    
    
              "publicKey": "B62qkRodi7nj6W1geB12UuW2XAx2yidWZCcDthJvkf9G4A6G5GFasVQ"
            }, 
            "source": {
    
    
              "publicKey": "B62qmLLkkx3y22DXfNACbF24dSiBVjvAQVLup9RBXhNvgDVQkkvLbmT"
            }, 
            "to": "B62qkRodi7nj6W1geB12UuW2XAx2yidWZCcDthJvkf9G4A6G5GFasVQ", 
            "toAccount": {
    
    
              "publicKey": "B62qkRodi7nj6W1geB12UuW2XAx2yidWZCcDthJvkf9G4A6G5GFasVQ", 
              "token": 1
            }, 
            "token": 1
          }
        ]
      }, 
      "winnerAccount": {
    
    
        "balance": {
    
    
          "blockHeight": 131444, 
          "liquid": 1180040858424360, 
          "locked": 1000000000, 
          "stateHash": "3NL3TectUwQUQW86AurwRCWz1H4n4cQRXyJZa5a7AmtxQ3cCe4NH", 
          "total": 1180041858424360, 
          "unknown": 1180041858424360
        }, 
        "publicKey": "B62qmanR1vreSJgKYZcHSiNrov8jvXShfcjioaBLpCGbr7vrt3DxZq9"
      }
    }
  ]
}

附录1. Mina系列博客

Mina系列博客有:

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/124408198