# 1. 简介
Donut是一个离线的合约开发工具，实现通过简单命令完成合约编译、部署、发布，并支持单元测试框架，提高合约的开发、测试效率。

## 1.1 核心功能
1. 多合约链环境配置；
2. 支持复杂单元测试用例编写，自动化测试；
3. 支持合约部署、发布的依赖逻辑编写；
4. 支持Solidity和C++（wasm）合约开发；

# 2 环境准备
## 2.1 安装工具
- 安装 nodejs，[地址](https://nodejs.org/en/) (推荐版本: v10.11.0及以上版本)

## 2.2 准备SSL连接文件
连接到区块链平台，需要三个相关文件：ca.crt、client.key、client.crt。
如果您尚未在合约链申请证书，可按照 [申请证书](https://tech.antfin.com/docs/2/73764#%E7%94%B3%E8%AF%B7%E5%90%88%E7%BA%A6%E9%93%BE%E6%9D%83%E9%99%90) 的操作说明去生成和申请证书相关文件，引导过程中请记录相关密码，并下载自动生成的文件。
这三个文件的详细说明如下：
| 文件 | 说明 | 来源 |
| --- | --- | --- |
| ca.crt | 合约链的认证ca，客户端使用用来验证合约链服务身份 | BaaS平台下载 |
| client.key | RSA密钥 | 通过BaaS申请证书引导在线生成 |
| client.crt | RSA证书，与client.key是一对 | 通过BaaS申请证书引导在线生成 |

## 2.3 准备合约链的账户
在合约链上提交交易，需要使用一个已经在链上存在的账户，Donut需要使用账户的“**账户名称**”和“**私钥文件**”。账户可以通过BaaS平台申请创建。在 [申请证书](https://tech.antfin.com/docs/2/73764#%E7%94%B3%E8%AF%B7%E5%90%88%E7%BA%A6%E9%93%BE%E6%9D%83%E9%99%90) 的过程的第3步中，填写创建的“**账户名称**”（比如本文代码示例中为：'Tester001'），“账户公钥”和“恢复公钥”，其中与“账户公钥”对应的“user.key”文件就是账户的私钥文件，对此私钥文件做一次转换，方便在工具中使用：
```bash
openssl ec -in user.key -passin pass:${key_password} -passout pass:${key_password} -aes256 -out user.pem
```
>${key_password} 替换为私钥密码

## 2.4 查看合约链节点信息
为了与合约链交互，需要获取链节点的IP地址和端口号。在BaaS平台，通过查看目标合约链**详情**，在`区块浏览器`中查看**节点详情**可获取链节点的IP地址和端口号。

## 2.5 配置项说明
config.js 是Donut合约项目的配置文件，连接合约链的相关配置都在此配置文件中。

##### 整体配置说明：
| 选项 | 必选| 配置说明 | 配置举例说明 |
| -----| ----- | ----- | ----- |
|compileUrl | false | 对于C++合约，可以选择使用本地安装mycdt工具编译，或者使用远程编译服务，目前只支持本地mycdt工具编译，[mycdt安装参考](https://tech.antfin.com/docs/2/107547) |  'local'  |
|chain| true | 合约链的具体配置，是一个对象 |   |

##### 链配置项的具体说明：
| 选项 | 必选| 配置说明 | 配置举例说明 |
| -----| ----- | ----- | ----- |
| type | true | 连接合约链的协议说明，可配置为`https`或`tls` |
| host |  true | 区块链节点的ip或者host name <br>在使用tls时是ip地址 <br>使用https时是host name | '127.0.0.1'， <br>'https://www.alipay.com'或'https://127.0.0.1' |
| port |  true | 区块链节点开放连接的端口号，类型为：number，通常tls配置为'18130'，https配置为'18131' | 18130 |
| timeout | true | 与合约链交互连接的超时时间，单位是毫秒 |  30000 |
| ca | true | 合约链服务端的ca证书，需要使用文件流读入使用，注意使用相对路径| ca: fs.readFileSync("./certs/ca.crt", { encoding: "utf8" }) |
| cert  | true | 客户端的client.crt证书文件，需要使用文件流读入使用，注意使用相对路径 |  fs.readFileSync("./certs/client.crt", { encoding: "utf8" })  |
| key | true | 客户端client.key文件，用于SSL认证签名，需要使用文件流读入使用，注意使用相对路径 | key: fs.readFileSync("./certs/client.key", { encoding: "utf8" }) |
| passphrase | true | key配置的client.key文件的解密密码，在BaaS申请证书阶段设置此密码 | p123456#Abc |
| user | true | 合约链上的账户名，在BaaS申请证书阶段创建的账户名 |  Tester001  |
| userPemFile | true | 合约链上的账户名对应的密钥文件，被加密处理，其中存有账户的私钥，注意pem内容格式 | 参考：2.3 准备合约链的账户 |
| userPassword |  true | "userPemFile"配置文件的解密密码，在BaaS申请证书阶段或者创建账户时设置的密码 | p123456#Abc |



```json
  chain: {
   //  开发环境
    dev: {
      host: '11.158.53.113',   //目标合约链的节点IP地址
      port: 18130,                  //目标合约链的节点端口
      timeout: 30000,            //与链交互的超时时间
      ca: fs.readFileSync("./certs/ca.crt", { encoding: "utf8" }),
      cert: fs.readFileSync("./certs/client.crt", { encoding: "utf8" }),
      key: fs.readFileSync("./certs/client.key", { encoding: "utf8" }),
      passphrase: 'p123456#Abc',                          // key配置的client.key文件的解密密码
	  user: 'Tester001',                                            //链上账户名，在BaaS申请证书流程中创建
      userPemFile: '/certs/Tester001.pem',            // 配置用户pem文件路径，certs需要在config.js同级目录下
      userPassword: 'p123456#Abc',                      //"userPemFile"配置文件的解密密码
      gas: ''
    }
}
```

# 3. 使用步骤

1. 初始化项目
在命令行运行一下命令：
```
mkdir demo-contract
cd demo-contract
donut init
```
终端提示：
```
? Select the contract type of the project! (Use arrow keys)
❯ Solidity 
  C++ 
```
可根据情况选用目标合约语言，当前支持Solidity和C++合约。
其它选项根据情况选择填入，参考如下：
```
? Select the contract type of the project! Solidity
? What is the project name? demo
? What is the project version? 0.0.1
? project author:  gushui
? project description:  A demo contract project
The project "demo" was created successfully!
```

新建的合约工程初始结构：
├── config.js         // 配置项文件
├── contracts       // 合约文件所在目录
 │   └── Demo.sol
├── migrations
 │   └── 1_migrations.js  // 合约部署脚本，可定义合约部署逻辑，
├── package.json
└── test
    └── Demo.js     //单元测试用例编写脚本

2. 合约链环境配置 
在工程的根目录创建`certs`目录，并将“2.2 准备SSL连接文件”中获取的相关文件放置此目录中。
按照“2.5 配置项说明”来对`config.js`进行环境配置。
配置后的工程结构为：
.
├── certs
 │   ├── Tester001.key
 │   ├── Tester001.pem
 │   ├── ca.crt
 │   ├── ca.key
 │   ├── client.crt
 │   └── client.key
├── config.js
├── contracts
 │   └── Demo.sol
├── migrations
 │   └── 1_migrations.js
├── package.json
└── test
    └── Demo.js

3. 编译合约
```shell
>donut compile

Compile ...
{ contracts: { '/Demo.sol': { Demo: [Object] } },
  errors:
   [ { component: 'general',
       formattedMessage:
        'Warning: This is a pre-release compiler version, please do not use it in production.\n',
       message:
        'This is a pre-release compiler version, please do not use it in production.',
       ....

Compile: Demo.json make success!

```
注意编译后的目录结构，`build`目录存放了合约的编译结果：
.
├── build
 │   └── contracts
 │       └── Demo.json
├── certs
 │   ├── Tester001.key
 │   ├── Tester001.pem
 │   ├── ca.crt
 │   ├── ca.key
 │   ├── client.crt
 │   └── client.key
├── config.js
├── contracts
 │   └── Demo.sol
├── migrations
 │   └── 1_migrations.js
├── package.json
└── test
    └── Demo.js


4. 部署合约
```shell
>donut deploy

Deploy ...
env: dev(11.158.53.113:18130)
[ name1567831829784 ] tx hash: 0xc1f9d3feafcd2ef7b50b3af4e3609e9d906202da939737844fbb3b1a43dc5db1
[ name1567831829784 ] contract ID: 0xc860b10b61c69608cff4cd87746e234d5c28366bd4a5af40f503bbab02c6d3d8
Demo success

```

5. 运行单元测试

``` shell
>donut test
Test ...
env: dev(11.158.53.113:18130)

  Demo Test
[ name1567831487130 ] tx hash: 0xe44d39ca049183a675724ee97a80f1a491950be6251f81dec6292aff6b48d0ad
[ name1567831487130 ] contract ID: 0x92bd033cadba22fbe898dd3e207810b25e23348bcee56cb509c6c26dbd1f9eea
    ✓ deploy (3423ms)
    ✓ set(10) (3159ms)
    ✓ get() (3234ms)


  3 passing (10s)

```
