Github地址:https://github.com/Bytom/bytom- k$ `: Y& j$ d m; w
Gitee地址:https://gitee.com/BytomBlockchain/bytom
tx_signer8 Q+ e' K8 A+ D a* R) Z' G$ U
Java implementation of signing transaction offline to bytomd. d5 [2 L/ Q4 N/ M
Pre- D3 k6 ^3 O; W+ C1 \
Get the source code6 b. \% O, v: s: [9 C' g
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom3 u# S& v/ W" H% }; b0 I
git checkout( w5 b( m9 H. O) Q! e* v
$ 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.
Build
$ cd $GOPATH/src/github.com/bytom, |! Z0 c% Y: G" N
$ make bytomd # build bytomd+ E x& y6 @+ B! z+ c! E/ Y& e
$ make bytomcli # build bytomcli
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively.4 y2 `1 _4 x4 h: H2 E% T
Initialize
First of all, initialize the node:
$ cd ./cmd/bytomd/ s# D0 Q8 c, r) K
$ ./bytomd init --chain_id solonet r7 Y+ y/ T; q1 c
launch
$ ./bytomd node --mining) l& j( j6 B: s( ~$ i9 C
Usage
Build jar! q4 A. o' ^/ Z( d& q8 w
first get source code
git clone https://github.com/successli/tx_signer.git [7 C7 N# d% y9 w% R& G
get jar package2 s4 b0 P% ~3 c. g
$ mvn assembly:assembly -Dmaven.test.skip=true
You can get a jar with dependencies, and you can use it in your project.9 k. T- Y9 Z: X7 L! Q9 A8 w2 c
4 u1 i; u5 v' p+ U$ D, S
Test cases
Need 3 Parameters:
Call method:9 O/ o( A8 h2 X2 y/ o$ E
// return a Template object signed offline basically.; t7 A& v3 `( e
Template result = signatures.generateSignatures(privates, template, rawTransaction); U- v; U/ L* d
// use result's raw_transaction call sign transaction api to build another data but not need password or private key.
Single-key Example:
@Test3 E3 J/ A2 A# x+ b2 S
// 使用 SDK 来构造 Template 对象参数, 单签0 \$ E2 @' h0 T- I: B& X3 j% }( ?# R
public void testSignSingleKey() throws BytomException {
Client client = Client.generateClient();- V; O9 @( F' u
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";$ N1 l8 o) Y7 P5 l) X8 i+ A% R
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.addAction(! ^6 N: p' O: w6 C9 u8 W9 m# G
new Transaction.Action.SpendFromAccount()$ ^ F# K3 X1 P7 M, v+ R- X
.setAccountId("0G0NLBNU00A02")* S2 i4 z* l- X& Y, U. j; G6 f
.setAssetId(asset_id)
.setAmount(40000000)
)2 x3 N! t, }" q- r: I/ l$ B
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)
.setAmount(300000000)
)
.addAction(
new Transaction.Action.ControlWithAddress()! }4 q/ k# I# r3 \) l# T
.setAddress(address)
.setAssetId(asset_id)% T! f f, y1 A' X3 n
.setAmount(30000000)
).build(client);4 z* ?: W4 i% d* y5 P# H; g
logger.info("template: " + template.toJson());
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object$ N5 y& C' o& _7 s$ z( O$ i, h
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);" w4 \6 V* l8 Q, g1 z
logger.info("decodeTx: " + decodedTx.toJson());5 \0 u b. R0 v6 ^$ ?# i# }
// need a private key array2 P" p" u& y. p" N0 c; j
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);
// 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());! L' m& Q% @1 h, L9 m1 |$ |
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String+ _* t) C: x* N: L2 P
Template result = new Transaction.SignerBuilder().sign(client,5 S3 B) ^8 Z% u! D0 s& D
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());$ |0 P, U- N8 t8 D% j+ G( W; z! C
// success to submit transaction
}
Multi-keys Example:9 s$ w' W- J- h5 N% t
Need an account has two or more keys.
@Test
// 使用 SDK 来构造 Template 对象参数, 多签
public void testSignMultiKeys() throws BytomException {5 L5 Q7 i6 m( a" P, P
Client client = Client.generateClient();# R Y, J* ^" M' K3 j+ J& j
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";7 Y, C& k% b E" _" J' x
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";7 x- m/ ]* c3 w/ ~9 q# b) B
// build transaction obtain a Template object5 p! X' {8 }8 t! e. g2 S! v
// account 0G1RPP6OG0A06 has two keys
Template template = new Transaction.Builder()
.setTtl(10)( f# e2 ]1 Q+ v* ]+ Q4 W* Y
.addAction(2 J! {/ D# e( E: x& I' w) P2 ?
new Transaction.Action.SpendFromAccount()8 ^5 l! D9 X$ V0 A$ U( N8 b
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)7 Q7 f9 F8 X! z% U
.setAmount(40000000)
)' C( W r4 b0 d
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")5 e$ B- N+ c* T$ O$ D
.setAssetId(asset_id)
.setAmount(300000000)
)& z% A2 }9 p! s7 V
.addAction() `! K, i+ v+ D9 G/ t, x7 A
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)1 U* |! ]6 m. i& P2 x# J
.setAmount(30000000)
).build(client);/ D" n f% ]: w. m* f
logger.info("template: " + template.toJson());, e" X" q' Z* w
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object6 m2 k3 ]0 B3 f; f0 z4 F9 v
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);" \# J! B, F% @4 W7 {2 f- S0 o& I
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);' j8 w" K0 t8 T: g" c w. ?
// call offline sign method to obtain a basic offline signed template9 }6 |1 Q( o5 Z; x
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);* n+ O( b% V4 _
logger.info("basic signed raw: " + basicSigned.toJson());9 T0 _ q3 z& r! H. `9 I) x
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String3 P+ | b! Q7 N
Template result = new Transaction.SignerBuilder().sign(client,9 f8 m/ J+ n7 l4 J* D T4 y
basicSigned, "");1 Z% h( J3 r4 u' ~) s6 b) Y
logger.info("result raw_transaction: " + result.toJson());( {& z; r& j- B: \1 ~3 n! l
// success to submit transaction
}$ [% ] t9 S4 h: e( a* }
Multi-keys and Multi-inputs Example:$ i/ P" M+ A- J4 X; [: k
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入1 ]; F' B. j* y6 t' ~1 K
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();0 Q9 s! S1 y* J: t4 Z! o
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";) S1 S( m! D& x- l0 g4 r) Q( e& m; u
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.setTtl(10)9 z. u& y6 V& Z+ H; |$ H* ?* {& s
// 1 input
.addAction(# A2 l. f9 s& q# W3 H1 c, b
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") // Multi-keys account0 b4 M4 u& h* s q: f
.setAssetId(asset_id); X, M A1 q( q7 A. J# V
.setAmount(40000000); w9 P; H/ r0 M2 z( ~
)
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(300000000)
) // 2 input) b" T. d8 m# ^6 N. Q3 E# ]5 J6 G' E
.addAction(
new Transaction.Action.SpendFromAccount()8 h( v& m [ e
.setAccountId("0G1Q6V1P00A02") // Multi-keys account
.setAssetId(asset_id); a8 C) m5 W& C+ C$ w% g% {6 @
.setAmount(40000000)* m8 L: O' E$ E0 V: H$ _; c
)) F! @% i( A U% O
.addAction(
new Transaction.Action.SpendFromAccount()% j' S$ M. J9 j B8 W) x
.setAccountId("0G1Q6V1P00A02")
.setAssetId(asset_id)
.setAmount(300000000)5 p& ~' q( v1 [- v
)1 Y8 }; [/ v+ s q f' Z
.addAction(* j8 N. p+ k/ `, T; w0 H. M
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)
.setAmount(60000000)
).build(client);
logger.info("template: " + template.toJson());5 W( @, S- v5 q+ i! b- k
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object" ^" h9 ^& ]* `$ d+ e1 E* w9 `: W( A
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);1 x9 K/ Q$ q" S
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array! i2 X; o" s4 l
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",0 P0 c" n& t/ r- b# ]2 x
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};
logger.info("private key 1:" + privateKeys[0]);/ B0 ~2 P |, ?/ u1 J- p9 m
logger.info("private key 2:" + privateKeys[1]);3 y/ @3 ^$ V% Z0 z% c8 h$ h" L
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();" d' F2 I# ~ L0 A2 [$ F
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id' J3 P( Z2 R" H7 W, n7 ~
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");1 m2 ^1 q6 b' V) R4 {/ w
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}