准备账户

在星云链上,每个地址表示一个唯一的账户,一一对应。

在发送交易前,我们需要准备两个账户:一个账户用来发送代币 (称为"from") 和另一个账户来接受代币 (称为"to").

发送者账户

在这里,我们将会使用配置文件conf/default/genesis.conf中预分配过代币的账户中选择一个作为发送者账户,我选择:n1XkoVVjswb5Gek3rRufqjKNpwrDdsnQ7Hq。

接受者账户

我们使用如下指令创建一个全新的账户来做接受者,请记住输入的密码。

$ ./neb account new
Your new account is locked with a passphrase. Please give a passphrase. Do not forget this passphrase.
Passphrase:         #设置密码
Repeat passphrase:  #确认设置的密码

执行结果:

Address: n1UCDvzgjk5mPCtFtcpjYQWnnaVx8a8g5rL

n1UCDvzgjk5mPCtFtcpjYQWnnaVx8a8g5rL即为新账号,新账户的keystore文件将会被放置在$GOPATH/src/github.com/nebulasio/go-nebulas/keydir/内。

Keystore 文件是以星云链存储私钥的一种文件格式 (JSON)。它使用用户自定义密码加密,以起到一定程度上的保护作用,而保护的程度取决于用户加密该钱包的密码强度,如果类似于 123456 这样的密码,是极为不安全的。 在使用 Keystore 时有两点需要注意:1. 使用不常用,并且尽可能复杂的密码加密 Keystore 文件;2. 一定要记住加密 Keystore 的密码,一旦忘记密码,那么你就失去了 Keystore 的使用权

Keystore内容如下:

{
    "address": "n1UCDvzgjk5mPCtFtcpjYQWnnaVx8a8g5rL",
    "crypto": {
        "cipher": "aes-128-ctr",
        "ciphertext": "52a9403411fecf7b98f16b485634450f2087fa99d70ac8bfe883bae8b96e4d7d",
        "cipherparams": {
            "iv": "9ae06485a0c1e3d853e7383c518e0804"
        },
        "kdf": "scrypt",
        "kdfparams": {
            "dklen": 32,
            "n": 4096,
            "p": 1,
            "r": 8,
            "salt": "845fb918f7bf28ac514cd5dd716cef97948ff248a307a24d820607ff4e75f7e1"
        },
        "mac": "8d26f96b9c8c300db97858f306677bed3b33e017425aad4292eb3b02cfc0ab1f",
        "machash": "sha3256"
    },
    "id": "5121caf3-e328-434c-b91f-8b3c2aa1f258",
    "version": 4
}

启动私有链

见《星云链智能合约开发(三):运行星云链》

星云链交互,在链上发送交易

星云链提供给开发者HTTP API, RPC API和CLI来和运行中的星云节点交互,HTTP API(API Module | Admin Module)。星云链的HTTP服务默认端口号为8685。

检查账户状态

我们可以通过API Module中的/v1/user/accountstate接口来获取账户信息

使用curl工具,命令行输入:

curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE"}'

执行结果:

{
    "result": {
        "balance": "5000000000000000000000000",
        "nonce": "0",
        "type": 87
    }
}
  • Type用于标记账户类型。88表示该账户为智能合约账户,部署一个合约之后,就可以得到一个合约账户。87表示非合约账户,我们通过./neb account new创建的账户就是非合约账户,用户存储链上资产。
  • Nonce用于标记账户发起的所有交易的顺序。同一个账户,每发起一个新的交易,Nonce就加一,初始为0,第一个交易的Nonce为1。
  • balance为账户余额,5000000000000000000000000(5 * 10^24)表示5NAS

发送交易

Nebulas提供了三种方式去发送我们的交易:

  • 签名 & 发送
  • 密码 & 发送
  • 解锁 & 发送

    签名 & 发送

    使用这种方式,我们可以在离线环境下先使用私钥签名好交易,然后把签好名的交易在联网的机器上发出。这是最安全的发送交易的方式,私钥可以完全离线保存,不触网。Web-Wallet正是基于Neb.js采用这种方法发送的交易。

首先,我们使用Admin Module中的v1/admin/sign接口给准备发的交易签名,得到交易的二进制数据。

curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/sign -d '{"transaction":{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":1,"gasPrice":"1000000","gasLimit":"2000000"}, "passphrase":"passphrase"}'

执行结果:

{
    "result": {
        "data": "CiAbjMP5dyVsTWILfXL1MbwZ8Q6xOgX/JKinks1dpToSdxIaGVcH+WT/SVMkY18ix7SG4F1+Z8evXJoA35caGhlXbip8PupTNxwV4SRM87r798jXWADXpWngIhAAAAAAAAAAAA3gtrOnZAAAKAEwuKuC1wU6CAoGYmluYXJ5QGRKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAAehIBYAWJBVVuRHWSNY1e3bigbVKd9i6ci4f1LruDC7AUtXDLirHlsmTDZXqjSMGLio1ziTmxYJiLj+Jht5RoZxFKqFncOIQA="
    }
}

在发送交易时,对于同一个账户,只有当他Nonce为N的交易上链后,Nonce为N+1的交易才能上链,有严格的顺序,Nonce必须严格加1。可以通过GetAccountState接口查看最新的Nonce。

然后,我们将签好名的交易原始数据提交到本地私有链里的星云节点,下面的data字段的内容就是上面产生的。

curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/rawtransaction -d '{"data":"CiAbjMP5dyVsTWILfXL1MbwZ8Q6xOgX/JKinks1dpToSdxIaGVcH+WT/SVMkY18ix7SG4F1+Z8evXJoA35caGhlXbip8PupTNxwV4SRM87r798jXWADXpWngIhAAAAAAAAAAAA3gtrOnZAAAKAEwuKuC1wU6CAoGYmluYXJ5QGRKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAAehIBYAWJBVVuRHWSNY1e3bigbVKd9i6ci4f1LruDC7AUtXDLirHlsmTDZXqjSMGLio1ziTmxYJiLj+Jht5RoZxFKqFncOIQA="}'

执行结果:

{
    "result": {
        "txhash": "1b8cc3f977256c4d620b7d72f531bc19f10eb13a05ff24a8a792cd5da53a1277",
        "contract_address": ""
    }
}

密码 & 发送

如果你信任一个星云节点帮你保存keystore文件,你可以使用第二种方法发送交易。

首先,上传你的keystore文件到你信任的星云节点的keydir文件夹下,我们前面在本地创建的账号的keystore自动存储在keydir下。

然后,我们发送交易的同时,带上我们keystore的密码,在被信任的节点使用SendTransactionWithPassphrase接口上一次性完成签名和发送过程。

curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -d '{"transaction":{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":2,"gasPrice":"1000000","gasLimit":"2000000"},"passphrase":"passphrase"}'

执行结果:

{
    "result": {
        "txhash": "3cdd38a66c8f399e2f28134e0eb556b292e19d48439f6afde384ca9b60c27010",
        "contract_address": ""
    }
}

解锁 & 发送

这是最危险的发送交易的方法。除非你完全信任一个星云节点,否则不要使用这种方法来发送交易。

首先,上传你的keystore文件到你信任的星云节点的keydir文件夹下。

然后,使用你的keystore文件的密码,在指定的时间范围来在被信任的节点上使用Unlock接口解锁账户。时间单位为纳秒,300000000000为300s,表示300s也就是5分钟内账户都处于解锁状态,直接进行操作不再需要验证。

curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/unlock -d '{"address":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","passphrase":"passphrase","duration":"300000000000"}'

执行成功后的结果:

{"result":{"result":true}}

然后,发送交易

curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transaction -d '{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":3,"gasPrice":"1000000","gasLimit":"2000000"}'

执行结果:

{
    "result": {
        "txhash": "8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf",
        "contract_address": ""
    }
}

交易收据

不论使用的哪一种方法发送交易,我们都会得到两个返回值,txhash和contract_address。其中txhash为交易hash,是一个交易的唯一标识。如果当前交易是一个部署合约的交易,contract_address将会是合约地址,调用合约时都会使用这个地址,是合约的唯一标识。

使用txhash我们可以查看交易收据,知道当前交易的状态。

curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/getTransactionReceipt -d '{"hash":"8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf"}'

执行结果:

{
    "result": {
        "hash": "8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf",
        "chainId": 100,
        "from": "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE",
        "to": "n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
        "value": "1000000000000000000",
        "nonce": "3",
        "timestamp": "1524667888",
        "type": "binary",
        "data": null,
        "gas_price": "1000000",
        "gas_limit": "2000000",
        "contract_address": "",
        "status": 1,
        "gas_used": "20000"
    }
}

这里的status可能有三种状态值,0,1和2。

  • 0: 交易失败. 表示当前交易已经上链,但是执行失败了。可能是因为部署合约或者调用合约参数错误。
  • 1: 交易成功. 表示当前交易已经上链,而且执行成功了。
  • 2: 交易待定. 表示当前交易还没有上链。可能是因为当前交易还没有被打包;如果长时间处于当前状态,可能是因为当前交易的发送者账户的余额不够支付上链手续费。