Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockchain/bytom1 @+ a1 L) @6 ?0 K0 W' C. d
tx_signer6 ]5 ^# S& H G. x8 c% c- u
Java implementation of signing transaction offline to bytomd.
Pre
Get the source code% G, s- L2 m, {
$ 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.* q. o/ Q5 z2 \. `" d2 D4 [$ D
Build4 w6 n+ H$ e) _; v) q! C8 d/ i
$ cd $GOPATH/src/github.com/bytom. b! j- E& ~9 e7 l) C/ L( u. a" {* X
$ make bytomd # build bytomd
$ make bytomcli # build bytomcli& i4 |( q: H" A5 i: h
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively.
Initialize. Y7 ]! r; w) R1 \
First of all, initialize the node:
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet4 R8 |( m0 D' g- b T$ {' `0 C/ O
launch) {. U6 V7 T% @6 H) [' h$ y
$ ./bytomd node --mining
Usage0 T' M6 i$ u5 { w! s/ k
Build jar: S1 r/ }( f" J, J/ \, Q) Z
first get source code9 {& q! p8 k; n* `0 O4 w
git clone https://github.com/successli/tx_signer.git, h8 f6 X4 J8 g' p9 q
get jar package
$ mvn assembly:assembly -Dmaven.test.skip=true( b6 M6 r1 p. D+ z( ]' ~
You can get a jar with dependencies, and you can use it in your project.8 P9 R6 B* C2 X8 F1 @' a
2 t9 |9 r( i3 l+ ?+ S
Test cases
Need 3 Parameters:
Call method:
// return a Template object signed offline basically.5 S2 ~% I4 \4 x( e: o4 ]% c
Template result = signatures.generateSignatures(privates, template, rawTransaction);4 {5 R5 l8 x- E; X. f
// use result's raw_transaction call sign transaction api to build another data but not need password or private key.0 U, Q2 h* G: ]! q$ P: D' f
Single-key Example:0 B( c3 W: s" |( A, D! m+ J
@Test' b0 b4 s e [1 S! N
// 使用 SDK 来构造 Template 对象参数, 单签
public void testSignSingleKey() throws BytomException {
Client client = Client.generateClient();6 G" C. c( o, u
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";1 _4 e: a# m: B
// build transaction obtain a Template object
Template template = new Transaction.Builder()* G/ d7 r W# k% s4 j; D) N% B/ J
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)- K4 `- T' u4 j) ~2 j/ [1 p8 P
.setAmount(40000000)
)
.addAction(. D. k3 |# |8 o+ R x W. b
new Transaction.Action.SpendFromAccount()! F* [& A* Z. k) p: ~
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)
.setAmount(300000000), Y9 m% k _+ G8 W# v
)7 i: W- n$ d% I7 \6 f. C9 e
.addAction(; T% }' R! G2 T1 p& o
new Transaction.Action.ControlWithAddress()
.setAddress(address)/ b; c/ a, J: W% C8 ]5 ?1 U
.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);0 P: u# h- f9 Q+ e0 j% e
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array( [# u% m' k1 D5 }$ r
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);
// call offline sign method to obtain a basic offline signed template+ N6 Z: e! B1 Q2 j k+ g. b
Signatures signatures = new SignaturesImpl();
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 String1 S7 S, X! i5 {( C! N. ]- B9 S
Template result = new Transaction.SignerBuilder().sign(client,2 l9 E& k$ S' T
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());% I2 O9 b. I5 e: ~, O
// success to submit transaction
}
Multi-keys Example:* |% `; [4 E, p, k
$ @; A6 p& @/ v3 U2 W' Q
Need an account has two or more keys.
% s( C7 F; u* F+ w# K
@Test! @& j1 d" B- A! M6 X6 X% m' r
// 使用 SDK 来构造 Template 对象参数, 多签4 k1 ]2 s y) K/ o* _* B
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";4 u* b. m! g1 ?& |3 r6 H* l
// build transaction obtain a Template object2 ?* A$ x2 T+ |% V' G
// account 0G1RPP6OG0A06 has two keys
Template template = new Transaction.Builder()
.setTtl(10)% U+ Q* F& E2 i, T3 r
.addAction(* m5 R; [' j* [7 K
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") s5 k1 W: c7 J1 ]) Z6 a0 q5 B
.setAssetId(asset_id)
.setAmount(40000000)! u4 f( N$ l. r9 X) T2 p
)
.addAction(
new Transaction.Action.SpendFromAccount()" G! `; _$ W8 s0 O& J0 v1 O
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)& T; m. x" t7 E4 f: U
.setAmount(300000000)
)7 q& H7 a( C8 q$ m* _. P
.addAction(
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)* a5 a5 ?( Q! \4 E' y0 i% m& ?
.setAmount(30000000)) v" {# e4 X# y4 R& R0 {7 |
).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);' Q! _* g( U% ?4 x1 k
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array9 W+ s/ u: X# K8 Y, T3 _6 f
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",: `' N. j- N$ y$ \, B
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);9 u) N6 n! u3 \/ F* L$ V6 V3 Y! [
logger.info("private key 2:" + privateKeys[1]);% m$ L/ g$ k5 Q
// call offline sign method to obtain a basic offline signed template* `% S& D: n1 \' C
Signatures signatures = new SignaturesImpl();4 A) f* {/ X. X6 `2 B
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);) a* T- P0 Q. f) B' O
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id& L' e* E, V, T% H' V) [5 W' L
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,' b" v& P' b1 R( l( X! h
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}
Multi-keys and Multi-inputs Example:' [% ]- w7 G7 F& ~( X( k
@Test3 j, t% ]$ ~) M- A' C: d9 D$ \
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();& x k& i3 i$ g! I1 T
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; y4 K& J% x- E" p1 I
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";; K7 `3 A( x/ q) R# ~7 {" n# T
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.setTtl(10)
// 1 input+ |% V/ \2 Q6 y" V/ w/ P
.addAction(, V" I5 e' X1 l' r
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)' q- f- F* D" p, s# B
.setAmount(40000000)- l- R( j4 ^) g1 C' ~
)
.addAction(
new Transaction.Action.SpendFromAccount() r' {5 r1 m8 J
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)8 d; z o) U# n2 R* V
.setAmount(300000000)* D8 ]5 H |. z. D: R
) // 2 input/ Y4 I, {0 T) n
.addAction(
new Transaction.Action.SpendFromAccount()# I0 c; j- g" W
.setAccountId("0G1Q6V1P00A02") // Multi-keys account3 T* S- v/ ~: ~; C% d
.setAssetId(asset_id)
.setAmount(40000000)
)
.addAction(
new Transaction.Action.SpendFromAccount()2 |7 q* U8 J- f
.setAccountId("0G1Q6V1P00A02")9 k# ~0 ~8 A9 V" L) n, P0 G, t
.setAssetId(asset_id)) b. l( }9 X$ c+ P7 {- I6 W
.setAmount(300000000)
)
.addAction(1 C. P& g# g- D: @
new Transaction.Action.ControlWithAddress(), T4 Z# i2 {, J1 j( u* a7 X
.setAddress(address)
.setAssetId(asset_id), s! v8 l) D8 B5 n9 d; H
.setAmount(60000000)# z9 w* Q4 p/ ]9 L8 s1 g& A( z
).build(client);0 q+ C% w+ _/ J4 [' l( O3 c
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);8 d) y* }$ S l5 g4 W( m
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array0 \# R7 W ?% u& K# I. O" P, M& }! }
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",. }: [- M/ @1 C9 r9 D
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};: P. G" M0 @5 m' C
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);
// call offline sign method to obtain a basic offline signed template! W+ G& r) l5 I$ Q5 _! o
Signatures signatures = new SignaturesImpl();' w5 o' O+ H' k# T* l- f+ o
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);6 d, z4 u, t% _6 N9 @
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id W P& C4 n1 W1 H( I4 `
// sign password is None or another random String1 Q$ o" U! r+ O2 t( b
Template result = new Transaction.SignerBuilder().sign(client, P) J* @$ X! \ ~3 D9 P! Y5 e
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());; |* G0 E+ N6 j) V
// success to submit transaction
}