Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockchain/bytom. p' A) \0 F0 E' ]# x6 ?+ K8 n9 r
tx_signer, {3 B* G7 O d
Java implementation of signing transaction offline to bytomd.7 |3 r7 b3 A& @! n5 Y1 K# e
Pre" b0 i5 [* p/ L' r# l6 {
Get the source code) g# ^+ s; R, G8 [ T2 l, j$ z
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom
git checkout
$ git checkout dev
Why need dev branch? Because you could call decode transaction api from dev branch and obtain tx_id and some inputs ids.1 u r H) N9 j
Build
$ cd $GOPATH/src/github.com/bytom8 W" m# K' B# f: ^
$ make bytomd # build bytomd
$ make bytomcli # build bytomcli4 r( H9 S( e. i& V) D
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively./ ~& O8 E! ^6 t+ q# J
Initialize
First of all, initialize the node:; \8 l8 l2 l# }& a$ r
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet
launch `) `% ^+ O2 }& q1 {; m
$ ./bytomd node --mining4 P" d# m6 X& X' {$ u
Usage
Build jar
first get source code. ~$ c+ J. c- g& c0 p% U
git clone https://github.com/successli/tx_signer.git- L H+ p! X" j$ D g
get jar package
$ mvn assembly:assembly -Dmaven.test.skip=true
You can get a jar with dependencies, and you can use it in your project.
Test cases
Need 3 Parameters:
Call method:
// return a Template object signed offline basically.! w6 C% E, _1 [+ a. f) E5 p
Template result = signatures.generateSignatures(privates, template, rawTransaction);; S) z% G4 b; K# ]
// use result's raw_transaction call sign transaction api to build another data but not need password or private key.+ K% T, w* H$ F
Single-key Example:' P, _: q2 \1 P' m" X7 R
@Test
// 使用 SDK 来构造 Template 对象参数, 单签; v( @* {3 i8 l# T8 K$ T7 s
public void testSignSingleKey() throws BytomException {2 \& y" c& s) o: u
Client client = Client.generateClient();$ D( |9 r0 s1 g7 G2 G
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";' O, q% ?8 Q0 F) Z
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";7 c' B: N# z! Z" Q N
// build transaction obtain a Template object; \5 R6 _% F3 x0 i* J2 E9 u
Template template = new Transaction.Builder()8 \& e0 c! X2 o( O' `8 b0 O' v/ G
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")5 q& K* P# U' L l5 ]2 G. S# f, N
.setAssetId(asset_id) S5 u% k6 C* M$ c8 h$ S
.setAmount(40000000)
)' r% U: R) |: x( x7 l8 i
.addAction(9 J& `6 Q7 y6 G( a
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")7 ^4 ~7 u2 K9 T6 F, ]6 Y6 m
.setAssetId(asset_id)4 i8 p4 |; F9 O7 i+ R9 i4 p" |5 q
.setAmount(300000000)1 Q" o7 ~2 w8 A3 f& M: _
)# c j! R2 i, y) C
.addAction(
new Transaction.Action.ControlWithAddress()7 @# O/ P! \, k
.setAddress(address)+ A8 z5 \$ S# ?0 z+ N
.setAssetId(asset_id)
.setAmount(30000000)
).build(client);
logger.info("template: " + template.toJson());
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);
logger.info("decodeTx: " + decodedTx.toJson());6 D& j' e* p& T+ g/ c
// need a private key array- h9 A+ Y8 N; V [. G% W$ z
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);6 D3 a7 p: T5 m2 ?5 b6 q: v9 D
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());% I5 `( p6 m4 K8 U6 k
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,: V, t. C) Z% M
basicSigned, "");6 E: w5 L. g2 o3 J" ?8 \6 g
logger.info("result raw_transaction: " + result.toJson());( Q1 W& H: Q) ~0 f5 S: N
// success to submit transaction# x4 W/ z7 s8 ?) `) X4 i5 \* x
}; H# q+ q* q ?3 X8 M b0 w# w
Multi-keys Example:* P& U; H Q4 J$ ~ J: ~) ~
Need an account has two or more keys.
" C; V! _6 r) Y- X' X: K
@Test
// 使用 SDK 来构造 Template 对象参数, 多签0 N; c, g% W6 e- q9 p: Q
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em"; K E( n6 E3 L% v
// build transaction obtain a Template object) X( Z- x$ B+ D* ?+ ]) J0 U
// account 0G1RPP6OG0A06 has two keys
Template template = new Transaction.Builder()$ C) V |! O3 r, l# J( Q' N1 b# Y2 p( E
.setTtl(10)
.addAction() z+ e; c1 N$ @) e: ]
new Transaction.Action.SpendFromAccount() [/ v7 R4 H! U
.setAccountId("0G1RPP6OG0A06")$ n5 Q6 V. {% z9 W) k
.setAssetId(asset_id)/ V3 r4 Q- L. |) ] Q4 m
.setAmount(40000000)+ p0 x: @* U* `) c& o
)- m& \" {0 c7 Z/ k2 x" @8 }
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")% E+ v+ l9 ?: s- _& P
.setAssetId(asset_id)
.setAmount(300000000)- R$ M) t% W' y" _, t" B
)5 [% } N, Q4 N2 G: f; _1 Z3 f
.addAction(
new Transaction.Action.ControlWithAddress()) H0 e# L! c F+ t+ I8 q" Y
.setAddress(address)
.setAssetId(asset_id)
.setAmount(30000000). ~, r+ K- u7 y V! s
).build(client);
logger.info("template: " + template.toJson());7 u! t% v4 V$ _2 a9 N' Q" \
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);
logger.info("decodeTx: " + decodedTx.toJson());0 h& T6 C) @" i4 O. {
// need a private key array d( P: r% B7 l" f) x" B& ^
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",, W% f* A7 E" U6 t2 F9 b
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);6 @8 l# L3 |: r, ~ K! m: b
// call offline sign method to obtain a basic offline signed template, l: F2 T$ E' i1 Q5 P1 \) s
Signatures signatures = new SignaturesImpl();" O/ T$ F# u5 q5 o. {
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);( ~1 r8 _( [" U; H
logger.info("basic signed raw: " + basicSigned.toJson());$ A4 i) x6 S# S9 [
// call sign transaction api to calculate whole raw_transaction id# |+ b8 b1 O! B2 Y% P) I
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,: @, S8 T8 b' |. D/ N! c$ a, R
basicSigned, "");8 u# }! ?) I4 U+ g. A* j) k5 s, e
logger.info("result raw_transaction: " + result.toJson());3 ?1 g Q: ?3 l2 b
// success to submit transaction2 Q2 y, T$ P! i6 V) x
}
Multi-keys and Multi-inputs Example:
@Test. g' s7 Q+ ]5 g
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();8 V5 d/ y2 z6 }( F% w. }
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";8 j7 c' {! c/ M) e9 f0 ]" z
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object3 ?. e- ^$ J' Q* S& x1 Y* T
Template template = new Transaction.Builder()
.setTtl(10); f- U1 P5 o! {& ? p8 d {" j
// 1 input! F% w3 R/ T) C) }- T9 I2 y2 y* i0 p' i
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)
.setAmount(40000000)
)9 _' ?8 B0 j1 @8 X
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(300000000)$ W# H: A! \- ~+ b' k2 j
) // 2 input B( p4 q4 \) I8 Q
.addAction(
new Transaction.Action.SpendFromAccount()' m$ s- Y9 P, H( V; K6 n$ M
.setAccountId("0G1Q6V1P00A02") // Multi-keys account3 k9 _; C; F2 }2 t) \+ B- Y
.setAssetId(asset_id)
.setAmount(40000000)+ \' j: S4 F7 f# z, B# I" V
), f0 L. T* r' [, T/ }
.addAction(. W2 }( x% l; a! C3 z
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1Q6V1P00A02")# D6 T. t" V4 x' O/ F0 y( N# |6 `: U- U
.setAssetId(asset_id)8 J. U: q, ~9 r0 U& q. G
.setAmount(300000000)* G r" x2 J& Z. u5 y* b
)
.addAction(
new Transaction.Action.ControlWithAddress() Q i* o2 M' L& {+ b/ E+ [9 w: J
.setAddress(address)
.setAssetId(asset_id)
.setAmount(60000000)
).build(client);$ w/ h H) n. V
logger.info("template: " + template.toJson());! S8 ~# `# T0 t% S" B& u$ r8 X
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object2 g4 C# P/ [3 o( K! {
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array+ h, x* t! h |% E" b" h. @; o. G, p
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",1 ^4 g& }; W, U. }2 ?
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);/ n, A" f/ Q4 Z! w$ X
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);5 [. C& C1 d0 ]9 x3 W
logger.info("basic signed raw: " + basicSigned.toJson());$ o- S1 [1 M3 r( }5 i
// call sign transaction api to calculate whole raw_transaction id4 j4 k3 k9 _% m
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,/ p& v5 v( U7 y
basicSigned, "");! W5 X& V6 i, i: I( b
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction' i7 n7 b* w2 w6 \- [% b( K
}