No.2-区块链XLM之golang调用实践

作者: 太白菜Rennbon | 来源:发表于2018-07-22 18:41 被阅读181次

前言

上一周通过btc的对接以及之前的一些区块链概念的知识铺垫,对于区块链代币已经有了一个初步的了解,这周准备接另外一个不同的币种,对此通过粗糙的筛选,整理的一个代币市值比较靠前的技术栈表格如下(btc,eth类的这里没做表述,主要通过官网整理的下属表格,):

序号 币种 中文 支持语种 golang地址
1 BTC 比特币 C++;C;Java;Go;Python;Haskell;C# btcd
2 ETH 以太坊 Go;Java;Python;JS;C++;Ruby;Rust;Haskell go-ethereum
3 XRP 瑞波币 C++;
4 XLM 恒星币 JS;C#;Ruby;Python;Java;Go;Scala go
5 NEO 小蚁 C#;
6 LTC 莱特币 C++;
7 ADA 艾达币 Haskell;
8 MIOTA 埃欧塔 JS;C;C++;Java;Python;Go; giota
9 USDT 泰达币 Ruby;Python;PHP;
10 TRX 波场 Java;
11 XMR 门罗币 C++;
12 DASH 达世币 C++;
13 BNB 币安币 基于以太坊
14 XEM 新经币 JS;Java;
15 VEN 唯链 Go thor

另外这里只实现了账号生成和tx离线签名等一些基础的功能,对于其他高级功能,如trustline,offer没做过多解释。

xlm资料相关

没错这一个地址就基本够了,xlm的官方很给力,一定要认真看。

一些碰到的细节概念

相对于普通区块链的代币而言,因为xlm的目标锚定的是法币和xlm的兑换,所以相应的功能也会因为业务的发展,代码里面有一些逻辑还是需要去细细品味的。

  • fees
    这里不简简单单只是交易的时候产生的小费,这里还有operations和Minimum Account Balance的概念。
    • base fee 和 operations的关系
      普通层面上的小费,目前约定的小费基数(base fee)是0.0001xlm,有一个公式用来计算当前的tx需要多少小费:totalFee = n × baseFee,其中n代表着操作类型的数量比如转账是一种,创建账户是一种,创建trustline又是一种,还有其他的,所以单个tx中涉及了我说的这3种,那么最后的计算公式就是: 3 × 0.0001 = 0.0003 xlm。
  • minimum Account Balance
    这就是账户的最小余额押金了,目前官方定是账户保证金基数0.5xlm,也有一个公式:(2 + n) × baseReserve,这里的n代表有以下几项:
    • Trustlines
    • Offers
    • Signers
    • Data entries
      那么如果我的账户对应了3组签名,3个offers,基础押金就是: (2+3+3)0.5 = 4 xlm

所以创建新的tx的时候对上面的概念是必须要了解的。

配置相关

相比btc,eth而言,如果是单独的用来测试和接入公链,那么配置文件这种东西都不需要我们来倒腾,内置在了代码里面,官方自己维护着一个测试用的公链,在开源的源码里面你能找到链的地址。我们需要做的是记住以下4个变量:

//真实环境
 horizon.DefaultPublicNetClient
 build.PublicNetwork
//测试环境
 horizon.DefaultTestNetClient
 build.TestNetwork

证书生成

这里是用seed和publickey的概念,官方解释道:privatekey是seed定向生成的,所以代码中略过了使用privatekey,这样会方便很多,seed是随机的,可以改写Random来替换成我们想要的生成机制

//  生成种子和address
func (*XlmCertService) GenerateSimpleKey() (*Key, error) {
    pair, err := keypair.Random()

    if err != nil {
        return nil, err
    }
    //只有address和seed有用
    key := &Key{Address: pair.Address(), Seed: pair.Seed()}
    return key, nil
}
func (*XlmCertService) GetNewAddress(seed string) (string, error) {
    kp, err := keypair.Parse(seed)
    if err != nil {
        return "", err
    }
    return kp.Address(), nil
}

将账户地址广播到公链

xlm新账户同步到公链是需要一个source账号来付钱的,差不多是注册金的概念,在测试网络中,第一个账号可以用以下代码中注释掉的“测试网络源账号创建”的代码来生成,也可以用官网的测试网络UI页面直接生成。

//生成新账号
//
func (*XlmService) GetNewAddress(account string, mode AcountRunMode) (address, accountOut string, err error) {
    key, err := certXlmSrv.GenerateSimpleKey()
    if err != nil {
        return
    }

    //----------测试网络源账号创建--------start------------
    /*resp, err := http.Get("https://friendbot.stellar.org/?addr=" + key.Address)
    if err != nil {
        return
    }
    defer resp.Body.Close()
    _, err = ioutil.ReadAll(resp.Body)
    if err != nil {
        return
    }*/
    //----------测试网络源账号创建--------end------------
    //----------源账号创建--------start------------
    //创建新账户必须从已有资金的账户转账生成,所以理论上生产环境要用,必须要提供一个有钱的账户作为God来来创造一切
    godSeed := "SAACHR2TWFAJKLLLC5TEYTSYPXA7AIBM6A2KZ7MQ4XEYRJEZFNOR6VOC"
    godAddress := "GBZKTZBJIMLFPUGZUNCUTJCUUREEG4W4UF74K5DRJRZISQNYQP3QOUYX"

    //源账号不要开通其他付费条目
    //源账户保底剩余 基础保证金*2
    //新账户保底创建 基础保证金*2
    comparedAmount := baseReserve*2 + baseFee + baseReserve*2
    if err = checkBalanceEnough(godAddress, comparedAmount); err != nil {
        return
    }
    //获取序列数
    num, err := client.SequenceForAccount(godAddress)
    if err != nil {
        return
    }
    /*
        Trustlines
        Offers
        Signers    新用户初始化会有一条singer
        Data entries
    */
    amount := baseReserve * 2 //基础+Singer=2条
    amountStr := strconv.FormatFloat(amount, 'f', 8, 64)
    tx, err := build.Transaction(
        build.TestNetwork,
        build.Sequence{uint64(num) + 1}, //这里用autoSequence 失败了,公链可以在尝试下
        build.SourceAccount{godSeed},
        build.MemoText{"Create Account"}, //元数据,就是便签
        build.CreateAccount(
            build.Destination{key.Address},
            build.NativeAmount{amountStr}, //初始账号最小为0.5Lumens
        ),
        build.BaseFee{baseFeeLemuns},
    )
    if err != nil {
        return
    }
    txe, err := tx.Sign(godSeed) //画押
    if err != nil {
        return
    }
    txeB64, err := txe.Base64()
    if err != nil {
        return
    }
    _, err = client.SubmitTransaction(txeB64) //提交tx
    if err != nil {
        return
    }
    //----------源账号创建--------end------------
    err = dhSrv.AddAccount(account, key.PrivKey, key.PubKey, key.Address, key.Seed, database.XLM)
    if err != nil {
        return
    }
    return key.Address, account, nil
}

交易离线签名

其实和上面的生成新地址并广播到公链基本上差不多

//转账
//addrForm来源地址,addrTo去向地址
//transfer 转账金额
//fee 小费
func (*XlmService) SendAddressToAddress(addrFrom, addrTo string, transfer, fee float64) (txId string, err error) {
    //数据库获取prv pub key等信息,便于调试--------START------
    actf, err := dhSrv.GetAccountByAddress(addrFrom)
    if err != nil {
        return
    }
    //----------------------------------------END-----------
    //验证地址是否有效
    if _, err = client.LoadAccount(addrTo); err != nil {
        return
    }
    //100 stroops (0.00001 XLM).
    //The base fee (currently 100 stroops) is used in transaction fees.
    //sumfee = num of operations × base fee
    //The base reserve (currently 0.5 XLM) is used in minimum account balances.
    //(2 + n) × base reserve = 2.5 XLM.
    amount := strconv.FormatFloat(transfer, 'f', 8, 64)
    //验证金额总数
    comparedAmount := transfer + baseFee + baseReserve*2*2
    if err = checkBalanceEnough(addrFrom, comparedAmount); err != nil {
        return
    }
    //小费是自己扣的,不需要这边实现,金额总数也不需要验证,当然可以验证
    tx, err := build.Transaction(
        build.TestNetwork,
        build.SourceAccount{addrFrom}, //lumens(代币名称)当前主人的地址
        build.AutoSequence{client},    //sequence序列号自动
        build.MemoText{"Just do it"},  //元数据,就是便签
        build.Payment(
            build.Destination{addrTo},  // lumens(代币名称)下个主人的地址
            build.NativeAmount{amount}, //官方payments用string主要防止精度丢失
        ),
        //build.BaseFee{baseFeeLemuns},//小费不写也会扣,只要钱够
    )

    if err != nil {
        return
    }
    // Sign the transaction to prove you are actually the person sending it.
    txe, err := tx.Sign(actf.Seed) //签名需要用seed
    if err != nil {
        return
    }

    txeB64, err := txe.Base64()
    if err != nil {
        return
    }

    // And finally, send it off to Stellar!
    resp, err := client.SubmitTransaction(txeB64) //提交tx
    if err != nil {
        return
    }
    //存储到数据库,方便检验
    dhSrv.AddTx(resp.Hash, addrFrom, []string{addrTo})
    return resp.Hash, nil
}

demo地址

demo里有简单的单元测试及mongodb的简单使用方便调试,只是为了方便!!!
https://github.com/Rennbon/blockchainDemo.git
以后应该还会有其他区块链的尝试,希望自己能走的一步一步往上爬。

这里import的本地路径没有替换成Github路径,只是为了我自己方便。

相关文章

网友评论

    本文标题:No.2-区块链XLM之golang调用实践

    本文链接:https://www.haomeiwen.com/subject/bfalmftx.html