Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockchain/bytom% Y) W4 e7 ?% h" Q
tx_signer" S. Y: z1 f8 c, E( i
Java implementation of signing transaction offline to bytomd.( b# E: Q* F$ d2 w! `
Pre
Get the source code
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom( ?! ]% @4 V$ H7 j
git checkout
$ git checkout dev- w- c3 G7 i% V, {3 Q8 F
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 U( ^, f8 Q2 G
$ make bytomd # build bytomd/ Q- A5 r$ c/ z
$ make bytomcli # build bytomcli) w8 c: d) ?; u' b/ _8 l
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively.
Initialize0 L6 T- F: e+ s3 v
First of all, initialize the node:
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet7 K* }6 F. R4 ], L$ B4 Y% s E
launch u) q$ d% e! u& c9 f
$ ./bytomd node --mining
Usage* _+ l7 o) v& h! N" o
Build jar2 e+ J" [' _- U$ S
first get source code
git clone https://github.com/successli/tx_signer.git2 W! i5 G8 d8 p/ S$ \
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.4 a1 B2 i& }7 k$ R8 p
Test cases
Need 3 Parameters:
Call method:; K Z% w; u- B( `: E& O2 B
// return a Template object signed offline basically.+ P) Q5 ?1 [% ^' {* m3 o. e
Template result = signatures.generateSignatures(privates, template, rawTransaction);: _; Y. ?" e) q& Q' `. P9 ^
// use result's raw_transaction call sign transaction api to build another data but not need password or private key.6 b/ S- \6 |3 P: k
Single-key Example:
@Test d2 c9 d/ x- N: }1 d o: U* y, X
// 使用 SDK 来构造 Template 对象参数, 单签
public void testSignSingleKey() throws BytomException {+ D- H) z1 T' j. [
Client client = Client.generateClient();" ~& ~5 G6 Y7 X" o2 q
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.addAction(
new Transaction.Action.SpendFromAccount()$ g) s d; f3 s+ S* h! A
.setAccountId("0G0NLBNU00A02")$ i- a9 l5 o. S7 c+ ~) e+ O+ F$ L
.setAssetId(asset_id)2 O: l1 W$ q- f; @ e
.setAmount(40000000)
)' ?5 b: h( ] x3 w
.addAction(
new Transaction.Action.SpendFromAccount() M" t: T0 [# w3 U C, b
.setAccountId("0G0NLBNU00A02")7 D2 [8 f# O6 s, m1 n
.setAssetId(asset_id)
.setAmount(300000000)4 a9 S# W8 G X
)
.addAction(3 m( b6 m2 m. S0 [+ j
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)" L7 B6 x& J2 E! D& x
.setAmount(30000000)& W1 z* c3 @3 f( I7 _
).build(client);4 K* g) r. x& i* B- M2 `5 d2 t
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);7 y( K9 T! j! J3 L+ e& o0 i
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};. J8 |0 m/ _8 y
logger.info("private key:" + privateKeys[0]);
// call offline sign method to obtain a basic offline signed template/ ?3 i7 }: N) @* `" ?+ e) i8 R+ I
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$ \/ E4 N) M e; P/ S' L
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client," O5 b4 c5 S4 R) o
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());- U3 p' c2 y+ G" J1 ?! [% U, R
// success to submit transaction
}
Multi-keys Example:
$ X! d. F, p! `- ]" D
Need an account has two or more keys." s; n0 t" K8 w! ?/ g
5 Y0 A. a9 E! R+ n. O# k
@Test4 v9 _4 g( [# n- Y. `. P6 X
// 使用 SDK 来构造 Template 对象参数, 多签
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();2 s: k2 g5 q4 h
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";/ p1 ^4 V% P* }" b% k& ^: P$ W
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
// account 0G1RPP6OG0A06 has two keys* Y* q& \3 z2 y2 v$ C
Template template = new Transaction.Builder()
.setTtl(10)6 q; a$ Z' ~* s& [4 Z% b/ ]1 K7 R
.addAction(% P, W: T2 H0 R& q( D1 h$ x0 s
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")% a) j1 m) V9 I% j. O6 W$ }9 y
.setAssetId(asset_id)0 H' ~% C7 n* W* W( z4 z
.setAmount(40000000)
)5 S: x8 S, W: K. }
.addAction(: B% W+ A3 ^2 |0 Q4 @
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id). v# D5 ?- d' i+ f Z6 o
.setAmount(300000000)
)( }! |; V ]" k- M
.addAction(
new Transaction.Action.ControlWithAddress()0 X( [, @, A; Y6 i5 x0 G( a
.setAddress(address)
.setAssetId(asset_id)
.setAmount(30000000)
).build(client);! e9 G9 e! d# \) c6 f; a( i
logger.info("template: " + template.toJson());- w6 U# s. W' [' [' Q: T3 H: J
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object2 Y$ b. L/ p" @" ?# j/ B% W* Q
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array1 @5 r* D( J) b5 H
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",: L1 @: b" l+ q% B
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);! f6 v+ J) T, s! B9 @: e$ B
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);! m8 |- L, n! p1 S
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id2 I4 b& f" T& Z
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");" z" r9 s0 p7 `4 e$ K$ E7 D: M+ S+ }
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}4 ]6 ?: T& t! R$ ]7 H) i
Multi-keys and Multi-inputs Example:* ^5 G! Q! ]8 A4 a Z: c
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入" j" Z8 l2 Y- T! S. D; D* w
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();; W/ C1 q5 O. B
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";8 V9 v( z. c$ [4 W7 w5 n
// build transaction obtain a Template object( \' U1 H) d, i+ A D
Template template = new Transaction.Builder()) U Q7 d' c! y
.setTtl(10)
// 1 input
.addAction($ d3 J: G' D3 Y( ^! Z5 ` m& n
new Transaction.Action.SpendFromAccount()# D+ @' i9 f) U) {( F
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)
.setAmount(40000000)2 Q6 g" P7 ]7 i, ^0 _3 P
)2 J/ G/ M4 L+ ^% q/ K
.addAction(. k% c7 }6 ^* I1 R
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")& n/ u- Q5 `* j0 e
.setAssetId(asset_id)
.setAmount(300000000)
) // 2 input8 f% F) d( n& Q' m# W. | ^8 Y
.addAction(, A' X( t! L! E* C% W; M8 [* B- b
new Transaction.Action.SpendFromAccount()# n" s+ x' f; t* k" t' T9 y; R$ j
.setAccountId("0G1Q6V1P00A02") // Multi-keys account
.setAssetId(asset_id)# [. u: n! O1 f C) ~) Y9 @ M) H. G
.setAmount(40000000)
)
.addAction(
new Transaction.Action.SpendFromAccount()* [4 t2 j7 f% y- M* _4 Q6 O
.setAccountId("0G1Q6V1P00A02")4 B) x4 S8 I5 H2 U$ K0 b- v% D
.setAssetId(asset_id): U* Z& f6 l5 G4 T& g
.setAmount(300000000)
)5 {. ?" F6 {0 j8 Z( J) j
.addAction(
new Transaction.Action.ControlWithAddress()
.setAddress(address)" J" H0 S4 A& H% r' e
.setAssetId(asset_id)
.setAmount(60000000): d5 m7 r0 q* A8 o. {- b6 ]8 @
).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());! A" v% R5 { M, u7 @1 G2 N
// need a private key array* A% [8 b, o3 s" L+ w" u, R6 Q
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};
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
Signatures signatures = new SignaturesImpl();. [* `+ J2 d9 x% g' p, D; m, D
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());# O6 N' P: j7 o @; f$ w: x7 Q& {
// call sign transaction api to calculate whole raw_transaction id- a$ H" j8 p7 m! H2 T7 V2 n
// sign password is None or another random String* x! o# S1 H7 o* [/ X
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}