Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockchain/bytom) i1 d2 n$ ^ K( Z2 t/ R& M; S
tx_signer6 {$ I& q: b9 q+ s
Java implementation of signing transaction offline to bytomd.3 D4 ]! K w4 x; Z
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.
Build; f( }; p& L! T8 K; M
$ cd $GOPATH/src/github.com/bytom0 t: r+ h7 x- O; A" A" v6 u
$ make bytomd # build bytomd! x/ A& d O1 M% @! ^+ x& U
$ make bytomcli # build bytomcli/ h9 ~( ~' p4 W" y
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively.
Initialize
First of all, initialize the node:
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet
launch3 n( @& |% N; `1 |) R- X. ^3 |4 `
$ ./bytomd node --mining" _3 A! d' D$ h _% j
Usage
Build jar
first get source code
git clone https://github.com/successli/tx_signer.git7 r N* ?0 U' r4 E; L
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.
( K/ p4 Q3 T" g+ G$ S
- M' o y ^0 i
Test cases
Need 3 Parameters:
Call method:
// return a Template object signed offline basically.9 C# P" z/ P5 m" a3 k, y! s
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:; w: J; I) O( s1 u
@Test0 O& ? p8 h8 W7 R
// 使用 SDK 来构造 Template 对象参数, 单签1 S/ e1 b' o) Y
public void testSignSingleKey() throws BytomException {1 H$ F* j9 i2 C0 I- S( p' P
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";2 |$ d) F% D, e9 d
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object3 @8 j! S9 c, y5 Q
Template template = new Transaction.Builder()! N8 S1 I8 C; n( R! }$ W" ]
.addAction(
new Transaction.Action.SpendFromAccount()( g. i: {8 P# V
.setAccountId("0G0NLBNU00A02")6 _/ e& \2 i) z( c
.setAssetId(asset_id)' Q. }7 w% ]' ~5 b6 ?( f- y$ d
.setAmount(40000000), ?2 T+ n$ P; e: @5 k2 T
)) S" K* T% W4 a' y+ Y
.addAction(
new Transaction.Action.SpendFromAccount(): C3 J. P0 P" w2 x" A/ f
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id). o( }9 q. h% p/ h' y# ?
.setAmount(300000000): h( N- o- h: E$ v; x
)$ w0 P( j: w% }* c+ S
.addAction(% ]1 b' Q; T, S$ J+ q' y
new Transaction.Action.ControlWithAddress(): h) d4 ^* `2 c! I* T" H, l
.setAddress(address)3 t0 h3 T& i0 P z
.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);) Z2 b! P) L2 F2 c3 ^7 ]4 x
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);
// call offline sign method to obtain a basic offline signed template+ X, M6 S% s" ~; n5 u
Signatures signatures = new SignaturesImpl();8 l- ^+ O; x7 m$ b$ O3 d) |
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);3 z6 d/ ~( V4 j. q2 d
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id- z, Z9 k; y3 n) _7 M9 h
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction6 r7 \" T; N; _
}
Multi-keys Example:
' K6 G- f$ G' y7 X# o# p, g1 m
Need an account has two or more keys.$ F$ I" f; o% r+ Y; _; r
' B* D) D2 B6 D( X$ v
@Test
// 使用 SDK 来构造 Template 对象参数, 多签
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";, L# u; i X% o4 q8 o4 ?5 E
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
// account 0G1RPP6OG0A06 has two keys* C$ a0 }! Z+ M+ Z' H' V- L' I+ N) E: D
Template template = new Transaction.Builder(). [& j( g/ s# k* e
.setTtl(10), E" ?% x" p; q$ [- S/ H, t$ \
.addAction(( G; I4 Q" w7 v$ a5 l
new Transaction.Action.SpendFromAccount()1 G: k+ k2 P+ @) f# Q H
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(40000000)7 c, m2 i1 m: b% H" y
)) x0 }/ W) Z' q
.addAction(6 G8 t" k, Y+ Y0 X& j: Z
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(300000000)) ~! M6 N# E; }( v5 J8 g
)) n* q# x1 P7 x# t! `" V" P2 D
.addAction(3 R8 T9 B2 E4 S- ] k" F9 L! `
new Transaction.Action.ControlWithAddress()
.setAddress(address)' Y& o' ?# u# R* D. ]
.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% m' O% S% Y# o+ V! K
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);
logger.info("decodeTx: " + decodedTx.toJson());" R% D' v, W% |* q6 G
// need a private key array
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",& M: y2 O* ?6 N2 m4 L
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};! |" c2 D' V( `' `* s1 x" v8 }
logger.info("private key 1:" + privateKeys[0]);3 Z0 H8 T0 F( D4 `8 ]
logger.info("private key 2:" + privateKeys[1]);
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();# N) {7 H& u1 p( M: Q
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);1 A+ e( V7 h, _$ Z6 A) L. I) I- s
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,
basicSigned, "");5 Z) K( z- v; V9 a# @& j9 G
logger.info("result raw_transaction: " + result.toJson());' |% v; H& K% c/ _ q% e: w
// success to submit transaction7 ?& |+ ^: m! {4 b! V& m7 k4 _
}- r. W$ I. D+ @( e) K! S7 t
Multi-keys and Multi-inputs Example:. C" K8 E5 a+ i. m. M
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入8 o& |) q5 X! D, ]8 c, e. K/ l
public void testSignMultiKeysMultiInputs() throws BytomException {; I3 t7 O- ]( G% M( z
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object0 ]: i" [! s" C; t, ^1 g
Template template = new Transaction.Builder()' H' q* O3 i/ K
.setTtl(10)
// 1 input1 N( ^7 z# ^6 q6 h q4 ~
.addAction(9 e; ^# ^; B1 P% f! e
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") // Multi-keys account
.setAssetId(asset_id)# T8 ~9 w/ i# h7 `, W: n4 J' {
.setAmount(40000000)' |# G: |. m) |! j4 f: d
)
.addAction(/ y! z. i% `0 D7 _! `5 ^
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)
.setAmount(300000000)/ q8 }! H9 \, J: i
) // 2 input4 B% Z0 ]* c& {! c" r' F& C
.addAction(( w. d4 r- \1 k) p$ N) J; |
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1Q6V1P00A02") // Multi-keys account
.setAssetId(asset_id)
.setAmount(40000000)
)' H- j* Q0 M5 U: O0 ]" g" A3 X
.addAction(
new Transaction.Action.SpendFromAccount()4 A; u- {3 e( e% Y( t
.setAccountId("0G1Q6V1P00A02")
.setAssetId(asset_id)
.setAmount(300000000)" @6 N4 b8 g# N' |1 [5 P& {) C, W
), t; z- H3 m7 m7 a
.addAction(
new Transaction.Action.ControlWithAddress()7 j4 d; { \& {! M5 p4 p4 x; x
.setAddress(address)
.setAssetId(asset_id)
.setAmount(60000000)" z3 s/ ]. N1 k, g5 d
).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);% c& c& i4 d/ g1 N5 V9 C* j3 ~( G
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array, ]7 o m1 @, {, k7 F1 x- o
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};9 C7 [( t9 {& W6 M0 V# @/ Y7 w
logger.info("private key 1:" + privateKeys[0]);; S4 G4 B x c% F! E$ `, M
logger.info("private key 2:" + privateKeys[1]);, {# s2 D8 s, J% \* r2 E- I
// call offline sign method to obtain a basic offline signed template2 u6 C4 }6 c0 b; t
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());5 W5 u$ a+ {$ D5 H6 a8 q
// call sign transaction api to calculate whole raw_transaction id% y, l$ w. v% ?$ N0 A: T( W9 Z# Y
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}