Bytom交易说明(账户管理模式)
V刘晨曦
发表于 2022-11-13 23:52:49
116
0
0
Github地址:https://github.com/Bytom/bytom, N8 a d$ l. H
Gitee地址:https://gitee.com/BytomBlockchain/bytom+ ~( I J8 N0 T M8 K n! n
该部分主要针对用户使用bytom自带的账户模式发送交易
1、构建交易) F' Y/ S, ]# {$ f
API接口 build-transaction,代码api/transact.go#L1208 A9 Y7 l4 \8 q) Y( K; f- {
以标准的非BTM资产转账交易为例,资产ID为全F表示BTM资产,在该示例中BTM资产仅作为手续费,该交易表示花费99个特定的资产到指定地址中。其中构建交易的输入请求json格式如下:9 H) G! g4 M0 ^1 Q9 m( D6 [; E
{ A$ C" e/ C8 I K! H
"base_transaction": null,3 [2 U5 N& ?5 e
"actions": [
{, C. j" s* d: d8 t
"account_id": "0ER7MEFGG0A02",
"amount": 20000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",. M! Y i- S K2 a e( q
"type": "spend_account"2 N! P$ C: c. _9 J2 m' P. g& ^
},
{) p2 {0 n; G# ^- M1 T0 s* \" [
"account_id": "0ER7MEFGG0A02",1 d$ b0 v/ r4 j- P
"amount": 99,
"asset_id": "42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f",
"type": "spend_account"0 I/ `& t; v1 ^7 B8 _
},' ?. i7 P& v% i _3 P/ ]8 N
{
"amount": 99,
"asset_id": "42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f",1 G, Z. @. t) l' S; u
"address": "sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me",
"type": "control_address"+ |! Z' s, n1 P+ j' y8 _
}( j: ]7 b7 R: v3 _( G+ x" a
],
"ttl": 0,4 F. G, b" l- m9 r: X$ @
"time_range": 00 a2 s# m# i3 T( v
}0 V. V2 H) M) V( ? r p1 E
对应源代码的请求对象如下:
// BuildRequest is main struct when building transactions
type BuildRequest struct {
Tx *types.TxData `json:"base_transaction"`9 r8 g$ w! d, Q1 U# R% [
Actions []map[string]interface{} `json:"actions"`4 ~" T% N* [1 y% N' N3 O
TTL json.Duration `json:"ttl"`3 N; c5 b9 ?9 m7 E( x' K' v4 X7 q! }% C
TimeRange uint64 `json:"time_range"`
}- [0 y o0 t; `; o* S
结构字段说明如下:
Tx 交易的TxData部分,该字段为预留字段,为空即可
TTL 构建交易的生存时间(单位为毫秒),意味着在该时间范围内,已经缓存的utxo不能用于再一次build交易,除非剩余的utxo足以构建一笔新的交易,否则会报错。当ttl为0时会被默认设置为600s,即5分钟
TimeRange 时间戳,意味着该交易将在该时间戳(区块高度)之后不会被提交上链,为了防止交易在网络中传输延迟而等待太久时间,如果交易没有在特定的时间范围内被打包,该交易便会自动失效
Actions 交易的actions结构,所有的交易都是由action构成的,map类型的interface{}保证了action类型的可扩展性。其中action中必须包含type字段,用于区分不同的action类型,action主要包含input和output两种类型,其详细介绍如下:# U; e' W- z4 S, u4 X3 T
input action 类型:
issue 发行资产& [6 c P; V' z9 h7 b5 @& Q
spend_account 以账户的模式花费utxo* p( f0 J. V& A/ B
spend_account_unspent_output 直接花费指定的utxo5 K8 @3 [& N6 {) X4 |. d8 j8 ]
output action 类型:
control_address 接收方式为地址模式# P; T! V) d. D- o
control_program 接收方式为(program)合约模式( n0 U4 O. B5 @8 w
retire 销毁资产/ J" }4 Q' B d+ @9 [* h
+ u1 L' ^1 W+ b# s% C
注意事项:5 T" E* K* \/ V* \# x ^8 n
一个交易必须至少包含一个input和output(coinbase交易除外,因为coinbase交易是由系统产生,故不在此加以描述),否则交易将会报错。3 K7 K+ k& u& j3 ~; Q$ x& W
除了BTM资产(所有交易都是以BTM资产作为手续费)之外,其他资产在构建input和output时,所有输入和输出的资产总和必须相等,否则交易会报出输入输出不平衡的错误信息。
交易的手续费: 所有inputs的BTM资产数量 - 所有outputs的BTM资产数量
交易中的资产amount都是neu为单位的,BTM的单位换算如下:1 BTM = 1000 mBTM = 100000000 neu3 v6 c! e1 ]/ R2 O
: | y/ h5 H! `5 I
action简介# r" u8 G0 W* m( r; E
下面对构建交易时用到的各种action类型进行详细说明:
issue1 U" p8 M! M' D! y" E2 x' Y
issueAction结构体源代码如下:
type issueAction struct {) w4 O/ l# M2 h1 I
assets *Registry
bc.AssetAmount4 i* ]6 D" q! z: p1 f# k9 z3 y
}& k$ Q$ m, E9 { w7 z& R
type AssetAmount struct {
AssetId *AssetID `protobuf:"bytes,1,opt,name=asset_id,json=assetId" json:"asset_id,omitempty"`$ {0 o$ a9 ?" N9 M' U
Amount uint64 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`7 N) q3 L0 x& Q% C2 ~
}+ G% v; O8 f' d) `) y
结构字段说明如下:
assets 主要用于资产的管理,无需用户设置参数
AssetAmount 表示用户需要发行的资产ID和对应的资产数目,这里的AssetID需要通过create-asset创建,并且这里不能使用BTM的资产ID! r& a/ u# r% s2 _6 w% s
issueAction的json格式为:% `3 d1 B; v. S1 r9 l6 y% ?
{5 T- w9 J7 i% _7 {" o; g( x, x/ h( r
"amount": 100000000,. G* d; A0 K. O
"asset_id": "3152a15da72be51b330e1c0f8e1c0db669269809da4f16443ff266e07cc43680",* m6 [7 f( ^/ r7 V) S# h; a' ]
"type": "issue"
}8 n2 j& k* Y4 b" [6 t0 L
例如发行一笔资产的交易示例如下:; Z$ | h+ Y+ Q* w" Q" j
(该交易表示发行数量为900000000个assetID的42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f的资产到接收地址sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me中, 其中手续费为20000000neu的BTM资产)
{; d7 ^6 _# b# h1 m
"base_transaction": null,
"actions": [
{
"account_id": "0ER7MEFGG0A02",( i3 {) u: d2 `$ ^ l+ y4 D
"amount": 20000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",# P, L3 Y+ J$ D
"type": "spend_account"6 w5 J1 \$ d: A3 Y+ A- t' d$ M& d u% U
},
{9 T$ ~' D" L+ P k
"amount": 900000000,4 _3 o. Q, D+ v& R0 X8 A
"asset_id": "42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f",
"type": "issue"3 ^ B% j& F/ B, Q- n, Z
},
{2 U$ w! D$ A& N* J
"amount": 900000000,3 t) B- {4 i( b& o
"asset_id": "42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f",
"address": "sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me",3 B: E% I; G# i+ h, y
"type": "control_address"
}' Z4 ^5 [! G V- A" G0 ?' S
],. f) I9 K7 y, c" G- C9 o0 ]% E
"ttl": 0,
"time_range": 0' n, H+ P. u2 ~
}* Y* ^' S N" j X/ O/ t
spend_account& s, R4 w- f; O+ ^
spendAction结构体源代码如下:
type spendAction struct {1 g$ W. A! R- r8 e! p( `
accounts *Manager
bc.AssetAmount+ @9 O( s+ p& g' i5 U1 C
AccountID string `json:"account_id"`
ClientToken *string `json:"client_token"`( o8 L g0 ~4 ]; I+ n) o4 V" X! k
}- B$ S9 J( N1 f% E+ N
type AssetAmount struct {6 G$ h8 h& T' b0 t+ H X5 b! \7 H
AssetId *AssetID `protobuf:"bytes,1,opt,name=asset_id,json=assetId" json:"asset_id,omitempty"`
Amount uint64 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`
}
结构字段说明如下: A& g$ ~ ^2 _) f3 k, \
accounts 主要用于账户的管理,无需用户设置参数
AccountID 表示需要花费资产的账户ID
AssetAmount 表示花费的资产ID和对应的资产数目
ClientToken 表示Reserve用户UTXO的限制条件,目前不填或为空即可
# N. P3 w# y5 G& Q$ O: y
spendAction的json格式为:
{
"account_id": "0BF63M2U00A04",
"amount": 2000000000,# \6 ~ e$ t& P6 r
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",3 ^6 r+ R2 w( Q J2 K( K
"type": "spend_account"- h0 x& O) X" V" ?5 I
}2 M j5 u) \. n$ ^% \5 G6 P4 z4 Q9 P0 c
例如转账一笔资产的交易示例如下:
(该交易表示通过账户的方式转账100000000neu的BTM资产到地址sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me中, 其中手续费20000000neu = 输入BTM资产数量 - 输出BTM资产数量)- G% T6 P; `. C' x! @8 L
{
"base_transaction": null,
"actions": [
{* B7 d8 r" _2 g; F
"account_id": "0ER7MEFGG0A02",( J7 a' B" Z# U6 Q5 o
"amount": 120000000," s( Y: R2 Z: J+ [$ {- c; w: y0 s, {8 C
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",- s! ?0 N1 }) D7 B- c& y
"type": "spend_account"
},; L( b7 f8 H! A' d8 i: p
{
"amount": 100000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",! }9 w6 C' _9 ^. W# [- B
"address": "sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me",, `* F( o( k* P1 l& y
"type": "control_address"8 w/ I% a. V, X. b9 o
}
],
"ttl": 0,( }4 G( j" m3 z8 {, m: t/ U/ Y
"time_range": 0
}+ b3 I6 l1 P n
5 |! e( \/ \( s! N. N; Z" H
spend_account_unspent_output
spendUTXOAction结构体源代码如下:+ O3 j# p9 G0 t/ x& J {1 G S
type spendUTXOAction struct {
accounts *Manager
OutputID *bc.Hash `json:"output_id"`
ClientToken *string `json:"client_token"`
}' ?: x5 m' N3 j- s6 R! ~
结构字段说明如下:7 F# n: y" K8 [( }& H% Z: X2 X
accounts 主要用于账户的管理,无需用户设置参数% R4 h. h; Q5 e! x! a
OutputID 表示需要花费的UTXO的ID,可以根据list-unspent-outputs查询可用的UTXO,其中OutputID对应该API返回结果的id字段
ClientToken 表示Reserve用户UTXO的限制条件,目前不填或为空即可
" {& t/ J! N7 f5 r# \9 S: A
spendUTXOAction的json格式为:1 }/ t1 O6 }8 {4 w5 z1 P
{
"type": "spend_account_unspent_output",$ v; c _% K4 M$ y
"output_id": "58f29f0f85f7bd2a91088bcbe536dee41cd0642dfb1480d3a88589bdbfd642d9"% Y: L. K1 Z; Y
}
例如通过花费UTXO的方式转账一笔资产的交易示例如下: V8 g8 P# S! C- b5 L$ a' }
(该交易表示通过直接花费UTXO的方式转账100000000neu的BTM资产到地址sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me中, 其中手续费 = 输入BTM资产的UTXO值 - 输出BTM资产数量)
{6 A' I% s5 g8 w( v' H0 _6 H. g( l
"base_transaction": null,
"actions": [/ D% d- T* f9 W; Y( U* W
{1 l3 |% R S! E7 }; K; K
"output_id": "58f29f0f85f7bd2a91088bcbe536dee41cd0642dfb1480d3a88589bdbfd642d9"," j( h8 q' S! [2 b4 d
"type": "spend_account_unspent_output"
},
{" I* S& c/ Q- s, M7 @
"amount": 100000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",& i. Q4 C- m" F; l
"address": "sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me",
"type": "control_address": [. M: S9 i3 o. @
}
],
"ttl": 0,1 H* Y8 C' r/ \6 @, D. z
"time_range": 0
}
control_address
controlAddressAction结构体源代码如下:
type controlAddressAction struct {4 ?' p0 K w# y3 H, D
bc.AssetAmount
Address string `json:"address"`9 a5 A) L. x; s8 S4 n) C5 x
}
type AssetAmount struct {
AssetId *AssetID `protobuf:"bytes,1,opt,name=asset_id,json=assetId" json:"asset_id,omitempty"`- G; _" d, F) I# a( W
Amount uint64 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`
}
结构字段说明如下:
Address 表示接收资产的地址,可以根据 create-account-receiver API接口创建地址
AssetAmount 表示接收的资产ID和对应的资产数目% g8 L+ s% I% }& ^* J: X/ S
controlAddressAction的json格式为:
{
"amount": 100000000,( R; V+ z+ |( ?
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",3 [/ P( N4 f& k
"address": "bm1q50u3z8empm5ke0g3ngl2t3sqtr6sd7cepd3z68",* S, |$ O( |% s: L1 A7 d8 P
"type": "control_address"5 q2 z9 J* O' v: m
}
例如转账一笔资产的交易示例如下:
(该交易表示通过账户的方式转账100000000neu的BTM资产到地址sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me中, 其中control_address类型表示以地址作为接收方式)3 z* ~- G( w$ H' ?* f2 u' T6 D3 b( g
{9 T- }" Q: e% H- J
"base_transaction": null,( y" `' n- }7 w J2 T- d8 w
"actions": [: N. u# H* a# N: i5 g" y4 R8 l
{
"account_id": "0ER7MEFGG0A02", c' E! M' b! C2 P- d$ y
"amount": 120000000," v: O5 ]- k- W
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"type": "spend_account"4 r9 k) U0 N: ^" `
},$ f2 W( E- l$ h( V3 Q8 U
{
"amount": 100000000,5 F) W' p* N; A n' O9 |
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"address": "sm1qxe4jwhkekgnxkezu7xutu5gqnnpmyc8ppq98me",
"type": "control_address"
}# @. O/ l( m4 F& A3 g
],. f0 D( q. R' W! _
"ttl": 0,
"time_range": 02 y3 U; Q c! Y0 s3 Z
}2 s) U- Q0 J/ n
! }4 T. m5 ]. Q9 l7 }) P) V/ f
control_program: G7 A# A1 b3 f' m
controlProgramAction结构体源代码如下:
type controlProgramAction struct {
bc.AssetAmount6 H9 n9 O9 \. A" |9 ?7 ^
Program json.HexBytes `json:"control_program"`
}( w" t7 ]; r D# c( R
type AssetAmount struct {
AssetId *AssetID `protobuf:"bytes,1,opt,name=asset_id,json=assetId" json:"asset_id,omitempty"`
Amount uint64 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`& A7 g! s' ?) \4 c* D4 O o) p
}
结构字段说明如下:6 W. u' I: I$ f' }3 v% D+ u7 P
Program 表示接收资产的合约脚本,可以根据 create-account-receiver API接口创建接收program(返回结果的 program 和 address 是一一对应的)5 V' o1 @: a' u3 M8 k1 V
AssetAmount 表示接收的资产ID和对应的资产数目* M, V9 |/ s6 H- g
controlProgramAction的json格式为:& z$ c( P- r+ ?' t& t
{* B! `, m" z/ P; s+ r5 Z
"amount": 100000000,( o% b* [. q2 k: @2 x
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"control_program":"0014a3f9111f3b0ee96cbd119a3ea5c60058f506fb19",
"type": "control_program"! ~8 I7 S5 j+ C6 |* X
}
例如转账一笔资产的交易示例如下:$ v7 w. S4 V2 d# H6 a
(该交易表示通过账户的方式转账100000000neu的BTM资产到接收program(跟address是一一对应的)0014a3f9111f3b0ee96cbd119a3ea5c60058f506fb19中, 其中control_program类型表示以program作为接收方式)
{: Z6 o8 g9 r$ @ h, j
"base_transaction": null,
"actions": [
{
"account_id": "0ER7MEFGG0A02",
"amount": 120000000,4 C+ Q; W# H) }7 q
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"type": "spend_account"
},$ i9 H6 K: P- q8 S& ^
{
"amount": 100000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",7 L# N& G; F- o( Z9 @/ r# b( z
"control_program": "0014a3f9111f3b0ee96cbd119a3ea5c60058f506fb19",2 V/ R* a) \; Y. }
"type": "control_program"' b& _! C3 r3 y1 M- l$ t' {
}6 Z3 u0 x& Q/ q" k
],
"ttl": 0,7 j# J) G4 d0 m: F& K7 @7 v
"time_range": 0( Y+ B+ m7 }! ^3 N; Z
}
2 ^) J/ g; t& p# ^
retire4 l N( U. N! T) E' d7 }. X3 S1 I
retireAction结构体源代码如下:
type retireAction struct {% m) N6 t4 U R* @ g
bc.AssetAmount1 H' p# |0 y" p* [, U" k
}1 R0 o1 K/ T8 x, u9 o' e
type AssetAmount struct {4 I2 ]' o& H4 _. |- P
AssetId *AssetID `protobuf:"bytes,1,opt,name=asset_id,json=assetId" json:"asset_id,omitempty"`
Amount uint64 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`
}
结构字段说明如下:
AssetAmount 表示销毁的资产ID和对应的资产数目
3 j) z% S s" m6 Y) ? d: p
retireAction的json格式为:: P/ b, N. t/ m; C
{2 b$ w2 a2 W8 w: @) E9 P _+ E
"amount": 900000000,
"asset_id": "3152a15da72be51b330e1c0f8e1c0db669269809da4f16443ff266e07cc43680",
"type": "retire"
}
例如销毁一笔资产的交易示例如下:
(该交易表示通过账户的方式将100000000neu的BTM资产销毁, retire表示销毁指定数量的资产)
{
"base_transaction": null,- n, D! R& Q4 D( S
"actions": [ Y# n6 y- a! `+ P- t
{
"account_id": "0ER7MEFGG0A02",
"amount": 120000000,2 E* Q. d+ o* ]1 B |
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",7 f) r* Q+ n8 h5 c+ A; `# F
"type": "spend_account"0 [( a/ O( [% ^& A" X% A, l! R7 L$ |
},
{) h% q7 W- n. u0 c; ~: L: `0 r
"amount": 100000000,
"asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"type": "retire"
}
], ?- j3 D6 S! M# P' j1 U1 O0 M
"ttl": 0,: p. e: E- i9 ]( R
"time_range": 00 C5 M8 k" U# r3 x% q+ e
}
i: ]! _; ?; K! Q$ a# V6 _. F
build-transaction的输入构造完成之后,便可以通过http的调用方式进行发送交易,构建交易请求成功之后返回的json结果如下:& }; B$ D" y, J7 ^( H* {
{8 h* {! p( j+ K: o+ b
"allow_additional_actions": false,' ?9 p4 W9 e6 }8 j1 C9 M
"raw_transaction": "070100020161015f1190c60818b4aff485c865113c802942f29ce09088cae1b117fc4c8db2292212ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014a86c83ee12e6d790fb388345cc2e2b87056a077301000161015fb018097c4040c8dd86d95611a13c24f90d4c9d9d06b25f5c9ed0556ac8abd73442275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f80a094a58d1d0101160014068840e56af74038571f223b1c99f1b60caaf456010003013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bfffcb9901011600140b946646626c55a52a325c8bb48de792284d9b7200013e42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f9d9f94a58d1d01160014c8b4391bab4923a83b955170d24ee4ca5b6ec3fb00013942275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f6301160014366b275ed9b2266b645cf1b8be51009cc3b260e100",9 _: y [& ?* j0 q9 e5 t
"signing_instructions": [
{
"position": 0,3 v& @) t% \1 o; U4 Q) i9 Q
"witness_components": [
{- B( \* [' \7 {( }- O m
"keys": [( ]+ E" _! E/ o2 F8 t
{
"derivation_path": [" f0 p* @( W# S6 M w- W9 v
"010100000000000000",# D4 z0 x* k- k7 M3 d+ `
"0100000000000000"
],
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"
}
],
"quorum": 1,- K$ `4 d3 v5 L' U
"signatures": null,
"type": "raw_tx_signature"
},, j4 X6 l7 t- q) |9 K
{
"type": "data",4 n: H# g9 @6 z0 l: p
"value": "d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd5"
}
]1 B4 \5 I( m7 o9 \1 n
},# k. W' f) u' F
{1 T$ w4 U' O+ b2 G" `# J7 \6 M
"position": 1,
"witness_components": [
{" c5 L# y" w% C* R& f4 \
"keys": [9 c0 m [6 _. t5 M: s
{. }" |5 V) [$ w# S ~! L/ J( v/ q' ~
"derivation_path": [
"010100000000000000",- M/ H4 ]6 {8 k5 _' h3 }. }* r3 X3 A
"0800000000000000"
],
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"
}
],. u4 Z+ l" O& \4 v
"quorum": 1,
"signatures": null,! H. Z0 M. ^$ J2 n
"type": "raw_tx_signature"
},
{ i, k7 k: m: Q; O- \
"type": "data"," g9 m7 x# u$ A3 @, Z
"value": "05cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe61540535419"
}
]* a" C: f/ a& z' T( G0 r7 j
}
]2 p6 H: ^) Q- T8 Z
}5 S! F6 v5 y3 v3 B* h1 X
对应响应对象的源代码如下:
// Template represents a partially- or fully-signed transaction.; L |6 [- D) Y( l5 k
type Template struct {
Transaction *types.Tx `json:"raw_transaction"`% N! h6 {+ v# }- H" F
SigningInstructions []*SigningInstruction `json:"signing_instructions"`! O7 C/ r2 \ z$ k+ N$ m4 q: Z6 |
// AllowAdditional affects whether Sign commits to the tx sighash or5 J' P/ P7 m" O) j! I
// to individual details of the tx so far. When true, signatures
// commit to tx details, and new details may be added but existing7 Y/ @- L' O: Y: P0 J- K! H. Y, `6 F
// ones cannot be changed. When false, signatures commit to the tx
// as a whole, and any change to the tx invalidates the signature.; n- M: m0 @9 a( H% x* s
AllowAdditional bool `json:"allow_additional_actions"`
}) \- B7 _8 {8 A! r$ B
结构字段说明如下:
Transaction 交易相关信息,该字段包含TxData和bc.Tx两个部分:
TxData 表示给用户展示的交易数据部分,该部分对用户可见5 Q" r2 C- h; U [9 ~
Version 交易版本 ~- N4 b. N1 w0 w
SerializedSize 交易序列化之后的size
TimeRange 交易提交上链的最大时间戳(区块高度)(主链区块高度到达该时间戳(区块高度)之后,如果交易没有被提交上链,该交易便会失效)/ e# ^2 E, S" Q2 J' p
Inputs 交易输入, ?+ F8 h8 W: k" I; U- g5 E( K
Outputs 交易输出
bc.Tx 表示系统中处理交易用到的转换结构,该部分对用户不可见,故不做详细描述# Y0 Z) }1 J, a# J
SigningInstructions 交易的签名信息 K& F2 K5 W; @* b
Position 对input action签名的位置
WitnessComponents 对input action签名需要的数据信息,其中build交易的signatures为null,表示没有签名; 如果交易签名成功,则该字段会存在签名信息。该字段是一个interface接口,主要包含3种不同的类型:
SignatureWitness 对交易模板Template中交易input action位置的合约program进行哈希,然后对hash值进行签名0 _& c- ^9 ~- o+ r- s
signatures (数组类型)交易的签名,sign-transaction执行完成之后才会有值存在
keys (数组类型)包含主公钥xpub和派生路径derivation_path,通过它们可以在签名阶段找到对应的派生私钥child_xprv,然后使用派生私钥进行签名
quorum 账户key 的个数,必须和上面的keys的长度相等。如果quorum 等于1,则表示单签账户,否则为多签账户# j& t8 h1 `# `; S7 _ T k2 D
program 签名的数据部分,program的hash值作为签名数据。如果program为空,则会根据当前交易ID和对应action位置的InputID两部分生成一个hash,然后把它们作为指令数据自动构造一个program
RawTxSigWitness 对交易模板Template的交易ID和对应input action位置的InputID(该字段位于bc.Tx中)进行哈希,然后对hash值进行签名
signatures (数组类型)交易的签名,sign-transaction执行完成之后才会有值存在
keys (数组类型)包含主公钥xpub和派生路径derivation_path,通过它们可以在签名阶段找到对应的派生私钥child_xprv,然后使用派生私钥进行签名
quorum 账户key的个数,必须和上面的keys 的长度相等。如果quorum 等于1,则表示单签账户,否则为多签账户7 N" `' M1 e, O1 L7 c( v& Q! L) V( b. K
DataWitness 该类型无需签名,验证合约program的附加数据
$ t; ]% [, W! |7 |. o
AllowAdditional 是否允许交易的附加数据,如果为true,则交易的附加数据会添加到交易中,但是不会影响交易的执行的program脚本,对签名结果不会造成影响; 如果为false,则整个交易作为一个整体进行签名,任何数据的改变将影响整个交易的签名
# L* @" H# U' a, N6 p
估算手续费# c: t+ L6 h: `1 K: y
估算手续费接口estimate-transaction-gas是对build-transaction的结果进行手续费的预估,估算的结果需要重新加到build-transaction的结果中,然后对交易进行签名和提交。其主要流程如下:' I% a: G# _$ s6 v
build - estimate - build - sign - submit
估算手续费的输入请求json格式如下:
{, _ f" t; V9 ]5 y' D3 l
"transaction_template": {, c9 @% u0 I- d
"allow_additional_actions": false,
"raw_transaction": "070100020161015f1190c60818b4aff485c865113c802942f29ce09088cae1b117fc4c8db2292212ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014a86c83ee12e6d790fb388345cc2e2b87056a077301000161015fb018097c4040c8dd86d95611a13c24f90d4c9d9d06b25f5c9ed0556ac8abd73442275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f80a094a58d1d0101160014068840e56af74038571f223b1c99f1b60caaf456010003013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bfffcb9901011600140b946646626c55a52a325c8bb48de792284d9b7200013e42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f9d9f94a58d1d01160014c8b4391bab4923a83b955170d24ee4ca5b6ec3fb00013942275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f6301160014366b275ed9b2266b645cf1b8be51009cc3b260e100",. n' [! Z& z" f5 d5 ?
"signing_instructions": [7 M4 C; M' k$ z: V2 N' J/ y
{% z: H5 K) L+ i' U1 l- c
"position": 0,
"witness_components": [& d! ^9 F* b& A3 y. `
{ k4 B! l- G9 [6 d
"keys": [+ q' U: x4 ?* k( j+ |
{
"derivation_path": [) V& N Y+ m/ |4 `5 P; F7 {
"010100000000000000",
"0100000000000000": w) y% M7 o5 y0 g/ n) C$ N# c7 |% }
],' s5 _% w+ s9 `. K
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"
}" t( {! c% M2 g( q' N7 @ C4 o
],) [7 h8 ]4 E0 Q) ^$ d
"quorum": 1,
"signatures": null,/ B1 l7 k; O8 }, f! |( Y
"type": "raw_tx_signature"/ {1 j$ Y; c9 ]2 ^5 t; G
},
{
"type": "data",
"value": "d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd5"
} Z: @2 S8 s1 p. K6 ~
]
},5 P) E, B3 {/ H- N1 w
{0 h" Y8 C: j; I9 V! H( W- R+ Y
"position": 1,* s. m' I/ s) B( ~8 ]( I% r& @
"witness_components": [: Q! \/ B3 G2 _' Z
{3 z$ _3 L/ T m- f9 w/ [- M* J/ {6 D. T
"keys": [2 ^5 P* S. U5 p- j9 E
{
"derivation_path": [
"010100000000000000",, E: \& G% X' @- {
"0800000000000000"
],# N) c, B% h5 G8 [% Q& y
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"- G7 R w& f1 C1 b& T
}7 g1 t) ~+ q6 u6 R; \
],# K3 z1 H, a9 O2 V% Q8 P' q) N
"quorum": 1,
"signatures": null,6 d) j$ R, B w; r
"type": "raw_tx_signature"- _, C$ |1 o! y
},
{
"type": "data",/ v4 L- [/ P( _% l: y& r
"value": "05cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe61540535419"
}
], g$ E! V# i1 O4 \
}
]7 I6 ]& O5 D8 Q9 S
}/ Y, \: u' \) e2 c- I% |6 |( Y9 `) i
}+ U4 U# F! X% M0 L5 |
对应响应对象的源代码如下:# y- d/ {2 J ] e5 ?
type request struct{
TxTemplate txbuilder.Template `json:"transaction_template"`
}: f ?! Q0 u0 o" I+ I3 S
// Template represents a partially- or fully-signed transaction.
type Template struct {
Transaction *types.Tx `json:"raw_transaction"`
SigningInstructions []*SigningInstruction `json:"signing_instructions"`6 E) S" e' i: @" ?8 k% I, |( `1 K
// AllowAdditional affects whether Sign commits to the tx sighash or* M! g& z' w/ Q; C1 F
// to individual details of the tx so far. When true, signatures' L* E' s _. ^8 Y Q$ k
// commit to tx details, and new details may be added but existing
// ones cannot be changed. When false, signatures commit to the tx/ n% q3 x6 `% a/ q& N
// as a whole, and any change to the tx invalidates the signature.+ ^0 p# B' S0 p" ^3 R% O2 c) b
AllowAdditional bool `json:"allow_additional_actions"`
}5 @, |$ I4 d Y9 R3 o$ r$ V
其中TxTemplate相关字段的说明见build-transaction的结果描述$ j5 l% e( t! L. ]3 p) j
调用estimate-transaction-gas接口成功之后返回的json结果如下:3 Q1 G8 J7 ~- L4 v# `9 p
{
"total_neu": 5000000,+ m0 ^: V+ o- n6 Q5 ?' B
"storage_neu": 3840000,; ?: G* k0 [9 v* W
"vm_neu": 1419000) `7 V* n2 J0 C$ [" m
}! k6 Q0 W9 w& \. H: H7 I
对应响应对象的源代码如下:2 i7 v7 a& P" w; X. w: S% F' s N
// EstimateTxGasResp estimate transaction consumed gas
type EstimateTxGasResp struct {
TotalNeu int64 `json:"total_neu"`
StorageNeu int64 `json:"storage_neu"`; \& }- U1 Q: y/ T
VMNeu int64 `json:"vm_neu"`' `4 n* J7 q8 _, p
}
结构字段说明如下:
TotalNeu 预估的总手续费(单位为neu),该值直接加到build-transaction的BTM资产输入action中即可
StorageNeu 存储交易的手续费7 X [7 W4 ^8 X. I: {7 e2 ?* V
VMNeu 运行虚拟机的手续费
3 l7 U, ^7 d) h7 K+ M v8 U
2、签名交易
API接口 sign-transaction,代码api/hsm.go#L532 l# a4 t8 i2 I4 j
签名交易的输入请求json格式如下:4 V# s* a# g5 x Y7 @) D3 A/ m% G, b
{; `& L! ?' l+ D0 c
"password": "123456",
"transaction": {
"allow_additional_actions": false,
"raw_transaction": "070100020161015f1190c60818b4aff485c865113c802942f29ce09088cae1b117fc4c8db2292212ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014a86c83ee12e6d790fb388345cc2e2b87056a077301000161015fb018097c4040c8dd86d95611a13c24f90d4c9d9d06b25f5c9ed0556ac8abd73442275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f80a094a58d1d0101160014068840e56af74038571f223b1c99f1b60caaf456010003013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bfffcb9901011600140b946646626c55a52a325c8bb48de792284d9b7200013e42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f9d9f94a58d1d01160014c8b4391bab4923a83b955170d24ee4ca5b6ec3fb00013942275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f6301160014366b275ed9b2266b645cf1b8be51009cc3b260e100",8 P4 h7 e' d: G# \: ~
"signing_instructions": [
{
"position": 0,6 M. l! |* @" q8 d& F
"witness_components": [
{
"keys": [2 I8 U) Q7 d% `6 O* `3 r
{
"derivation_path": [
"010100000000000000",
"0100000000000000"% Q, D9 }! _& _, r. \4 K5 c
],
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"# F s; A3 i1 W& L" V/ E
}
],. V- @8 E0 g: r7 d
"quorum": 1,
"signatures": null,' P4 F! A; N$ R5 M" f
"type": "raw_tx_signature"
}, X( ~6 A9 p. X. a) ~/ K
{5 j' n% T- s# F n {% p
"type": "data",8 M( S* K; U# p
"value": "d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd5"4 r/ U) B; p' s7 X! Y
}; O# L" q& D; r4 E, r- c+ k% r( g
]( h7 ]# v" m! g! g
},! `' n* m3 z/ f/ n& ]# m4 M- b
{* [+ R) a1 ^3 w; Q1 V" m; Z
"position": 1, [0 r: h# Z2 C. w' Z& s% c
"witness_components": [
{/ z1 `9 O3 C! M" z$ i: K9 {
"keys": [# d8 C9 F1 U. o0 G7 K! s* o' w
{
"derivation_path": [2 {7 D. S/ T' ~: u) q5 f- H
"010100000000000000",/ b( h% d) b" g3 Z
"0800000000000000"
],
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"1 c; A7 m/ o. E, l
}, `3 g) U0 i* L+ u! ?
],
"quorum": 1,+ M) v+ _% D; M- j
"signatures": null,! l. ?0 a$ z2 X5 h* p8 V
"type": "raw_tx_signature"
},
{" X0 S1 W$ T" O4 T9 C {2 B4 v
"type": "data",
"value": "05cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe61540535419"- Q7 g; g$ H0 J
}9 H8 j* h+ z) W- u2 c
]. |7 b( J. r! W) b6 ~
}
]- G. F: J7 z" J+ A( g3 D
}
}
对应请求对象的源代码如下:
type SignRequest struct { //function pseudohsmSignTemplates request
Password string `json:"password"`0 b" w% z% B* t7 f
Txs txbuilder.Template `json:"transaction"`8 B L7 b" }" t. Z9 s2 D
}2 t9 `% m, ?$ b4 {4 r
结构字段说明如下:
Password 签名的密码,根据密码可以从节点服务器上解析出用户的私钥,然后用私钥对交易进行签名3 I% d. K$ T! _. @1 s. _$ i
Txs 交易模板,build-transaction的返回结果,结构类型为 txbuilder.Template,相关字段的说明见build-transaction的结果描述2 @# r# a; @# G+ N, W. ?5 ~5 a2 b
签名交易sign-transaction请求成功之后返回的json结果如下:
{. m, P: u( N" X: e' _$ _0 Q, ?
"sign_complete": true,$ ]* v, P1 O S, D) g' T* D
"transaction": {
"allow_additional_actions": false,
"raw_transaction": "070100020161015f1190c60818b4aff485c865113c802942f29ce09088cae1b117fc4c8db2292212ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014a86c83ee12e6d790fb388345cc2e2b87056a0773630240273d5fc4fb06909fbc2968ea91c411fd20f690c88e74284ce2732052400129948538562fe432afd6cf17e590e8645b80edf80b9d9581d0a980d5f9f859e3880620d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd50161015fb018097c4040c8dd86d95611a13c24f90d4c9d9d06b25f5c9ed0556ac8abd73442275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f80a094a58d1d0101160014068840e56af74038571f223b1c99f1b60caaf4566302400cf0beefceaf9fbf1efadedeff7aee5b38ee7a25a20d78b630b01613bc2f8c9230555a6e09aaa11a82ba68c0fc9e98a47c852dfe3de851d93f9b2b7ce256f90d2005cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe6154053541903013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bfffcb9901011600140b946646626c55a52a325c8bb48de792284d9b7200013e42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f9d9f94a58d1d01160014c8b4391bab4923a83b955170d24ee4ca5b6ec3fb00013942275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f6301160014366b275ed9b2266b645cf1b8be51009cc3b260e100",
"signing_instructions": [
{
"position": 0,
"witness_components": [$ O, u8 D8 m/ Q# N' s
{. q8 q% e6 x* C: q: ~! G9 r
"keys": [8 t3 p; |3 C% b* ~; S# w: R
{
"derivation_path": [% t, [1 T+ S/ G/ O
"010100000000000000",
"0100000000000000"% V* n; T' `- ~: g7 b' A6 Q
], O' `* V$ r% y7 _# D
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"
}
],
"quorum": 1,
"signatures": [
"273d5fc4fb06909fbc2968ea91c411fd20f690c88e74284ce2732052400129948538562fe432afd6cf17e590e8645b80edf80b9d9581d0a980d5f9f859e38806"
],
"type": "raw_tx_signature"
},
{
"type": "data",
"value": "d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd5"" V, _( P. ]: a; v* N& R# {2 Y; j
}
]9 X7 U. \( S8 m
}, Q x8 D8 X8 i- U$ l" N9 v8 O. E
{- O& }) S# e0 J# ^9 i
"position": 1,
"witness_components": [
{
"keys": [/ V+ J8 ~$ e# u3 ?' b! Q- {; m
{
"derivation_path": [
"010100000000000000",
"0800000000000000"- ^& \: m- c- D2 Y) F* ~
],
"xpub": "de0db655c091b2838ccb6cddb675779b0a9a4204b122e61699b339867dd10eb0dbdc926882ff6dd75c099c181c60d63eab0033a4b0a4d0a8c78079e39d7ad1d8"; p" _3 }: q1 U S' R- q
}2 K5 O+ [8 c S* @) I
], A3 `% k+ _4 x+ o
"quorum": 1,
"signatures": [9 E5 U" |1 _& h* e
"0cf0beefceaf9fbf1efadedeff7aee5b38ee7a25a20d78b630b01613bc2f8c9230555a6e09aaa11a82ba68c0fc9e98a47c852dfe3de851d93f9b2b7ce256f90d"
],
"type": "raw_tx_signature"
},
{; X' F( C- m- l$ q( G% N2 D
"type": "data",
"value": "05cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe61540535419"% y- ]1 J/ V) w/ w, O" R
}
]
}
]
}
}8 I) \/ w5 Q& @( z1 _' y0 w
对应响应对象的源代码如下:9 Q2 {% c$ d9 y" z' M8 Y
type signResp struct {
Tx *txbuilder.Template `json:"transaction"`4 }7 R ?: k7 t) z; q) \, @0 V2 m
SignComplete bool `json:"sign_complete"`5 F& O# g! R( C/ B [ Z/ f
}$ Z, z$ Q: n5 F* [: g- N% F8 F
结构字段说明如下:
Tx 签名之后的交易模板txbuilder.Template,如果签名成功则signatures会由null变成签名的值,而raw_transaction的长度会变长,是因为bc.Tx部分添加了验证签名的参数信息( K/ O7 T9 Y) I2 S" a6 {
SignComplete 签名是否完成标志,如果为true表示签名完成,否则为false表示签名未完成,单签的话一般可能为签名密码错误; 而多签的话一般为还需要其他签名。签名失败只需将签名的交易数据用正确的密码重新签名即可,无需再次build-transaction构建交易( r9 n# }2 V5 A, ?& h6 E' J
3、提交交易3 O3 {6 U* I2 ?( w. t
API接口 submit-transaction,代码api/transact.go#L1351 P& z3 I z- x; h$ y' y+ f
提交交易的输入请求json格式如下:" ]! B5 ]9 L& a$ y/ ^
{
"raw_transaction": "070100020161015f1190c60818b4aff485c865113c802942f29ce09088cae1b117fc4c8db2292212ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014a86c83ee12e6d790fb388345cc2e2b87056a0773630240273d5fc4fb06909fbc2968ea91c411fd20f690c88e74284ce2732052400129948538562fe432afd6cf17e590e8645b80edf80b9d9581d0a980d5f9f859e3880620d174db6506e35f2decb5be148c2984bfd0f6c67f043365bf642d1af387c04fd50161015fb018097c4040c8dd86d95611a13c24f90d4c9d9d06b25f5c9ed0556ac8abd73442275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f80a094a58d1d0101160014068840e56af74038571f223b1c99f1b60caaf4566302400cf0beefceaf9fbf1efadedeff7aee5b38ee7a25a20d78b630b01613bc2f8c9230555a6e09aaa11a82ba68c0fc9e98a47c852dfe3de851d93f9b2b7ce256f90d2005cdbcc705f07ad87521835bbba226ad7b430cc24e5e3f008edbe6154053541903013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bfffcb9901011600140b946646626c55a52a325c8bb48de792284d9b7200013e42275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f9d9f94a58d1d01160014c8b4391bab4923a83b955170d24ee4ca5b6ec3fb00013942275aacbeda1522cd41580f875c3c452daf5174b17ba062bf0ab71a568c123f6301160014366b275ed9b2266b645cf1b8be51009cc3b260e100"
}
对应源代码的请求对象如下:1 W- T$ p6 f' W, ~" M" i
type SubmitRequest struct { //function submit request9 G! J. M& f7 T! u2 |5 J
Tx types.Tx `json:"raw_transaction"`
}* m1 ]& A2 ?. V% S) Y
结构字段说明如下:8 y( a O$ V# b- q: m4 g9 L
Tx 签名完成之后的交易信息。这里需要注意该字段中的raw_transaction不是签名交易sign-transaction的全部返回结果,而是签名交易返回结果中transaction中的raw_transaction字段。
0 b9 E& E- p! I* r2 \5 \, V9 J' { H
submit-transaction请求成功之后返回的json结果如下:
{' t" B/ n/ z+ j& Q V/ g
"tx_id": "2c0624a7d251c29d4d1ad14297c69919214e78d995affd57e73fbf84ece361cd"1 B. f) S+ q- y$ e
}
对应源代码的响应对象如下:
type submitTxResp struct {: U9 U% y, F$ p! R3 y
TxID *bc.Hash `json:"tx_id"`
}& {0 A+ _5 _+ H* ]& ?- ?
结构字段说明如下:. a3 b7 F6 ^( G5 ~8 ?
TxID 交易ID,当交易被提交到交易池之后会显示该信息,否则表示交易失败' `) A! B* |3 s: Z7 K
成为第一个吐槽的人