创建文章

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial Thumbnail
入门 · 30分钟

从准备开发环境到发送交易

本教程是开发人员在 Algorand 区块链上构建应用的入门指南。我们将从这里开始学习在 Algorand 上构建应用意味着什么,以及如何快速上手。

需要的工具和环境

接下来介绍有哪些开发需要用到的软件。

软件开发工具包 (SDK)

Algorand 正式支持四个应用开发 SDK:JavascriptJavaPythonGo。此外,还有社区提供的 SDK 帮助扩展开发范围。

可以参考 SDK 文档中的安装说明来安装您需要的 SDK。

命令行接口 (CLI) 工具

Algorand 提供三个打包进 Algorand 节点软件的命令行实用工具:goalkmdalgokey

goal 是最主要的节点软件,也可以管理密钥、签发交易、创建资产和执行 SDK 中可用的很多相同或相似功能。尽管不是应用创建必备,测试和验证期间将 goal 用作补充工具,还是会让运行节点的开发人员感到某种程度的顺畅。采用专用网络设置更高级的测试环境则必然需要 goal

kmd 是 Algorand 密钥管理进程的 CLI,而 algokey 是用于生成 Algorand 账户和签名交易的独立实用工具,常用做安全密钥签名的轻量级离线客户端。这两个工具不是入门必备,所以其使用细节我们在其他地方描述。

algodkmd 进程都有可以用来调用的 REST API。

Indexer

Algorand 提供独立的 algorand-indexer 进程,可从 Algorand 区块链读取已提交的区块,并维护着交易和账户的本地数据库,可供搜索和索引。应用开发人员还可以调用 REST API,对账户、交易、资产等执行丰富而高效的查询。

设置环境变量

如果使用 goal,建议设置 ALGORAND_DATA 环境变量,以免每个 goal 命令都需要指定这个变量。也推荐将 goalkmdalgokey 放入您的可执行程序路径(PATH)。这些文档中的示例假定您已经完成了上述两步设置。

这样你就可以使用:

$ goal node status

而不是:

$ goal node status -d <your-node-directory>

背景

在 Algorand 上开发应用意味着您的应用直接或间接读写 Algorand 区块链。写入 Algorand 区块链等同于发布交易,该交易随后将在一个区块中得到确认。从区块链读取意味着读回先前区块中已确认的交易。

以下是对一些术语和 Algorand 开发环境组件间关系的简要介绍。下面的图说明了这些组件,以及它们是如何组合在一起的。

Figure 1. Algorand Developer Environment

Algorand 区块链是个由节点组成的分布式系统,每个节点基于对区块历史和其间交易的验证维持自己的本地状态。状态数据由 algod 进程实现的共识协议维护,这个进程常被称为节点软件。作为开发人员,这很可能就是您应用程序的基层了。

应用通过 algod 客户端连接 Algorand 区块链。algod 客户端从连接欲交互网络的 Algorand 节点获取有效 algod REST 端点 IP 地址algod 令牌

步骤

1. 获得 algod 地址和 API Token

可采用三种推荐方式获取 algod REST 端点 IP 地址访问令牌,取决于您的目的,这三种方式各有利弊。下面分别阐述每种方式,并随附一张横向比较表。

1.采用第三方服务

如果您打算使用 SDK 或 algod RESTful 接口,且想要尽快连接,那推荐选择这种方式。

第三方服务运行节点,并通过其自有 API 密钥提供对节点的访问。注册后服务会向您提供 algod 地址和 API 密钥,此密钥将替代您的 algod 令牌。

可在社区项目页面查阅已知 API 服务列表。

2.使用 Docker 沙箱

如果您需要访问包括 goalkmdalgokey 在内的所有开发人员工具,但不能干耗几天等待节点追赶,那推荐采取这种方式。

可访问此 Github 链接查看沙箱安装说明。

警告

从快照引导绕过了常规节点追赶程序,因此无法加密验证区块链的整个历史,而这个过程对于维护健康网络是必不可少的。因此,该方法推荐用于早期应用开发阶段,借以规避追赶等待时间并快速上手。千万不要用此方式运行生产环境中的节点或参与共识。在将应用投入生产环境之前,请确保应用迁移至已完成追赶的节点。

3.运行自有节点

如果您需要访问包括 goalkmdalgokey 在内的所有开发人员工具,且想设置生产就绪的环境,那推荐采取这种方式。这是选项 2 的后续,推荐用于在 MainNet 上发布应用之前。此方法可赋予您对节点及其配置的完全控制。

参阅文档了解如何安装和运行节点。

安装后,可以在这里找到您的 REST 端点 IP 地址:

$ cat $ALGORAND_DATA/algod.net

还有您的 algod 令牌(您的应用要用此令牌通过 algod 身份验证):

$ cat $ALGORAND_DATA/algod.token

横向比较

采用第三方服务 使用 Docker 沙箱 运行自有节点
耗时 数秒 - 只需注册 数分钟 - 与运行无追赶过程的节点相同 数天 - 需等待节点追赶
信任 1 方 1 方 您自己
成本 开发通常免费;生产环境中按速率限制付费 可变(有免费选项)- 参见节点类型 可变(有免费选项)- 参见节点类型
私有网络
goalalgokeykmd
平台 各不相同 MacOS;Linux MacOS;Linux
生产就绪

2. 链接节点

在上一个章节获得的 algod IP 地址和访问令牌,是您的应用与 Algorand 区块链交互的凭证。应用通过 algod 客户端使用 SDK 与 Algorand 区块链交互。如果直接使用 algod REST API,这些凭证需随每个请求提供。

深入了解节点 algod 进程。

信息

本章节中的示例已更新至 2020 年 6 月 16 日发布到 MainNet 的 v2 API。参阅 v2 迁移指南,了解如何将您的代码从 v1 迁移至 v2。可于此处获取存档的 v1 示例。

创建 algod 客户端

用所选 SDK 实例化 algod 客户端。

const algosdk = require('algosdk');

const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const algodServer = "http://localhost";
const algodPort = 4001;

let algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort);
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Client;

final String ALGOD_API_ADDR = "localhost";
final Integer ALGOD_PORT = 4001;
final String ALGOD_API_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

AlgodClient client = (AlgodClient) new AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN);
from algosdk.v2client import algod

algod_address = "http://localhost:4001"
algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
algod_client = algod.AlgodClient(algod_token, algod_address)
package main

import (
    "github.com/algorand/go-algorand-sdk/client/v2/algod" 
)

const algodAddress = "http://localhost:4001"
const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

func main() {
    algodClient, err := algod.MakeClient(algodAddress, algodToken)
    if err != nil {
        fmt.Printf("Issue with creating algod client: %s\n", err)
        return
    }
}

如果采用第三方服务,在实例化 algod 客户端时换成 API 密钥头。

from algosdk.v2client import algod

const algodServer = "https://api.host.com";
const port = "";
const token = {
    'X-API-Key': "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
};
let algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort);
from algosdk.v2client import algod

algod_address = "https://api.host.com"
algod_token = ""
headers = {
    "X-API-Key": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}

algod_client = algod.AlgodClient(algod_token, algod_address, headers)
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Client;

final String ALGOD_API_ADDR = "https://api.host.com";
final String ALGOD_API_KEY = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";

AlgodClient client = new AlgodClient();
client.setBasePath(ALGOD_API_ADDR);
client.addDefaultHeader("X-API-Key", ALGOD_API_KEY);
AlgodApi algodApiInstance = new AlgodApi(client);
package main

import (
    "github.com/algorand/go-algorand-sdk/client/v2/algod" 
)

const algodAddress = "https://api.host.com"
const apiKey = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

func main() {
    var headers []*algod.Header
    headers = append(headers, &algod.Header{"X-API-Key", apiKey})

    algodClient, err := algod.MakeClientWithHeaders(algodAddress, "", headers)
    if err != nil {
        fmt.Printf("Issue with creating algod client: %s\n", err)
        return
    }
}

检查节点状态

调用 algod 客户端的 status 方法查看节点连接详情。此信息也可通过 REST API 调用和 goal 命令获取。

let status = (await algodclient.status().do());
console.log("Algorand network status: %o", status);
status = algod_client.status()
print(json.dumps(status, indent=4))
try {
    NodeStatus status = algodApiInstance.getStatus();
    System.out.println("Algorand network status: " + status);
} catch (ApiException e) {
    System.err.println("Exception when calling algod#getStatus");
    e.printStackTrace();
}
    status, err := algodClient.Status().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting status: %s\n", err)
        return
    }
    statusJSON, err := json.MarshalIndent(status, "", "\t")
    if err != nil {
        fmt.Printf("Can not marshall status data: %s\n", err)
    }
    fmt.Printf("%s\n", statusJSON)
curl -i -X GET \
   -H "X-Algo-API-Token:<algod-token>" \
 'http://<algod-address>:<algod-port>/v2/status'
$ goal node status
Last committed block: [LATEST_ROUND]
Time since last block: [TIME_IN_SECONDS]
Sync Time: [TIME_IN_SECONDS]
Last consensus protocol: [LINK_TO_CURRENT_PROTOCOL_SPEC]
Next consensus protocol: [LINK_TO_FUTURE_PROTOCOL_SPEC]
Round for next consensus protocol: [ROUND_FOR_FUTURE_PROTOCOL]
Next consensus protocol supported: [true|false]
Has Synced Since Startup: [true|false]
Last Catchpoint: []
Genesis ID: [GENESIS_ID]
Genesis hash: [GENESIS_HASH]

status 方法返回节点的状态信息,例如标为 last-round 的最近一轮(即最高区块),所有信息皆出自所连接节点的视角。各 SDK 每次调用返回的信息可能略有不同。下面的响应信息出自 REST API 调用。

{
    "catchpoint": "",
    "catchpoint-acquired-blocks": 0,
    "catchpoint-processed-accounts": 0,
    "catchpoint-total-accounts": 0,
    "catchpoint-total-blocks": 0,
    "catchup-time": 0,
    "last-catchpoint": "",
    "last-round": 4243027,
    "last-version": "https://github.com/algorandfoundation/specs/tree/4a9db6a25595c6fd097cf9cc137cc83027787eaa",
    "next-version": "https://github.com/algorandfoundation/specs/tree/4a9db6a25595c6fd097cf9cc137cc83027787eaa",
    "next-version-round": 4243028,
    "next-version-supported": true,
    "stopped-at-unsupported-round": false,
    "time-since-last-round": 4261519666,
}

通过公有区块浏览器这样的工具来验证节点是否同步。作为辅助检查,查看您的 catchup-time 是否为 0,轮次是否以平均不到 5 秒的速度进行。这是在 Algorand 上确认一个区块的耗时。注意,time-since-last-round 以纳秒计。

警告

如果您的节点未与网络上其他节点同步,您就不能发送交易,账户余额信息就过时了。

检查建议交易参数

/v2/transactions/params API 返回网络身份信息和新交易构造参数。

...
    let params = await algodClient.getTransactionParams().do();
    console.log("Algorand suggested parameters: %o", params)
...
...
    try:
        params = algod_client.suggested_params()
        print(json.dumps(params, indent=4))
    except Exception as e:
        print(e)
...
    ...
        try {
            TransactionParametersResponse params = client.TransactionParams().execute().body();
            System.out.println("Algorand suggested parameters: " + TransactionParametersResponse);
        } catch (ApiException e) {
            System.err.println("Exception when calling algod#TransactionParams");
            e.printStackTrace();
        }
    ...
...
    txParams, err := algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error Algorand suggested parameters: %s\n", err)
        return
    }
    JSON, err := json.MarshalIndent(txParams, "", "\t")
    if err != nil {
        fmt.Printf("Can not marshall suggested parameters data: %s\n", err)
    }
    fmt.Printf("%s\n", JSON)
...
curl -i -X GET \
   -H "X-Algo-API-Token:<algod-token>" \
 'http://<algod-address>:<algod-port>/v2/transactions/params'
$ goal node status
Last committed block: [LATEST_ROUND]
Time since last block: [TIME_IN_SECONDS]
Sync Time: [TIME_IN_SECONDS]
Last consensus protocol: [LINK_TO_CURRENT_PROTOCOL_SPEC]
Next consensus protocol: [LINK_TO_FUTURE_PROTOCOL_SPEC]
Round for next consensus protocol: [ROUND_FOR_FUTURE_PROTOCOL]
Next consensus protocol supported: [true|false]
Has Synced Since Startup: [true|false]
Last Catchpoint: []
Genesis ID: [GENESIS_ID]
Genesis hash: [GENESIS_HASH]

检查下面 REST 响应所示的 genesis-idgenesis-hash。确保在继续下一步之前二者均匹配您选择的网络。

{
    "consensus-version": "https://github.com/algorandfoundation/specs/tree/e5f565421d720c6f75cdd186f7098495caf9101f",
    "fee": 1,
    "genesis-hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
    "genesis-id": "testnet-v1.0",
    "last-round": 7430522,
    "min-fee": 1000
}

3. 发送你的第一笔交易

使用所选 SDK 成功连接至 algod 后,您可以探索读写区块链的各种方法。要记得,写入 Algorand 区块链就是向此网络发送交易,该交易稍后在区块中得到确认。

按照下面的指南在 Algorand 上发送您的第一笔交易,熟悉各 SDK 的一些核心函数。如果相同或类似,goal 命令和 REST API 调用的示例也包含在内,供您熟悉所有可用工具及平台和交叉验证使用。

简洁明了起见,代码片段有所节略。各 SDK 的完整代码示例可在本指南底部获取。

信息

本章节中的示例已更新至 2020 年 6 月 16 日发布到 MainNet 的 v2 API。参阅 v2 迁移指南,了解如何将您的代码从 v1 迁移至 v2。

每个 SDK 和两个 API 版本的完整运行代码示例可在 GitHub 仓库 (/examples/start_building) 中查看和下载 (.zip)。

创建账户

想要发送交易,得先在 Algorand 上创建账户。生成 Algorand 公钥/私钥对,然后在所选网络上给公有地址充入 Algo 币,即可创建账户。

信息

账户公钥地址这三个术语在某些情形下可互换,但意思略有差别。可在账户概览中深入了解这些差异。

生成公钥/私钥对

const algosdk = require('algosdk');

var account = algosdk.generateAccount();
var passphrase = algosdk.secretKeyToMnemonic(account.sk);
console.log( "My address: " + account.addr );
console.log( "My passphrase: " + passphrase );
from algosdk import account, mnemonic

def generate_algorand_keypair():
    private_key, address = account.generate_account()
    print("My address: {}".format(address))
    print("My passphrase: {}".format(mnemonic.from_private_key(private_key)))
import com.algorand.algosdk.account.Account;    

Account myAccount = new Account();
System.out.println("My Address: " + myAccount.getAddress());
System.out.println("My Passphrase: " + myAccount.toMnemonic());
import (
    "fmt"

    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
)

func main() {
    account := crypto.GenerateAccount()
    passphrase, err := mnemonic.FromPrivateKey(account.PrivateKey)

    if err != nil {
        fmt.Printf("Error creating transaction: %s\n", err)
    } else {
        fmt.Printf("My address: %s\n", account.Address)
        fmt.Printf("My passphrase: %s\n", passphrase)
    }
}
$ goal account new
Created new account with address [ADDRESS]

$ goal account export -a <address>
Exported key for account [ADDRESS]: [PASSPHRASE]
$ algokey generate
Private key mnemonic: [PASSPHRASE]
Public key: [ADDRESS]

深入了解在 Algorand创建账户

领取测试币

对于 TestNetBetaNet,将您的密钥对的公开部分复制粘贴进相应的水龙头弹出框,点击“提交”。返回 200 意味着交易通过,您的余额增长 100,000,000 microAlgo(即 100 Algo)。

信息

返回的数额以 Algo 币基本单位 microAlgo 计。micro 指单位 x 10^-6。因此,1 Algo 等于 1,000,000 microAlgo。

连接客户端

每个 SDK 都提供客户端,必须在连接 API 端点前实例化这个客户端。您应赋值 <algod-address><port>`。CLI 工具原生实现此客户端。

深入了解连接节点

const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const algodServer = "http://localhost";
const algodPort = 4001;

let algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort);
from algosdk.v2client import algod

algod_address = "http://localhost:4001"
algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
algod_client = algod.AlgodClient(algod_token, algod_address)
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Client;

final String ALGOD_API_ADDR = "localhost";
final Integer ALGOD_PORT = 4001;
final String ALGOD_API_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

AlgodClient client = (AlgodClient) new AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN);
package main

import (
    "github.com/algorand/go-algorand-sdk/client/v2/algod" 
)

const algodAddress = "http://localhost:4001"
const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

func main() {
    algodClient, err := algod.MakeClient(algodAddress, algodToken)
    if err != nil {
        fmt.Printf("Issue with creating algod client: %s\n", err)
        return
    }
}

查看余额

查看您的余额,确认已注入资金。

    const passphrase = "Your 25-word mnemonic generated and displayed above";

    let myAccount = algosdk.mnemonicToSecretKey(passphrase)
    console.log("My address: %s", myAccount.addr)

    let accountInfo = await algodClient.accountInformation(myAccount.addr).do();
    console.log("Account balance: %d microAlgos", accountInfo.amount);
passphrase = "Your 25-word mnemonic generated and displayed above"

private_key = mnemonic.to_private_key(passphrase)
my_address = mnemonic.to_public_key(passphrase)
print("My address: {}".format(my_address))

account_info = algod_client.account_info(my_address)
print("Account balance: {} microAlgos".format(account_info.get('amount')))
final String PASSPHRASE = "Your 25-word mnemonic generated and displayed above";
String myAddress = myAccount.getAddress().toString();

com.algorand.algosdk.v2.client.model.Account accountInfo = client.AccountInformation(myAccount.getAddress()).execute().body();

System.out.println(String.format("Account Balance: %d microAlgos", accountInfo.amount));
passphrase := "Your 25-word mnemonic generated and displayed above"
privateKey, err := mnemonic.ToPrivateKey(passphrase)
if err != nil {
    fmt.Printf("Issue with mnemonic conversion: %s\n", err)
    return
}

var myAddress types.Address
publicKey := privateKey.Public()
cpk := publicKey.(ed25519.PublicKey)
copy(myAddress[:], cpk[:])
fmt.Printf("My address: %s\n", myAddress.String())

accountInfo, err := algodClient.AccountInformation(myAddress.String()).Do(context.Background())
if err != nil {
    fmt.Printf("Error getting account info: %s\n", err)
    return
}
fmt.Printf("Account balance: %d microAlgos\n", accountInfo.Amount)
curl -i -X GET \
   -H "X-Algo-API-Token:<algod-token> \
 'http://<algod-address>:<algod-port>/v1/account/<address>'
$ goal account balance -a <my-address>
[AMOUNT] microAlgos

构建交易

创建一笔交易,从您的账户发送 1 个 Algo 到 TestNet 水龙头地址 (GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A),并备注“Hello World”。

需要一定的最小参数集,交易才有效。必备字段包括轮次有效范围费用,以及目标网络的创世区块哈希。可参阅交易功能指南,全面了解交易类型、字段和配置。至于目前,如下所示构建一笔付款交易。使用建议参数方法初始化网络相关字段。

let params = await algodClient.getTransactionParams().do();
// comment out the next two lines to use suggested fee
params.fee = 1000;
params.flatFee = true;
const receiver = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A";
let note = algosdk.encodeObj("Hello World");

let txn = algosdk.makePaymentTxnWithSuggestedParams(myAccount.addr, receiver, 1000000, undefined, note, params);        
params = algod_client.suggested_params()
# comment out the next two (2) lines to use suggested fees
params.flat_fee = True
params.fee = 1000
receiver = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A"
note = "Hello World".encode()

unsigned_txn = PaymentTxn(my_address, params, receiver, 1000000, None, note)
// Construct the transaction
final String RECEIVER = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A";
String note = "Hello World";
TransactionParametersResponse params = client.TransactionParams().execute().body();
Transaction txn = Transaction.PaymentTransactionBuilder()
.sender(myAddress)
.note(note.getBytes())
.amount(100000)
.receiver(new Address(RECEIVER))
.suggestedParams(params)
.build();
txParams, err := algodClient.SuggestedParams().Do(context.Background())
if err != nil {
    fmt.Printf("Error getting suggested tx params: %s\n", err)
    return
}
// comment out the next two (2) lines to use suggested fees
txParams.FlatFee = true
txParams.Fee = 1000

fromAddr := myAddress.String()
toAddr := "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A"
var amount uint64 = 1000000
var minFee uint64 = 1000
note := []byte("Hello World")
genID := txParams.GenesisID
genHash := txParams.GenesisHash
firstValidRound := uint64(txParams.FirstRoundValid)
lastValidRound := uint64(txParams.LastRoundValid)

txn, err := transaction.MakePaymentTxnWithFlatFee(fromAddr, toAddr, minFee, amount, firstValidRound, lastValidRound, note, "", genID, genHash)
if err != nil {
    fmt.Printf("Error creating transaction: %s\n", err)
    return
}
$ goal clerk sign --infile="hello-world.txn" --outfile="hello-world.stxn"

信息

某些 SDK 提供用来创建特定类型交易的封装函数,如 Go 中的 makePaymentTxn

深入了解 Algorand 交易结构

签名交易

用您的私钥签名交易。这会在 SDK 中创建一个新的已签名交易对象。检索这个已签名交易的交易 ID。

let signedTxn = txn.signTxn(myAccount.sk);
let txId = txn.txID().toString();
console.log("Signed transaction with txID: %s", txId);
signed_txn = unsigned_txn.sign(mnemonic.to_private_key(passphrase))
SignedTransaction signedTxn = myAccount.signTransaction(txn);
System.out.println("Signed transaction with txid: " + signedTxn.transactionID);
txID, signedTxn, err := crypto.SignTransaction(privateKey, txn)
if err != nil {
    fmt.Printf("Failed to sign transaction: %s\n", err)
    return
}
fmt.Printf("Signed txid: %s\n", txID)
$ goal clerk sign --infile="hello-world.txn" --outfile="hello-world.stxn"

深入了解在 Algorand 上授权交易。

提交交易

用您的 algod 客户端将已签名交易发送至网络。

await algodClient.sendRawTransaction(signedTxn).do();
txid = algod_client.send_transaction(signed_txn)
print("Successfully sent transaction with txID: {}".format(txid)
byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTxn);
String id = client.RawTransaction().rawtxn(encodedTxBytes).execute().body().txId;
System.out.println("Successfully sent tx with ID: " + id);
sendResponse, err := algodClient.SendRawTransaction(signedTxn).Do(context.Background())
if err != nil {
    fmt.Printf("failed to send transaction: %s\n", err)
    return
}
fmt.Printf("Submitted transaction %s\n", sendResponse)
curl -i -X POST \
   -H "X-Algo-API-Token:<algod-token> \
   -H "Content-Type:application/x-binary" \
   -T "hello-world.stxn" \
 'http://<algod-address>:<algod-port>/v1/transactions'
$ goal clerk rawsend --filename="hello-world.stxn"
Sent 1000000 MicroAlgos from account [ADDRESS] to address GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A, transaction ID: [TXID]. Fee set to 1000
Transaction [TXID] still pending as of round [LAST_ROUND]
Transaction [TXID] committed in round [COMMITTED_ROUND]

# Or construct, sign, and submit in one line
$ goal clerk send --from=<my-account> --to=GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A --fee=1000 --amount=1000000 --note="Hello World"
Sent 1000000 MicroAlgos from account [ADDRESS] to address GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A, transaction ID: [TXID]. Fee set to 1000
Transaction [TXID] still pending as of round [LAST_ROUND]
Transaction [TXID] committed in round [COMMITTED_ROUND]

等待确认

成功向网络提交交易,并不意味着网络会确认您的交易。在进行下一步之前,务必核实网络已在一个区块中确认了您的交易。

信息

在 Algorand 上,交易被纳入区块后立即得到最终确认,而区块平均每 5 秒产生一个。这意味着交易的平均确认时间是 5 !深入了解 Algorand 共识协议,以及 Algorand 如何达到如此之快的确认速度和即时交易敲定。

const waitForConfirmation = async function (algodclient, txId) {
    let status = (await algodclient.status().do());
    let lastRound = status["last-round"];
    while (true) {
        const pendingInfo = await algodclient.pendingTransactionInformation(txId).do();
        if (pendingInfo["confirmed-round"] !== null && pendingInfo["confirmed-round"] > 0) {
            //Got the completed Transaction
            console.log("Transaction " + txId + " confirmed in round " + pendingInfo["confirmed-round"]);
            break;
        }
        lastRound++;
        await algodclient.statusAfterBlock(lastRound).do();
    }
};
def wait_for_confirmation(client, txid):
    """
    Utility function to wait until the transaction is
    confirmed before proceeding.
    """
    last_round = client.status().get('last-round')
    txinfo = client.pending_transaction_info(txid)
    while not (txinfo.get('confirmed-round') and txinfo.get('confirmed-round') > 0):
        print("Waiting for confirmation")
        last_round += 1
        client.status_after_block(last_round)
        txinfo = client.pending_transaction_info(txid)
    print("Transaction {} confirmed in round {}.".format(txid, txinfo.get('confirmed-round')))
    return txinfo
// utility function to wait on a transaction to be confirmed    
public void waitForConfirmation( String txID ) throws Exception{
    if( client == null ) this.client = connectToNetwork();
    Long lastRound = client.GetStatus().execute().body().lastRound;
    while(true) {
        try {
            //Check the pending tranactions
            Response<PendingTransactionResponse> pendingInfo = client.PendingTransactionInformation(txID).execute();
            if (pendingInfo.body().confirmedRound != null && pendingInfo.body().confirmedRound > 0) {
                //Got the completed Transaction
                System.out.println("Transaction " + txID + " confirmed in round " + pendingInfo.body().confirmedRound);
                break;
            } 
            lastRound++;
            client.WaitForBlock(lastRound).execute();
        } catch (Exception e) {
            throw( e );
        }
    }
}
// Function that waits for a given txId to be confirmed by the network
func waitForConfirmation(txID string, client *algod.Client) {
    status, err := client.Status().Do(context.Background())
    if err != nil {
        fmt.Printf("error getting algod status: %s\n", err)
        return
    }
    lastRound := status.LastRound
    for {
        pt, _, err := client.PendingTransactionInformation(txID).Do(context.Background())
        if err != nil {
            fmt.Printf("error getting pending transaction: %s\n", err)
            return
        }
        if pt.ConfirmedRound > 0 {
            fmt.Printf("Transaction "+txID+" confirmed in round %d\n", pt.ConfirmedRound)
            break
        }
        fmt.Printf("waiting for confirmation\n")
        lastRound++
        status, err = client.StatusAfterBlock(lastRound).Do(context.Background())
    }
}
curl -i -X GET \
   -H "X-Algo-API-Token:<algod-token> \
 'http://<algod-address>:<algod-port>/v1/transactions/pending/<txid>'
$ goal clerk rawsend --filename="hello-world.stxn"
Sent 1000000 MicroAlgos from account [ADDRESS] to address GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A, transaction ID: [TXID]. Fee set to 1000
Transaction [TXID] still pending as of round [LAST_ROUND]
Transaction [TXID] committed in round [COMMITTED_ROUND]

# Or construct, sign, and submit in one line
$ goal clerk send --from=<my-account> --to=GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A --fee=1000 --amount=1000000 --note="Hello World"
Sent 1000000 MicroAlgos from account [ADDRESS] to address GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A, transaction ID: [TXID]. Fee set to 1000
Transaction [TXID] still pending as of round [LAST_ROUND]
Transaction [TXID] committed in round [COMMITTED_ROUND]

从区块链读取交易

从区块链读回您的交易。

信息

尽管您可以读取区块链上的任何交易,但只有归档节点会存储整个历史。默认情况下,大多数节点仅存储最近 1000 轮,若要读取更早轮次的信息,API 会返回错误。如需访问更早的数据,请确保您的 algod 客户端连接归档的索引器节点。可参阅“网络参与指南”进一步理解节点配置,或咨询您的服务提供商,了解他们的节点是如何配置的。

let confirmedTxn = await algodClient.pendingTransactionInformation(txId).do();
console.log("Transaction information: %o", confirmedTxn.txn.txn);
console.log("Decoded note: %s", algosdk.decodeObj(confirmedTxn.txn.txn.note));
confirmed_txn = algod_client.pending_transaction_info(txid)
print("Transaction information: {}".format(json.dumps(confirmed_txn, indent=4)))
print("Decoded note: {}".format(base64.b64decode(confirmed_txn["txn"]["txn"]["note"]).decode()))
PendingTransactionResponse pTrx = client.PendingTransactionInformation(id).execute().body();
System.out.println("Transaction information (with notes): " + pTrx.toString());
System.out.println("Decoded note: " + new String(pTrx.txn.tx.note));
confirmedTxn, stxn, err := algodClient.PendingTransactionInformation(txID).Do(context.Background())
if err != nil {
    fmt.Printf("Error retrieving transaction %s\n", txID)
    return
}
txnJSON, err := json.MarshalIndent(confirmedTxn.Transaction.Txn, "", "\t")
if err != nil {
    fmt.Printf("Can not marshall txn data: %s\n", err)
}
fmt.Printf("Transaction information: %s\n", txnJSON)
fmt.Printf("Decoded note: %s\n", string(stxn.Txn.Note))
curl -i -X GET \
   -H "X-Algo-API-Token:<algod-token> \
 'http://<algod-address>:<port>/v1/account/<my-address>/transaction/<txid>'

注意上面构建、授权、提交交易,以及确认交易已纳入区块的方式。您需要熟悉这个框架,因为它经常出现在区块链相关开发中。

信息

本页面提供示例代码片段。每个 SDK 的完整运行代码示例可在 GitHub 仓库 (/examples/start_building) 中查看和下载 (.zip)。