Github地址:https://github.com/Bytom/bytom( A' ~. w1 _+ x' `
Gitee地址:https://gitee.com/BytomBlockchain/bytom' S# y* E7 C1 |/ j
tx_signer) A+ N2 q6 {. S j/ ` R7 x: [
Java implementation of signing transaction offline to bytomd.
Pre# c( {8 {0 b, U/ F0 j
Get the source code0 y: P$ M7 M$ b: Z# l- B( r4 V
$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom
git checkout
$ git checkout dev) } G/ S% b% {
Why need dev branch? Because you could call decode transaction api from dev branch and obtain tx_id and some inputs ids.
Build2 ^. I. v* j* Q1 [2 v) M, o
$ cd $GOPATH/src/github.com/bytom
$ make bytomd # build bytomd
$ 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; i1 u& N* g( k! v& l# s" C- ^
First of all, initialize the node:! _( i- B* r/ Z# f7 U; t% g# U
$ cd ./cmd/bytomd
$ ./bytomd init --chain_id solonet
launch! f$ ?1 q: ]% |3 J5 @
$ ./bytomd node --mining4 ^$ N4 c7 y# Z3 b' B
Usage2 O* A! D w* T/ ^4 ]
Build jar
first get source code
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.
1 w/ n8 e- _1 H7 y
& a, ^' D( { s6 k1 h
Test cases$ Y) n) E g6 U5 G8 y
Need 3 Parameters:0 R* b' I2 V& r4 Y# ?$ r
Call method:6 d4 _4 W! m" Y1 I
// return a Template object signed offline basically.& G9 E) V Q: Z
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.- ?# z5 u( f2 |+ P
Single-key Example:
@Test5 a/ U |) Q; R
// 使用 SDK 来构造 Template 对象参数, 单签: a, i% M1 k' |$ s$ S* \$ m
public void testSignSingleKey() throws BytomException {, t5 U" C) p# ^ J0 L& c0 M( a4 e
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";. z2 w4 v! S+ p) W/ G' B! z
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.addAction(/ V9 U+ r& M. P4 q( X9 U+ G6 }
new Transaction.Action.SpendFromAccount()
.setAccountId("0G0NLBNU00A02")
.setAssetId(asset_id)
.setAmount(40000000)
)1 M; Y# H: k+ |/ Y$ U) b: {
.addAction(( G. v! ] ~. r- W7 F% o
new Transaction.Action.SpendFromAccount()! z# d7 N* y f& F
.setAccountId("0G0NLBNU00A02")6 a( `- p3 U3 N% p5 l% z
.setAssetId(asset_id)
.setAmount(300000000)
)
.addAction(
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)( l! ^7 T( q; z4 Y' b% e; r- g
.setAmount(30000000). f9 v9 j/ i: G" S" z
).build(client);8 D7 B3 P4 ?% p, I, [0 m( l
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* [ D5 x( P9 Q
String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};& r" r# t) Z- S m
logger.info("private key:" + privateKeys[0]);8 `: H1 Y$ _" L" y6 m9 j2 a
// call offline sign method to obtain a basic offline signed template; Z! S; Z- g2 Z3 T N5 C( c
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 id7 e* b$ Z. H, p4 \/ c0 v
// sign password is None or another random String' L" d9 O6 H1 P, G4 Z
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction! S) q$ r- r' i9 `- y' b# a
}5 L& s" w& |' V( w$ L( U, G
Multi-keys Example:5 r4 V6 G- I8 a9 O5 q% o+ v
( c" ~; ~, x. n, T
Need an account has two or more keys.. d% e" W0 L, _! _$ P: L0 ~* H
@Test
// 使用 SDK 来构造 Template 对象参数, 多签
public void testSignMultiKeys() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";+ O- G: p* l, L9 r' U% q2 f
// build transaction obtain a Template object
// account 0G1RPP6OG0A06 has two keys3 h% X% q/ B, X1 b
Template template = new Transaction.Builder()
.setTtl(10)
.addAction() e* I; ]. `& O4 h
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06")
.setAssetId(asset_id)- P/ l3 q$ q; z, ?5 t2 N
.setAmount(40000000)! |; j& N" @3 B l% O+ y3 d
); C, h& _' b0 f E z& [
.addAction(7 p7 ~' F4 D9 f' P+ a, B
new Transaction.Action.SpendFromAccount()
.setAccountId("0G1RPP6OG0A06") y6 U8 F+ H5 c: ]! p
.setAssetId(asset_id)0 K# G! m" _* m+ F% ^
.setAmount(300000000)4 X- P; k1 \0 {1 _+ o
)
.addAction(- i( c$ ^9 c* P8 B1 K* a& Z
new Transaction.Action.ControlWithAddress()
.setAddress(address)
.setAssetId(asset_id)6 x# L( Q, x) Z, L% L$ M4 N
.setAmount(30000000)
).build(client);: M* R- q. D* ]. D F
logger.info("template: " + template.toJson());
// use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object1 V% {0 a& o. v+ k" i7 V) ?8 C- }
RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);0 x, }6 }! f1 v% v4 u1 T& B7 w1 J1 N
logger.info("decodeTx: " + decodedTx.toJson());6 V, j5 F" ^5 }" n! {& ^0 I; l- {1 N
// need a private key array
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",) M# m) `4 D: c: F& j
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};
logger.info("private key 1:" + privateKeys[0]);& T' ~5 }- Q% D: H
logger.info("private key 2:" + privateKeys[1]);& m2 L5 T# x1 Y
// call offline sign method to obtain a basic offline signed template
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);- ~" T) g! s5 |9 ^2 N: g6 g
logger.info("basic signed raw: " + basicSigned.toJson());
// call sign transaction api to calculate whole raw_transaction id C6 l$ K: s; N7 _/ S# z
// sign password is None or another random String
Template result = new Transaction.SignerBuilder().sign(client,
basicSigned, "");2 Z7 j5 Y4 \; i
logger.info("result raw_transaction: " + result.toJson());7 [$ P% K2 F) r2 m3 x
// success to submit transaction
}3 {) _% L$ J* h$ u! s* f% L
Multi-keys and Multi-inputs Example:1 d3 I( _( s% m0 t
@Test# M5 P2 `, X+ ^
// 使用 SDK 来构造 Template 对象参数, 多签, 多输入
public void testSignMultiKeysMultiInputs() throws BytomException {
Client client = Client.generateClient();
String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";" X! @) ^! E% }4 Z# ?0 e( ^
// build transaction obtain a Template object
Template template = new Transaction.Builder()
.setTtl(10)
// 1 input |* ~) @) G1 @
.addAction(
new Transaction.Action.SpendFromAccount()- N2 k: G$ O8 U' h
.setAccountId("0G1RPP6OG0A06") // Multi-keys account; \. y2 E6 T4 v3 F& W) o/ X
.setAssetId(asset_id)+ n" ^0 h! c2 Z
.setAmount(40000000)5 y. z0 l4 s- {' P8 D
)
.addAction(. u6 W; F% v# R5 F
new Transaction.Action.SpendFromAccount()' @* E6 ^4 w8 B
.setAccountId("0G1RPP6OG0A06"), {! u! X3 j$ ~( x, J+ h$ H
.setAssetId(asset_id)$ e1 N7 b# F( K* L- T
.setAmount(300000000)
) // 2 input. y& L& U: ]" j6 [. y
.addAction(
new Transaction.Action.SpendFromAccount()- m( W4 z7 w' S0 n! S# _/ \8 c
.setAccountId("0G1Q6V1P00A02") // Multi-keys account# U- P/ ]' W* a: `; J: B5 S
.setAssetId(asset_id)3 H: z; b, O6 Z- W$ P+ x
.setAmount(40000000)' ]/ L4 {: A1 s) [, X) e
)' b _$ ^2 O: I
.addAction(+ R9 z& k5 E& A d) v" g+ N
new Transaction.Action.SpendFromAccount()) m' t5 @$ W2 P# b1 C
.setAccountId("0G1Q6V1P00A02")
.setAssetId(asset_id)( |9 C0 n7 T. L5 y7 e- T
.setAmount(300000000)& }: J8 G7 R$ l2 q3 \7 l
)
.addAction(
new Transaction.Action.ControlWithAddress()2 r2 u0 K( ^, a" I2 A
.setAddress(address)) v' T' s# ]) D, n
.setAssetId(asset_id)
.setAmount(60000000)
).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);- T3 }* G% H! `4 m
logger.info("decodeTx: " + decodedTx.toJson());+ w+ ]. z0 I5 t) g
// need a private key array
String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",, ~* M/ S. Q$ y" }
"40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",
"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};
logger.info("private key 1:" + privateKeys[0]);
logger.info("private key 2:" + privateKeys[1]);% m5 E: |+ N1 _
// call offline sign method to obtain a basic offline signed template' {4 R' Q7 p! Q
Signatures signatures = new SignaturesImpl();
Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);8 T P, J/ D1 v0 K( y) S, S
logger.info("basic signed raw: " + basicSigned.toJson());* N) h4 h; U g( x
// 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, "");
logger.info("result raw_transaction: " + result.toJson());
// success to submit transaction
}