Mina开发者中文文档
欢迎来到Mina开发者文档-请快速熟悉一下下面的链接,了解一下情况。
探索Github上的代码库。
请参阅贡献指南来开始为Mina贡献代码。协议和CLI是用OCaml编写的。在这些工具中的任何级别的经验水平都是可以的。
与贡献代码相关的其他文档包括:
● 风格指南
● 代码评审指南
● 库结构
● BIP44信息
Mina是完全开源的,代码是按照Apache License 2.0的条款发布的。
Mina 区块链协议是由静态函数式编程语言OCaml编写而成。
OCaml的初学者可通过浏览 Real World OCaml,详细了解该语言,如有兴趣,可对特定主题深入探究。若对OCaml已有基本了解,可在此处了解更多其在Mina区块链协议中的应用。
OCaml编译器针对的是字节码和本地编译。我们的代码与一些程序库静态链接,因此无法编译成字节码。此外,在REP环境下,不能很好地发挥作用。我们的构建系统是Dune。Dune有一个文件夹的概念,文件夹代表模块,其中的每个文件都代表一个模块。若文件夹中有一个与该文件夹同名的文件,那这个文件本质上就等同于Node中的index.js
。
OCaml还有接口文件——其文件扩展名为.mli
,其中包含了一个模块的类型签名和结构。与之相对应的编译器必须有相同的文件名,但文件扩展名为.ml
。从其他模块只能访问接口规定的内容。若模块没有接口文件,所有内容默认都可以被公开访问。注意:.rei 和 .re 文件扩展名也遵循同样的逻辑。
在链接步骤,dune
使在后台使用ldd
。还可使用-O3
之类的命令实现最优化。可使用gdb
调试程序,虽然OCaml不能完全支持这一调试工具,但这是一种有效的方式。即将发布的OCaml(4.08)可与gdb
兼容。
学习OCaml的一个难点在于程序库文档的定位和读取。特别是Core
库,其结构如下:
Base
|
Core_kernel -> Async_kernel
| |
Unix <- Core -> Async
事实上,Core
文档有多个源文件。要找到正确的文档,首先要登录GitHub,然后使用该平台定位正确文档。若未能找到所需模块,即登录Core_kernel
,若仍未找到,则查找Base
。还需要注意的是若字节段并未扩展,则该查询无效。
与其使用HTML文档,更好的方法是利用如merlin
等提供类型提示的集成编辑器。但Merlin的一个缺点是只能在你的代码能够编译的情况下运行。此外还可提供代码跳转功能,如选中代码并转至其初始定义的代码段。
OPAM是OCmal的程序包管理器,通常与程序库共同运载文档。可通过merlin访问该管理器。
OCaml有一个编译器插件的概念,名为ppx。这只是允许编译时代码生成的钩子(触发器)。
下面是一个类型签名扩展的例子
type t =
| A
| B [@ to_yojson f]
[@@ deriving yojson]
在以上例子中,单个@
表示对单个表达式的扩展,@@
表明该表达式将在调用上下文的范围内扩展。
结构或值的扩展要用到以下语法。
%
返回一个值/表达式
%%
插入一个语句
let x = [% ...]
[%% ...]
let y =
let%... z = ... in
match%... ... with
| ...
| ... in
[%% if x]
let x = y
[%% else]
let x = z
[%% endif]
总之,你在任何时候看到的 [@ ...] [@@ ...] [% ...] [%% ...]
,都是扩展语言。
其函数式可非常直接地书写运算,无需样板代码。单体允许我们抽象上升到更高的运算,同时提供胶水函数。换句话说,单体是可编程的分号。
例如,下列命令式运算:
function example(x) {
if ( x == null ) return null;
x = x + 1;
if ( !isEven(x) ) return null;
return x;
}
同样可由单体函数式用option来表达:
type a' option =
| None
| Some of 'a
let return x = Some x
(* Bind infix operation, applies f to m *)
let (>>=) m f =
match m with
| Some x -> f x
| None -> None
(**
Map infix operation
Essentially the same as bind, but the inner function unwraps the value.
**)
let (>>|) m f = m >>= (fun x -> return (f x))
现在我们可以使用这些原语重新运行上述命令示例,如下图所示。
let add_one = ((+) 1)
let bind_even : int -> int option =
fun x -> if x mod 2 = 0 then Some x else None
let example x = x >>| add_one >>= bind_even;
OCaml has a `ppx` that makes writing monads much easier to follow, using the let syntax.
let%bind x = y in
f x
(* This compiles down to the following *)
y >>= (fun x -> f x)
本质上,这个语法是从let语句取值,放在bind-infix-call函数式左侧,将赋值插入lambda表达式。
Async函数在后台运行单子,但用的是Ivar。a' Ivar.t
本质上是一个只能填入一次的互斥量。一旦返回计算的结果值,就会填入Ivar。剩下的语法糖会取Ivar的值,并借Deferred
单体让数值通过。
确实有一个yield
函数,但是由于其有某些奇怪特性,我们尽量避免使用。相反,我们通常运行Deferred
约束之间的包裹值。
警告:需要注意的另一点是Async.Pipe
,它的操作本质上就像一个缓冲期。请勿使用。这是不安全的,因为其默认不限制缓存(内存溢出),奇怪函数会影响管道功能,产生古怪的行为。取而代之的是使用Strict_pipe,
它封装了管道,但为我们使用提供确定的保障。我们编写了一种更加个性化的助手Broadcast_pipe
,可允许单个管道连接到多个下游管道。此外,在编写之前的Linear_pipe
之时,我们尚未真正明白管道的缺陷,相比于更佳的Strict_pipe
和Broadcast_pipe
来说,前者现已过时。
● dockerfiles /
包含Docker相关脚本- TODO能够更好地解释这个内容
● docs /
这里有关于代码和贡献过程的文档。带有预排文档的文档网站位于frontend/website/docs.
● frontend/
所有与Mina前端UI和产品相关的代码
- wallet/
Mina钱包的源代码
- website/
代码为 https://minaprotocol.com
- docs /
关于加入Mina网络的文档和说明 https://minaprotocol.com/docs
- posts/
博客文章的Markdown文档
- src /
该网站的源代码
- static/
静态文件,如图像等。
● rfcs /
这个目录包含了所有根据RFC流程做出的接受的RFC(或“评论请求”)。
● scripts/
● src /
所有协议源代码,包括应用程序和库代码,都在这个目录中。
- opam
这些文件是我们的构建dune系统所需要的。lib中的每个库都必须有一个。当您创建库lib/foo_lib时,使用一个dune文件,将库的名称指定为foo_lib,您必须创建一个foo_lib.opam
文件。
- config/
构建时间配置——这些.mlh文件定义了编译时间常量及其值。
- app /
应用程序在这里。
- cli /
这是mina客户端/守护进程。它是您用来运行staker、snarker或发送和接收交易的简单客户机的工具。
- website/
很快就会被弃用的网站目录-大部分代码已经迁移到前端/网站/
- reformat/
这个程序在源代码树中的大多数文件上运行ocamlformat,只有少数例外。
- logproc /
这个实用程序从stdin读取数据,可以过滤和打印mina守护进程发出的日志消息。
- libp2p_helper /
这个程序使用go-libp2p来实现Mina守护进程所需要的点对点管道。
- external/
外部库的本地副本,我们需要做一些调整。
- lib /
所有库都支持mina。这里的库基本上分为两类。
index | hexa | symbol | coin |
---|---|---|---|
12586 | 0x8000312a | MINA | Mina |
m / purpose' / coin_type' / account' / change / address_index
密钥对派生时,只改变account
,同时保持change
和address_index
为0
m / 44' / 12586' / account' / 0 / 0
account | path |
---|---|
0 | m/44'/12586'/0'/0/0 |
271 | m/44'/12586'/271'/0/0 |
一个好的拉取请求:
● 做了一件事(新特性,bug修复,等等)
● 为任何新功能添加测试和文档
● 当修复一个bug时,添加或修复可以捕获该bug的测试
● 是否遵循了样式指南?
● 签名有意义吗?它们是最小的和可重复使用的吗?
● 有什么需要被更改的吗?
● 是否存在没有正确处理的错误情况?
● 对_exn函数的调用是否合理?它们不抛出异常的先决条件是否满足?它抛出的异常有用吗?
● 不应该有注释掉的代码。
● 没有零散的调试代码。
● 任何日志记录都是合适的。所有日志记录器。跟踪日志应该是无关紧要的,因为它们在默认情况下不会显示给任何人。
● 这个代码应该保存在它的库中吗?它应该放在另一个库吗?
● 代码让你感到迷惑吗?也许应该有一个评论,或者它应该有不同的结构。
● 一个行为改变,是否会打破对其他代码作出的假设?
我们的风格指南是对几个现有风格指南的扩展。第一个是ocamlformat,相当于我们编码风格的主要事实来源。实际上,ocamlformat是对持续集成(CI)的阻断,因此你的代码必须经过编程风格格式化以便并入母版页。但是ocamlformat并不会处理所有风格问题,因为它只是用来定义代码如何间隔和缩进。对于ocamlformat没有涉及到的内容,可以查阅Jane Street styleguide 。我们在此处阐释的这一风格指南旨在成为janestreet 风格指南的扩展,注重涉及我们代码库常用一些特定结构的细节。
若.ml
自动派生接口不同,则*.mli
文件不会被包括在前者中。我们代码库中许多*.ml
文件只包含签名和一个仿函数。在这种情况下,重新定义*.mli
文件没有意义,因为该文件中没有新信息或限制性信息。若一个*.ml
文件包含root结构上的实现,则多半可能会创建一个*.mli
文件。
t
,T和S
等是模块中常见的短名称,用来表达特定含义。
t
用来指模块的root类型。例如,若Account
模块中包含了与账户有关的类型和值,则Account.t
就是账户的类型。若模块的root类型应只有一个值,t还可用作一个值。如,若你想在Logger模块里包含单个全局记录器,Logger.t
可作为全局记录器的类型,其值就是Logger.t
类型的全局记录器值。
T
用于简述一个模块的root类型相关的root类型和基本定义。在你想实例化一个模块的root类型的一些仿函数,让该模块本身有实例的情况下,这是一种常用方法。例如,我们通常将Comparable.Make
称为仿函数以便衍生不同的comparable类型的helper值或模块。在这种情况下,若我们又有一个Account
模块,并且想衍生Comparable.S
签名,则我们会在定义一个root类型t
的Account
中定义一个T模块以及Comparable.Make
仿函数实际参数(在这种情况下为compare
)所需函数。有了T
模块,我们则可以将include T
和include Comparable.Make (T)
包含在Account
模块中,以便向Account.t类型引入所有相关值或模块。下面是一个完整例子:
module Account = struct
module T = struct
type t = ... [@@deriving compare]
end
include T
include Comparable.Make (T)
end
模块类型名S
用于定义一个模块的root签名,这种方法最常用于当你有一个包含一个仿函数的模块的情况。在此情况下,我们往往定义Make
这个函数,并声明这个函数的返回值的类型为S
,将这些值放在同一个模块里。回看我们之前的例子,Core_kernel
的Comparable
模块遵循了这个模式:Comparable.Make
是一个返回值为Comparable.S
的仿函数。
根据一般经验,每一个模块都应限定单一类型。这种模式有助于分离关注点,并且反过来允许值的名称变得更短,因为它们都被固定在上下文中。以Merkle_tree
模块为例,该模块需要一个Merkle_tree.t
的类型,该类型代表着整个树算法(或上面的一个节点)。一个Merkle_tree
也需要一个path
类型。最好将path 类型放在其嵌套的模块中(Merkle_tree.Path.t
而不是Merkle_tree.path
)。现在,Merkle_tree
包含的值(函数)不仅与树算法类型本身有关,还与树算法的path
有关。为了清楚起见,自然而然假设所有的值名称与path_
类型的路径有关(如path_map,path_length
等)。通过限定Path自己的模块,我们可以缩短这些名称,值的上下文保持清楚。此外,如果我们选择这种方式,可以压缩 Path
的实现细节,使用一个限定性签名,通过编译器实施会让关注点分离更清楚。
我们代码库明确没有monkey补丁模块。monkey补丁的定义是将现有的模块用扩展或修改值重新定义。更简单的说,只是表单上的改变。
module A = struct
module M = struct
let x = ...
end
end
module M = struct
include A.M
let y = ...
(* or `let x = ...` *)
end
有时monkey补丁或许是编译代码的最简单方法,但总的来说,会造成代码库的混乱和/或技术债务。若你需要对一个模块运行monkey补丁,前提下你应有充分的理由。
仿函数所产生的模块签名的With语句签名应尽可能地限定在S with module M1 = M2
表单中。代替等式:=
应限于签名比例需限定的情况下(比如,签名的一个嵌套模块在现有的结构范围内已有定义的情况)的include语句中。此外,也不会优先考虑S with type t = ...
表单,因为随着一个仿函数所涉及的签名之间的一般依赖数目提升,该表单表现不佳,要注意这点更加强调janestreet指南风格中“优先选择标准签名而非手写接口”的规定。
仿函数最多可有3个元数(元数是实际参数的数量;在这种情况下,指的是嵌套仿函数的数量,嵌套仿函数指的是返回值为仿函数的仿函数)。若一个仿函数需要3个以上模块作为实际参数,则所需模块应嵌套进一个模块中。其标准模式是为你的仿函数定义一个Inputs_intf
签名,这反过来会定义仿函数的模块实际参数。如下所示:
module type Inputs_intf = sig
module A : A.S
module B : B.S
module C : C.S
module D : D.S
end
module type S = sig
include Inputs_intf
(* ... *)
end
module Make (Inputs : Inputs_intf)
: S
with module A = Inputs.A
and module B = Inputs.B
and module C = Inputs.C
and module D = Inputs.D =
struct
open Inputs
(* ... *)
end
我们采用了OCaml的独特风格。以下是一些要点。
type ('payload, 'pk, 'signature) t_ =
{payload: 'payload; sender: 'pk; signature: 'signature}
[@@deriving eq, sexp, hash]
type t = (Payload.t, Public_key.t, Signature.t) t_
[@@deriving eq, sexp, hash]
(* ... *)
type var = (Payload.var, Public_key.var, Signature.var) t_
我们用所有类型的记录字段的类型定义基本类型变量t_
。然后用这些类型变量定义记录。最终,我们用type t
将记录实例化,这就是OCaml类型。此外,在一个SNARK(简短无交互证明)电路中type var
是这个值的类型。我们稍后会就这点展开介绍。无论我们想在SNARK(简短无交互证明)电路中程序化什么内容,我们通过这种方式来定义,以便跨越两种类型重复利用记录定义。
还有一些说法是转向OCaml对象类型因此不必去处理位置实参。或许我(@bkase)将不定时详写一份与之相关的RFC。
type t = int [@@deriving sexp, eq]
这是我们首次看到宏指令。此处我们用了ppx_jane的 sexp
,ppx_deriving的eq
。
module Stable : sig
module V1 : sig
type t = (* ... *)
[@@deriving bin_io, (*...*)]
end
end
对我们而言,只要是可序列化的类型,一旦发布了稳定版,保持向后兼容非常重要。理想情况下,我们不会以bin_io
定义除Stable.V1
外的任何类型定义。若我们改变数据类型结构,我们会在Stable
之下创建一个V2
。
Core运行着我们尽可能在单元测试的时候使用的QuickCheck。举一个付款的Quickcheck.Generator.t
的签名示例。
(* Generate a single payment between
\* $a, b \in keys$
\* for fee $\in [0,max_fee]$
\* and an amount $\in [1,max_amount]$
*)
val gen :
keys:Signature_keypair.t array
-> max_amount:int
-> max_fee:int
-> t Quickcheck.Generator.t
类型安全不变式(帮助命名这一字段)
关于Mina,很多时候我们需要对特定数据组进行重要检测。例如。我们需要确认在网络上收到的用户命令的签名是有效的。这样的检测可能非常昂贵,因此我们只想检测一次。但是我们要记得已经做过的检测。
(* inside user_command.mli *)
module With_valid_signature : sig
type nonrec t = private t [@@deriving sexp, eq]
(*...*)
end
val check : t -> With_valid_signature.t option
现在我们定义With_valid_signature
(常用的是User_command.With_valid_signature.t
),利用type nonrec t = private t
允许其向上转型至User_command.t
,但是避免其向下转型。将User_command.t
转化为User_command.With_valid_signature.t
的唯一方法是对它进行check
。于是编译器会捕捉我们的错误。
我们使用 ppx_inline_test来进行单元测试。当然只要条件允许,我们会将其与QuickCheck
相结合。
let%test_unit =
Quickcheck.test ~sexp:[%sexp_of: Int.t] Int.quickcheck_generator
~f:(fun x -> assert (Int.equal (f_inv (f x)) x))
我们正在转化成使用模块签名等式——见the above section和 the rfc for rationale,但是我们仍然有大量使用类型替换(with type foo := bar
)的代码。
首先我们定义仿函数的结果模块类型,将所有我们要处理的仿函数抽象化。
module type S = sig
type boolean_var
type curve
type curve_var
(*...*)
end
然后我们定义这个仿函数:
module Schnorr
(Impl : Snark_intf.S)
(Curve : sig (*...*) end)
(Message : Message_intf
with type boolean_var := Impl.Boolean.var
(*...*))
: S with type boolean_var := Impl.Boolean.var
and type curve := Curve.t
and type curve_var := Curve.var
(*...*)
= struct
(* here we implement the signature described in S *)
end
自定义SNARK(简短无交互证明)电路逻辑
这也是我们首次提到自定义SNARK(简短交互证明)电路逻辑。我们所使用的模式适用于你想在子模块module Checked
下的SNARK中运行的所有操作。
例如,在sgn.mli 中,我们可以看到:
(* ... *)
val negate : t -> t
module Checked : sig
val negate : var -> var
end
negate
是OCmal中执行的函数版本,Checked.negate
是在一个SNARK(简短交互证明)电路中执行的版本。
Mina 沙盒节点将使您能够测试并熟悉协议的核心特性,并在稳定的环境中构建工具。它是一个使用和现有测试网相同配置的单节点私有网络。这个沙盒支持多个帐户,在它们之间发送交易,还将支持执行SNARK工作、委托和质押。事实上,由于这是一个单节点网络,你将获得所有的区块奖励!
沙盒不能将您连接到一个运行中网络。
Docker是一个可移植运行应用程序的工具。Docker打包了Mina 沙盒节点。它很容易安装——我们建议使用Docker桌面。
安装Docker后,运行以下命令启动Mina沙盒。
docker run \
--publish 3085:3085 \
-d \
--name mina \
minaprotocol/mina-demo:sandbox-32a.1
这个命令将在docker容器内启动一个后台程序,并向您的计算机公开GraphQL端口(3085),该端口用于与客户端通信。这个后台程序将自动在后台运行,并带有块生成器和snark worker。
您可以通过执行操作查看日志。
docker logs --follow mina
运行日志来停止mina。
docker stop mina
您可以使用Mina CLI与沙盒节点交互。下面的命令在docker容器中打开一个shell,在那里你可以发出任何可用的mina命令。
docker exec -it mina bash
容器有一个具有此公钥的帐户:
B62qrPN5Y5yq8kGE3FbVKbGTdTAJNdtNtB5sNVpxyRwWGcDEhpMzc8g
该帐户的密码是空字符串(没有密码——可以将密码字段留空)。
现在你可以用你的沙盒做以下几件事:
● 像往常一样安装Mina并使用许多客户端命令。因为这个后台程序已经在容器中运行了,所以你不需要运行mina后台程序!
● 安装GUI钱包应用程序以使用图形界面到您的节点。在安装过程中输入‘127.0.0.1’作为节点的主机。
● 请访问http://localhost:3085/graphql直接使用GraphQL API。
● Mina API仍在构建中,因此这些端点可能会发生变化。
● 默认情况下,GraphQL端口会绑定到本地主机。向互联网公开GraphQL API这一操作使得任何人都可以从已知帐户将Mina发送给守护进程。
Mina守护进程会公开一个GraphQL API,这是用于向运行中的节点请求信息并向其提交命令的。默认情况下,HTTP服务器使用3085端口,可以通过守护进程启动命令中的-rest-server参数来进行配置。
要使用GraphQL API,请将GraphQL客户机连接到http://localhost:3085/graphql或在浏览器中打开以使用GraphQL IDE。默认情况下,为了安全起见,本项操作只允许来自本地主机的连接。如果要监听所有接口,守护进程的启动命令中添加-insecure-rest-server参数。
除了关于正在运行的节点信息之外,GraphQL API还可以将网络最新块的数据返回。然而,由于区块链的历史状态没有在Mina中持久化,只有在节点的转换边界中的区块才能返回,也就是最后的k块。对于其他历史数据来说,应该使用归档节点,而该节点的设计目的是保留和检索历史数据。
这里提供了完整的Mina GraphQL模式。
Mina GraphQL API有许多从运行节点中提取数据的查询。GraphQL查询允许指定响应的数据。例如,获取守护进程已知的最新块和创建者信息:
query {
bestChain(maxLength: 1) {
creator
stateHash
protocolState {
consensusState {
blockHeight
}
previousStateHash
}
transactions {
coinbase
}
}
}
下面的请求是在查询交易池中所有待处理的交易及其手续费。此查询可用于估算交易的建议费用:
query {
pooledUserCommands {
id,
fee
}
}
交易返回的memo字段是经过Base58Check编码的。
为了获取主mina代币帐户的详细信息:
query {
account(publicKey: "<B62...>") {
balance {
total
}
delegate
nonce
}
}
您可以从节点的命令行提交GraphQL请求。例如,使用cURL获取节点已知的最后10个区块创建者:
curl -d '{"query": "{
bestChain(maxLength: 10) {
creator
}
}"}' -H 'Content-Type: application/json' http://localhost:3085/graphql
GraphQL突变以某种方式修改正在运行的节点。例如,突变可以用于发送支付、创建新帐户或添加额外的对等节点。
查询GraphQL模式以获取所有可用的突变。
添加一个新的对等节点:
mutation {
addPeers(peers:{
libp2p_port:10511,
host:"34.73.68.198",
peer_id:"12D3KooWSJB2gZWi3ruVmtTF9JBCEBpCrJfuWCWzzRr8mMQWFQ9U"
})
}
更新一个snark工作者要支付0.1 mina的费用:
mutation {
setSnarkWorkFee(input: {fee: "100000000"})
}
GraphQL订阅允许在事件发生时向GraphQL客户机推送数据。在Mina中,有以下若干订阅情况:
● newSyncUpdate——当节点的同步状态改变时发生。
● newBlock -当接收到一个新的区块时发生。
● chainReorganisation——当节点的最佳TIP以一种不正常的方式改变时发生。
例如,订阅所有产生的新区块:
subscription {
newBlock {
creator
stateHash
protocolState {
consensusState {
blockHeight
}
previousStateHash
}
}
}
新的区块订阅也可能被限制为只返回由已定义的带publicKey参数的公钥创建的新区块。
虽然不建议向网络公开GraphQL API,但可以使用一个GraphQL代理实现来删除那些构成威胁的查询,但仍然允许您从公共端点查询节点的数据。
Mina Client Javascript SDK允许生成密钥对、签名和验证消息,以及可以将签名交易在网络上进行广播。
该项目包含Typescript和ReasonML的类型,但也可以从普通的NodeJS中使用。有以下几种方法:
● genKeys -生成公钥/私钥对。
● derivePublicKey -导出对应私钥的公钥。
● signMessage -对任意消息进行签名。
● verifyMessage -验证签名是否与消息匹配。
● signPayment -使用私钥对支付进行签名。
● signstakes delegation -使用私钥对权益委托进行签名。
客户端SDK允许在从不向网络公开私钥的设备上生成和进行交易签名。
yarn add @o1labs/client-sdk
\# or with npm:
npm install --save @o1labs/client-sdk
import * as MinaSDK from "@o1labs/client-sdk";
let keys = MinaSDK.genKeys();
let signed = MinaSDK.signMessage("hello", keys);
if (MinaSDK.verifyMessage(signed)) {
console.log("Message was verified successfully")
};
let signedPayment = MinaSDK.signPayment({
to: keys.publicKey,
from: keys.publicKey,
amount: 1,
fee: 1,
nonce: 0
}, keys);
const MinaSDK = require("@o1labs/client-sdk");
let keys = MinaSDK.genKeys();
let signed = MinaSDK.signMessage("hello", keys);
if (MinaSDK.verifyMessage(signed)) {
console.log("Message was verified successfully")
};
let signedPayment = MinaSDK.signPayment({
to: keys.publicKey,
from: keys.publicKey,
amount: 1,
fee: 1,
nonce: 0
}, keys);
● 安装 gentype: yarn add -D gentype
● 安装 bs-platform: yarn add -D bs-platform
● 编译依赖: yarn bsb -make-world
module MinaSDK = O1labsClientSdk.CodaSDK;
let keys = MinaSDK.genKeys();
let signed = MinaSDK.signMessage(. "hello", keys);
if (MinaSDK.verifyMessage(. signed)) {
Js.log("Message was verified successfully");
};
let signedPayment = MinaSDK.signPayment({
to_: keys.publicKey,
from: keys.publicKey,
amount: "1",
fee: "1",
nonce: "0"
}, keys);
当产生交易时,您需要指定帐户。您可以通过CLI使用mina advanced get-nonce -address </public_key>
或使用GraphQL来获得这个账户。
Links need to be raplaced
https://docs.minaprotocol.com/en/developers/graphql-api
Mina日志位于配置目录~/. Mina -config中。该目录包含以下日志文件:
● mina.log -该文件包含守护进程的最新日志。每个日志文件的大小限制为10兆字节(MiB),并可以在50个日志文件之间轮换。轮转后的日志文件命名为mina.log.x。0到mina.log.50。
● mina-best-tip.log -这个文件包含守护进程的最佳tip日志。这个文件使得从节点更容易地去收集所需的日志,从而确定任何硬分叉的最佳状态。该文件的大小被限制为5兆字节(MiB),并可在单个日志文件mina-best-tip.log.0上进行旋转。
● mina-prover.log -这个文件包含关于验证程序内存使用情况和批量处理大小的日志。它被限制为128兆字节(MiB),并在单个日志文件上进行轮转。
● mina-verifier.log -这个文件包含验证器的内存使用和批量处理大小的日志。它被限制为128兆字节(MiB),并在单个日志文件上进行旋转。
要跟踪日志,请使用以下命令之一,这取决于您如何启动守护进程:
● Docker: Docker log --follow mina
● 作为一个服务运行:journalctl --user -u mina -n 1000 -f
● Other: tail -f ~/.mina-config/mina.log
Mina守护进程按照以下优先级顺序,对以下级别的日志进行了记录:
● Spam,垃圾邮件
● Trace,跟踪
● Debug,调试
● Info,信息
● Warn,警告
● Error,错误
● Fatal,致命
指定一个Info级别的log-level 将只显示来自该日志级别及以上的消息,以及显著的减少日志的详细信息。
日志的格式定义如下:
{
"timestamp": timestamp,
"level": level,
"source": {
"module": string,
"location": string
},
"message": format_string,
"metadata": {{...}}
}
错误信息的示例如下:
{
"timestamp": "2020-12-23 21:25:04.616526Z",
"level": "Error",
"source": {
"module": "Transition_router",
"location": "File \"src/lib/transition_router/transition_router.ml\", line 231, characters 12-24"
},
"message": "Failed to find any peers during initialization (crashing because this is not a seed node)",
"metadata": {
"host": "48.241.118.000",
"peer_id": "12D3KooWGQSPgo7ypy9717D3Vgz2RHaqB2ndDRHEFcAfJDAv284q",
"pid": 12,
"port": 8302
}
}
Mina的守护进程提供了一个CLI助手,用于压缩和打包来自节点的可用日志文件。
对于正在运行的守护进程,可以运行mina client export-logs,其中可以传递一个可选的标志-tarfile来指定tar存档文件的文件名,该文件名默认为当前日期-时间。该命令压缩可用的日志,并将它们导出到~/ mina-config目录中的exported_logs目录之下。
您可以通过GraphQL从运行中的节点导出日志,从而使用exportLogs变种。
如果需要从未运行的节点导出日志,可以使用mina client export-local-logs命令压缩可用的本地日志文件。通过tarfile选项指定tar归档文件的文件名,日志将被压缩并导出到~/中的exported_logs目录。mina-config目录。
在GitHub上创建或更新问题时,可以将导出的tar.gz文件直接上传到问题中。
在启动守护进程时可以设置以下选项来定制日志记录行为:
● -file-log-level日志文件的日志级别(默认为Trace)
● -log-level -通过标准输出使之输出到终端的日志级别(默认为Info)
● -log- JSON -以JSON的形式打印日志输出(默认为纯文本)
● -log-block-creation -记录在一个区块中包括交易和snark工作的步骤(默认:true)
● -log-received-blocks -从对等节点接收到的日志区块数(默认为false)
● -log-snark-work-gossip -log从对等节点收到的snark-pool差异(默认为false)
● -log-txn-pool-gossip -从对等节点收到的交易池差异日志(默认:false)
● -log-precompute -blocks -在日志中包含预计算的区块(默认为false)
如果守护进程崩溃但能够使用,那么它将生成一个崩溃报告。只有最新的崩溃报告将被保留,文件名表示崩溃的日期时间,并且压缩文件将被导出到~/.mina-config 目录之中。您可以上传这个崩溃报告到任何有关GitHub问题中,确保有新的内容和可供调试的援助。
崩溃报告将包含以下部分或全部日志文件:
● crash_summary.json -包含崩溃瘫痪的摘要,包括系统信息、版本信息和最终的日志输出。
● mina_status.json -一个mina client status命令的输出,它是守护进程最终状态的摘要。
● mina_short.log -包含mina.log文件的最后4MiB。
● registered_mask.dot-包含最新的账本分类账可视化。
● frontier.dot -包含最新的前端前沿可视化。
● daemon.json一个守护进程配置文件的副本。
Mina是一个开源项目,其使命是建立一个包容和可持续的社区驱动协议。因此,Mina欢迎任何希望进一步实现这一目标的人提供贡献。
目前,该协议正在开发中,将从编写代码、用户测试、文档和社区支持等形式的支持中受益。下面,您将看到在这些领域中作出支持的具体说明。
如果有任何关于如何参与的一般性问题,请随时联系Discord server上的Mina社区。
Mina公开测试网beta已经上线。技术技能不是必需的,我们鼓励任何对加密货币和区块链感兴趣的人参与进来。
具体来说,我们需要在报告故障、验证经济假设、测试节点基础设施、普遍参与共识和压缩区块链等方面获得帮助。请加入Discord server,并检查#testnet-general和#mentor-nodes通道而后加入进来。您也可以在这里注册初始计划,我们会随时更新您的消息!
前往测试网络登陆页面,看看如何参与每周挑战,并获得测试网络排行榜。
Mina是完全开源的,代码是按照Apache License 2.0的条款进行发布的。
O(1)实验室为某些与Mina开发相关的项目提供资助——请访问Mina资助页面了解更多细节。
目前,这些项目主要集中在编程方面,但更多的项目将加入设计和社区开发的领域。如果您对资助计划有任何疑问,请联系Discord。
如果您注意到任何违反行为准则的行为,请按照该文件中的报告指导方针提交报告,并将任何不良行为反应到社区。
如果您在协议中遇到任何严重的错误或漏洞,请向security@minaprotocol.com报告。对于小故障和问题,请在Github上进行创建。
我们使用一种特定的OCaml样式。这里有一些重要的事情。
type ('payload, 'pk, 'signature) t_ =
{payload: 'payload; sender: 'pk; signature: 'signature}
[@@deriving eq, sexp, hash]
type t = (Payload.t, Public_key.t, Signature.t) t_
[@@deriving eq, sexp, hash]
(* ... *)
type var = (Payload.var, Public_key.var, Signature.var) t_
我们用所有类型的记录字段的类型变量定义基类型t_。然后,我们使用这些类型变量来定义记录。最后,我们用类型t实例化记录,这是OCaml类型。也可以输入var这是在SNARK电路中值的类型。稍后我们将详细介绍这一点。每当我们想要在一个SNARK电路中进行编程时,我们就以这种方式定义它,这样我们就可以跨两种类型重用记录定义。
有人说要使用OCaml对象类型来做这类事情,这样我们就不需要处理位置参数了。也许我(@bkase)会在某个时候为此写一个RFC。
type t = int [@@deriving sexp, eq]
这是我们第一次看到宏观层面。这里我们使用ppx_jane中的sexp和ppx_derivative中的eq。
module Stable : sig
module V1 : sig
type t = (* ... *)
[@@deriving bin_io, (*...*)]
endend
只要类型是可序列化的,一旦我们有了稳定的发行版,就必须保持向后兼容性。理想情况下,我们不会在Stable.V1之外的任何类型上定义bin_io。当我们改变数据类型的结构时,我们将在Stable下创建一个V2。
Core有一个QuickCheck的实现,我们可以在单元测试中随时使用它。下面是支付的Quickcheck.Generator.t的签名示例。
(* Generate a single payment between
\* $a, b \in keys$
\* for fee $\in [0,max_fee]$
\* and an amount $\in [1,max_amount]$
*)
val gen :
keys:Signature_keypair.t array
-> max_amount:int
-> max_fee:int
-> t Quickcheck.Generator.t
通常在Mina中,我们需要对特定的数据片段执行非常重要的检查。例如,我们需要确认签名对我们通过网络接收到的用户命令是有效的。这样的检查可能很昂贵,所以我们只想做一次,但我们要记住我们已经做过了。
(* inside user_command.mli *)
module With_valid_signature : sig
type nonrec t = private t [@@deriving sexp, eq]
(*...*)end
val check : t -> With_valid_signature.t option
这里我们定义了With_valid_signature(用法是User_command.With_valid_signature.t),使用类型nonrec t = private t来允许向上转换到User_command.t,但防止向下投射。将一个User_command转换成一个User_command.With_valid_signature.t的唯一办法就是是检查它。现在编译器会发现我们的错误。
我们使用ppx_inline_test进行单元测试。当然,只要有可能,我们就会把它和QuickCheck结合起来。
let%test_unit =
Quickcheck.test ~sexp:[%sexp_of: Int.t] Int.quickcheck_generator
~f:(fun x -> assert (Int.equal (f_inv (f x)) x))
我们正处于迁移去使用模块签名等式的过程中——基本原理见上一节和rfc,但是我们仍然有很多使用类型替换(foo:= bar类型)的代码。
首先,我们定义functor的结果模块类型,然后我们要保持functor的所有类型都是抽象的。
module type S = sig
type boolean_var
type curve
type curve_var
(*...*)end
然后定义函子:
module Schnorr
(Impl : Snark_intf.S)
(Curve : sig (*...*) end)
(Message : Message_intf
with type boolean_var := Impl.Boolean.var
(*...*))
: S with type boolean_var := Impl.Boolean.var
and type curve := Curve.t
and type curve_var := Curve.var
(*...*)
= struct
(* here we implement the signature described in S *)end
这也是我们第一次看到自定义SNARK电路逻辑。我们一直使用的一个模式是将您想要在检查的子模块下的SNARK中运行的所有操作限定为范围。
例如,在sgn.mli 内部我们看到:
(* ... *)val negate : t -> t
module Checked : sig
val negate : var -> varend
negate是在OCaml中运行并被检查的函数的版本。Checked.negate是在snark电路中运行的。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!