Github地址:https://github.com/Bytom/bytom! ~$ G" L9 ~4 c) w; K* L
Gitee地址:https://gitee.com/BytomBlockchain/bytom5 O# }5 s y! W8 L/ V
tx_signer3 r+ i/ {: d, _; V
Java implementation of signing transaction offline to bytomd.
Pre# x7 y- E j0 |
Get the source code- N8 _- x) |& ~9 [& B
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom
git checkout
$ git checkout dev$ ^3 @" j6 C: X# y
Why need dev branch? Because you could call decode transaction api from dev branch and obtain tx_id and some inputs ids.
Build
$ cd $GOPATH/src/github.com/bytom
$ make bytomd # build bytomd
$ make bytomcli # build bytomcli8 Q2 _& H' O3 s& _+ |$ o; Y+ L1 @
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively./ c; ]. r/ W# ^1 |( s4 ? p
Initialize
First of all, initialize the node:
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet
launch ^0 \: ^$ j; I$ ]- P; h' d
$ ./bytomd node --mining
Usage* U" q) ]4 s# ~) @7 o3 }
Build jar
first get source code }( P1 F9 G2 p( y3 H+ h4 M
git clone https://github.com/successli/tx_signer.git( }4 S4 o; Q7 R) A, L0 T
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.9 U9 Z: R0 D8 A z2 n+ ]
$ c( D- [+ U& V* z3 ^
Test cases; d4 N% S/ ^( q* W4 y
Need 3 Parameters:
Call method:, @3 E0 g* \! E9 K d
// return a Template object signed offline basically.
Template result = signatures.generateSignatures(privates, template, rawTransaction);
// use result's raw_transaction call sign transaction api to build another data but not need password or private key.
Single-key Example:
@Test
// 使用 SDK 来构造 Template 对象参数, 单签2 S: T9 c5 N1 t) U
public void testSignSingleKey() throws BytomException {$ e A4 |% A0 l e. _
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";! ?8 x7 @: K, J% z; a0 h) n
// build transaction obtain a Template object
Template template = new Transaction.Builder()0 ~0 R$ A- |2 d: F% D4 J% f2 o
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)& D5 T9 y" k& }0 R: E# n( R3 V
.setAmount(40000000); \- a' `7 A6 c$ z. q$ L. k7 K
)% S0 i5 j1 m8 [8 t8 @2 M& L* t1 r
.addAction(
new Transaction.Action.SpendFromAccount()3 Z6 Z7 t! S5 k7 _* u0 m5 R8 \& Y* l
.setAccountId("0G0NLBNU00A02"). k$ Q" n9 b& F6 B7 b3 N
.setAssetId(asset_id)
.setAmount(300000000)
)8 E9 L' ~" c7 _& Y
.addAction(
new Transaction.Action.ControlWithAddress()
.setAddress(address)* ?0 d; ~$ {& @
.setAssetId(asset_id): x& y9 |) `( R0 `/ w5 ]
.setAmount(30000000)
).build(client);
logger.info("template: " + template.toJson());
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object& k1 ^, y1 I: _; c( B+ P
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);( D1 n4 X0 q6 j, A3 e
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array/ Y" L" \0 H+ h; J$ j; D: y3 J
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};3 Y1 r5 t9 t& n% e) U& d
logger.info("private key:" + privateKeys[0]);1 l, P* X5 P/ P0 i6 K
// 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());- s) w! p2 T# @2 V. _3 y1 t
// call sign transaction api to calculate whole raw_transaction id6 ?" f7 \7 R. ]0 D: Y
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,' m* G P+ Y \* o: K) \0 @+ w
basicSigned, "");$ B I0 N7 E: L+ L- P; ^
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}
Multi-keys Example:! }& I) {+ f2 w4 C' Y4 Z2 L
6 d |) y/ _: h0 X$ u
Need an account has two or more keys.) x# c2 f! i0 ^: ~6 w5 M
9 Z% `' g, ^8 ~
@Test
// 使用 SDK 来构造 Template 对象参数, 多签2 L4 z/ U/ v9 H
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();9 o, j; l1 U' O, l; m
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";$ S" k5 H* m1 e, t2 k# ?; ?* Y* Y
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";% f/ ~# b* z4 x- a6 S, Y0 l
// build transaction obtain a Template object
// account 0G1RPP6OG0A06 has two keys7 q1 |1 C# F0 ^% k8 r& Y
Template template = new Transaction.Builder()
.setTtl(10)
.addAction(
new Transaction.Action.SpendFromAccount(), o" H4 M7 Q* ?. G$ f
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)# w; Y" B: B8 y1 b$ t# i
.setAmount(40000000)' V! U' F* i! Y2 t
)
.addAction(
new Transaction.Action.SpendFromAccount()+ m7 D4 E& F1 W: N6 y
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(300000000)
)
.addAction(- f( K( ^8 ^' K" {1 N) K1 E
new Transaction.Action.ControlWithAddress()
.setAddress(address)' a7 M7 [8 W, t5 g3 S3 B+ W, Z
.setAssetId(asset_id)
.setAmount(30000000)
).build(client);
logger.info("template: " + template.toJson());5 k7 t9 T `+ I: L
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object5 Z" v4 y- a+ H- D+ f5 s
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);, V5 K( ^) R& e# o! _
logger.info("decodeTx: " + decodedTx.toJson());: F+ e; _2 r+ e7 s' D- x7 a
// need a private key array+ j. S$ A, O3 m. e) t4 W. n6 F0 Q
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",: E7 U# }* T+ m4 G' L2 d
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};7 B- N* N0 ~, M0 b( w$ \* |
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);
// call offline sign method to obtain a basic offline signed template3 {6 k1 [( b% D
Signatures signatures = new SignaturesImpl();& [( j# B' Z. Y; i
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String6 _- e2 J. Y) J1 Z
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");# X1 w: Y; @! F; A# c9 K$ x1 }
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction& t, k- W* {* ]0 A* B
}
Multi-keys and Multi-inputs Example:
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入
public void testSignMultiKeysMultiInputs() throws BytomException {' L! m6 k5 N6 v' M+ r! F% D# W# W. x
Client client = Client.generateClient();/ e9 K; f: p+ h) B4 r5 h) I
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
Template template = new Transaction.Builder()# W" H# s q$ a2 @
.setTtl(10)
// 1 input
.addAction(7 @& w6 s2 P8 w/ @/ e$ r
new Transaction.Action.SpendFromAccount()1 n$ {$ Q6 } _: j; ~8 g
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)$ m+ T& S% ^: Y. B
.setAmount(40000000)
)
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")' Y# R9 n5 F- L- a( _
.setAssetId(asset_id). p1 u; P I/ N' W
.setAmount(300000000)8 G4 I R9 c" @: J1 l8 R# r
) // 2 input
.addAction(/ N a) {5 }( ]4 |. ^
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1Q6V1P00A02") // Multi-keys account
.setAssetId(asset_id)/ {: t) g$ ^6 j7 @! ]
.setAmount(40000000)
)
.addAction(6 f5 B9 C, R0 z, H) w
new Transaction.Action.SpendFromAccount(): j5 D% u8 [* B! f0 n- E" @6 y
.setAccountId("0G1Q6V1P00A02")( t5 V& b9 ?# j! K" X1 e
.setAssetId(asset_id)% R- M# _& I* D" o- C& r* T
.setAmount(300000000)+ `! F. p# B8 J% \
)
.addAction(9 Q; t8 Y3 o7 e! W M, U- v+ D
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)
.setAmount(60000000)
).build(client);6 x+ }- J! l8 g1 R
logger.info("template: " + template.toJson());) V- _, I: H3 i; C& N
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);5 c1 j: `/ a- r" q% ~: w7 D% [& y
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array* ~) m9 s, V' |# x
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};& s5 W/ I! h7 O9 y% H2 q) h
logger.info("private key 1:" + privateKeys[0]);# d9 W; p9 C# [8 I7 c9 M' b
logger.info("private key 2:" + privateKeys[1]);
// call offline sign method to obtain a basic offline signed template3 g& P8 z, p( k1 w3 F. B @9 {" R) l
Signatures signatures = new SignaturesImpl();" }1 {% U+ n% @ ]5 m: I7 R
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());8 Q: ?0 ^1 o) ]1 F4 K8 Y5 R
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String k* u+ T9 Q) V6 p
Template result = new Transaction.SignerBuilder().sign(client,( Y" z, W# K8 N
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());* F5 c% H. S/ A& A" M" h4 {0 {' I
// success to submit transaction& m0 M4 C7 y: M: B
}