Github地址:https://github.com/Bytom/bytom# n3 q& S$ o5 d* J
Gitee地址:https://gitee.com/BytomBlockchain/bytom
tx_signer* n X2 `9 G0 u* ~' `( O" U; F
Java implementation of signing transaction offline to bytomd.
Pre: F( E s8 P7 _. m
Get the source code
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom# o; N1 u0 M( a0 B: ]% c/ ]% o/ }
git checkout
$ git checkout dev) Z+ T1 l. _3 g m# s, w1 L: F& M
Why need dev branch? Because you could call decode transaction api from dev branch and obtain tx_id and some inputs ids.+ }& l. N' t9 ^3 R1 {5 W: ~
Build; n; w A) P7 v1 f' W
$ cd $GOPATH/src/github.com/bytom
$ make bytomd # build bytomd
$ make bytomcli # build bytomcli. |& C" V0 }3 A/ h c
When successfully building the project, the bytom and bytomcli binary should be present in cmd/bytomd and cmd/bytomcli directory, respectively.) x2 G& N: W) p) T) C
Initialize
First of all, initialize the node:3 N, b/ @ r4 e6 U7 ?! [4 q1 b2 O
$ cd ./cmd/bytomd# c5 Q9 S0 R. w# ~ z# D
$ ./bytomd init --chain_id solonet/ P. S# M$ E" u
launch
$ ./bytomd node --mining
Usage
Build jar5 J) Y) M0 c7 Y7 O4 V) F8 L% S
first get source code5 A1 Q9 T. G) x! R/ T U ^
git clone https://github.com/successli/tx_signer.git
get jar package) X* Z9 R) a5 e- j5 ?
$ mvn assembly:assembly -Dmaven.test.skip=true
You can get a jar with dependencies, and you can use it in your project.+ g6 g5 i6 d5 { @# N
Test cases8 {2 W; s& g8 u$ q# S( u
Need 3 Parameters:
Call method:
// return a Template object signed offline basically.
Template result = signatures.generateSignatures(privates, template, rawTransaction);, Q( V0 Z, w* `
// use result's raw_transaction call sign transaction api to build another data but not need password or private key., W1 Z2 L0 L3 p4 Z% n# i+ I, E
Single-key Example:& k# o* K3 Q6 d: ^# V [
@Test% ~) B. i+ T# h- i0 P9 O1 ~1 C
// 使用 SDK 来构造 Template 对象参数, 单签4 y% x8 ]' k. j
public void testSignSingleKey() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";4 I" z4 g% X; E0 b+ }
// build transaction obtain a Template object
Template template = new Transaction.Builder()- H6 ~* L# ` } P; z# }0 m
.addAction(; F4 s0 c% g/ |2 r z$ t
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")! Q N5 n0 i( R# y+ P1 M
.setAssetId(asset_id)
.setAmount(40000000)
): r2 R+ O' {. C: m+ J
.addAction(! F# K8 } v9 t$ k, P d* [& D& z) o
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)
.setAmount(300000000)- ]! m" w1 T8 R
)" D T3 I" W3 |+ x, M8 E: {% }
.addAction(
new Transaction.Action.ControlWithAddress()& V M& a* F9 W- V! d6 V) R
.setAddress(address)
.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);
logger.info("decodeTx: " + decodedTx.toJson());
// need a private key array
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};
logger.info("private key:" + privateKeys[0]);6 x3 P% v9 K. b4 U, I' o' b
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);* H1 K- m2 s t- U4 `
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,+ a+ y F% b! y3 C/ O7 J& e& w
basicSigned, "");% w( C9 i$ R) W/ }% c2 j5 [
logger.info("result raw_transaction: " + result.toJson());; r+ T6 r$ L( w4 p. a$ ^
// success to submit transaction( K A3 e: f0 R8 _( t5 a. q3 p
}$ k; a+ S: W, ]5 }0 f- v
Multi-keys Example:0 S- a2 P; M: p8 u' ]
Need an account has two or more keys.1 C- f. p: b2 O, i+ s
@Test
// 使用 SDK 来构造 Template 对象参数, 多签) m: ?$ [' U! }
public void testSignMultiKeys() throws BytomException {5 X' R7 u3 z) W9 A) |7 W+ v7 V" c
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object) Y1 }$ S1 n$ v! w0 Y. x
// account 0G1RPP6OG0A06 has two keys# T0 V$ w* {+ ?5 u$ Y+ w6 I7 W: q: z
Template template = new Transaction.Builder(). l5 H4 H, l2 y" ?
.setTtl(10)
.addAction(6 ~, X$ w Q$ k
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")5 _ `( N K, I# W" h
.setAssetId(asset_id)
.setAmount(40000000)5 Y6 l) _1 B' X8 N X9 k. E; j4 V
)
.addAction(& Y t& r+ N7 A
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06"); F, T0 k7 D/ J* V" ?
.setAssetId(asset_id)4 M2 l3 l2 c( H% `8 [# b
.setAmount(300000000)
)1 V( X) \0 b1 w7 F" z8 x
.addAction(
new Transaction.Action.ControlWithAddress()
.setAddress(address)8 F& R& D( j/ ?4 J' b
.setAssetId(asset_id)
.setAmount(30000000)
).build(client);
logger.info("template: " + template.toJson());! ~5 [& k/ W6 d. m/ N
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);: ?* B8 `% b7 M, b. @
logger.info("decodeTx: " + decodedTx.toJson()); q! P' b1 f4 l, w @- w
// need a private key array; j+ U- S _$ ~5 }$ v
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",$ a9 h7 b. F7 b& P3 K/ _
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};3 t4 q, O$ @& V o: h( \
logger.info("private key 1:" + privateKeys[0]);. x/ U8 N& c- [- E6 `/ O+ W8 t
logger.info("private key 2:" + privateKeys[1]);
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();8 D) B4 \1 O9 l$ M$ Y- K2 X; N
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());; S/ P0 {* Z* n2 o7 H
// call sign transaction api to calculate whole raw_transaction id6 m% t9 `- K: [/ f ]
// sign password is None or another random String: f- S% j' }( v: h$ M$ }4 R5 W
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());4 f5 @( j4 u3 o$ k- G
// success to submit transaction
}
Multi-keys and Multi-inputs Example:: F3 {5 S5 \. {5 e1 X, p% [
@Test
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();# B s, ^/ _) V! r
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; S4 G9 X3 @6 g9 x1 ^7 v, K
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
Template template = new Transaction.Builder()( Q: ]( n+ q% C* [6 T
.setTtl(10)' y N J- z6 Z+ x7 v0 E
// 1 input
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") // Multi-keys account6 U, d' q* f2 H2 Z- e( [8 a5 Z
.setAssetId(asset_id)0 c' p' i/ l5 H B* Z
.setAmount(40000000)
)0 P$ [9 ] S i: o5 d- X* a- _
.addAction(
new Transaction.Action.SpendFromAccount()9 E7 d; o* ?8 j8 Q8 x
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)* P% }$ {& D* u8 p6 b5 p" L
.setAmount(300000000)
) // 2 input* Z* S! U& t- o
.addAction(
new Transaction.Action.SpendFromAccount()# O/ @; P* Y& E
.setAccountId("0G1Q6V1P00A02") // Multi-keys account8 a0 W* }3 p( r& V, W* W
.setAssetId(asset_id)
.setAmount(40000000)
)
.addAction(
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1Q6V1P00A02")2 U6 i, B- ]3 s$ c6 C$ b. u/ B
.setAssetId(asset_id)
.setAmount(300000000)
)
.addAction(( ]$ t) x$ k& O+ A5 V2 N8 e; i
new Transaction.Action.ControlWithAddress() `2 H2 o2 B3 P* T$ o
.setAddress(address)
.setAssetId(asset_id)
.setAmount(60000000): W6 @* a8 _4 a x
).build(client);* f3 k/ t) k% u3 l i
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());6 |5 Z v8 M S0 @, n8 P
// need a private key array- z! H. C+ B. n; I% T) j6 a' O, f
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};3 H' v- V* D# G+ \4 [0 n/ `- A# E
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();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);
logger.info("basic signed raw: " + basicSigned.toJson());5 @- F. R( {( [
// call sign transaction api to calculate whole raw_transaction id
// sign password is None or another random String3 O' ^: h* {4 S+ N x
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}