前言
上一周通过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。
- base fee 和 operations的关系
- 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路径,只是为了我自己方便。
网友评论