Github地址:https://github.com/Bytom/bytom' j* P# @5 c& S$ a
Gitee地址:https://gitee.com/BytomBlockchain/bytom' f! j# @0 [, Q
tx_signer
Java implementation of signing transaction offline to bytomd.4 a7 I, r" [" k; l: p
Pre
Get the source code
$ 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.0 F) i" ~; s6 v- j
Build
$ cd $GOPATH/src/github.com/bytom: ~2 x+ P- [2 j; z6 s
$ make bytomd # build bytomd) T [) u4 [; _2 b9 E4 z7 H
$ 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.
Initialize% ^5 e, e. i: n% ?9 h1 |& o/ r
First of all, initialize the node:
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet
launch. i! e5 i# L7 i I% h; t2 u
$ ./bytomd node --mining
Usage) K2 Q; S! l, [. B# G6 ~
Build jar/ Y% Z' e3 x% [, ~2 l; {& T9 Q% T0 ^- f1 c
first get source code3 x4 b: ^% H0 i! \& O
git clone https://github.com/successli/tx_signer.git
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 n F8 @; k- ~, P' U9 k
Test cases
Need 3 Parameters:
Call method:) W, u; J, m: f# D/ U G3 x
// return a Template object signed offline basically.! Q* ~5 I4 {' x; A
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:- ^1 N' w6 q+ F A6 j' g
@Test5 K0 F Y' S4 C! v5 W
// 使用 SDK 来构造 Template 对象参数, 单签: G" z/ u* R- `& B: `
public void testSignSingleKey() throws BytomException {! N2 b- a4 Y- e7 f
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object0 m, |. ^7 u' J+ d2 {! G s. w
Template template = new Transaction.Builder()0 M4 ^! M: Z1 n, ^: \/ e
.addAction(6 J k6 X5 G$ X
new Transaction.Action.SpendFromAccount(). _5 w2 W8 S' F0 u' b
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)
.setAmount(40000000)0 `" r1 }! K! `1 n0 y
)) y& Q. b, n2 b7 z9 u W' }& I) e
.addAction(" ~: r. r% s/ @ w
new Transaction.Action.SpendFromAccount()% ~6 B" k% W+ E( S$ A# a6 f
.setAccountId("0G0NLBNU00A02"); p$ |7 h" w1 x
.setAssetId(asset_id)
.setAmount(300000000)% Q' `5 i+ Q, ?5 d
). d' S: o$ S5 V/ A) |
.addAction(5 k, k& T2 ~% n8 g; {, R
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)# h5 m5 e/ X; }6 F
.setAmount(30000000)
).build(client);
logger.info("template: " + template.toJson());$ C! x9 G6 f7 v& L4 U& u. r* y( T
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object. {+ }8 R9 s& R) P3 o5 _1 ^# s
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);. R' `# ^' N% B7 U- r1 O7 A+ F% n2 A1 h
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array1 H" _+ H5 _$ z+ Q9 |
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);
// call offline sign method to obtain a basic offline signed template* e5 V: `; U" `. q9 _3 Q. h
Signatures signatures = new SignaturesImpl();2 B- A8 w8 R1 U5 E" ~$ J" F
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);, m8 q! k) W# P3 k
logger.info("basic signed raw: " + basicSigned.toJson());# C9 z; f* K6 }1 p# T' T& f8 @
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String1 V9 T9 [+ N0 S
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());0 J1 Z7 w. m" A3 R: a
// success to submit transaction
}9 k- ?7 s4 U, R$ t! n
Multi-keys Example:
# }; I ^0 U: y& t1 F
Need an account has two or more keys.
@Test
// 使用 SDK 来构造 Template 对象参数, 多签- u5 N i q1 }6 }* _
public void testSignMultiKeys() throws BytomException {3 i9 F: U; I F7 u3 L9 }2 N4 F# I
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";0 o% Q/ S5 Z* B. {) R8 s: x
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";, w6 t5 Q" T" Y: D
// build transaction obtain a Template object
// account 0G1RPP6OG0A06 has two keys
Template template = new Transaction.Builder()
.setTtl(10)
.addAction(+ f' [7 N) i/ D
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(40000000): V! g0 P0 q$ n$ Z+ ?6 K8 g. W
)5 r7 D! P& X1 L6 ^0 o* F
.addAction(
new Transaction.Action.SpendFromAccount()) c& ^ O; U$ N1 h& K: Y4 v* G
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)* U3 m- f6 K8 G. z
.setAmount(300000000); }" Y( K8 Z; ?* m y4 F
)
.addAction(" e' \4 q4 |- w" T* v/ Z7 O
new Transaction.Action.ControlWithAddress()- _9 m7 O6 P' h
.setAddress(address)
.setAssetId(asset_id)! S4 m3 e% G! @1 P% \8 S
.setAmount(30000000)% F/ E$ S; K" r; J! H7 i& w
).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);+ h% j+ u v1 u
logger.info("decodeTx: " + decodedTx.toJson());$ f" @0 O% D: \) }! _
// need a private key array6 H W7 A5 W9 c8 y$ B* N
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);+ `; E+ s8 h) {) C3 c
logger.info("private key 2:" + privateKeys[1]);8 L6 r3 L7 \1 e5 m; |; s W2 \
// call offline sign method to obtain a basic offline signed template2 S* D! b! ?* h( g
Signatures signatures = new SignaturesImpl();3 Y9 Y/ b2 \6 c% g# A3 X
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 String
Template result = new Transaction.SignerBuilder().sign(client,( q( o! ]" g) Q& A r4 x6 t
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());% Q1 N* [4 h, I( L3 L2 U
// success to submit transaction7 j1 G/ `* V, y0 F
}4 f6 C- v `& j. W
Multi-keys and Multi-inputs Example:& V. G+ V7 n+ F1 o* d
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入! [# @8 H5 O/ G' P' B* l
public void testSignMultiKeysMultiInputs() throws BytomException {- g. X* C+ m. a3 n
Client client = Client.generateClient();! }4 d% [7 X( H/ G3 I3 k
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";5 G. I3 v. a* ], U% `& R
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object; n% @, l$ e+ u- ], `% S9 z6 y
Template template = new Transaction.Builder()
.setTtl(10)
// 1 input8 u5 d0 q6 I2 q" ?% p9 Z
.addAction(
new Transaction.Action.SpendFromAccount()4 B+ d' x! W6 \
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)
.setAmount(40000000) |4 c8 i5 E- S
)& j) O b( j' W4 l, z" X# q8 l
.addAction(6 k; H, S9 t" |7 [! k
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06"): j2 j& M$ P a9 c) e, k6 q* c0 [
.setAssetId(asset_id)
.setAmount(300000000)
) // 2 input
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1Q6V1P00A02") // Multi-keys account3 [4 C- q7 T# h) ^5 v- B3 |7 h
.setAssetId(asset_id)
.setAmount(40000000)
)
.addAction(
new Transaction.Action.SpendFromAccount(); g& k2 Y) z) l* `/ D
.setAccountId("0G1Q6V1P00A02")
.setAssetId(asset_id)
.setAmount(300000000)
)
.addAction(6 d- Y( b+ P. y+ s a9 M3 H
new Transaction.Action.ControlWithAddress()
.setAddress(address)6 k; V0 a7 Z0 {8 Q2 a( b
.setAssetId(asset_id)
.setAmount(60000000)' N: l! [( l3 F' [' O+ f) a- x
).build(client);
logger.info("template: " + template.toJson());8 C( y" I4 f- Q x7 ^8 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 O0 E1 ?" } ^
// need a private key array
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};% s8 D+ E$ z8 b" u2 d: _
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);9 F# H2 n' s: _+ G/ c9 n& P; R, o
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);' ]1 q; F, x" O9 ?% O
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String! ^8 N- C. o, _7 ?& G% i$ E! s
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());( {; f9 N" G# x. R" ~" F
// success to submit transaction
}