#substrate #stellar #transaction #sdk #operation #xdr #type

no-std substrate-stellar-sdk

适用于 Stellar 的 Substrate 兼容 SDK

12 个版本

0.3.0 2024 年 5 月 29 日
0.2.4 2022 年 11 月 7 日
0.2.3 2022 年 6 月 28 日
0.2.2 2021 年 7 月 15 日
0.1.5 2021 年 6 月 21 日

#460神奇豆子

Download history 83/week @ 2024-05-24 23/week @ 2024-05-31 5/week @ 2024-06-07 1/week @ 2024-06-14 5/week @ 2024-06-28 26/week @ 2024-07-05

每月 624 次下载

Apache-2.0

745KB
15K SLoC

Substrate 项目使用的 Stellar SDK

这是一个适用于 Stellar 的 SDK,适用于在 Substrate 项目中使用。它不依赖于标准库,而是依赖于 crate sp-std,这是 Substrate 对 Rust 标准库的替代品。

crate 功能

此 crate 有三个功能

  • std:此功能将启用标准库。默认情况下,它已启用,因此此 crate 需要在 Substrate 项目中使用 default-features = false 进行导入。
  • offchain:这是一个可用于 offchain 工作人员的功能集合,其中可以进行 HTTP 请求。它主要包含对 Horizon API 部分的抽象层。
  • all-types:这将提供对 Stellar 中定义的所有类型的访问,包括仅为 Stellar 共识协议内部所需的类型。否则,此 crate 仅提供对用户界面类型(如 TransactionOperation)的访问(请参阅有关 Stellar 类型 的部分)

转换特征

为了使SDK API能够方便地与各种输入类型一起使用,我们大量使用了转换特性。例如,类型Horizon实现了方法fetch_account,该方法的第一参数是账户ID。这个参数可以提供为表示账户ID字符串编码的字符串

horizon.fetch_account("GDGRDTRINPF66FNC47H22NY6BNWMCD5Q4XZTVA2KG7PFZ64WHRIU62TQ", 1000)?;

由于Substrate中的字符串通常表示为Vec<u8>,也可以使用该字符串的向量表示作为参数

let vec: Vec<u8> = Vec::from("GDGRDTRINPF66FNC47H22NY6BNWMCD5Q4XZTVA2KG7PFZ64WHRIU62TQ".as_bytes());
horizon.fetch_account(vec, 1000)?;

此外,u8切片和u8数组也是可能的参数类型

let slice: &[u8] = "GDGRDTRINPF66FNC47H22NY6BNWMCD5Q4XZTVA2KG7PFZ64WHRIU62TQ".as_bytes();
let array: [u8; 56] = slice.try_into()?;
horizon.fetch_account(slice, 1000)?;
horizon.fetch_account(array, 1000)?;

还可以提供AccountId结构本身

let account_id = AccountId::from_encoding("GDGRDTRINPF66FNC47H22NY6BNWMCD5Q4XZTVA2KG7PFZ64WHRIU62TQ")?;
horizon.fetch_account(account_id, 1000)?;

此软件包提供了以下转换特性

IntoAccountId

这是表示AccountId的参数的特例。它由以下类型实现

  • 实现AsRef<[u8]>的类似字符串类型,例如&str[u8; N][u8]Vec<u8>:账户ID的字符串编码(使用Stellar的账户ID编码)
  • AccountId:一个AccountId

IntoAmount

这是表示资产数量的参数的特例。这用于清楚地区分stroop数量和lumen数量(其中1 lumen等于10_000_000 stroops)。它由以下类型实现

  • LumenAmount:表示lumen数量的包装f64
  • StroopAmount:表示stroop数量的包装i64
  • 实现AsRef<[u8]>的类似字符串类型,例如&str[u8; N][u8]Vec<u8>:这是金额的十进制字符串。该字符串可以有最多7位小数。这种表示通常用于Horizon API的响应中

建议使用LumenAmount或字符串表示,因为它们可以无损失地转换为Stellar内部金额的stroop表示

IntoClaimableBalanceId

这是表示ClaimableBalanceId的参数的特例。它由以下类型实现

  • AsBinary:这可以是可主张余额ID(36字节)的原始二进制值(AsBinary::Binary)或其作为字符串的十六进制编码(AsBinary::Hex
  • ClaimableBalanceId:一个ClaimableBalanceId

IntoDataValue

这是代表DataValue参数的特质。它由以下类型实现:

  • 实现AsRef<[u8]>的类似字符串类型,如&str[u8; N][u8]Vec<u8>:数据值字符串
  • DataValue:一个DataValue

IntoHash

这是代表Hash参数的特质。它由以下类型实现:

  • AsBinary:这可以是哈希的原始二进制值(32字节)或其作为字符串的十六进制编码(AsBinary::Hex
  • Hash:一个Hash

IntoMuxedAccountId

这是代表MuxedAccount参数的特质。请注意,类型MuxedAccount包含简单账户ID以及正确的多路复用账户ID。它由以下类型实现:

  • 实现AsRef<[u8]>的类似字符串类型,如&str[u8; N][u8]Vec<u8>:多路复用账户ID的字符串编码(使用Stellar的多路复用账户ID编码)
  • AccountId:一个简单AccountId,作为MuxedAccount表示
  • MuxedAccount:一个MuxedAccount

IntoPublicKey

这是代表PublicKey参数的特质。请注意,类型AccountIdPublicKey是别名(在Stellar中它们是同义词)。特质IntoPublicKey存在是为了方便,并且在将账户ID用作公钥时使其更加清晰。它由以下类型实现:

  • 实现了 AsRef<[u8]> 的类似字符串类型,例如 &str[u8; N][u8]Vec<u8>:公钥的字符串编码(使用Stellar的公钥编码)
  • PublicKey:一个 PublicKey(它是 AccountId 的别名)

IntoSecretKey

这是表示 SecretKey 的参数的特质。它通过以下类型实现:

  • 实现了 AsRef<[u8]> 的类似字符串类型,例如 &str[u8; N][u8]Vec<u8>:私钥的字符串编码(使用Stellar的私钥编码)
  • SecretKey:一个 SecretKey

IntoTimePoint

这是表示交易时间界限所使用的时间点的特质。它通过以下类型实现:

  • MilliSecondEpochTime:以毫秒表示的Unix时间戳
  • SecondEpochTime:以秒表示的Unix时间戳
  • ():用于无限时间界限

创建、签名和提交交易

这是使用此SDK时的主要工作流程之一。注意,这些功能仅在启用 offchain 仓库功能时可用。通常包括以下步骤:

  1. 创建 Horizon 类型的实例并提供horizon服务器的基地址。
  2. 从horizon服务器获取交易源账户的下一个序列号。下一个序列号比账户当前的序列号高一位,必须用作交易序列号才能有效。
  3. 构建交易并提供以下信息:
    • 源账户ID
    • 获取的序列号
    • 每操作费用
    • 时间界限(可选)
    • 备忘录(可选)
  4. 将操作追加到交易中。交易需要是可变的。每个操作都有构造方法,例如 Operation::new_payment(用于支付操作)或 Operation::new_bump_sequence(用于增加序列号操作)。构造函数使用了 转换特质。注意,如果需要为操作设置特定的源账户,则需要链式调用 set_source_account 方法。
  5. 将交易转换为交易信封。这将创建一个不带签名的信封。请注意,此操作会改变交易的费率,并将交易构建时提供的每个操作的费率乘以附加操作的数目。在交易信封中的交易在此步骤之后不应再被修改,否则随后添加的签名将不再有效。
  6. 使用所需的密钥对交易信封进行签名。签名仅对某些Stellar网络密码短语有效(因此交易可以用于公共网络或测试网络)。因此,签名交易的方法需要一个网络密码短语。
  7. 使用Horizon API将签名后的信封提交到Stellar网络。

以下代码展示了完整示例。

use substrate_stellar_sdk::{
    horizon::Horizon, network::TEST_NETWORK, Asset, IntoSecretKey, Memo, MilliSecondEpochTime,
    MuxedAccount, Operation, Price, PublicKey as StellarPublicKey, SecondEpochTime, StroopAmount,
    TimeBounds, Transaction, TransactionEnvelope, XdrCodec,
};

// ...
// in some function:

const ACCOUNT_ID1: &str = "GDGRDTRINPF66FNC47H22NY6BNWMCD5Q4XZTVA2KG7PFZ64WHRIU62TQ";
const ACCOUNT_ID2: &str = "GBNKQVTFRP25TIQRODMU5GJGSXDKHCEUDN7LNMOS5PNM427LMR77NV4M";
const ACCOUNT_ID3: &str = "GCACWDM2VEYTXGUI3CUYLBJ453IBEPQ3XEJKA772ARAP5XDQ4NMGFZGJ";

const SIGNER1: &str = "SCVKZEONBSU3XD6OTHXGAP6BTEWHOU4RPZQZJJ5AVAGPXUZ5A4D7MU6S";
const SIGNER3: &str = "SDOKV37I4TI655LMEMDQFOWESJ3LK6DDFKIVTYKN4YYTSAYFIBPP7MYI";

// Step 1: instantiate horizon server
let horizon = Horizon::new("https://horizon-testnet.stellar.org");

// Step 2: fetch next sequence number of account
let next_sequence_number = horizon.fetch_next_sequence_number(ACCOUNT_ID1, 10_000)?;

debug::info!("Sequence number: {}", next_sequence_number);

// Step 3: build transaction
let mut transaction = Transaction::new(
    ACCOUNT_ID1,
    next_sequence_number,
    Some(321),
    Some(TimeBounds::from_time_points(
        SecondEpochTime(162620000),
        MilliSecondEpochTime(1_667_257_200_000),
    )),
    Some(Memo::from_text_memo("Hello World!")?),
)?;

// Step 4: add operations
transaction.append_operation(
    Operation::new_payment(
        ACCOUNT_ID2,
        Asset::from_asset_code("USD", ACCOUNT_ID3)?,
        StroopAmount(1_234_560_000),
    )?
    .set_source_account(ACCOUNT_ID3)?,
)?;

transaction.append_operation(Operation::new_manage_sell_offer(
    Asset::from_asset_code("DOMINATION", ACCOUNT_ID2)?,
    Asset::native(),
    "152.103",
    Price::from_float(4.58)?,
    Some(1742764),
)?)?;

// Step 5: transform into transaction envelope and sign
let mut envelope = transaction.into_transaction_envelope();

// Step 6: sign transaction envelope
envelope.sign(
    &TEST_NETWORK,
    vec![&SIGNER1.into_secret_key()?, &SIGNER3.into_secret_key()?],
)?;

// Step 7: submit transaction
let submission_response = horizon.submit_transaction(&envelope, 60_000, true);
debug::info!("Response: {:?}", submission_response);

Stellar XDR类型

Stellar在协议级别定义了一系列数据类型。这些类型使用XDR标准进行序列化和反序列化。

此库包含所有这些Stellar定义的类型(在substrate_stellar_sdk::types中),包括用于这些类型的完整XDR编码器和解码器。为此,每个类型都实现了XdrCodec特性,它提供了以下方法

  • fn to_xdr(&self) -> Vec<u8>:以二进制XDR编码
  • fn to_base64_xdr(&self) -> Vec<u8>:以XDR编码,然后对结果进行base64编码
  • from_xdr<T: AsRef<[u8]>>(input: T) -> Result<Self, DecodeError>:解码二进制XDR
  • from_base64_xdr<T: AsRef<[u8]>>(input: T) -> Result<Self, DecodeError>:先解码base64,然后解码结果为XDR

自动生成器

类型和XDR解码器通过位于/autogenerator中的工具自动生成。此生成器将从Stellar Core GitHub仓库下载最新的Stellar类型,并生成类型和XDR解码器。

库特性 all-types

默认情况下,不会生成所有Stellar类型。缺失的类型是仅用于内部共识机制的特定类型。为了生成所有类型,请使用功能标志 all-types

依赖关系

~4–19MB
~288K SLoC