OSDN Git Service

merge tx signer
authorshenao78 <shenao.78@163.com>
Wed, 27 Feb 2019 03:18:01 +0000 (11:18 +0800)
committershenao78 <shenao.78@163.com>
Wed, 27 Feb 2019 03:18:01 +0000 (11:18 +0800)
116 files changed:
java-sdk/README.md [moved from README.md with 100% similarity]
java-sdk/doc/index.md [moved from doc/index.md with 100% similarity]
java-sdk/doc/transactions.md [moved from doc/transactions.md with 100% similarity]
java-sdk/pom.xml [new file with mode: 0644]
java-sdk/src/main/java/io/bytom/api/AccessToken.java [moved from src/main/java/io/bytom/api/AccessToken.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Account.java [moved from src/main/java/io/bytom/api/Account.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Asset.java [moved from src/main/java/io/bytom/api/Asset.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Balance.java [moved from src/main/java/io/bytom/api/Balance.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Block.java [moved from src/main/java/io/bytom/api/Block.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/CoreConfig.java [moved from src/main/java/io/bytom/api/CoreConfig.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Key.java [moved from src/main/java/io/bytom/api/Key.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Message.java [moved from src/main/java/io/bytom/api/Message.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/RawTransaction.java [moved from src/main/java/io/bytom/api/RawTransaction.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Receiver.java [moved from src/main/java/io/bytom/api/Receiver.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Transaction.java [moved from src/main/java/io/bytom/api/Transaction.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/UnconfirmedTransaction.java [moved from src/main/java/io/bytom/api/UnconfirmedTransaction.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/UnspentOutput.java [moved from src/main/java/io/bytom/api/UnspentOutput.java with 100% similarity]
java-sdk/src/main/java/io/bytom/api/Wallet.java [moved from src/main/java/io/bytom/api/Wallet.java with 100% similarity]
java-sdk/src/main/java/io/bytom/common/Configuration.java [moved from src/main/java/io/bytom/common/Configuration.java with 100% similarity]
java-sdk/src/main/java/io/bytom/common/ParameterizedTypeImpl.java [moved from src/main/java/io/bytom/common/ParameterizedTypeImpl.java with 100% similarity]
java-sdk/src/main/java/io/bytom/common/Utils.java [moved from src/main/java/io/bytom/common/Utils.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/APIException.java [moved from src/main/java/io/bytom/exception/APIException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/BadURLException.java [moved from src/main/java/io/bytom/exception/BadURLException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/BuildException.java [moved from src/main/java/io/bytom/exception/BuildException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/BytomException.java [moved from src/main/java/io/bytom/exception/BytomException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/ConfigurationException.java [moved from src/main/java/io/bytom/exception/ConfigurationException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/ConnectivityException.java [moved from src/main/java/io/bytom/exception/ConnectivityException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/HTTPException.java [moved from src/main/java/io/bytom/exception/HTTPException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/exception/JSONException.java [moved from src/main/java/io/bytom/exception/JSONException.java with 100% similarity]
java-sdk/src/main/java/io/bytom/http/BatchResponse.java [moved from src/main/java/io/bytom/http/BatchResponse.java with 100% similarity]
java-sdk/src/main/java/io/bytom/http/Client.java [moved from src/main/java/io/bytom/http/Client.java with 100% similarity]
java-sdk/src/main/java/io/bytom/http/SuccessMessage.java [moved from src/main/java/io/bytom/http/SuccessMessage.java with 100% similarity]
java-sdk/src/main/resources/config.properties [moved from src/main/resources/config.properties with 100% similarity]
java-sdk/src/main/resources/log4j.properties [moved from src/main/resources/log4j.properties with 100% similarity]
java-sdk/src/test/java/io/bytom/AppTest.java [moved from src/test/java/io/bytom/AppTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/TestUtils.java [moved from src/test/java/io/bytom/TestUtils.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/AccessTokenTest.java [moved from src/test/java/io/bytom/integration/AccessTokenTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/AccountTest.java [moved from src/test/java/io/bytom/integration/AccountTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/AssetTest.java [moved from src/test/java/io/bytom/integration/AssetTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/BalanceTest.java [moved from src/test/java/io/bytom/integration/BalanceTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/BlockTest.java [moved from src/test/java/io/bytom/integration/BlockTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/CoreConfigTest.java [moved from src/test/java/io/bytom/integration/CoreConfigTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/KeyTest.java [moved from src/test/java/io/bytom/integration/KeyTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/MessageTest.java [moved from src/test/java/io/bytom/integration/MessageTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/RawTransactionTest.java [moved from src/test/java/io/bytom/integration/RawTransactionTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/TransactionTest.java [moved from src/test/java/io/bytom/integration/TransactionTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/UTXOTest.java [moved from src/test/java/io/bytom/integration/UTXOTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/UnconfirmedTransactionTest.java [moved from src/test/java/io/bytom/integration/UnconfirmedTransactionTest.java with 100% similarity]
java-sdk/src/test/java/io/bytom/integration/WalletTest.java [moved from src/test/java/io/bytom/integration/WalletTest.java with 100% similarity]
pom.xml
tx-signer/README.md [new file with mode: 0755]
tx-signer/pom.xml [new file with mode: 0644]
tx-signer/src/main/java/com/google/crypto/tink/subtle/Bytes.java [new file with mode: 0755]
tx-signer/src/main/java/com/google/crypto/tink/subtle/Curve25519.java [new file with mode: 0755]
tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519.java [new file with mode: 0755]
tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519Constants.java [new file with mode: 0755]
tx-signer/src/main/java/com/google/crypto/tink/subtle/Field25519.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/MapTransaction.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/Receiver.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/SignTransaction.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/Signer.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/SubmitTransaction.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/Transaction.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/api/UTXO.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/Configuration.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/Constants.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/DerivePrivateKey.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/DeriveXpub.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/ExpandedPrivateKey.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/FindDst.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/NonHardenedChild.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/ParameterizedTypeImpl.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/SuccessRespon.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/common/Utils.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/APIException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/BadURLException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/BuildException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/BytomException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/ConfigurationException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/ConnectivityException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/HTTPException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/exception/JSONException.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/http/BatchResponse.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/http/Client.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Asset.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/AssetAmount.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/AssetDefinition.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/AssetID.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Entry.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/ExpandedKeys.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Hash.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Issue.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Mux.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Output.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Program.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Retirement.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/Spend.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/TxHeader.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/ValueDestination.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/types/ValueSource.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/util/OutputUtil.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/util/PathUtil.java [new file with mode: 0755]
tx-signer/src/main/java/io/bytom/util/SHA3Util.java [new file with mode: 0755]
tx-signer/src/main/resources/config.properties [new file with mode: 0755]
tx-signer/src/main/resources/log4j.properties [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/AppTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/TestUtil.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/api/SignTransactionTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/api/SignerTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/api/UTXOTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/common/DerivePrivateKeyTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/common/DeriveXpubTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/common/ExpandedPrivateKeyTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/common/NonHardenedChildTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/types/AssetTest.java [new file with mode: 0755]
tx-signer/src/test/java/io/bytom/util/SHA3256Test.java [new file with mode: 0755]

similarity index 100%
rename from README.md
rename to java-sdk/README.md
similarity index 100%
rename from doc/index.md
rename to java-sdk/doc/index.md
diff --git a/java-sdk/pom.xml b/java-sdk/pom.xml
new file mode 100644 (file)
index 0000000..da329b2
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>bytom-sdk</artifactId>
+        <groupId>io.bytom</groupId>
+        <version>1.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>java-sdk</artifactId>
+    <version>1.0.0</version>
+
+
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 015fe50..699501d 100644 (file)
--- a/pom.xml
+++ b/pom.xml
             </plugin>
         </plugins>
     </build>
-    <packaging>jar</packaging>
+    <modules>
+        <module>java-sdk</module>
+        <module>tx-signer</module>
+    </modules>
+    <packaging>pom</packaging>
 
     <name>bytom-sdk-java</name>
     <url>https://github.com/Bytom</url>
diff --git a/tx-signer/README.md b/tx-signer/README.md
new file mode 100755 (executable)
index 0000000..d26e3cd
--- /dev/null
@@ -0,0 +1,254 @@
+# tx_signer\r
+\r
+Java implementation of signing transaction offline to bytomd.\r
+\r
+## Pre\r
+\r
+#### Get the source code\r
+\r
+```\r
+$ git clone https://github.com/Bytom/bytom.git $GOPATH/src/github.com/bytom\r
+```\r
+\r
+#### git checkout\r
+\r
+```\r
+$ git checkout dev\r
+```\r
+\r
+**Why need dev branch? Because you could call decode transaction api from dev branch and obtain tx_id and some inputs ids.**\r
+\r
+#### Build\r
+\r
+```\r
+$ cd $GOPATH/src/github.com/bytom\r
+$ make bytomd    # build bytomd\r
+$ make bytomcli  # build bytomcli\r
+```\r
+\r
+When successfully building the project, the `bytom` and `bytomcli` binary should be present in `cmd/bytomd` and `cmd/bytomcli` directory, respectively.\r
+\r
+#### Initialize\r
+\r
+First of all, initialize the node:\r
+\r
+```\r
+$ cd ./cmd/bytomd\r
+$ ./bytomd init --chain_id solonet\r
+```\r
+\r
+#### launch\r
+\r
+```\r
+$ ./bytomd node --mining\r
+```\r
+\r
+## Usage\r
+\r
+#### Build jar\r
+\r
+1. first get source code\r
+\r
+   ```\r
+   git clone https://github.com/successli/tx_signer.git\r
+   ```\r
+\r
+2. get jar package\r
+\r
+   ```\r
+   $ mvn assembly:assembly -Dmaven.test.skip=true\r
+   ```\r
+\r
+   You can get a jar with dependencies, and you can use it in your project.\r
+\r
+#### Test cases\r
+\r
+Need 3 Parameters:\r
+\r
+- Private Keys Array\r
+- Template Object\r
+  - After call build transaction api return a Template json object. [build transaction api](https://github.com/Bytom/bytom/wiki/API-Reference#build-transaction)\r
+  - use bytom java sdk return a Template object.\r
+- Raw Transaction\r
+  - call decode raw-transaction api from dev branch. [decode raw-transaction api](https://github.com/Bytom/bytom/wiki/API-Reference#decode-raw-transaction)\r
+\r
+Call method:\r
+\r
+```java\r
+// return a Template object signed offline basically.\r
+Template result = signatures.generateSignatures(privates, template, rawTransaction);\r
+// use result's raw_transaction call sign transaction api to build another data but not need password or private key.\r
+```\r
+\r
+Single-key Example:\r
+\r
+```java\r
+@Test\r
+// 使用 SDK 来构造 Template 对象参数, 单签\r
+public void testSignSingleKey() throws BytomException {\r
+    Client client = Client.generateClient();\r
+\r
+    String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
+    String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";\r
+    // build transaction obtain a Template object\r
+    Template template = new Transaction.Builder()\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G0NLBNU00A02")\r
+        .setAssetId(asset_id)\r
+        .setAmount(40000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G0NLBNU00A02")\r
+        .setAssetId(asset_id)\r
+        .setAmount(300000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.ControlWithAddress()\r
+        .setAddress(address)\r
+        .setAssetId(asset_id)\r
+        .setAmount(30000000)\r
+    ).build(client);\r
+    logger.info("template: " + template.toJson());\r
+    // use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object\r
+    RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);\r
+    logger.info("decodeTx: " + decodedTx.toJson());\r
+    // need a private key array\r
+    String[] privateKeys = new String[]{"10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"};\r
+    logger.info("private key:" + privateKeys[0]);\r
+    // call offline sign method to obtain a basic offline signed template\r
+    Signatures signatures = new SignaturesImpl();\r
+    Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);\r
+    logger.info("basic signed raw: " + basicSigned.toJson());\r
+    // call sign transaction api to calculate whole raw_transaction id\r
+    // sign password is None or another random String\r
+    Template result = new Transaction.SignerBuilder().sign(client,\r
+                                                           basicSigned, "");\r
+    logger.info("result raw_transaction: " + result.toJson());\r
+    // success to submit transaction\r
+}\r
+```\r
+\r
+Multi-keys Example:\r
+\r
+> Need an account has two or more keys.\r
+\r
+```java\r
+@Test\r
+// 使用 SDK 来构造 Template 对象参数, 多签\r
+public void testSignMultiKeys() throws BytomException {\r
+    Client client = Client.generateClient();\r
+\r
+    String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
+    String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";\r
+    // build transaction obtain a Template object\r
+    // account 0G1RPP6OG0A06 has two keys\r
+    Template template = new Transaction.Builder()\r
+        .setTtl(10)\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1RPP6OG0A06")\r
+        .setAssetId(asset_id)\r
+        .setAmount(40000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1RPP6OG0A06")\r
+        .setAssetId(asset_id)\r
+        .setAmount(300000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.ControlWithAddress()\r
+        .setAddress(address)\r
+        .setAssetId(asset_id)\r
+        .setAmount(30000000)\r
+    ).build(client);\r
+    logger.info("template: " + template.toJson());\r
+    // use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object\r
+    RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);\r
+    logger.info("decodeTx: " + decodedTx.toJson());\r
+    // need a private key array\r
+    String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",\r
+                                        "40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b"};\r
+    logger.info("private key 1:" + privateKeys[0]);\r
+    logger.info("private key 2:" + privateKeys[1]);\r
+    // call offline sign method to obtain a basic offline signed template\r
+    Signatures signatures = new SignaturesImpl();\r
+    Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);\r
+    logger.info("basic signed raw: " + basicSigned.toJson());\r
+    // call sign transaction api to calculate whole raw_transaction id\r
+    // sign password is None or another random String\r
+    Template result = new Transaction.SignerBuilder().sign(client,\r
+                                                           basicSigned, "");\r
+    logger.info("result raw_transaction: " + result.toJson());\r
+    // success to submit transaction\r
+}\r
+```\r
+Multi-keys and Multi-inputs Example:\r
+\r
+```java\r
+@Test\r
+// 使用 SDK 来构造 Template 对象参数, 多签, 多输入\r
+public void testSignMultiKeysMultiInputs() throws BytomException {\r
+    Client client = Client.generateClient();\r
+\r
+    String asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
+    String address = "sm1qvyus3s5d7jv782syuqe3qrh65fx23lgpzf33em";\r
+    // build transaction obtain a Template object\r
+    Template template = new Transaction.Builder()\r
+        .setTtl(10)\r
+        // 1 input\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1RPP6OG0A06") // Multi-keys account\r
+        .setAssetId(asset_id)\r
+        .setAmount(40000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1RPP6OG0A06")\r
+        .setAssetId(asset_id)\r
+        .setAmount(300000000)\r
+    )  // 2 input\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1Q6V1P00A02") // Multi-keys account\r
+        .setAssetId(asset_id)\r
+        .setAmount(40000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.SpendFromAccount()\r
+        .setAccountId("0G1Q6V1P00A02")\r
+        .setAssetId(asset_id)\r
+        .setAmount(300000000)\r
+    )\r
+        .addAction(\r
+        new Transaction.Action.ControlWithAddress()\r
+        .setAddress(address)\r
+        .setAssetId(asset_id)\r
+        .setAmount(60000000)\r
+    ).build(client);\r
+    logger.info("template: " + template.toJson());\r
+    // use Template object's raw_transaction id to decode raw_transaction obtain a RawTransaction object\r
+    RawTransaction decodedTx = RawTransaction.decode(client, template.rawTransaction);\r
+    logger.info("decodeTx: " + decodedTx.toJson());\r
+    // need a private key array\r
+    String[] privateKeys = new String[]{"08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67",\r
+                                        "40c821f736f60805ad59b1fea158762fa6355e258601dfb49dda6f672092ae5adf072d5cab2ceaaa0d68dd3fe7fa04869d95afed8c20069f446a338576901e1b",\r
+                                        "08bdbd6c22856c5747c930f64d0e5d58ded17c4473910c6c0c3f94e485833a436247976253c8e29e961041ad8dfad9309744255364323163837cbef2483b4f67"};\r
+    logger.info("private key 1:" + privateKeys[0]);\r
+    logger.info("private key 2:" + privateKeys[1]);\r
+    // call offline sign method to obtain a basic offline signed template\r
+    Signatures signatures = new SignaturesImpl();\r
+    Template basicSigned = signatures.generateSignatures(privateKeys, template, decodedTx);\r
+    logger.info("basic signed raw: " + basicSigned.toJson());\r
+    // call sign transaction api to calculate whole raw_transaction id\r
+    // sign password is None or another random String\r
+    Template result = new Transaction.SignerBuilder().sign(client,\r
+                                                           basicSigned, "");\r
+    logger.info("result raw_transaction: " + result.toJson());\r
+    // success to submit transaction\r
+}\r
+```\r
+\r
diff --git a/tx-signer/pom.xml b/tx-signer/pom.xml
new file mode 100644 (file)
index 0000000..2050e9f
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>bytom-sdk</artifactId>
+        <groupId>io.bytom</groupId>
+        <version>1.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tx-signer</artifactId>
+    <version>1.0.0</version>
+
+    <dependencies>
+        <!-- ed25519 google-->
+        <dependency>
+            <groupId>com.google.crypto.tink</groupId>
+            <artifactId>tink</artifactId>
+            <version>1.1.1</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.protobuf</groupId>
+                    <artifactId>protobuf-java</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/tx-signer/src/main/java/com/google/crypto/tink/subtle/Bytes.java b/tx-signer/src/main/java/com/google/crypto/tink/subtle/Bytes.java
new file mode 100755 (executable)
index 0000000..6cb43be
--- /dev/null
@@ -0,0 +1,183 @@
+// Copyright 2017 Google Inc.\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+//      http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+//\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+package com.google.crypto.tink.subtle;\r
+\r
+import java.nio.ByteBuffer;\r
+import java.security.GeneralSecurityException;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Helper methods that deal with byte arrays.\r
+ *\r
+ * @since 1.0.0\r
+ */\r
+public final class Bytes {\r
+    /**\r
+     * Best effort fix-timing array comparison.\r
+     *\r
+     * @return true if two arrays are equal.\r
+     */\r
+    public static final boolean equal(final byte[] x, final byte[] y) {\r
+        if (x == null || y == null) {\r
+            return false;\r
+        }\r
+        if (x.length != y.length) {\r
+            return false;\r
+        }\r
+        int res = 0;\r
+        for (int i = 0; i < x.length; i++) {\r
+            res |= x[i] ^ y[i];\r
+        }\r
+        return res == 0;\r
+    }\r
+\r
+    /**\r
+     * Returns the concatenation of the input arrays in a single array. For example, {@code concat(new\r
+     * byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, c}}.\r
+     *\r
+     * @return a single array containing all the values from the source arrays, in order\r
+     */\r
+    public static byte[] concat(byte[]... chunks) throws GeneralSecurityException {\r
+        int length = 0;\r
+        for (byte[] chunk : chunks) {\r
+            if (length > Integer.MAX_VALUE - chunk.length) {\r
+                throw new GeneralSecurityException("exceeded size limit");\r
+            }\r
+            length += chunk.length;\r
+        }\r
+        byte[] res = new byte[length];\r
+        int pos = 0;\r
+        for (byte[] chunk : chunks) {\r
+            System.arraycopy(chunk, 0, res, pos, chunk.length);\r
+            pos += chunk.length;\r
+        }\r
+        return res;\r
+    }\r
+\r
+    /**\r
+     * Computes the xor of two byte arrays, specifying offsets and the length to xor.\r
+     *\r
+     * @return a new byte[] of length len.\r
+     */\r
+    public static final byte[] xor(\r
+            final byte[] x, int offsetX, final byte[] y, int offsetY, int len) {\r
+        if (len < 0 || x.length - len < offsetX || y.length - len < offsetY) {\r
+            throw new IllegalArgumentException(\r
+                    "That combination of buffers, offsets and length to xor result in out-of-bond accesses.");\r
+        }\r
+        byte[] res = new byte[len];\r
+        for (int i = 0; i < len; i++) {\r
+            res[i] = (byte) (x[i + offsetX] ^ y[i + offsetY]);\r
+        }\r
+        return res;\r
+    }\r
+\r
+    /**\r
+     * Computes the xor of two byte buffers, specifying the length to xor, and\r
+     * stores the result to {@code output}.\r
+     *\r
+     * @return a new byte[] of length len.\r
+     */\r
+    public static final void xor(ByteBuffer output, ByteBuffer x, ByteBuffer y, int len) {\r
+        if (len < 0 || x.remaining() < len || y.remaining() < len || output.remaining() < len) {\r
+            throw new IllegalArgumentException(\r
+                    "That combination of buffers, offsets and length to xor result in out-of-bond accesses.");\r
+        }\r
+        for (int i = 0; i < len; i++) {\r
+            output.put((byte) (x.get() ^ y.get()));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Computes the xor of two byte arrays of equal size.\r
+     *\r
+     * @return a new byte[] of length x.length.\r
+     */\r
+    public static final byte[] xor(final byte[] x, final byte[] y) {\r
+        if (x.length != y.length) {\r
+            throw new IllegalArgumentException("The lengths of x and y should match.");\r
+        }\r
+        return xor(x, 0, y, 0, x.length);\r
+    }\r
+\r
+    /**\r
+     * xors b to the end of a.\r
+     *\r
+     * @return a new byte[] of length x.length.\r
+     */\r
+    public static final byte[] xorEnd(final byte[] a, final byte[] b) {\r
+        if (a.length < b.length) {\r
+            throw new IllegalArgumentException("xorEnd requires a.length >= b.length");\r
+        }\r
+        int paddingLength = a.length - b.length;\r
+        byte[] res = Arrays.copyOf(a, a.length);\r
+        for (int i = 0; i < b.length; i++) {\r
+            res[paddingLength + i] ^= b[i];\r
+        }\r
+        return res;\r
+    }\r
+\r
+    // TODO(thaidn): add checks for boundary conditions/overflows.\r
+\r
+    /**\r
+     * Transforms a passed value to a LSB first byte array with the size of the specified capacity\r
+     *\r
+     * @param capacity size of the resulting byte array\r
+     * @param value    that should be represented as a byte array\r
+     */\r
+    public static byte[] intToByteArray(int capacity, int value) {\r
+        final byte[] result = new byte[capacity];\r
+        for (int i = 0; i < capacity; i++) {\r
+            result[i] = (byte) ((value >> (8 * i)) & 0xFF);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Transforms a passed LSB first byte array to an int\r
+     *\r
+     * @param bytes that should be transformed to a byte array\r
+     */\r
+    public static int byteArrayToInt(byte[] bytes) {\r
+        return byteArrayToInt(bytes, bytes.length);\r
+    }\r
+\r
+    /**\r
+     * Transforms a passed LSB first byte array to an int\r
+     *\r
+     * @param bytes  that should be transformed to a byte array\r
+     * @param length amount of the passed {@code bytes} that should be transformed\r
+     */\r
+    public static int byteArrayToInt(byte[] bytes, int length) {\r
+        return byteArrayToInt(bytes, 0, length);\r
+    }\r
+\r
+    /**\r
+     * Transforms a passed LSB first byte array to an int\r
+     *\r
+     * @param bytes  that should be transformed to a byte array\r
+     * @param offset start index to start the transformation\r
+     * @param length amount of the passed {@code bytes} that should be transformed\r
+     */\r
+    public static int byteArrayToInt(byte[] bytes, int offset, int length) {\r
+        int value = 0;\r
+        for (int i = 0; i < length; i++) {\r
+            value += (bytes[i + offset] & 0xFF) << (i * 8);\r
+        }\r
+        return value;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/com/google/crypto/tink/subtle/Curve25519.java b/tx-signer/src/main/java/com/google/crypto/tink/subtle/Curve25519.java
new file mode 100755 (executable)
index 0000000..cd5007f
--- /dev/null
@@ -0,0 +1,431 @@
+// Copyright 2017 Google Inc.\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+//      http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+//\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+package com.google.crypto.tink.subtle;\r
+\r
+import com.google.crypto.tink.annotations.Alpha;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * This class implements point arithmetic on the elliptic curve <a\r
+ * href="https://cr.yp.to/ecdh/curve25519-20060209.pdf">Curve25519</a>.\r
+ *\r
+ * <p>This class only implements point arithmetic, if you want to use the ECDH Curve25519 function,\r
+ * please checkout {@link X25519}.\r
+ *\r
+ * <p>This implementation is based on <a\r
+ * href="https://github.com/agl/curve25519-donna/blob/master/curve25519-donna.c">curve255-donna C\r
+ * implementation</a>.\r
+ */\r
+@Alpha\r
+final class Curve25519 {\r
+    // https://cr.yp.to/ecdh.html#validate doesn't recommend validating peer's public key. However,\r
+    // validating public key doesn't harm security and in certain cases, prevents unwanted edge\r
+    // cases.\r
+    // As we clear the most significant bit of peer's public key, we don't have to include public keys\r
+    // that are larger than 2^255.\r
+    static final byte[][] BANNED_PUBLIC_KEYS =\r
+            new byte[][]{\r
+                    // 0\r
+                    new byte[]{\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00\r
+                    },\r
+                    // 1\r
+                    new byte[]{\r
+                            (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                    },\r
+                    // 325606250916557431795983626356110631294008115727848805560023387167927233504\r
+                    new byte[]{\r
+                            (byte) 0xe0, (byte) 0xeb, (byte) 0x7a, (byte) 0x7c,\r
+                            (byte) 0x3b, (byte) 0x41, (byte) 0xb8, (byte) 0xae,\r
+                            (byte) 0x16, (byte) 0x56, (byte) 0xe3, (byte) 0xfa,\r
+                            (byte) 0xf1, (byte) 0x9f, (byte) 0xc4, (byte) 0x6a,\r
+                            (byte) 0xda, (byte) 0x09, (byte) 0x8d, (byte) 0xeb,\r
+                            (byte) 0x9c, (byte) 0x32, (byte) 0xb1, (byte) 0xfd,\r
+                            (byte) 0x86, (byte) 0x62, (byte) 0x05, (byte) 0x16,\r
+                            (byte) 0x5f, (byte) 0x49, (byte) 0xb8, (byte) 0x00,\r
+                    },\r
+                    // 39382357235489614581723060781553021112529911719440698176882885853963445705823\r
+                    new byte[]{\r
+                            (byte) 0x5f, (byte) 0x9c, (byte) 0x95, (byte) 0xbc,\r
+                            (byte) 0xa3, (byte) 0x50, (byte) 0x8c, (byte) 0x24,\r
+                            (byte) 0xb1, (byte) 0xd0, (byte) 0xb1, (byte) 0x55,\r
+                            (byte) 0x9c, (byte) 0x83, (byte) 0xef, (byte) 0x5b,\r
+                            (byte) 0x04, (byte) 0x44, (byte) 0x5c, (byte) 0xc4,\r
+                            (byte) 0x58, (byte) 0x1c, (byte) 0x8e, (byte) 0x86,\r
+                            (byte) 0xd8, (byte) 0x22, (byte) 0x4e, (byte) 0xdd,\r
+                            (byte) 0xd0, (byte) 0x9f, (byte) 0x11, (byte) 0x57\r
+                    },\r
+                    // 2^255 - 19 - 1\r
+                    new byte[]{\r
+                            (byte) 0xec, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,\r
+                    },\r
+                    // 2^255 - 19\r
+                    new byte[]{\r
+                            (byte) 0xed, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f\r
+                    },\r
+                    // 2^255 - 19 + 1\r
+                    new byte[]{\r
+                            (byte) 0xee, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,\r
+                            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f\r
+                    }\r
+            };\r
+\r
+    /**\r
+     * Computes Montgomery's double-and-add formulas.\r
+     *\r
+     * <p>On entry and exit, the absolute value of the limbs of all inputs and outputs are < 2^26.\r
+     *\r
+     * @param x2     x projective coordinate of output 2Q, long form\r
+     * @param z2     z projective coordinate of output 2Q, long form\r
+     * @param x3     x projective coordinate of output Q + Q', long form\r
+     * @param z3     z projective coordinate of output Q + Q', long form\r
+     * @param x      x projective coordinate of input Q, short form, destroyed\r
+     * @param z      z projective coordinate of input Q, short form, destroyed\r
+     * @param xprime x projective coordinate of input Q', short form, destroyed\r
+     * @param zprime z projective coordinate of input Q', short form, destroyed\r
+     * @param qmqp   input Q - Q', short form, preserved\r
+     */\r
+    private static void monty(\r
+            long[] x2,\r
+            long[] z2,\r
+            long[] x3,\r
+            long[] z3,\r
+            long[] x,\r
+            long[] z,\r
+            long[] xprime,\r
+            long[] zprime,\r
+            long[] qmqp) {\r
+        long[] origx = Arrays.copyOf(x, Field25519.LIMB_CNT);\r
+        long[] zzz = new long[19];\r
+        long[] xx = new long[19];\r
+        long[] zz = new long[19];\r
+        long[] xxprime = new long[19];\r
+        long[] zzprime = new long[19];\r
+        long[] zzzprime = new long[19];\r
+        long[] xxxprime = new long[19];\r
+\r
+        Field25519.sum(x, z);\r
+        // |x[i]| < 2^27\r
+        Field25519.sub(z, origx); // does x - z\r
+        // |z[i]| < 2^27\r
+\r
+        long[] origxprime = Arrays.copyOf(xprime, Field25519.LIMB_CNT);\r
+        Field25519.sum(xprime, zprime);\r
+        // |xprime[i]| < 2^27\r
+        Field25519.sub(zprime, origxprime);\r
+        // |zprime[i]| < 2^27\r
+        Field25519.product(xxprime, xprime, z);\r
+        // |xxprime[i]| < 14*2^54: the largest product of two limbs will be < 2^(27+27) and {@ref\r
+        // Field25519#product} adds together, at most, 14 of those products. (Approximating that to\r
+        // 2^58 doesn't work out.)\r
+        Field25519.product(zzprime, x, zprime);\r
+        // |zzprime[i]| < 14*2^54\r
+        Field25519.reduceSizeByModularReduction(xxprime);\r
+        Field25519.reduceCoefficients(xxprime);\r
+        // |xxprime[i]| < 2^26\r
+        Field25519.reduceSizeByModularReduction(zzprime);\r
+        Field25519.reduceCoefficients(zzprime);\r
+        // |zzprime[i]| < 2^26\r
+        System.arraycopy(xxprime, 0, origxprime, 0, Field25519.LIMB_CNT);\r
+        Field25519.sum(xxprime, zzprime);\r
+        // |xxprime[i]| < 2^27\r
+        Field25519.sub(zzprime, origxprime);\r
+        // |zzprime[i]| < 2^27\r
+        Field25519.square(xxxprime, xxprime);\r
+        // |xxxprime[i]| < 2^26\r
+        Field25519.square(zzzprime, zzprime);\r
+        // |zzzprime[i]| < 2^26\r
+        Field25519.product(zzprime, zzzprime, qmqp);\r
+        // |zzprime[i]| < 14*2^52\r
+        Field25519.reduceSizeByModularReduction(zzprime);\r
+        Field25519.reduceCoefficients(zzprime);\r
+        // |zzprime[i]| < 2^26\r
+        System.arraycopy(xxxprime, 0, x3, 0, Field25519.LIMB_CNT);\r
+        System.arraycopy(zzprime, 0, z3, 0, Field25519.LIMB_CNT);\r
+\r
+        Field25519.square(xx, x);\r
+        // |xx[i]| < 2^26\r
+        Field25519.square(zz, z);\r
+        // |zz[i]| < 2^26\r
+        Field25519.product(x2, xx, zz);\r
+        // |x2[i]| < 14*2^52\r
+        Field25519.reduceSizeByModularReduction(x2);\r
+        Field25519.reduceCoefficients(x2);\r
+        // |x2[i]| < 2^26\r
+        Field25519.sub(zz, xx); // does zz = xx - zz\r
+        // |zz[i]| < 2^27\r
+        Arrays.fill(zzz, Field25519.LIMB_CNT, zzz.length - 1, 0);\r
+        Field25519.scalarProduct(zzz, zz, 121665);\r
+        // |zzz[i]| < 2^(27+17)\r
+        // No need to call reduceSizeByModularReduction here: scalarProduct doesn't increase the degree\r
+        // of its input.\r
+        Field25519.reduceCoefficients(zzz);\r
+        // |zzz[i]| < 2^26\r
+        Field25519.sum(zzz, xx);\r
+        // |zzz[i]| < 2^27\r
+        Field25519.product(z2, zz, zzz);\r
+        // |z2[i]| < 14*2^(26+27)\r
+        Field25519.reduceSizeByModularReduction(z2);\r
+        Field25519.reduceCoefficients(z2);\r
+        // |z2|i| < 2^26\r
+    }\r
+\r
+    /**\r
+     * Conditionally swap two reduced-form limb arrays if {@code iswap} is 1, but leave them unchanged\r
+     * if {@code iswap} is 0. Runs in data-invariant time to avoid side-channel attacks.\r
+     *\r
+     * <p>NOTE that this function requires that {@code iswap} be 1 or 0; other values give wrong\r
+     * results. Also, the two limb arrays must be in reduced-coefficient, reduced-degree form: the\r
+     * values in a[10..19] or b[10..19] aren't swapped, and all all values in a[0..9],b[0..9] must\r
+     * have magnitude less than Integer.MAX_VALUE.\r
+     */\r
+    static void swapConditional(long[] a, long[] b, int iswap) {\r
+        int swap = -iswap;\r
+        for (int i = 0; i < Field25519.LIMB_CNT; i++) {\r
+            int x = swap & (((int) a[i]) ^ ((int) b[i]));\r
+            a[i] = ((int) a[i]) ^ x;\r
+            b[i] = ((int) b[i]) ^ x;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Conditionally copies a reduced-form limb arrays {@code b} into {@code a} if {@code icopy} is 1,\r
+     * but leave {@code a} unchanged if 'iswap' is 0. Runs in data-invariant time to avoid\r
+     * side-channel attacks.\r
+     *\r
+     * <p>NOTE that this function requires that {@code icopy} be 1 or 0; other values give wrong\r
+     * results. Also, the two limb arrays must be in reduced-coefficient, reduced-degree form: the\r
+     * values in a[10..19] or b[10..19] aren't swapped, and all all values in a[0..9],b[0..9] must\r
+     * have magnitude less than Integer.MAX_VALUE.\r
+     */\r
+    static void copyConditional(long[] a, long[] b, int icopy) {\r
+        int copy = -icopy;\r
+        for (int i = 0; i < Field25519.LIMB_CNT; i++) {\r
+            int x = copy & (((int) a[i]) ^ ((int) b[i]));\r
+            a[i] = ((int) a[i]) ^ x;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Calculates nQ where Q is the x-coordinate of a point on the curve.\r
+     *\r
+     * @param resultx the x projective coordinate of the resulting curve point (short form).\r
+     * @param n       a little endian, 32-byte number.\r
+     * @param qBytes  a little endian, 32-byte number representing the public point' x coordinate.\r
+     * @throws InvalidKeyException   iff the public key is in the banned list or its length is not\r
+     *                               32-byte.\r
+     * @throws IllegalStateException iff there is arithmetic error.\r
+     */\r
+    static void curveMult(long[] resultx, byte[] n, byte[] qBytes) throws InvalidKeyException {\r
+        validatePubKeyAndClearMsb(qBytes);\r
+\r
+        long[] q = Field25519.expand(qBytes);\r
+        long[] nqpqx = new long[19];\r
+        long[] nqpqz = new long[19];\r
+        nqpqz[0] = 1;\r
+        long[] nqx = new long[19];\r
+        nqx[0] = 1;\r
+        long[] nqz = new long[19];\r
+        long[] nqpqx2 = new long[19];\r
+        long[] nqpqz2 = new long[19];\r
+        nqpqz2[0] = 1;\r
+        long[] nqx2 = new long[19];\r
+        long[] nqz2 = new long[19];\r
+        nqz2[0] = 1;\r
+        long[] t = new long[19];\r
+\r
+        System.arraycopy(q, 0, nqpqx, 0, Field25519.LIMB_CNT);\r
+\r
+        for (int i = 0; i < Field25519.FIELD_LEN; i++) {\r
+            int b = n[Field25519.FIELD_LEN - i - 1] & 0xff;\r
+            for (int j = 0; j < 8; j++) {\r
+                int bit = (b >> (7 - j)) & 1;\r
+\r
+                swapConditional(nqx, nqpqx, bit);\r
+                swapConditional(nqz, nqpqz, bit);\r
+                monty(nqx2, nqz2, nqpqx2, nqpqz2, nqx, nqz, nqpqx, nqpqz, q);\r
+                swapConditional(nqx2, nqpqx2, bit);\r
+                swapConditional(nqz2, nqpqz2, bit);\r
+\r
+                t = nqx;\r
+                nqx = nqx2;\r
+                nqx2 = t;\r
+                t = nqz;\r
+                nqz = nqz2;\r
+                nqz2 = t;\r
+                t = nqpqx;\r
+                nqpqx = nqpqx2;\r
+                nqpqx2 = t;\r
+                t = nqpqz;\r
+                nqpqz = nqpqz2;\r
+                nqpqz2 = t;\r
+            }\r
+        }\r
+\r
+        // Computes nqx/nqz.\r
+        long[] zmone = new long[Field25519.LIMB_CNT];\r
+        Field25519.inverse(zmone, nqz);\r
+        Field25519.mult(resultx, nqx, zmone);\r
+\r
+        // Nowadays it should be standard to protect public key crypto against flaws. I.e. if there is a\r
+        // computation error through a faulty CPU or if the implementation contains a bug, then if\r
+        // possible this should be detected at run time.\r
+        //\r
+        // The situation is a bit more tricky for X25519 where for example the implementation\r
+        // proposed in https://tools.ietf.org/html/rfc7748 only uses the x-coordinate. However, a\r
+        // verification is still possible, but depends on the actual computation.\r
+        //\r
+        // Tink's Java implementation is equivalent to RFC7748. We will use the loop invariant in the\r
+        // Montgomery ladder to detect fault computation. In particular, we use the following invariant:\r
+        // q, resultx, nqpqx/nqpqx  are x coordinates of 3 collinear points q, n*q, (n + 1)*q.\r
+        if (!isCollinear(q, resultx, nqpqx, nqpqz)) {\r
+            throw new IllegalStateException(\r
+                    "Arithmetic error in curve multiplication with the public key: "\r
+                            + Hex.encode(qBytes));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Validates public key and clear its most significant bit.\r
+     *\r
+     * @throws InvalidKeyException iff the {@code pubKey} is in the banned list or its length is not\r
+     *                             32-byte.\r
+     */\r
+    private static void validatePubKeyAndClearMsb(byte[] pubKey) throws InvalidKeyException {\r
+        if (pubKey.length != 32) {\r
+            throw new InvalidKeyException("Public key length is not 32-byte");\r
+        }\r
+        // Clears the most significant bit as in the method decodeUCoordinate() of RFC7748.\r
+        pubKey[31] &= (byte) 0x7f;\r
+\r
+        for (int i = 0; i < BANNED_PUBLIC_KEYS.length; i++) {\r
+            if (Bytes.equal(BANNED_PUBLIC_KEYS[i], pubKey)) {\r
+                throw new InvalidKeyException("Banned public key: " + Hex.encode(BANNED_PUBLIC_KEYS[i]));\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Checks whether there are three collinear points with x coordinate x1, x2, x3/z3.\r
+     *\r
+     * @return true if three collinear points with x coordianate x1, x2, x3/z3 are collinear.\r
+     */\r
+    private static boolean isCollinear(long[] x1, long[] x2, long[] x3, long[] z3) {\r
+        // If x1, x2, x3 (in this method x3 is represented as x3/z3) are the x-coordinates of three\r
+        // collinear points on a curve, then they satisfy the equation\r
+        //   y^2 = x^3 + ax^2 + x\r
+        // They also satisfy the equation\r
+        //   0 = (x - x1)(x - x2)(x - x3)\r
+        //     = x^3 + Ax^2 + Bx + C\r
+        // where\r
+        //   A = - x1 - x2 - x3\r
+        //   B = x1*x2 + x2*x3 + x3*x1\r
+        //   C = - x1*x2*x3\r
+        // Hence, the three points also satisfy\r
+        //   y^2 = (a - A)x^2 + (1 - B)x - C\r
+        // This is a quadratic curve. Three distinct collinear points can only be on a quadratic\r
+        // curve if the quadratic curve has a line as component. And if a quadratic curve has a line\r
+        // as component then its discriminant is 0.\r
+        // Therefore, discriminant((a - A)x^2 + (1-B)x - C) = 0.\r
+        // In particular:\r
+        //   a = 486662\r
+        //   lhs = 4 * ((x1 + x2 + a) * z3 + x3) * (x1 * x2 * x3)\r
+        //   rhs = ((x1 * x2 - 1) * z3 + x3 * (x1 + x2))**2\r
+        //   assert (lhs - rhs)  == 0\r
+        //\r
+        // There are 2 cases that we haven't discussed:\r
+        //\r
+        //   * If x1 and x2 are both points with y-coordinate 0 then the argument doesn't hold.\r
+        //   However, our ECDH computation doesn't allow points of low order (see {@code\r
+        //   validatePublicKey}). Therefore, this edge case never happen.\r
+        //\r
+        //   * x1, x2 or x3/y3 may be points on the twist. If so, they satisfy the equation\r
+        //     2y^2 = x^3 + ax^2 + x\r
+        //   Hence, the three points also satisfy\r
+        //     2y^2 = (a - A)x^2 + (1 - B)x - C\r
+        //   Thus, this collinear check should work for this case too.\r
+        long[] x1multx2 = new long[Field25519.LIMB_CNT];\r
+        long[] x1addx2 = new long[Field25519.LIMB_CNT];\r
+        long[] lhs = new long[Field25519.LIMB_CNT + 1];\r
+        long[] t = new long[Field25519.LIMB_CNT + 1];\r
+        long[] t2 = new long[Field25519.LIMB_CNT + 1];\r
+        Field25519.mult(x1multx2, x1, x2);\r
+        Field25519.sum(x1addx2, x1, x2);\r
+        long[] a = new long[Field25519.LIMB_CNT];\r
+        a[0] = 486662;\r
+        // t = x1 + x2 + a\r
+        Field25519.sum(t, x1addx2, a);\r
+        // t = (x1 + x2 + a) * z3\r
+        Field25519.mult(t, t, z3);\r
+        // t = (x1 + x2 + a) * z3 + x3\r
+        Field25519.sum(t, x3);\r
+        // t = ((x1 + x2 + a) * z3 + x3) * x1 * x2\r
+        Field25519.mult(t, t, x1multx2);\r
+        // t = ((x1 + x2 + a) * z3 + x3) * (x1 * x2 * x3)\r
+        Field25519.mult(t, t, x3);\r
+        Field25519.scalarProduct(lhs, t, 4);\r
+        Field25519.reduceCoefficients(lhs);\r
+\r
+        // t = x1 * x2 * z3\r
+        Field25519.mult(t, x1multx2, z3);\r
+        // t = x1 * x2 * z3 - z3\r
+        Field25519.sub(t, t, z3);\r
+        // t2 = (x1 + x2) * x3\r
+        Field25519.mult(t2, x1addx2, x3);\r
+        // t = x1 * x2 * z3 - z3 + (x1 + x2) * x3\r
+        Field25519.sum(t, t, t2);\r
+        // t = (x1 * x2 * z3 - z3 + (x1 + x2) * x3)^2\r
+        Field25519.square(t, t);\r
+        return Bytes.equal(Field25519.contract(lhs), Field25519.contract(t));\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519.java b/tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519.java
new file mode 100755 (executable)
index 0000000..6199c0a
--- /dev/null
@@ -0,0 +1,1846 @@
+// Copyright 2017 Google Inc.\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+//      http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+//\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+package com.google.crypto.tink.subtle;\r
+\r
+import static com.google.crypto.tink.subtle.Ed25519Constants.B2;\r
+import static com.google.crypto.tink.subtle.Ed25519Constants.B_TABLE;\r
+import static com.google.crypto.tink.subtle.Ed25519Constants.D;\r
+import static com.google.crypto.tink.subtle.Ed25519Constants.D2;\r
+import static com.google.crypto.tink.subtle.Ed25519Constants.SQRTM1;\r
+import static com.google.crypto.tink.subtle.Field25519.FIELD_LEN;\r
+import static com.google.crypto.tink.subtle.Field25519.LIMB_CNT;\r
+\r
+import java.security.GeneralSecurityException;\r
+import java.security.MessageDigest;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * This implementation is based on the ed25519/ref10 implementation in NaCl.\r
+ *\r
+ * <p>It implements this twisted Edwards curve:\r
+ *\r
+ * <pre>\r
+ * -x^2 + y^2 = 1 + (-121665 / 121666 mod 2^255-19)*x^2*y^2\r
+ * </pre>\r
+ *\r
+ * @see <a href="https://eprint.iacr.org/2008/013.pdf">Bernstein D.J., Birkner P., Joye M., Lange\r
+ * T., Peters C. (2008) Twisted Edwards Curves</a>\r
+ * @see <a href="https://eprint.iacr.org/2008/522.pdf">Hisil H., Wong K.KH., Carter G., Dawson E.\r
+ * (2008) Twisted Edwards Curves Revisited</a>\r
+ */\r
+public final class Ed25519 {\r
+\r
+    public static final int SECRET_KEY_LEN = FIELD_LEN;\r
+    public static final int PUBLIC_KEY_LEN = FIELD_LEN;\r
+    public static final int SIGNATURE_LEN = FIELD_LEN * 2;\r
+\r
+    // (x = 0, y = 1) point\r
+    private static final CachedXYT CACHED_NEUTRAL = new CachedXYT(\r
+            new long[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},\r
+            new long[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},\r
+            new long[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0});\r
+    private static final PartialXYZT NEUTRAL = new PartialXYZT(\r
+            new XYZ(new long[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},\r
+                    new long[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},\r
+                    new long[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}),\r
+            new long[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0});\r
+\r
+    /**\r
+     * Projective point representation (X:Y:Z) satisfying x = X/Z, y = Y/Z\r
+     * <p>\r
+     * Note that this is referred as ge_p2 in ref10 impl.\r
+     * Also note that x = X, y = Y and z = Z below following Java coding style.\r
+     * <p>\r
+     * See\r
+     * Koyama K., Tsuruoka Y. (1993) Speeding up Elliptic Cryptosystems by Using a Signed Binary\r
+     * Window Method.\r
+     * <p>\r
+     * https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html\r
+     */\r
+    private static class XYZ {\r
+\r
+        final long[] x;\r
+        final long[] y;\r
+        final long[] z;\r
+\r
+        XYZ() {\r
+            this(new long[LIMB_CNT], new long[LIMB_CNT], new long[LIMB_CNT]);\r
+        }\r
+\r
+        XYZ(long[] x, long[] y, long[] z) {\r
+            this.x = x;\r
+            this.y = y;\r
+            this.z = z;\r
+        }\r
+\r
+        XYZ(XYZ xyz) {\r
+            x = Arrays.copyOf(xyz.x, LIMB_CNT);\r
+            y = Arrays.copyOf(xyz.y, LIMB_CNT);\r
+            z = Arrays.copyOf(xyz.z, LIMB_CNT);\r
+        }\r
+\r
+        XYZ(PartialXYZT partialXYZT) {\r
+            this();\r
+            fromPartialXYZT(this, partialXYZT);\r
+        }\r
+\r
+        /**\r
+         * ge_p1p1_to_p2.c\r
+         */\r
+        static XYZ fromPartialXYZT(XYZ out, PartialXYZT in) {\r
+            Field25519.mult(out.x, in.xyz.x, in.t);\r
+            Field25519.mult(out.y, in.xyz.y, in.xyz.z);\r
+            Field25519.mult(out.z, in.xyz.z, in.t);\r
+            return out;\r
+        }\r
+\r
+        /**\r
+         * Encodes this point to bytes.\r
+         */\r
+        byte[] toBytes() {\r
+            long[] recip = new long[LIMB_CNT];\r
+            long[] x = new long[LIMB_CNT];\r
+            long[] y = new long[LIMB_CNT];\r
+            Field25519.inverse(recip, z);\r
+            Field25519.mult(x, this.x, recip);\r
+            Field25519.mult(y, this.y, recip);\r
+            byte[] s = Field25519.contract(y);\r
+            s[31] = (byte) (s[31] ^ (getLsb(x) << 7));\r
+            return s;\r
+        }\r
+\r
+        /**\r
+         * Checks that the point is on curve\r
+         */\r
+        boolean isOnCurve() {\r
+            long[] x2 = new long[LIMB_CNT];\r
+            Field25519.square(x2, x);\r
+            long[] y2 = new long[LIMB_CNT];\r
+            Field25519.square(y2, y);\r
+            long[] z2 = new long[LIMB_CNT];\r
+            Field25519.square(z2, z);\r
+            long[] z4 = new long[LIMB_CNT];\r
+            Field25519.square(z4, z2);\r
+            long[] lhs = new long[LIMB_CNT];\r
+            // lhs = y^2 - x^2\r
+            Field25519.sub(lhs, y2, x2);\r
+            // lhs = z^2 * (y2 - x2)\r
+            Field25519.mult(lhs, lhs, z2);\r
+            long[] rhs = new long[LIMB_CNT];\r
+            // rhs = x^2 * y^2\r
+            Field25519.mult(rhs, x2, y2);\r
+            // rhs = D * x^2 * y^2\r
+            Field25519.mult(rhs, rhs, D);\r
+            // rhs = z^4 + D * x^2 * y^2\r
+            Field25519.sum(rhs, z4);\r
+            // z^2 (y^2 - x^2) == z^4 + D * x^2 * y^2\r
+            return Bytes.equal(Field25519.contract(lhs), Field25519.contract(rhs));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Represents extended projective point representation (X:Y:Z:T) satisfying x = X/Z, y = Y/Z,\r
+     * XY = ZT\r
+     * <p>\r
+     * Note that this is referred as ge_p3 in ref10 impl.\r
+     * Also note that t = T below following Java coding style.\r
+     * <p>\r
+     * See\r
+     * Hisil H., Wong K.KH., Carter G., Dawson E. (2008) Twisted Edwards Curves Revisited.\r
+     * <p>\r
+     * https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html\r
+     */\r
+    private static class XYZT {\r
+\r
+        final XYZ xyz;\r
+        final long[] t;\r
+\r
+        XYZT() {\r
+            this(new XYZ(), new long[LIMB_CNT]);\r
+        }\r
+\r
+        XYZT(XYZ xyz, long[] t) {\r
+            this.xyz = xyz;\r
+            this.t = t;\r
+        }\r
+\r
+        XYZT(PartialXYZT partialXYZT) {\r
+            this();\r
+            fromPartialXYZT(this, partialXYZT);\r
+        }\r
+\r
+        /**\r
+         * ge_p1p1_to_p2.c\r
+         */\r
+        private static XYZT fromPartialXYZT(XYZT out, PartialXYZT in) {\r
+            Field25519.mult(out.xyz.x, in.xyz.x, in.t);\r
+            Field25519.mult(out.xyz.y, in.xyz.y, in.xyz.z);\r
+            Field25519.mult(out.xyz.z, in.xyz.z, in.t);\r
+            Field25519.mult(out.t, in.xyz.x, in.xyz.y);\r
+            return out;\r
+        }\r
+\r
+        /**\r
+         * Decodes {@code s} into an extented projective point.\r
+         * See Section 5.1.3 Decoding in https://tools.ietf.org/html/rfc8032#section-5.1.3\r
+         */\r
+        private static XYZT fromBytesNegateVarTime(byte[] s) throws GeneralSecurityException {\r
+            long[] x = new long[LIMB_CNT];\r
+            long[] y = Field25519.expand(s);\r
+            long[] z = new long[LIMB_CNT];\r
+            z[0] = 1;\r
+            long[] t = new long[LIMB_CNT];\r
+            long[] u = new long[LIMB_CNT];\r
+            long[] v = new long[LIMB_CNT];\r
+            long[] vxx = new long[LIMB_CNT];\r
+            long[] check = new long[LIMB_CNT];\r
+            Field25519.square(u, y);\r
+            Field25519.mult(v, u, D);\r
+            Field25519.sub(u, u, z); // u = y^2 - 1\r
+            Field25519.sum(v, v, z); // v = dy^2 + 1\r
+\r
+            long[] v3 = new long[LIMB_CNT];\r
+            Field25519.square(v3, v);\r
+            Field25519.mult(v3, v3, v); // v3 = v^3\r
+            Field25519.square(x, v3);\r
+            Field25519.mult(x, x, v);\r
+            Field25519.mult(x, x, u); // x = uv^7\r
+\r
+            pow2252m3(x, x); // x = (uv^7)^((q-5)/8)\r
+            Field25519.mult(x, x, v3);\r
+            Field25519.mult(x, x, u); // x = uv^3(uv^7)^((q-5)/8)\r
+\r
+            Field25519.square(vxx, x);\r
+            Field25519.mult(vxx, vxx, v);\r
+            Field25519.sub(check, vxx, u); // vx^2-u\r
+            if (isNonZeroVarTime(check)) {\r
+                Field25519.sum(check, vxx, u); // vx^2+u\r
+                if (isNonZeroVarTime(check)) {\r
+                    throw new GeneralSecurityException("Cannot convert given bytes to extended projective "\r
+                            + "coordinates. No square root exists for modulo 2^255-19");\r
+                }\r
+                Field25519.mult(x, x, SQRTM1);\r
+            }\r
+\r
+            if (!isNonZeroVarTime(x) && (s[31] & 0xff) >> 7 != 0) {\r
+                throw new GeneralSecurityException("Cannot convert given bytes to extended projective "\r
+                        + "coordinates. Computed x is zero and encoded x's least significant bit is not zero");\r
+            }\r
+            if (getLsb(x) == ((s[31] & 0xff) >> 7)) {\r
+                neg(x, x);\r
+            }\r
+\r
+            Field25519.mult(t, x, y);\r
+            return new XYZT(new XYZ(x, y, z), t);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Partial projective point representation ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T\r
+     * <p>\r
+     * Note that this is referred as complete form in the original ref10 impl (ge_p1p1).\r
+     * Also note that t = T below following Java coding style.\r
+     * <p>\r
+     * Although this has the same types as XYZT, it is redefined to have its own type so that it is\r
+     * readable and 1:1 corresponds to ref10 impl.\r
+     * <p>\r
+     * Can be converted to XYZT as follows:\r
+     * X1 = X * T = x * Z * T = x * Z1\r
+     * Y1 = Y * Z = y * T * Z = y * Z1\r
+     * Z1 = Z * T = Z * T\r
+     * T1 = X * Y = x * Z * y * T = x * y * Z1 = X1Y1 / Z1\r
+     */\r
+    private static class PartialXYZT {\r
+\r
+        final XYZ xyz;\r
+        final long[] t;\r
+\r
+        PartialXYZT() {\r
+            this(new XYZ(), new long[LIMB_CNT]);\r
+        }\r
+\r
+        PartialXYZT(XYZ xyz, long[] t) {\r
+            this.xyz = xyz;\r
+            this.t = t;\r
+        }\r
+\r
+        PartialXYZT(PartialXYZT other) {\r
+            xyz = new XYZ(other.xyz);\r
+            t = Arrays.copyOf(other.t, LIMB_CNT);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Corresponds to the caching mentioned in the last paragraph of Section 3.1 of\r
+     * Hisil H., Wong K.KH., Carter G., Dawson E. (2008) Twisted Edwards Curves Revisited.\r
+     * with Z = 1.\r
+     */\r
+    static class CachedXYT {\r
+\r
+        final long[] yPlusX;\r
+        final long[] yMinusX;\r
+        final long[] t2d;\r
+\r
+        CachedXYT() {\r
+            this(new long[LIMB_CNT], new long[LIMB_CNT], new long[LIMB_CNT]);\r
+        }\r
+\r
+        /**\r
+         * Creates a cached XYZT with Z = 1\r
+         *\r
+         * @param yPlusX  y + x\r
+         * @param yMinusX y - x\r
+         * @param t2d     2d * xy\r
+         */\r
+        CachedXYT(long[] yPlusX, long[] yMinusX, long[] t2d) {\r
+            this.yPlusX = yPlusX;\r
+            this.yMinusX = yMinusX;\r
+            this.t2d = t2d;\r
+        }\r
+\r
+        CachedXYT(CachedXYT other) {\r
+            yPlusX = Arrays.copyOf(other.yPlusX, LIMB_CNT);\r
+            yMinusX = Arrays.copyOf(other.yMinusX, LIMB_CNT);\r
+            t2d = Arrays.copyOf(other.t2d, LIMB_CNT);\r
+        }\r
+\r
+        // z is one implicitly, so this just copies {@code in} to {@code output}.\r
+        void multByZ(long[] output, long[] in) {\r
+            System.arraycopy(in, 0, output, 0, LIMB_CNT);\r
+        }\r
+\r
+        /**\r
+         * If icopy is 1, copies {@code other} into this point. Time invariant wrt to icopy value.\r
+         */\r
+        void copyConditional(CachedXYT other, int icopy) {\r
+            Curve25519.copyConditional(yPlusX, other.yPlusX, icopy);\r
+            Curve25519.copyConditional(yMinusX, other.yMinusX, icopy);\r
+            Curve25519.copyConditional(t2d, other.t2d, icopy);\r
+        }\r
+    }\r
+\r
+    private static class CachedXYZT extends CachedXYT {\r
+\r
+        private final long[] z;\r
+\r
+        CachedXYZT() {\r
+            this(new long[LIMB_CNT], new long[LIMB_CNT], new long[LIMB_CNT], new long[LIMB_CNT]);\r
+        }\r
+\r
+        /**\r
+         * ge_p3_to_cached.c\r
+         */\r
+        CachedXYZT(XYZT xyzt) {\r
+            this();\r
+            Field25519.sum(yPlusX, xyzt.xyz.y, xyzt.xyz.x);\r
+            Field25519.sub(yMinusX, xyzt.xyz.y, xyzt.xyz.x);\r
+            System.arraycopy(xyzt.xyz.z, 0, z, 0, LIMB_CNT);\r
+            Field25519.mult(t2d, xyzt.t, D2);\r
+        }\r
+\r
+        /**\r
+         * Creates a cached XYZT\r
+         *\r
+         * @param yPlusX  Y + X\r
+         * @param yMinusX Y - X\r
+         * @param z       Z\r
+         * @param t2d     2d * (XY/Z)\r
+         */\r
+        CachedXYZT(long[] yPlusX, long[] yMinusX, long[] z, long[] t2d) {\r
+            super(yPlusX, yMinusX, t2d);\r
+            this.z = z;\r
+        }\r
+\r
+        @Override\r
+        public void multByZ(long[] output, long[] in) {\r
+            Field25519.mult(output, in, z);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Addition defined in Section 3.1 of\r
+     * Hisil H., Wong K.KH., Carter G., Dawson E. (2008) Twisted Edwards Curves Revisited.\r
+     * <p>\r
+     * Please note that this is a partial of the operation listed there leaving out the final\r
+     * conversion from PartialXYZT to XYZT.\r
+     *\r
+     * @param extended extended projective point input\r
+     * @param cached   cached projective point input\r
+     */\r
+    private static void add(PartialXYZT partialXYZT, XYZT extended, CachedXYT cached) {\r
+        long[] t = new long[LIMB_CNT];\r
+\r
+        // Y1 + X1\r
+        Field25519.sum(partialXYZT.xyz.x, extended.xyz.y, extended.xyz.x);\r
+\r
+        // Y1 - X1\r
+        Field25519.sub(partialXYZT.xyz.y, extended.xyz.y, extended.xyz.x);\r
+\r
+        // A = (Y1 - X1) * (Y2 - X2)\r
+        Field25519.mult(partialXYZT.xyz.y, partialXYZT.xyz.y, cached.yMinusX);\r
+\r
+        // B = (Y1 + X1) * (Y2 + X2)\r
+        Field25519.mult(partialXYZT.xyz.z, partialXYZT.xyz.x, cached.yPlusX);\r
+\r
+        // C = T1 * 2d * T2 = 2d * T1 * T2 (2d is written as k in the paper)\r
+        Field25519.mult(partialXYZT.t, extended.t, cached.t2d);\r
+\r
+        // Z1 * Z2\r
+        cached.multByZ(partialXYZT.xyz.x, extended.xyz.z);\r
+\r
+        // D = 2 * Z1 * Z2\r
+        Field25519.sum(t, partialXYZT.xyz.x, partialXYZT.xyz.x);\r
+\r
+        // X3 = B - A\r
+        Field25519.sub(partialXYZT.xyz.x, partialXYZT.xyz.z, partialXYZT.xyz.y);\r
+\r
+        // Y3 = B + A\r
+        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.y);\r
+\r
+        // Z3 = D + C\r
+        Field25519.sum(partialXYZT.xyz.z, t, partialXYZT.t);\r
+\r
+        // T3 = D - C\r
+        Field25519.sub(partialXYZT.t, t, partialXYZT.t);\r
+    }\r
+\r
+    /**\r
+     * Based on the addition defined in Section 3.1 of\r
+     * Hisil H., Wong K.KH., Carter G., Dawson E. (2008) Twisted Edwards Curves Revisited.\r
+     * <p>\r
+     * Please note that this is a partial of the operation listed there leaving out the final\r
+     * conversion from PartialXYZT to XYZT.\r
+     *\r
+     * @param extended extended projective point input\r
+     * @param cached   cached projective point input\r
+     */\r
+    private static void sub(PartialXYZT partialXYZT, XYZT extended, CachedXYT cached) {\r
+        long[] t = new long[LIMB_CNT];\r
+\r
+        // Y1 + X1\r
+        Field25519.sum(partialXYZT.xyz.x, extended.xyz.y, extended.xyz.x);\r
+\r
+        // Y1 - X1\r
+        Field25519.sub(partialXYZT.xyz.y, extended.xyz.y, extended.xyz.x);\r
+\r
+        // A = (Y1 - X1) * (Y2 + X2)\r
+        Field25519.mult(partialXYZT.xyz.y, partialXYZT.xyz.y, cached.yPlusX);\r
+\r
+        // B = (Y1 + X1) * (Y2 - X2)\r
+        Field25519.mult(partialXYZT.xyz.z, partialXYZT.xyz.x, cached.yMinusX);\r
+\r
+        // C = T1 * 2d * T2 = 2d * T1 * T2 (2d is written as k in the paper)\r
+        Field25519.mult(partialXYZT.t, extended.t, cached.t2d);\r
+\r
+        // Z1 * Z2\r
+        cached.multByZ(partialXYZT.xyz.x, extended.xyz.z);\r
+\r
+        // D = 2 * Z1 * Z2\r
+        Field25519.sum(t, partialXYZT.xyz.x, partialXYZT.xyz.x);\r
+\r
+        // X3 = B - A\r
+        Field25519.sub(partialXYZT.xyz.x, partialXYZT.xyz.z, partialXYZT.xyz.y);\r
+\r
+        // Y3 = B + A\r
+        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.y);\r
+\r
+        // Z3 = D - C\r
+        Field25519.sub(partialXYZT.xyz.z, t, partialXYZT.t);\r
+\r
+        // T3 = D + C\r
+        Field25519.sum(partialXYZT.t, t, partialXYZT.t);\r
+    }\r
+\r
+    /**\r
+     * Doubles {@code p} and puts the result into this PartialXYZT.\r
+     * <p>\r
+     * This is based on the addition defined in formula 7 in Section 3.3 of\r
+     * Hisil H., Wong K.KH., Carter G., Dawson E. (2008) Twisted Edwards Curves Revisited.\r
+     * <p>\r
+     * Please note that this is a partial of the operation listed there leaving out the final\r
+     * conversion from PartialXYZT to XYZT and also this fixes a typo in calculation of Y3 and T3 in\r
+     * the paper, H should be replaced with A+B.\r
+     */\r
+    private static void doubleXYZ(PartialXYZT partialXYZT, XYZ p) {\r
+        long[] t0 = new long[LIMB_CNT];\r
+\r
+        // XX = X1^2\r
+        Field25519.square(partialXYZT.xyz.x, p.x);\r
+\r
+        // YY = Y1^2\r
+        Field25519.square(partialXYZT.xyz.z, p.y);\r
+\r
+        // B' = Z1^2\r
+        Field25519.square(partialXYZT.t, p.z);\r
+\r
+        // B = 2 * B'\r
+        Field25519.sum(partialXYZT.t, partialXYZT.t, partialXYZT.t);\r
+\r
+        // A = X1 + Y1\r
+        Field25519.sum(partialXYZT.xyz.y, p.x, p.y);\r
+\r
+        // AA = A^2\r
+        Field25519.square(t0, partialXYZT.xyz.y);\r
+\r
+        // Y3 = YY + XX\r
+        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.x);\r
+\r
+        // Z3 = YY - XX\r
+        Field25519.sub(partialXYZT.xyz.z, partialXYZT.xyz.z, partialXYZT.xyz.x);\r
+\r
+        // X3 = AA - Y3\r
+        Field25519.sub(partialXYZT.xyz.x, t0, partialXYZT.xyz.y);\r
+\r
+        // T3 = B - Z3\r
+        Field25519.sub(partialXYZT.t, partialXYZT.t, partialXYZT.xyz.z);\r
+    }\r
+\r
+    /**\r
+     * Doubles {@code p} and puts the result into this PartialXYZT.\r
+     */\r
+    private static void doubleXYZT(PartialXYZT partialXYZT, XYZT p) {\r
+        doubleXYZ(partialXYZT, p.xyz);\r
+    }\r
+\r
+    /**\r
+     * Compares two byte values in constant time.\r
+     * <p>\r
+     * Please note that this doesn't reuse {@link Curve25519#eq} method since the below inputs are\r
+     * byte values.\r
+     */\r
+    private static int eq(int a, int b) {\r
+        int r = ~(a ^ b) & 0xff;\r
+        r &= r << 4;\r
+        r &= r << 2;\r
+        r &= r << 1;\r
+        return (r >> 7) & 1;\r
+    }\r
+\r
+    /**\r
+     * This is a constant time operation where point b*B*256^pos is stored in {@code t}.\r
+     * When b is 0, t remains the same (i.e., neutral point).\r
+     * <p>\r
+     * Although B_TABLE[32][8] (B_TABLE[i][j] = (j+1)*B*256^i) has j values in [0, 7], the select\r
+     * method negates the corresponding point if b is negative (which is straight forward in elliptic\r
+     * curves by just negating y coordinate). Therefore we can get multiples of B with the half of\r
+     * memory requirements.\r
+     *\r
+     * @param t   neutral element (i.e., point 0), also serves as output.\r
+     * @param pos in B[pos][j] = (j+1)*B*256^pos\r
+     * @param b   value in [-8, 8] range.\r
+     */\r
+    private static void select(CachedXYT t, int pos, byte b) {\r
+        int bnegative = (b & 0xff) >> 7;\r
+        int babs = b - (((-bnegative) & b) << 1);\r
+\r
+        t.copyConditional(B_TABLE[pos][0], eq(babs, 1));\r
+        t.copyConditional(B_TABLE[pos][1], eq(babs, 2));\r
+        t.copyConditional(B_TABLE[pos][2], eq(babs, 3));\r
+        t.copyConditional(B_TABLE[pos][3], eq(babs, 4));\r
+        t.copyConditional(B_TABLE[pos][4], eq(babs, 5));\r
+        t.copyConditional(B_TABLE[pos][5], eq(babs, 6));\r
+        t.copyConditional(B_TABLE[pos][6], eq(babs, 7));\r
+        t.copyConditional(B_TABLE[pos][7], eq(babs, 8));\r
+\r
+        long[] yPlusX = Arrays.copyOf(t.yMinusX, LIMB_CNT);\r
+        long[] yMinusX = Arrays.copyOf(t.yPlusX, LIMB_CNT);\r
+        long[] t2d = Arrays.copyOf(t.t2d, LIMB_CNT);\r
+        neg(t2d, t2d);\r
+        CachedXYT minust = new CachedXYT(yPlusX, yMinusX, t2d);\r
+        t.copyConditional(minust, bnegative);\r
+    }\r
+\r
+    /**\r
+     * Computes {@code a}*B\r
+     * where a = a[0]+256*a[1]+...+256^31 a[31] and\r
+     * B is the Ed25519 base point (x,4/5) with x positive.\r
+     * <p>\r
+     * Preconditions:\r
+     * a[31] <= 127\r
+     *\r
+     * @throws IllegalStateException iff there is arithmetic error.\r
+     */\r
+    @SuppressWarnings("NarrowingCompoundAssignment")\r
+    private static XYZ scalarMultWithBase(byte[] a) {\r
+        byte[] e = new byte[2 * FIELD_LEN];\r
+        for (int i = 0; i < FIELD_LEN; i++) {\r
+            e[2 * i + 0] = (byte) (((a[i] & 0xff) >> 0) & 0xf);\r
+            e[2 * i + 1] = (byte) (((a[i] & 0xff) >> 4) & 0xf);\r
+        }\r
+        // each e[i] is between 0 and 15\r
+        // e[63] is between 0 and 7\r
+\r
+        // Rewrite e in a way that each e[i] is in [-8, 8].\r
+        // This can be done since a[63] is in [0, 7], the carry-over onto the most significant byte\r
+        // a[63] can be at most 1.\r
+        int carry = 0;\r
+        for (int i = 0; i < e.length - 1; i++) {\r
+            e[i] += carry;\r
+            carry = e[i] + 8;\r
+            carry >>= 4;\r
+            e[i] -= carry << 4;\r
+        }\r
+        e[e.length - 1] += carry;\r
+\r
+        PartialXYZT ret = new PartialXYZT(NEUTRAL);\r
+        XYZT xyzt = new XYZT();\r
+        // Although B_TABLE's i can be at most 31 (stores only 32 4bit multiples of B) and we have 64\r
+        // 4bit values in e array, the below for loop adds cached values by iterating e by two in odd\r
+        // indices. After the result, we can double the result point 4 times to shift the multiplication\r
+        // scalar by 4 bits.\r
+        for (int i = 1; i < e.length; i += 2) {\r
+            CachedXYT t = new CachedXYT(CACHED_NEUTRAL);\r
+            select(t, i / 2, e[i]);\r
+            add(ret, XYZT.fromPartialXYZT(xyzt, ret), t);\r
+        }\r
+\r
+        // Doubles the result 4 times to shift the multiplication scalar 4 bits to get the actual result\r
+        // for the odd indices in e.\r
+        XYZ xyz = new XYZ();\r
+        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));\r
+        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));\r
+        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));\r
+        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));\r
+\r
+        // Add multiples of B for even indices of e.\r
+        for (int i = 0; i < e.length; i += 2) {\r
+            CachedXYT t = new CachedXYT(CACHED_NEUTRAL);\r
+            select(t, i / 2, e[i]);\r
+            add(ret, XYZT.fromPartialXYZT(xyzt, ret), t);\r
+        }\r
+\r
+        // This check is to protect against flaws, i.e. if there is a computation error through a\r
+        // faulty CPU or if the implementation contains a bug.\r
+        XYZ result = new XYZ(ret);\r
+        if (!result.isOnCurve()) {\r
+            throw new IllegalStateException("arithmetic error in scalar multiplication");\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Computes {@code a}*B\r
+     * where a = a[0]+256*a[1]+...+256^31 a[31] and\r
+     * B is the Ed25519 base point (x,4/5) with x positive.\r
+     * <p>\r
+     * Preconditions:\r
+     * a[31] <= 127\r
+     */\r
+    public static byte[] scalarMultWithBaseToBytes(byte[] a) {\r
+        return scalarMultWithBase(a).toBytes();\r
+    }\r
+\r
+    @SuppressWarnings("NarrowingCompoundAssignment")\r
+    private static byte[] slide(byte[] a) {\r
+        byte[] r = new byte[256];\r
+        // Writes each bit in a[0..31] into r[0..255]:\r
+        // a = a[0]+256*a[1]+...+256^31*a[31] is equal to\r
+        // r = r[0]+2*r[1]+...+2^255*r[255]\r
+        for (int i = 0; i < 256; i++) {\r
+            r[i] = (byte) (1 & ((a[i >> 3] & 0xff) >> (i & 7)));\r
+        }\r
+\r
+        // Transforms r[i] as odd values in [-15, 15]\r
+        for (int i = 0; i < 256; i++) {\r
+            if (r[i] != 0) {\r
+                for (int b = 1; b <= 6 && i + b < 256; b++) {\r
+                    if (r[i + b] != 0) {\r
+                        if (r[i] + (r[i + b] << b) <= 15) {\r
+                            r[i] += r[i + b] << b;\r
+                            r[i + b] = 0;\r
+                        } else if (r[i] - (r[i + b] << b) >= -15) {\r
+                            r[i] -= r[i + b] << b;\r
+                            for (int k = i + b; k < 256; k++) {\r
+                                if (r[k] == 0) {\r
+                                    r[k] = 1;\r
+                                    break;\r
+                                }\r
+                                r[k] = 0;\r
+                            }\r
+                        } else {\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return r;\r
+    }\r
+\r
+    /**\r
+     * Computes {@code a}*{@code pointA}+{@code b}*B\r
+     * where a = a[0]+256*a[1]+...+256^31*a[31].\r
+     * and b = b[0]+256*b[1]+...+256^31*b[31].\r
+     * B is the Ed25519 base point (x,4/5) with x positive.\r
+     * <p>\r
+     * Note that execution time varies based on the input since this will only be used in verification\r
+     * of signatures.\r
+     */\r
+    private static XYZ doubleScalarMultVarTime(byte[] a, XYZT pointA, byte[] b) {\r
+        // pointA, 3*pointA, 5*pointA, 7*pointA, 9*pointA, 11*pointA, 13*pointA, 15*pointA\r
+        CachedXYZT[] pointAArray = new CachedXYZT[8];\r
+        pointAArray[0] = new CachedXYZT(pointA);\r
+        PartialXYZT t = new PartialXYZT();\r
+        doubleXYZT(t, pointA);\r
+        XYZT doubleA = new XYZT(t);\r
+        for (int i = 1; i < pointAArray.length; i++) {\r
+            add(t, doubleA, pointAArray[i - 1]);\r
+            pointAArray[i] = new CachedXYZT(new XYZT(t));\r
+        }\r
+\r
+        byte[] aSlide = slide(a);\r
+        byte[] bSlide = slide(b);\r
+        t = new PartialXYZT(NEUTRAL);\r
+        XYZT u = new XYZT();\r
+        int i = 255;\r
+        for (; i >= 0; i--) {\r
+            if (aSlide[i] != 0 || bSlide[i] != 0) {\r
+                break;\r
+            }\r
+        }\r
+        for (; i >= 0; i--) {\r
+            doubleXYZ(t, new XYZ(t));\r
+            if (aSlide[i] > 0) {\r
+                add(t, XYZT.fromPartialXYZT(u, t), pointAArray[aSlide[i] / 2]);\r
+            } else if (aSlide[i] < 0) {\r
+                sub(t, XYZT.fromPartialXYZT(u, t), pointAArray[-aSlide[i] / 2]);\r
+            }\r
+            if (bSlide[i] > 0) {\r
+                add(t, XYZT.fromPartialXYZT(u, t), B2[bSlide[i] / 2]);\r
+            } else if (bSlide[i] < 0) {\r
+                sub(t, XYZT.fromPartialXYZT(u, t), B2[-bSlide[i] / 2]);\r
+            }\r
+        }\r
+\r
+        return new XYZ(t);\r
+    }\r
+\r
+    /**\r
+     * Returns true if {@code in} is nonzero.\r
+     * <p>\r
+     * Note that execution time might depend on the input {@code in}.\r
+     */\r
+    private static boolean isNonZeroVarTime(long[] in) {\r
+        long[] inCopy = new long[in.length + 1];\r
+        System.arraycopy(in, 0, inCopy, 0, in.length);\r
+        Field25519.reduceCoefficients(inCopy);\r
+        byte[] bytes = Field25519.contract(inCopy);\r
+        for (byte b : bytes) {\r
+            if (b != 0) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Returns the least significant bit of {@code in}.\r
+     */\r
+    private static int getLsb(long[] in) {\r
+        return Field25519.contract(in)[0] & 1;\r
+    }\r
+\r
+    /**\r
+     * Negates all values in {@code in} and store it in {@code out}.\r
+     */\r
+    private static void neg(long[] out, long[] in) {\r
+        for (int i = 0; i < in.length; i++) {\r
+            out[i] = -in[i];\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Computes {@code in}^(2^252-3) mod 2^255-19 and puts the result in {@code out}.\r
+     */\r
+    private static void pow2252m3(long[] out, long[] in) {\r
+        long[] t0 = new long[LIMB_CNT];\r
+        long[] t1 = new long[LIMB_CNT];\r
+        long[] t2 = new long[LIMB_CNT];\r
+\r
+        // z2 = z1^2^1\r
+        Field25519.square(t0, in);\r
+\r
+        // z8 = z2^2^2\r
+        Field25519.square(t1, t0);\r
+        for (int i = 1; i < 2; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z9 = z1*z8\r
+        Field25519.mult(t1, in, t1);\r
+\r
+        // z11 = z2*z9\r
+        Field25519.mult(t0, t0, t1);\r
+\r
+        // z22 = z11^2^1\r
+        Field25519.square(t0, t0);\r
+\r
+        // z_5_0 = z9*z22\r
+        Field25519.mult(t0, t1, t0);\r
+\r
+        // z_10_5 = z_5_0^2^5\r
+        Field25519.square(t1, t0);\r
+        for (int i = 1; i < 5; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z_10_0 = z_10_5*z_5_0\r
+        Field25519.mult(t0, t1, t0);\r
+\r
+        // z_20_10 = z_10_0^2^10\r
+        Field25519.square(t1, t0);\r
+        for (int i = 1; i < 10; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z_20_0 = z_20_10*z_10_0\r
+        Field25519.mult(t1, t1, t0);\r
+\r
+        // z_40_20 = z_20_0^2^20\r
+        Field25519.square(t2, t1);\r
+        for (int i = 1; i < 20; i++) {\r
+            Field25519.square(t2, t2);\r
+        }\r
+\r
+        // z_40_0 = z_40_20*z_20_0\r
+        Field25519.mult(t1, t2, t1);\r
+\r
+        // z_50_10 = z_40_0^2^10\r
+        Field25519.square(t1, t1);\r
+        for (int i = 1; i < 10; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z_50_0 = z_50_10*z_10_0\r
+        Field25519.mult(t0, t1, t0);\r
+\r
+        // z_100_50 = z_50_0^2^50\r
+        Field25519.square(t1, t0);\r
+        for (int i = 1; i < 50; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z_100_0 = z_100_50*z_50_0\r
+        Field25519.mult(t1, t1, t0);\r
+\r
+        // z_200_100 = z_100_0^2^100\r
+        Field25519.square(t2, t1);\r
+        for (int i = 1; i < 100; i++) {\r
+            Field25519.square(t2, t2);\r
+        }\r
+\r
+        // z_200_0 = z_200_100*z_100_0\r
+        Field25519.mult(t1, t2, t1);\r
+\r
+        // z_250_50 = z_200_0^2^50\r
+        Field25519.square(t1, t1);\r
+        for (int i = 1; i < 50; i++) {\r
+            Field25519.square(t1, t1);\r
+        }\r
+\r
+        // z_250_0 = z_250_50*z_50_0\r
+        Field25519.mult(t0, t1, t0);\r
+\r
+        // z_252_2 = z_250_0^2^2\r
+        Field25519.square(t0, t0);\r
+        for (int i = 1; i < 2; i++) {\r
+            Field25519.square(t0, t0);\r
+        }\r
+\r
+        // z_252_3 = z_252_2*z1\r
+        Field25519.mult(out, t0, in);\r
+    }\r
+\r
+    /**\r
+     * Returns 3 bytes of {@code in} starting from {@code idx} in Little-Endian format.\r
+     */\r
+    private static long load3(byte[] in, int idx) {\r
+        long result;\r
+        result = (long) in[idx] & 0xff;\r
+        result |= (long) (in[idx + 1] & 0xff) << 8;\r
+        result |= (long) (in[idx + 2] & 0xff) << 16;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Returns 4 bytes of {@code in} starting from {@code idx} in Little-Endian format.\r
+     */\r
+    private static long load4(byte[] in, int idx) {\r
+        long result = load3(in, idx);\r
+        result |= (long) (in[idx + 3] & 0xff) << 24;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Input:\r
+     * s[0]+256*s[1]+...+256^63*s[63] = s\r
+     * <p>\r
+     * Output:\r
+     * s[0]+256*s[1]+...+256^31*s[31] = s mod l\r
+     * where l = 2^252 + 27742317777372353535851937790883648493.\r
+     * Overwrites s in place.\r
+     */\r
+    public static void reduce(byte[] s) {\r
+        // Observation:\r
+        // 2^252 mod l is equivalent to -27742317777372353535851937790883648493 mod l\r
+        // Let m = -27742317777372353535851937790883648493\r
+        // Thus a*2^252+b mod l is equivalent to a*m+b mod l\r
+        //\r
+        // First s is divided into chunks of 21 bits as follows:\r
+        // s0+2^21*s1+2^42*s3+...+2^462*s23 = s[0]+256*s[1]+...+256^63*s[63]\r
+        long s0 = 2097151 & load3(s, 0);\r
+        long s1 = 2097151 & (load4(s, 2) >> 5);\r
+        long s2 = 2097151 & (load3(s, 5) >> 2);\r
+        long s3 = 2097151 & (load4(s, 7) >> 7);\r
+        long s4 = 2097151 & (load4(s, 10) >> 4);\r
+        long s5 = 2097151 & (load3(s, 13) >> 1);\r
+        long s6 = 2097151 & (load4(s, 15) >> 6);\r
+        long s7 = 2097151 & (load3(s, 18) >> 3);\r
+        long s8 = 2097151 & load3(s, 21);\r
+        long s9 = 2097151 & (load4(s, 23) >> 5);\r
+        long s10 = 2097151 & (load3(s, 26) >> 2);\r
+        long s11 = 2097151 & (load4(s, 28) >> 7);\r
+        long s12 = 2097151 & (load4(s, 31) >> 4);\r
+        long s13 = 2097151 & (load3(s, 34) >> 1);\r
+        long s14 = 2097151 & (load4(s, 36) >> 6);\r
+        long s15 = 2097151 & (load3(s, 39) >> 3);\r
+        long s16 = 2097151 & load3(s, 42);\r
+        long s17 = 2097151 & (load4(s, 44) >> 5);\r
+        long s18 = 2097151 & (load3(s, 47) >> 2);\r
+        long s19 = 2097151 & (load4(s, 49) >> 7);\r
+        long s20 = 2097151 & (load4(s, 52) >> 4);\r
+        long s21 = 2097151 & (load3(s, 55) >> 1);\r
+        long s22 = 2097151 & (load4(s, 57) >> 6);\r
+        long s23 = (load4(s, 60) >> 3);\r
+        long carry0;\r
+        long carry1;\r
+        long carry2;\r
+        long carry3;\r
+        long carry4;\r
+        long carry5;\r
+        long carry6;\r
+        long carry7;\r
+        long carry8;\r
+        long carry9;\r
+        long carry10;\r
+        long carry11;\r
+        long carry12;\r
+        long carry13;\r
+        long carry14;\r
+        long carry15;\r
+        long carry16;\r
+\r
+        // s23*2^462 = s23*2^210*2^252 is equivalent to s23*2^210*m in mod l\r
+        // As m is a 125 bit number, the result needs to scattered to 6 limbs (125/21 ceil is 6)\r
+        // starting from s11 (s11*2^210)\r
+        // m = [666643, 470296, 654183, -997805, 136657, -683901] in 21-bit limbs\r
+        s11 += s23 * 666643;\r
+        s12 += s23 * 470296;\r
+        s13 += s23 * 654183;\r
+        s14 -= s23 * 997805;\r
+        s15 += s23 * 136657;\r
+        s16 -= s23 * 683901;\r
+        // s23 = 0;\r
+\r
+        s10 += s22 * 666643;\r
+        s11 += s22 * 470296;\r
+        s12 += s22 * 654183;\r
+        s13 -= s22 * 997805;\r
+        s14 += s22 * 136657;\r
+        s15 -= s22 * 683901;\r
+        // s22 = 0;\r
+\r
+        s9 += s21 * 666643;\r
+        s10 += s21 * 470296;\r
+        s11 += s21 * 654183;\r
+        s12 -= s21 * 997805;\r
+        s13 += s21 * 136657;\r
+        s14 -= s21 * 683901;\r
+        // s21 = 0;\r
+\r
+        s8 += s20 * 666643;\r
+        s9 += s20 * 470296;\r
+        s10 += s20 * 654183;\r
+        s11 -= s20 * 997805;\r
+        s12 += s20 * 136657;\r
+        s13 -= s20 * 683901;\r
+        // s20 = 0;\r
+\r
+        s7 += s19 * 666643;\r
+        s8 += s19 * 470296;\r
+        s9 += s19 * 654183;\r
+        s10 -= s19 * 997805;\r
+        s11 += s19 * 136657;\r
+        s12 -= s19 * 683901;\r
+        // s19 = 0;\r
+\r
+        s6 += s18 * 666643;\r
+        s7 += s18 * 470296;\r
+        s8 += s18 * 654183;\r
+        s9 -= s18 * 997805;\r
+        s10 += s18 * 136657;\r
+        s11 -= s18 * 683901;\r
+        // s18 = 0;\r
+\r
+        // Reduce the bit length of limbs from s6 to s15 to 21-bits.\r
+        carry6 = (s6 + (1 << 20)) >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry8 = (s8 + (1 << 20)) >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry10 = (s10 + (1 << 20)) >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+        carry12 = (s12 + (1 << 20)) >> 21;\r
+        s13 += carry12;\r
+        s12 -= carry12 << 21;\r
+        carry14 = (s14 + (1 << 20)) >> 21;\r
+        s15 += carry14;\r
+        s14 -= carry14 << 21;\r
+        carry16 = (s16 + (1 << 20)) >> 21;\r
+        s17 += carry16;\r
+        s16 -= carry16 << 21;\r
+\r
+        carry7 = (s7 + (1 << 20)) >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry9 = (s9 + (1 << 20)) >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry11 = (s11 + (1 << 20)) >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+        carry13 = (s13 + (1 << 20)) >> 21;\r
+        s14 += carry13;\r
+        s13 -= carry13 << 21;\r
+        carry15 = (s15 + (1 << 20)) >> 21;\r
+        s16 += carry15;\r
+        s15 -= carry15 << 21;\r
+\r
+        // Resume reduction where we left off.\r
+        s5 += s17 * 666643;\r
+        s6 += s17 * 470296;\r
+        s7 += s17 * 654183;\r
+        s8 -= s17 * 997805;\r
+        s9 += s17 * 136657;\r
+        s10 -= s17 * 683901;\r
+        // s17 = 0;\r
+\r
+        s4 += s16 * 666643;\r
+        s5 += s16 * 470296;\r
+        s6 += s16 * 654183;\r
+        s7 -= s16 * 997805;\r
+        s8 += s16 * 136657;\r
+        s9 -= s16 * 683901;\r
+        // s16 = 0;\r
+\r
+        s3 += s15 * 666643;\r
+        s4 += s15 * 470296;\r
+        s5 += s15 * 654183;\r
+        s6 -= s15 * 997805;\r
+        s7 += s15 * 136657;\r
+        s8 -= s15 * 683901;\r
+        // s15 = 0;\r
+\r
+        s2 += s14 * 666643;\r
+        s3 += s14 * 470296;\r
+        s4 += s14 * 654183;\r
+        s5 -= s14 * 997805;\r
+        s6 += s14 * 136657;\r
+        s7 -= s14 * 683901;\r
+        // s14 = 0;\r
+\r
+        s1 += s13 * 666643;\r
+        s2 += s13 * 470296;\r
+        s3 += s13 * 654183;\r
+        s4 -= s13 * 997805;\r
+        s5 += s13 * 136657;\r
+        s6 -= s13 * 683901;\r
+        // s13 = 0;\r
+\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        s12 = 0;\r
+\r
+        // Reduce the range of limbs from s0 to s11 to 21-bits.\r
+        carry0 = (s0 + (1 << 20)) >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry2 = (s2 + (1 << 20)) >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry4 = (s4 + (1 << 20)) >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry6 = (s6 + (1 << 20)) >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry8 = (s8 + (1 << 20)) >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry10 = (s10 + (1 << 20)) >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+\r
+        carry1 = (s1 + (1 << 20)) >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry3 = (s3 + (1 << 20)) >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry5 = (s5 + (1 << 20)) >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry7 = (s7 + (1 << 20)) >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry9 = (s9 + (1 << 20)) >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry11 = (s11 + (1 << 20)) >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        s12 = 0;\r
+\r
+        // Carry chain reduction to propagate excess bits from s0 to s5 to the most significant limbs.\r
+        carry0 = s0 >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry1 = s1 >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry2 = s2 >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry3 = s3 >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry4 = s4 >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry5 = s5 >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry6 = s6 >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry7 = s7 >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry8 = s8 >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry9 = s9 >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry10 = s10 >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+        carry11 = s11 >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+\r
+        // Do one last reduction as s12 might be 1.\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        // s12 = 0;\r
+\r
+        carry0 = s0 >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry1 = s1 >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry2 = s2 >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry3 = s3 >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry4 = s4 >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry5 = s5 >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry6 = s6 >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry7 = s7 >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry8 = s8 >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry9 = s9 >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry10 = s10 >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+\r
+        // Serialize the result into the s.\r
+        s[0] = (byte) s0;\r
+        s[1] = (byte) (s0 >> 8);\r
+        s[2] = (byte) ((s0 >> 16) | (s1 << 5));\r
+        s[3] = (byte) (s1 >> 3);\r
+        s[4] = (byte) (s1 >> 11);\r
+        s[5] = (byte) ((s1 >> 19) | (s2 << 2));\r
+        s[6] = (byte) (s2 >> 6);\r
+        s[7] = (byte) ((s2 >> 14) | (s3 << 7));\r
+        s[8] = (byte) (s3 >> 1);\r
+        s[9] = (byte) (s3 >> 9);\r
+        s[10] = (byte) ((s3 >> 17) | (s4 << 4));\r
+        s[11] = (byte) (s4 >> 4);\r
+        s[12] = (byte) (s4 >> 12);\r
+        s[13] = (byte) ((s4 >> 20) | (s5 << 1));\r
+        s[14] = (byte) (s5 >> 7);\r
+        s[15] = (byte) ((s5 >> 15) | (s6 << 6));\r
+        s[16] = (byte) (s6 >> 2);\r
+        s[17] = (byte) (s6 >> 10);\r
+        s[18] = (byte) ((s6 >> 18) | (s7 << 3));\r
+        s[19] = (byte) (s7 >> 5);\r
+        s[20] = (byte) (s7 >> 13);\r
+        s[21] = (byte) s8;\r
+        s[22] = (byte) (s8 >> 8);\r
+        s[23] = (byte) ((s8 >> 16) | (s9 << 5));\r
+        s[24] = (byte) (s9 >> 3);\r
+        s[25] = (byte) (s9 >> 11);\r
+        s[26] = (byte) ((s9 >> 19) | (s10 << 2));\r
+        s[27] = (byte) (s10 >> 6);\r
+        s[28] = (byte) ((s10 >> 14) | (s11 << 7));\r
+        s[29] = (byte) (s11 >> 1);\r
+        s[30] = (byte) (s11 >> 9);\r
+        s[31] = (byte) (s11 >> 17);\r
+    }\r
+\r
+    /**\r
+     * Input:\r
+     * a[0]+256*a[1]+...+256^31*a[31] = a\r
+     * b[0]+256*b[1]+...+256^31*b[31] = b\r
+     * c[0]+256*c[1]+...+256^31*c[31] = c\r
+     * <p>\r
+     * Output:\r
+     * s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l\r
+     * where l = 2^252 + 27742317777372353535851937790883648493.\r
+     */\r
+    public static void mulAdd(byte[] s, byte[] a, byte[] b, byte[] c) {\r
+        // This is very similar to Ed25519.reduce, the difference in here is that it computes ab+c\r
+        // See Ed25519.reduce for related comments.\r
+        long a0 = 2097151 & load3(a, 0);\r
+        long a1 = 2097151 & (load4(a, 2) >> 5);\r
+        long a2 = 2097151 & (load3(a, 5) >> 2);\r
+        long a3 = 2097151 & (load4(a, 7) >> 7);\r
+        long a4 = 2097151 & (load4(a, 10) >> 4);\r
+        long a5 = 2097151 & (load3(a, 13) >> 1);\r
+        long a6 = 2097151 & (load4(a, 15) >> 6);\r
+        long a7 = 2097151 & (load3(a, 18) >> 3);\r
+        long a8 = 2097151 & load3(a, 21);\r
+        long a9 = 2097151 & (load4(a, 23) >> 5);\r
+        long a10 = 2097151 & (load3(a, 26) >> 2);\r
+        long a11 = (load4(a, 28) >> 7);\r
+        long b0 = 2097151 & load3(b, 0);\r
+        long b1 = 2097151 & (load4(b, 2) >> 5);\r
+        long b2 = 2097151 & (load3(b, 5) >> 2);\r
+        long b3 = 2097151 & (load4(b, 7) >> 7);\r
+        long b4 = 2097151 & (load4(b, 10) >> 4);\r
+        long b5 = 2097151 & (load3(b, 13) >> 1);\r
+        long b6 = 2097151 & (load4(b, 15) >> 6);\r
+        long b7 = 2097151 & (load3(b, 18) >> 3);\r
+        long b8 = 2097151 & load3(b, 21);\r
+        long b9 = 2097151 & (load4(b, 23) >> 5);\r
+        long b10 = 2097151 & (load3(b, 26) >> 2);\r
+        long b11 = (load4(b, 28) >> 7);\r
+        long c0 = 2097151 & load3(c, 0);\r
+        long c1 = 2097151 & (load4(c, 2) >> 5);\r
+        long c2 = 2097151 & (load3(c, 5) >> 2);\r
+        long c3 = 2097151 & (load4(c, 7) >> 7);\r
+        long c4 = 2097151 & (load4(c, 10) >> 4);\r
+        long c5 = 2097151 & (load3(c, 13) >> 1);\r
+        long c6 = 2097151 & (load4(c, 15) >> 6);\r
+        long c7 = 2097151 & (load3(c, 18) >> 3);\r
+        long c8 = 2097151 & load3(c, 21);\r
+        long c9 = 2097151 & (load4(c, 23) >> 5);\r
+        long c10 = 2097151 & (load3(c, 26) >> 2);\r
+        long c11 = (load4(c, 28) >> 7);\r
+        long s0;\r
+        long s1;\r
+        long s2;\r
+        long s3;\r
+        long s4;\r
+        long s5;\r
+        long s6;\r
+        long s7;\r
+        long s8;\r
+        long s9;\r
+        long s10;\r
+        long s11;\r
+        long s12;\r
+        long s13;\r
+        long s14;\r
+        long s15;\r
+        long s16;\r
+        long s17;\r
+        long s18;\r
+        long s19;\r
+        long s20;\r
+        long s21;\r
+        long s22;\r
+        long s23;\r
+        long carry0;\r
+        long carry1;\r
+        long carry2;\r
+        long carry3;\r
+        long carry4;\r
+        long carry5;\r
+        long carry6;\r
+        long carry7;\r
+        long carry8;\r
+        long carry9;\r
+        long carry10;\r
+        long carry11;\r
+        long carry12;\r
+        long carry13;\r
+        long carry14;\r
+        long carry15;\r
+        long carry16;\r
+        long carry17;\r
+        long carry18;\r
+        long carry19;\r
+        long carry20;\r
+        long carry21;\r
+        long carry22;\r
+\r
+        s0 = c0 + a0 * b0;\r
+        s1 = c1 + a0 * b1 + a1 * b0;\r
+        s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;\r
+        s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;\r
+        s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;\r
+        s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;\r
+        s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;\r
+        s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;\r
+        s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1\r
+                + a8 * b0;\r
+        s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2\r
+                + a8 * b1 + a9 * b0;\r
+        s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3\r
+                + a8 * b2 + a9 * b1 + a10 * b0;\r
+        s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4\r
+                + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;\r
+        s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3\r
+                + a10 * b2 + a11 * b1;\r
+        s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3\r
+                + a11 * b2;\r
+        s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4\r
+                + a11 * b3;\r
+        s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;\r
+        s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;\r
+        s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;\r
+        s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;\r
+        s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;\r
+        s20 = a9 * b11 + a10 * b10 + a11 * b9;\r
+        s21 = a10 * b11 + a11 * b10;\r
+        s22 = a11 * b11;\r
+        s23 = 0;\r
+\r
+        carry0 = (s0 + (1 << 20)) >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry2 = (s2 + (1 << 20)) >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry4 = (s4 + (1 << 20)) >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry6 = (s6 + (1 << 20)) >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry8 = (s8 + (1 << 20)) >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry10 = (s10 + (1 << 20)) >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+        carry12 = (s12 + (1 << 20)) >> 21;\r
+        s13 += carry12;\r
+        s12 -= carry12 << 21;\r
+        carry14 = (s14 + (1 << 20)) >> 21;\r
+        s15 += carry14;\r
+        s14 -= carry14 << 21;\r
+        carry16 = (s16 + (1 << 20)) >> 21;\r
+        s17 += carry16;\r
+        s16 -= carry16 << 21;\r
+        carry18 = (s18 + (1 << 20)) >> 21;\r
+        s19 += carry18;\r
+        s18 -= carry18 << 21;\r
+        carry20 = (s20 + (1 << 20)) >> 21;\r
+        s21 += carry20;\r
+        s20 -= carry20 << 21;\r
+        carry22 = (s22 + (1 << 20)) >> 21;\r
+        s23 += carry22;\r
+        s22 -= carry22 << 21;\r
+\r
+        carry1 = (s1 + (1 << 20)) >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry3 = (s3 + (1 << 20)) >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry5 = (s5 + (1 << 20)) >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry7 = (s7 + (1 << 20)) >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry9 = (s9 + (1 << 20)) >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry11 = (s11 + (1 << 20)) >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+        carry13 = (s13 + (1 << 20)) >> 21;\r
+        s14 += carry13;\r
+        s13 -= carry13 << 21;\r
+        carry15 = (s15 + (1 << 20)) >> 21;\r
+        s16 += carry15;\r
+        s15 -= carry15 << 21;\r
+        carry17 = (s17 + (1 << 20)) >> 21;\r
+        s18 += carry17;\r
+        s17 -= carry17 << 21;\r
+        carry19 = (s19 + (1 << 20)) >> 21;\r
+        s20 += carry19;\r
+        s19 -= carry19 << 21;\r
+        carry21 = (s21 + (1 << 20)) >> 21;\r
+        s22 += carry21;\r
+        s21 -= carry21 << 21;\r
+\r
+        s11 += s23 * 666643;\r
+        s12 += s23 * 470296;\r
+        s13 += s23 * 654183;\r
+        s14 -= s23 * 997805;\r
+        s15 += s23 * 136657;\r
+        s16 -= s23 * 683901;\r
+        // s23 = 0;\r
+\r
+        s10 += s22 * 666643;\r
+        s11 += s22 * 470296;\r
+        s12 += s22 * 654183;\r
+        s13 -= s22 * 997805;\r
+        s14 += s22 * 136657;\r
+        s15 -= s22 * 683901;\r
+        // s22 = 0;\r
+\r
+        s9 += s21 * 666643;\r
+        s10 += s21 * 470296;\r
+        s11 += s21 * 654183;\r
+        s12 -= s21 * 997805;\r
+        s13 += s21 * 136657;\r
+        s14 -= s21 * 683901;\r
+        // s21 = 0;\r
+\r
+        s8 += s20 * 666643;\r
+        s9 += s20 * 470296;\r
+        s10 += s20 * 654183;\r
+        s11 -= s20 * 997805;\r
+        s12 += s20 * 136657;\r
+        s13 -= s20 * 683901;\r
+        // s20 = 0;\r
+\r
+        s7 += s19 * 666643;\r
+        s8 += s19 * 470296;\r
+        s9 += s19 * 654183;\r
+        s10 -= s19 * 997805;\r
+        s11 += s19 * 136657;\r
+        s12 -= s19 * 683901;\r
+        // s19 = 0;\r
+\r
+        s6 += s18 * 666643;\r
+        s7 += s18 * 470296;\r
+        s8 += s18 * 654183;\r
+        s9 -= s18 * 997805;\r
+        s10 += s18 * 136657;\r
+        s11 -= s18 * 683901;\r
+        // s18 = 0;\r
+\r
+        carry6 = (s6 + (1 << 20)) >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry8 = (s8 + (1 << 20)) >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry10 = (s10 + (1 << 20)) >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+        carry12 = (s12 + (1 << 20)) >> 21;\r
+        s13 += carry12;\r
+        s12 -= carry12 << 21;\r
+        carry14 = (s14 + (1 << 20)) >> 21;\r
+        s15 += carry14;\r
+        s14 -= carry14 << 21;\r
+        carry16 = (s16 + (1 << 20)) >> 21;\r
+        s17 += carry16;\r
+        s16 -= carry16 << 21;\r
+\r
+        carry7 = (s7 + (1 << 20)) >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry9 = (s9 + (1 << 20)) >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry11 = (s11 + (1 << 20)) >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+        carry13 = (s13 + (1 << 20)) >> 21;\r
+        s14 += carry13;\r
+        s13 -= carry13 << 21;\r
+        carry15 = (s15 + (1 << 20)) >> 21;\r
+        s16 += carry15;\r
+        s15 -= carry15 << 21;\r
+\r
+        s5 += s17 * 666643;\r
+        s6 += s17 * 470296;\r
+        s7 += s17 * 654183;\r
+        s8 -= s17 * 997805;\r
+        s9 += s17 * 136657;\r
+        s10 -= s17 * 683901;\r
+        // s17 = 0;\r
+\r
+        s4 += s16 * 666643;\r
+        s5 += s16 * 470296;\r
+        s6 += s16 * 654183;\r
+        s7 -= s16 * 997805;\r
+        s8 += s16 * 136657;\r
+        s9 -= s16 * 683901;\r
+        // s16 = 0;\r
+\r
+        s3 += s15 * 666643;\r
+        s4 += s15 * 470296;\r
+        s5 += s15 * 654183;\r
+        s6 -= s15 * 997805;\r
+        s7 += s15 * 136657;\r
+        s8 -= s15 * 683901;\r
+        // s15 = 0;\r
+\r
+        s2 += s14 * 666643;\r
+        s3 += s14 * 470296;\r
+        s4 += s14 * 654183;\r
+        s5 -= s14 * 997805;\r
+        s6 += s14 * 136657;\r
+        s7 -= s14 * 683901;\r
+        // s14 = 0;\r
+\r
+        s1 += s13 * 666643;\r
+        s2 += s13 * 470296;\r
+        s3 += s13 * 654183;\r
+        s4 -= s13 * 997805;\r
+        s5 += s13 * 136657;\r
+        s6 -= s13 * 683901;\r
+        // s13 = 0;\r
+\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        s12 = 0;\r
+\r
+        carry0 = (s0 + (1 << 20)) >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry2 = (s2 + (1 << 20)) >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry4 = (s4 + (1 << 20)) >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry6 = (s6 + (1 << 20)) >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry8 = (s8 + (1 << 20)) >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry10 = (s10 + (1 << 20)) >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+\r
+        carry1 = (s1 + (1 << 20)) >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry3 = (s3 + (1 << 20)) >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry5 = (s5 + (1 << 20)) >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry7 = (s7 + (1 << 20)) >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry9 = (s9 + (1 << 20)) >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry11 = (s11 + (1 << 20)) >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        s12 = 0;\r
+\r
+        carry0 = s0 >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry1 = s1 >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry2 = s2 >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry3 = s3 >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry4 = s4 >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry5 = s5 >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry6 = s6 >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry7 = s7 >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry8 = s8 >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry9 = s9 >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry10 = s10 >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+        carry11 = s11 >> 21;\r
+        s12 += carry11;\r
+        s11 -= carry11 << 21;\r
+\r
+        s0 += s12 * 666643;\r
+        s1 += s12 * 470296;\r
+        s2 += s12 * 654183;\r
+        s3 -= s12 * 997805;\r
+        s4 += s12 * 136657;\r
+        s5 -= s12 * 683901;\r
+        // s12 = 0;\r
+\r
+        carry0 = s0 >> 21;\r
+        s1 += carry0;\r
+        s0 -= carry0 << 21;\r
+        carry1 = s1 >> 21;\r
+        s2 += carry1;\r
+        s1 -= carry1 << 21;\r
+        carry2 = s2 >> 21;\r
+        s3 += carry2;\r
+        s2 -= carry2 << 21;\r
+        carry3 = s3 >> 21;\r
+        s4 += carry3;\r
+        s3 -= carry3 << 21;\r
+        carry4 = s4 >> 21;\r
+        s5 += carry4;\r
+        s4 -= carry4 << 21;\r
+        carry5 = s5 >> 21;\r
+        s6 += carry5;\r
+        s5 -= carry5 << 21;\r
+        carry6 = s6 >> 21;\r
+        s7 += carry6;\r
+        s6 -= carry6 << 21;\r
+        carry7 = s7 >> 21;\r
+        s8 += carry7;\r
+        s7 -= carry7 << 21;\r
+        carry8 = s8 >> 21;\r
+        s9 += carry8;\r
+        s8 -= carry8 << 21;\r
+        carry9 = s9 >> 21;\r
+        s10 += carry9;\r
+        s9 -= carry9 << 21;\r
+        carry10 = s10 >> 21;\r
+        s11 += carry10;\r
+        s10 -= carry10 << 21;\r
+\r
+        s[0] = (byte) s0;\r
+        s[1] = (byte) (s0 >> 8);\r
+        s[2] = (byte) ((s0 >> 16) | (s1 << 5));\r
+        s[3] = (byte) (s1 >> 3);\r
+        s[4] = (byte) (s1 >> 11);\r
+        s[5] = (byte) ((s1 >> 19) | (s2 << 2));\r
+        s[6] = (byte) (s2 >> 6);\r
+        s[7] = (byte) ((s2 >> 14) | (s3 << 7));\r
+        s[8] = (byte) (s3 >> 1);\r
+        s[9] = (byte) (s3 >> 9);\r
+        s[10] = (byte) ((s3 >> 17) | (s4 << 4));\r
+        s[11] = (byte) (s4 >> 4);\r
+        s[12] = (byte) (s4 >> 12);\r
+        s[13] = (byte) ((s4 >> 20) | (s5 << 1));\r
+        s[14] = (byte) (s5 >> 7);\r
+        s[15] = (byte) ((s5 >> 15) | (s6 << 6));\r
+        s[16] = (byte) (s6 >> 2);\r
+        s[17] = (byte) (s6 >> 10);\r
+        s[18] = (byte) ((s6 >> 18) | (s7 << 3));\r
+        s[19] = (byte) (s7 >> 5);\r
+        s[20] = (byte) (s7 >> 13);\r
+        s[21] = (byte) s8;\r
+        s[22] = (byte) (s8 >> 8);\r
+        s[23] = (byte) ((s8 >> 16) | (s9 << 5));\r
+        s[24] = (byte) (s9 >> 3);\r
+        s[25] = (byte) (s9 >> 11);\r
+        s[26] = (byte) ((s9 >> 19) | (s10 << 2));\r
+        s[27] = (byte) (s10 >> 6);\r
+        s[28] = (byte) ((s10 >> 14) | (s11 << 7));\r
+        s[29] = (byte) (s11 >> 1);\r
+        s[30] = (byte) (s11 >> 9);\r
+        s[31] = (byte) (s11 >> 17);\r
+    }\r
+\r
+    static byte[] getHashedScalar(final byte[] privateKey)\r
+            throws GeneralSecurityException {\r
+        MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");\r
+        digest.update(privateKey, 0, FIELD_LEN);\r
+        byte[] h = digest.digest();\r
+        // https://tools.ietf.org/html/rfc8032#section-5.1.2.\r
+        // Clear the lowest three bits of the first octet.\r
+        h[0] = (byte) (h[0] & 248);\r
+        // Clear the highest bit of the last octet.\r
+        h[31] = (byte) (h[31] & 127);\r
+        // Set the second highest bit if the last octet.\r
+        h[31] = (byte) (h[31] | 64);\r
+        return h;\r
+    }\r
+\r
+    /**\r
+     * Returns the EdDSA signature for the {@code message} based on the {@code hashedPrivateKey}.\r
+     *\r
+     * @param message          to sign\r
+     * @param hashedPrivateKey {@link Ed25519#getHashedScalar(byte[])} of the private key\r
+     * @return signature for the {@code message}.\r
+     * @throws GeneralSecurityException if there is no SHA-512 algorithm defined in\r
+     *                                  {@link EngineFactory}.MESSAGE_DIGEST.\r
+     */\r
+    public static byte[] sign(final byte[] message, final byte[] publicKey, final byte[] hashedPrivateKey)\r
+            throws GeneralSecurityException {\r
+        // Copying the message to make it thread-safe. Otherwise, if the caller modifies the message\r
+        // between the first and the second hash then it might leak the private key.\r
+        byte[] messageCopy = Arrays.copyOfRange(message, 0, message.length);\r
+        MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");\r
+        digest.update(hashedPrivateKey, FIELD_LEN, FIELD_LEN);\r
+        digest.update(messageCopy);\r
+        byte[] r = digest.digest();\r
+        reduce(r);\r
+\r
+        byte[] rB = Arrays.copyOfRange(scalarMultWithBase(r).toBytes(), 0, FIELD_LEN);\r
+        digest.reset();\r
+        digest.update(rB);\r
+        digest.update(publicKey);\r
+        digest.update(messageCopy);\r
+        byte[] hram = digest.digest();\r
+        reduce(hram);\r
+        byte[] s = new byte[FIELD_LEN];\r
+        mulAdd(s, hram, hashedPrivateKey, r);\r
+        return Bytes.concat(rB, s);\r
+    }\r
+\r
+\r
+    // The order of the generator as unsigned bytes in little endian order.\r
+    // (2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed, cf. RFC 7748)\r
+    static final byte[] GROUP_ORDER = new byte[]{\r
+            (byte) 0xed, (byte) 0xd3, (byte) 0xf5, (byte) 0x5c,\r
+            (byte) 0x1a, (byte) 0x63, (byte) 0x12, (byte) 0x58,\r
+            (byte) 0xd6, (byte) 0x9c, (byte) 0xf7, (byte) 0xa2,\r
+            (byte) 0xde, (byte) 0xf9, (byte) 0xde, (byte) 0x14,\r
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10};\r
+\r
+    // Checks whether s represents an integer smaller than the order of the group.\r
+    // This is needed to ensure that EdDSA signatures are non-malleable, as failing to check\r
+    // the range of S allows to modify signatures (cf. RFC 8032, Section 5.2.7 and Section 8.4.)\r
+    // @param s an integer in little-endian order.\r
+    private static boolean isSmallerThanGroupOrder(byte[] s) {\r
+        for (int j = FIELD_LEN - 1; j >= 0; j--) {\r
+            // compare unsigned bytes\r
+            int a = s[j] & 0xff;\r
+            int b = GROUP_ORDER[j] & 0xff;\r
+            if (a != b) {\r
+                return a < b;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Returns true if the EdDSA {@code signature} with {@code message}, can be verified with\r
+     * {@code publicKey}.\r
+     *\r
+     * @throws GeneralSecurityException if there is no SHA-512 algorithm defined in\r
+     *                                  {@link EngineFactory}.MESSAGE_DIGEST.\r
+     */\r
+    static boolean verify(final byte[] message, final byte[] signature,\r
+                          final byte[] publicKey) throws GeneralSecurityException {\r
+        if (signature.length != SIGNATURE_LEN) {\r
+            return false;\r
+        }\r
+        byte[] s = Arrays.copyOfRange(signature, FIELD_LEN, SIGNATURE_LEN);\r
+        if (!isSmallerThanGroupOrder(s)) {\r
+            return false;\r
+        }\r
+        MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");\r
+        digest.update(signature, 0, FIELD_LEN);\r
+        digest.update(publicKey);\r
+        digest.update(message);\r
+        byte[] h = digest.digest();\r
+        reduce(h);\r
+\r
+        XYZT negPublicKey = XYZT.fromBytesNegateVarTime(publicKey);\r
+        XYZ xyz = doubleScalarMultVarTime(h, negPublicKey, s);\r
+        byte[] expectedR = xyz.toBytes();\r
+        for (int i = 0; i < FIELD_LEN; i++) {\r
+            if (expectedR[i] != signature[i]) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519Constants.java b/tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519Constants.java
new file mode 100755 (executable)
index 0000000..33b0acb
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright 2017 Google Inc.\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+//      http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+//\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+package com.google.crypto.tink.subtle;\r
+\r
+import java.math.BigInteger;\r
+\r
+/**\r
+ * Constants used in {@link Ed25519}.\r
+ */\r
+final class Ed25519Constants {\r
+\r
+    // d = -121665 / 121666 mod 2^255-19\r
+    static final long[] D;\r
+    // 2d\r
+    static final long[] D2;\r
+    // 2^((p-1)/4) mod p where p = 2^255-19\r
+    static final long[] SQRTM1;\r
+\r
+    /**\r
+     * Base point for the Edwards twisted curve = (x, 4/5) and its exponentiations. B_TABLE[i][j] =\r
+     * (j+1)*256^i*B for i in [0, 32) and j in [0, 8). Base point B = B_TABLE[0][0]\r
+     */\r
+    static final Ed25519.CachedXYT[][] B_TABLE;\r
+    static final Ed25519.CachedXYT[] B2;\r
+\r
+    private static final BigInteger P_BI =\r
+            BigInteger.valueOf(2).pow(255).subtract(BigInteger.valueOf(19));\r
+    private static final BigInteger D_BI =\r
+            BigInteger.valueOf(-121665).multiply(BigInteger.valueOf(121666).modInverse(P_BI)).mod(P_BI);\r
+    private static final BigInteger D2_BI = BigInteger.valueOf(2).multiply(D_BI).mod(P_BI);\r
+    private static final BigInteger SQRTM1_BI =\r
+            BigInteger.valueOf(2).modPow(P_BI.subtract(BigInteger.ONE).divide(BigInteger.valueOf(4)), P_BI);\r
+\r
+    private static class Point {\r
+        private BigInteger x;\r
+        private BigInteger y;\r
+    }\r
+\r
+    private static BigInteger recoverX(BigInteger y) {\r
+        // x^2 = (y^2 - 1) / (d * y^2 + 1) mod 2^255-19\r
+        BigInteger xx =\r
+                y.pow(2)\r
+                        .subtract(BigInteger.ONE)\r
+                        .multiply(D_BI.multiply(y.pow(2)).add(BigInteger.ONE).modInverse(P_BI));\r
+        BigInteger x = xx.modPow(P_BI.add(BigInteger.valueOf(3)).divide(BigInteger.valueOf(8)), P_BI);\r
+        if (!x.pow(2).subtract(xx).mod(P_BI).equals(BigInteger.ZERO)) {\r
+            x = x.multiply(SQRTM1_BI).mod(P_BI);\r
+        }\r
+        if (x.testBit(0)) {\r
+            x = P_BI.subtract(x);\r
+        }\r
+        return x;\r
+    }\r
+\r
+    private static Point edwards(Point a, Point b) {\r
+        Point o = new Point();\r
+        BigInteger xxyy = D_BI.multiply(a.x.multiply(b.x).multiply(a.y).multiply(b.y)).mod(P_BI);\r
+        o.x =\r
+                (a.x.multiply(b.y).add(b.x.multiply(a.y)))\r
+                        .multiply(BigInteger.ONE.add(xxyy).modInverse(P_BI))\r
+                        .mod(P_BI);\r
+        o.y =\r
+                (a.y.multiply(b.y).add(a.x.multiply(b.x)))\r
+                        .multiply(BigInteger.ONE.subtract(xxyy).modInverse(P_BI))\r
+                        .mod(P_BI);\r
+        return o;\r
+    }\r
+\r
+    private static byte[] toLittleEndian(BigInteger n) {\r
+        byte[] b = new byte[32];\r
+        byte[] nBytes = n.toByteArray();\r
+        System.arraycopy(nBytes, 0, b, 32 - nBytes.length, nBytes.length);\r
+        for (int i = 0; i < b.length / 2; i++) {\r
+            byte t = b[i];\r
+            b[i] = b[b.length - i - 1];\r
+            b[b.length - i - 1] = t;\r
+        }\r
+        return b;\r
+    }\r
+\r
+    private static Ed25519.CachedXYT getCachedXYT(Point p) {\r
+        return new Ed25519.CachedXYT(\r
+                Field25519.expand(toLittleEndian(p.y.add(p.x).mod(P_BI))),\r
+                Field25519.expand(toLittleEndian(p.y.subtract(p.x).mod(P_BI))),\r
+                Field25519.expand(toLittleEndian(D2_BI.multiply(p.x).multiply(p.y).mod(P_BI))));\r
+    }\r
+\r
+    static {\r
+        Point b = new Point();\r
+        b.y = BigInteger.valueOf(4).multiply(BigInteger.valueOf(5).modInverse(P_BI)).mod(P_BI);\r
+        b.x = recoverX(b.y);\r
+\r
+        D = Field25519.expand(toLittleEndian(D_BI));\r
+        D2 = Field25519.expand(toLittleEndian(D2_BI));\r
+        SQRTM1 = Field25519.expand(toLittleEndian(SQRTM1_BI));\r
+\r
+        Point bi = b;\r
+        B_TABLE = new Ed25519.CachedXYT[32][8];\r
+        for (int i = 0; i < 32; i++) {\r
+            Point bij = bi;\r
+            for (int j = 0; j < 8; j++) {\r
+                B_TABLE[i][j] = getCachedXYT(bij);\r
+                bij = edwards(bij, bi);\r
+            }\r
+            for (int j = 0; j < 8; j++) {\r
+                bi = edwards(bi, bi);\r
+            }\r
+        }\r
+        bi = b;\r
+        Point b2 = edwards(b, b);\r
+        B2 = new Ed25519.CachedXYT[8];\r
+        for (int i = 0; i < 8; i++) {\r
+            B2[i] = getCachedXYT(bi);\r
+            bi = edwards(bi, b2);\r
+        }\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/com/google/crypto/tink/subtle/Field25519.java b/tx-signer/src/main/java/com/google/crypto/tink/subtle/Field25519.java
new file mode 100755 (executable)
index 0000000..714e8e3
--- /dev/null
@@ -0,0 +1,598 @@
+// Copyright 2017 Google Inc.\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+//      http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+//\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+package com.google.crypto.tink.subtle;\r
+\r
+import com.google.crypto.tink.annotations.Alpha;\r
+\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Defines field 25519 function based on <a\r
+ * href="https://github.com/agl/curve25519-donna/blob/master/curve25519-donna.c">curve25519-donna C\r
+ * implementation</a> (mostly identical).\r
+ *\r
+ * <p>Field elements are written as an array of signed, 64-bit limbs (an array of longs), least\r
+ * significant first. The value of the field element is:\r
+ *\r
+ * <pre>\r
+ * x[0] + 2^26·x[1] + 2^51·x[2] + 2^77·x[3] + 2^102·x[4] + 2^128·x[5] + 2^153·x[6] + 2^179·x[7] +\r
+ * 2^204·x[8] + 2^230·x[9],\r
+ * </pre>\r
+ *\r
+ * <p>i.e. the limbs are 26, 25, 26, 25, ... bits wide.\r
+ */\r
+@Alpha\r
+final class Field25519 {\r
+    /**\r
+     * During Field25519 computation, the mixed radix representation may be in different forms:\r
+     * <ul>\r
+     * <li> Reduced-size form: the array has size at most 10.\r
+     * <li> Non-reduced-size form: the array is not reduced modulo 2^255 - 19 and has size at most\r
+     * 19.\r
+     * </ul>\r
+     * <p>\r
+     * TODO(quannguyen):\r
+     * <ul>\r
+     * <li> Clarify ill-defined terminologies.\r
+     * <li> The reduction procedure is different from DJB's paper\r
+     * (http://cr.yp.to/ecdh/curve25519-20060209.pdf). The coefficients after reducing degree and\r
+     * reducing coefficients aren't guaranteed to be in range {-2^25, ..., 2^25}. We should check to\r
+     * see what's going on.\r
+     * <li> Consider using method mult() everywhere and making product() private.\r
+     * </ul>\r
+     */\r
+\r
+    static final int FIELD_LEN = 32;\r
+    static final int LIMB_CNT = 10;\r
+    private static final long TWO_TO_25 = 1 << 25;\r
+    private static final long TWO_TO_26 = TWO_TO_25 << 1;\r
+\r
+    private static final int[] EXPAND_START = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28};\r
+    private static final int[] EXPAND_SHIFT = {0, 2, 3, 5, 6, 0, 1, 3, 4, 6};\r
+    private static final int[] MASK = {0x3ffffff, 0x1ffffff};\r
+    private static final int[] SHIFT = {26, 25};\r
+\r
+    /**\r
+     * Sums two numbers: output = in1 + in2\r
+     * <p>\r
+     * On entry: in1, in2 are in reduced-size form.\r
+     */\r
+    static void sum(long[] output, long[] in1, long[] in2) {\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            output[i] = in1[i] + in2[i];\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Sums two numbers: output += in\r
+     * <p>\r
+     * On entry: in is in reduced-size form.\r
+     */\r
+    static void sum(long[] output, long[] in) {\r
+        sum(output, output, in);\r
+    }\r
+\r
+    /**\r
+     * Find the difference of two numbers: output = in1 - in2\r
+     * (note the order of the arguments!).\r
+     * <p>\r
+     * On entry: in1, in2 are in reduced-size form.\r
+     */\r
+    static void sub(long[] output, long[] in1, long[] in2) {\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            output[i] = in1[i] - in2[i];\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Find the difference of two numbers: output = in - output\r
+     * (note the order of the arguments!).\r
+     * <p>\r
+     * On entry: in, output are in reduced-size form.\r
+     */\r
+    static void sub(long[] output, long[] in) {\r
+        sub(output, in, output);\r
+    }\r
+\r
+    /**\r
+     * Multiply a number by a scalar: output = in * scalar\r
+     */\r
+    static void scalarProduct(long[] output, long[] in, long scalar) {\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            output[i] = in[i] * scalar;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Multiply two numbers: out = in2 * in\r
+     * <p>\r
+     * output must be distinct to both inputs. The inputs are reduced coefficient form,\r
+     * the output is not.\r
+     * <p>\r
+     * out[x] <= 14 * the largest product of the input limbs.\r
+     */\r
+    static void product(long[] out, long[] in2, long[] in) {\r
+        out[0] = in2[0] * in[0];\r
+        out[1] = in2[0] * in[1]\r
+                + in2[1] * in[0];\r
+        out[2] = 2 * in2[1] * in[1]\r
+                + in2[0] * in[2]\r
+                + in2[2] * in[0];\r
+        out[3] = in2[1] * in[2]\r
+                + in2[2] * in[1]\r
+                + in2[0] * in[3]\r
+                + in2[3] * in[0];\r
+        out[4] = in2[2] * in[2]\r
+                + 2 * (in2[1] * in[3] + in2[3] * in[1])\r
+                + in2[0] * in[4]\r
+                + in2[4] * in[0];\r
+        out[5] = in2[2] * in[3]\r
+                + in2[3] * in[2]\r
+                + in2[1] * in[4]\r
+                + in2[4] * in[1]\r
+                + in2[0] * in[5]\r
+                + in2[5] * in[0];\r
+        out[6] = 2 * (in2[3] * in[3] + in2[1] * in[5] + in2[5] * in[1])\r
+                + in2[2] * in[4]\r
+                + in2[4] * in[2]\r
+                + in2[0] * in[6]\r
+                + in2[6] * in[0];\r
+        out[7] = in2[3] * in[4]\r
+                + in2[4] * in[3]\r
+                + in2[2] * in[5]\r
+                + in2[5] * in[2]\r
+                + in2[1] * in[6]\r
+                + in2[6] * in[1]\r
+                + in2[0] * in[7]\r
+                + in2[7] * in[0];\r
+        out[8] = in2[4] * in[4]\r
+                + 2 * (in2[3] * in[5] + in2[5] * in[3] + in2[1] * in[7] + in2[7] * in[1])\r
+                + in2[2] * in[6]\r
+                + in2[6] * in[2]\r
+                + in2[0] * in[8]\r
+                + in2[8] * in[0];\r
+        out[9] = in2[4] * in[5]\r
+                + in2[5] * in[4]\r
+                + in2[3] * in[6]\r
+                + in2[6] * in[3]\r
+                + in2[2] * in[7]\r
+                + in2[7] * in[2]\r
+                + in2[1] * in[8]\r
+                + in2[8] * in[1]\r
+                + in2[0] * in[9]\r
+                + in2[9] * in[0];\r
+        out[10] =\r
+                2 * (in2[5] * in[5] + in2[3] * in[7] + in2[7] * in[3] + in2[1] * in[9] + in2[9] * in[1])\r
+                        + in2[4] * in[6]\r
+                        + in2[6] * in[4]\r
+                        + in2[2] * in[8]\r
+                        + in2[8] * in[2];\r
+        out[11] = in2[5] * in[6]\r
+                + in2[6] * in[5]\r
+                + in2[4] * in[7]\r
+                + in2[7] * in[4]\r
+                + in2[3] * in[8]\r
+                + in2[8] * in[3]\r
+                + in2[2] * in[9]\r
+                + in2[9] * in[2];\r
+        out[12] = in2[6] * in[6]\r
+                + 2 * (in2[5] * in[7] + in2[7] * in[5] + in2[3] * in[9] + in2[9] * in[3])\r
+                + in2[4] * in[8]\r
+                + in2[8] * in[4];\r
+        out[13] = in2[6] * in[7]\r
+                + in2[7] * in[6]\r
+                + in2[5] * in[8]\r
+                + in2[8] * in[5]\r
+                + in2[4] * in[9]\r
+                + in2[9] * in[4];\r
+        out[14] = 2 * (in2[7] * in[7] + in2[5] * in[9] + in2[9] * in[5])\r
+                + in2[6] * in[8]\r
+                + in2[8] * in[6];\r
+        out[15] = in2[7] * in[8]\r
+                + in2[8] * in[7]\r
+                + in2[6] * in[9]\r
+                + in2[9] * in[6];\r
+        out[16] = in2[8] * in[8]\r
+                + 2 * (in2[7] * in[9] + in2[9] * in[7]);\r
+        out[17] = in2[8] * in[9]\r
+                + in2[9] * in[8];\r
+        out[18] = 2 * in2[9] * in[9];\r
+    }\r
+\r
+    /**\r
+     * Reduce a long form to a reduced-size form by taking the input mod 2^255 - 19.\r
+     * <p>\r
+     * On entry: |output[i]| < 14*2^54\r
+     * On exit: |output[0..8]| < 280*2^54\r
+     */\r
+    static void reduceSizeByModularReduction(long[] output) {\r
+        // The coefficients x[10], x[11],..., x[18] are eliminated by reduction modulo 2^255 - 19.\r
+        // For example, the coefficient x[18] is multiplied by 19 and added to the coefficient x[8].\r
+        //\r
+        // Each of these shifts and adds ends up multiplying the value by 19.\r
+        //\r
+        // For output[0..8], the absolute entry value is < 14*2^54 and we add, at most, 19*14*2^54 thus,\r
+        // on exit, |output[0..8]| < 280*2^54.\r
+        output[8] += output[18] << 4;\r
+        output[8] += output[18] << 1;\r
+        output[8] += output[18];\r
+        output[7] += output[17] << 4;\r
+        output[7] += output[17] << 1;\r
+        output[7] += output[17];\r
+        output[6] += output[16] << 4;\r
+        output[6] += output[16] << 1;\r
+        output[6] += output[16];\r
+        output[5] += output[15] << 4;\r
+        output[5] += output[15] << 1;\r
+        output[5] += output[15];\r
+        output[4] += output[14] << 4;\r
+        output[4] += output[14] << 1;\r
+        output[4] += output[14];\r
+        output[3] += output[13] << 4;\r
+        output[3] += output[13] << 1;\r
+        output[3] += output[13];\r
+        output[2] += output[12] << 4;\r
+        output[2] += output[12] << 1;\r
+        output[2] += output[12];\r
+        output[1] += output[11] << 4;\r
+        output[1] += output[11] << 1;\r
+        output[1] += output[11];\r
+        output[0] += output[10] << 4;\r
+        output[0] += output[10] << 1;\r
+        output[0] += output[10];\r
+    }\r
+\r
+    /**\r
+     * Reduce all coefficients of the short form input so that |x| < 2^26.\r
+     * <p>\r
+     * On entry: |output[i]| < 280*2^54\r
+     */\r
+    static void reduceCoefficients(long[] output) {\r
+        output[10] = 0;\r
+\r
+        for (int i = 0; i < LIMB_CNT; i += 2) {\r
+            long over = output[i] / TWO_TO_26;\r
+            // The entry condition (that |output[i]| < 280*2^54) means that over is, at most, 280*2^28 in\r
+            // the first iteration of this loop. This is added to the next limb and we can approximate the\r
+            // resulting bound of that limb by 281*2^54.\r
+            output[i] -= over << 26;\r
+            output[i + 1] += over;\r
+\r
+            // For the first iteration, |output[i+1]| < 281*2^54, thus |over| < 281*2^29. When this is\r
+            // added to the next limb, the resulting bound can be approximated as 281*2^54.\r
+            //\r
+            // For subsequent iterations of the loop, 281*2^54 remains a conservative bound and no\r
+            // overflow occurs.\r
+            over = output[i + 1] / TWO_TO_25;\r
+            output[i + 1] -= over << 25;\r
+            output[i + 2] += over;\r
+        }\r
+        // Now |output[10]| < 281*2^29 and all other coefficients are reduced.\r
+        output[0] += output[10] << 4;\r
+        output[0] += output[10] << 1;\r
+        output[0] += output[10];\r
+\r
+        output[10] = 0;\r
+        // Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29 so |over| will be no more\r
+        // than 2^16.\r
+        long over = output[0] / TWO_TO_26;\r
+        output[0] -= over << 26;\r
+        output[1] += over;\r
+        // Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The bound on\r
+        // |output[1]| is sufficient to meet our needs.\r
+    }\r
+\r
+    /**\r
+     * A helpful wrapper around {@ref Field25519#product}: output = in * in2.\r
+     * <p>\r
+     * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.\r
+     * <p>\r
+     * The output is reduced degree (indeed, one need only provide storage for 10 limbs) and\r
+     * |output[i]| < 2^26.\r
+     */\r
+    static void mult(long[] output, long[] in, long[] in2) {\r
+        long[] t = new long[19];\r
+        product(t, in, in2);\r
+        // |t[i]| < 14*2^54\r
+        reduceSizeByModularReduction(t);\r
+        reduceCoefficients(t);\r
+        // |t[i]| < 2^26\r
+        System.arraycopy(t, 0, output, 0, LIMB_CNT);\r
+    }\r
+\r
+    /**\r
+     * Square a number: out = in**2\r
+     * <p>\r
+     * output must be distinct from the input. The inputs are reduced coefficient form, the output is\r
+     * not.\r
+     * <p>\r
+     * out[x] <= 14 * the largest product of the input limbs.\r
+     */\r
+    private static void squareInner(long[] out, long[] in) {\r
+        out[0] = in[0] * in[0];\r
+        out[1] = 2 * in[0] * in[1];\r
+        out[2] = 2 * (in[1] * in[1] + in[0] * in[2]);\r
+        out[3] = 2 * (in[1] * in[2] + in[0] * in[3]);\r
+        out[4] = in[2] * in[2]\r
+                + 4 * in[1] * in[3]\r
+                + 2 * in[0] * in[4];\r
+        out[5] = 2 * (in[2] * in[3] + in[1] * in[4] + in[0] * in[5]);\r
+        out[6] = 2 * (in[3] * in[3] + in[2] * in[4] + in[0] * in[6] + 2 * in[1] * in[5]);\r
+        out[7] = 2 * (in[3] * in[4] + in[2] * in[5] + in[1] * in[6] + in[0] * in[7]);\r
+        out[8] = in[4] * in[4]\r
+                + 2 * (in[2] * in[6] + in[0] * in[8] + 2 * (in[1] * in[7] + in[3] * in[5]));\r
+        out[9] = 2 * (in[4] * in[5] + in[3] * in[6] + in[2] * in[7] + in[1] * in[8] + in[0] * in[9]);\r
+        out[10] = 2 * (in[5] * in[5]\r
+                + in[4] * in[6]\r
+                + in[2] * in[8]\r
+                + 2 * (in[3] * in[7] + in[1] * in[9]));\r
+        out[11] = 2 * (in[5] * in[6] + in[4] * in[7] + in[3] * in[8] + in[2] * in[9]);\r
+        out[12] = in[6] * in[6]\r
+                + 2 * (in[4] * in[8] + 2 * (in[5] * in[7] + in[3] * in[9]));\r
+        out[13] = 2 * (in[6] * in[7] + in[5] * in[8] + in[4] * in[9]);\r
+        out[14] = 2 * (in[7] * in[7] + in[6] * in[8] + 2 * in[5] * in[9]);\r
+        out[15] = 2 * (in[7] * in[8] + in[6] * in[9]);\r
+        out[16] = in[8] * in[8] + 4 * in[7] * in[9];\r
+        out[17] = 2 * in[8] * in[9];\r
+        out[18] = 2 * in[9] * in[9];\r
+    }\r
+\r
+    /**\r
+     * Returns in^2.\r
+     * <p>\r
+     * On entry: The |in| argument is in reduced coefficients form and |in[i]| < 2^27.\r
+     * <p>\r
+     * On exit: The |output| argument is in reduced coefficients form (indeed, one need only provide\r
+     * storage for 10 limbs) and |out[i]| < 2^26.\r
+     */\r
+    static void square(long[] output, long[] in) {\r
+        long[] t = new long[19];\r
+        squareInner(t, in);\r
+        // |t[i]| < 14*2^54 because the largest product of two limbs will be < 2^(27+27) and SquareInner\r
+        // adds together, at most, 14 of those products.\r
+        reduceSizeByModularReduction(t);\r
+        reduceCoefficients(t);\r
+        // |t[i]| < 2^26\r
+        System.arraycopy(t, 0, output, 0, LIMB_CNT);\r
+    }\r
+\r
+    /**\r
+     * Takes a little-endian, 32-byte number and expands it into mixed radix form.\r
+     */\r
+    static long[] expand(byte[] input) {\r
+        long[] output = new long[LIMB_CNT];\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            output[i] = ((((long) (input[EXPAND_START[i]] & 0xff))\r
+                    | ((long) (input[EXPAND_START[i] + 1] & 0xff)) << 8\r
+                    | ((long) (input[EXPAND_START[i] + 2] & 0xff)) << 16\r
+                    | ((long) (input[EXPAND_START[i] + 3] & 0xff)) << 24) >> EXPAND_SHIFT[i]) & MASK[i & 1];\r
+        }\r
+        return output;\r
+    }\r
+\r
+    /**\r
+     * Takes a fully reduced mixed radix form number and contract it into a little-endian, 32-byte\r
+     * array.\r
+     * <p>\r
+     * On entry: |input_limbs[i]| < 2^26\r
+     */\r
+    @SuppressWarnings("NarrowingCompoundAssignment")\r
+    static byte[] contract(long[] inputLimbs) {\r
+        long[] input = Arrays.copyOf(inputLimbs, LIMB_CNT);\r
+        for (int j = 0; j < 2; j++) {\r
+            for (int i = 0; i < 9; i++) {\r
+                // This calculation is a time-invariant way to make input[i] non-negative by borrowing\r
+                // from the next-larger limb.\r
+                int carry = -(int) ((input[i] & (input[i] >> 31)) >> SHIFT[i & 1]);\r
+                input[i] = input[i] + (carry << SHIFT[i & 1]);\r
+                input[i + 1] -= carry;\r
+            }\r
+\r
+            // There's no greater limb for input[9] to borrow from, but we can multiply by 19 and borrow\r
+            // from input[0], which is valid mod 2^255-19.\r
+            {\r
+                int carry = -(int) ((input[9] & (input[9] >> 31)) >> 25);\r
+                input[9] += (carry << 25);\r
+                input[0] -= (carry * 19);\r
+            }\r
+\r
+            // After the first iteration, input[1..9] are non-negative and fit within 25 or 26 bits,\r
+            // depending on position. However, input[0] may be negative.\r
+        }\r
+\r
+        // The first borrow-propagation pass above ended with every limb except (possibly) input[0]\r
+        // non-negative.\r
+        //\r
+        // If input[0] was negative after the first pass, then it was because of a carry from input[9].\r
+        // On entry, input[9] < 2^26 so the carry was, at most, one, since (2**26-1) >> 25 = 1. Thus\r
+        // input[0] >= -19.\r
+        //\r
+        // In the second pass, each limb is decreased by at most one. Thus the second borrow-propagation\r
+        // pass could only have wrapped around to decrease input[0] again if the first pass left\r
+        // input[0] negative *and* input[1] through input[9] were all zero.  In that case, input[1] is\r
+        // now 2^25 - 1, and this last borrow-propagation step will leave input[1] non-negative.\r
+        {\r
+            int carry = -(int) ((input[0] & (input[0] >> 31)) >> 26);\r
+            input[0] += (carry << 26);\r
+            input[1] -= carry;\r
+        }\r
+\r
+        // All input[i] are now non-negative. However, there might be values between 2^25 and 2^26 in a\r
+        // limb which is, nominally, 25 bits wide.\r
+        for (int j = 0; j < 2; j++) {\r
+            for (int i = 0; i < 9; i++) {\r
+                int carry = (int) (input[i] >> SHIFT[i & 1]);\r
+                input[i] &= MASK[i & 1];\r
+                input[i + 1] += carry;\r
+            }\r
+        }\r
+\r
+        {\r
+            int carry = (int) (input[9] >> 25);\r
+            input[9] &= 0x1ffffff;\r
+            input[0] += 19 * carry;\r
+        }\r
+\r
+        // If the first carry-chain pass, just above, ended up with a carry from input[9], and that\r
+        // caused input[0] to be out-of-bounds, then input[0] was < 2^26 + 2*19, because the carry was,\r
+        // at most, two.\r
+        //\r
+        // If the second pass carried from input[9] again then input[0] is < 2*19 and the input[9] ->\r
+        // input[0] carry didn't push input[0] out of bounds.\r
+\r
+        // It still remains the case that input might be between 2^255-19 and 2^255. In this case,\r
+        // input[1..9] must take their maximum value and input[0] must be >= (2^255-19) & 0x3ffffff,\r
+        // which is 0x3ffffed.\r
+        int mask = gte((int) input[0], 0x3ffffed);\r
+        for (int i = 1; i < LIMB_CNT; i++) {\r
+            mask &= eq((int) input[i], MASK[i & 1]);\r
+        }\r
+\r
+        // mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus this conditionally\r
+        // subtracts 2^255-19.\r
+        input[0] -= mask & 0x3ffffed;\r
+        input[1] -= mask & 0x1ffffff;\r
+        for (int i = 2; i < LIMB_CNT; i += 2) {\r
+            input[i] -= mask & 0x3ffffff;\r
+            input[i + 1] -= mask & 0x1ffffff;\r
+        }\r
+\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            input[i] <<= EXPAND_SHIFT[i];\r
+        }\r
+        byte[] output = new byte[FIELD_LEN];\r
+        for (int i = 0; i < LIMB_CNT; i++) {\r
+            output[EXPAND_START[i]] |= input[i] & 0xff;\r
+            output[EXPAND_START[i] + 1] |= (input[i] >> 8) & 0xff;\r
+            output[EXPAND_START[i] + 2] |= (input[i] >> 16) & 0xff;\r
+            output[EXPAND_START[i] + 3] |= (input[i] >> 24) & 0xff;\r
+        }\r
+        return output;\r
+    }\r
+\r
+    /**\r
+     * Computes inverse of z = z(2^255 - 21)\r
+     * <p>\r
+     * Shamelessly copied from agl's code which was shamelessly copied from djb's code. Only the\r
+     * comment format and the variable namings are different from those.\r
+     */\r
+    static void inverse(long[] out, long[] z) {\r
+        long[] z2 = new long[Field25519.LIMB_CNT];\r
+        long[] z9 = new long[Field25519.LIMB_CNT];\r
+        long[] z11 = new long[Field25519.LIMB_CNT];\r
+        long[] z2To5Minus1 = new long[Field25519.LIMB_CNT];\r
+        long[] z2To10Minus1 = new long[Field25519.LIMB_CNT];\r
+        long[] z2To20Minus1 = new long[Field25519.LIMB_CNT];\r
+        long[] z2To50Minus1 = new long[Field25519.LIMB_CNT];\r
+        long[] z2To100Minus1 = new long[Field25519.LIMB_CNT];\r
+        long[] t0 = new long[Field25519.LIMB_CNT];\r
+        long[] t1 = new long[Field25519.LIMB_CNT];\r
+\r
+        square(z2, z);                          // 2\r
+        square(t1, z2);                         // 4\r
+        square(t0, t1);                         // 8\r
+        mult(z9, t0, z);                        // 9\r
+        mult(z11, z9, z2);                      // 11\r
+        square(t0, z11);                        // 22\r
+        mult(z2To5Minus1, t0, z9);              // 2^5 - 2^0 = 31\r
+\r
+        square(t0, z2To5Minus1);                // 2^6 - 2^1\r
+        square(t1, t0);                         // 2^7 - 2^2\r
+        square(t0, t1);                         // 2^8 - 2^3\r
+        square(t1, t0);                         // 2^9 - 2^4\r
+        square(t0, t1);                         // 2^10 - 2^5\r
+        mult(z2To10Minus1, t0, z2To5Minus1);    // 2^10 - 2^0\r
+\r
+        square(t0, z2To10Minus1);               // 2^11 - 2^1\r
+        square(t1, t0);                         // 2^12 - 2^2\r
+        for (int i = 2; i < 10; i += 2) {       // 2^20 - 2^10\r
+            square(t0, t1);\r
+            square(t1, t0);\r
+        }\r
+        mult(z2To20Minus1, t1, z2To10Minus1);   // 2^20 - 2^0\r
+\r
+        square(t0, z2To20Minus1);               // 2^21 - 2^1\r
+        square(t1, t0);                         // 2^22 - 2^2\r
+        for (int i = 2; i < 20; i += 2) {       // 2^40 - 2^20\r
+            square(t0, t1);\r
+            square(t1, t0);\r
+        }\r
+        mult(t0, t1, z2To20Minus1);             // 2^40 - 2^0\r
+\r
+        square(t1, t0);                         // 2^41 - 2^1\r
+        square(t0, t1);                         // 2^42 - 2^2\r
+        for (int i = 2; i < 10; i += 2) {       // 2^50 - 2^10\r
+            square(t1, t0);\r
+            square(t0, t1);\r
+        }\r
+        mult(z2To50Minus1, t0, z2To10Minus1);   // 2^50 - 2^0\r
+\r
+        square(t0, z2To50Minus1);               // 2^51 - 2^1\r
+        square(t1, t0);                         // 2^52 - 2^2\r
+        for (int i = 2; i < 50; i += 2) {       // 2^100 - 2^50\r
+            square(t0, t1);\r
+            square(t1, t0);\r
+        }\r
+        mult(z2To100Minus1, t1, z2To50Minus1);  // 2^100 - 2^0\r
+\r
+        square(t1, z2To100Minus1);              // 2^101 - 2^1\r
+        square(t0, t1);                         // 2^102 - 2^2\r
+        for (int i = 2; i < 100; i += 2) {      // 2^200 - 2^100\r
+            square(t1, t0);\r
+            square(t0, t1);\r
+        }\r
+        mult(t1, t0, z2To100Minus1);            // 2^200 - 2^0\r
+\r
+        square(t0, t1);                         // 2^201 - 2^1\r
+        square(t1, t0);                         // 2^202 - 2^2\r
+        for (int i = 2; i < 50; i += 2) {       // 2^250 - 2^50\r
+            square(t0, t1);\r
+            square(t1, t0);\r
+        }\r
+        mult(t0, t1, z2To50Minus1);             // 2^250 - 2^0\r
+\r
+        square(t1, t0);                         // 2^251 - 2^1\r
+        square(t0, t1);                         // 2^252 - 2^2\r
+        square(t1, t0);                         // 2^253 - 2^3\r
+        square(t0, t1);                         // 2^254 - 2^4\r
+        square(t1, t0);                         // 2^255 - 2^5\r
+        mult(out, t1, z11);                     // 2^255 - 21\r
+    }\r
+\r
+\r
+    /**\r
+     * Returns 0xffffffff iff a == b and zero otherwise.\r
+     */\r
+    private static int eq(int a, int b) {\r
+        a = ~(a ^ b);\r
+        a &= a << 16;\r
+        a &= a << 8;\r
+        a &= a << 4;\r
+        a &= a << 2;\r
+        a &= a << 1;\r
+        return a >> 31;\r
+    }\r
+\r
+    /**\r
+     * returns 0xffffffff if a >= b and zero otherwise, where a and b are both non-negative.\r
+     */\r
+    private static int gte(int a, int b) {\r
+        a -= b;\r
+        // a >= 0 iff a >= b.\r
+        return ~(a >> 31);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/MapTransaction.java b/tx-signer/src/main/java/io/bytom/api/MapTransaction.java
new file mode 100755 (executable)
index 0000000..296a0c3
--- /dev/null
@@ -0,0 +1,110 @@
+package io.bytom.api;\r
+\r
+import io.bytom.types.*;\r
+import io.bytom.util.SHA3Util;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.security.SecureRandom;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+public class MapTransaction {\r
+\r
+    public static Transaction mapTx(Transaction tx) {\r
+        Map<Hash, Entry> entryMap = new HashMap<>();\r
+        ValueSource[] muxSources = new ValueSource[tx.inputs.size()];\r
+        List<Spend> spends = new ArrayList<>();\r
+        List<Issue> issuances = new ArrayList<>();\r
+        try {\r
+            for (int i = 0; i < tx.inputs.size(); i++) {\r
+                Transaction.AnnotatedInput input = tx.inputs.get(i);\r
+                switch (input.type) {\r
+                    case 1: {\r
+                        Program pro = new Program(input.type, Hex.decode(input.controlProgram));\r
+                        AssetID assetID = new AssetID(input.assetId);\r
+                        AssetAmount assetAmount = new AssetAmount(assetID, input.amount);\r
+                        Hash sourceID = new Hash(input.sourceId);\r
+                        ValueSource src = new ValueSource(sourceID, assetAmount, input.sourcePosition);\r
+\r
+                        Output prevout = new Output(src, pro, 0);\r
+                        Hash prevoutID = addEntry(entryMap, prevout);\r
+                        Spend spend = new Spend(prevoutID, i);\r
+                        Hash spendID = addEntry(entryMap, spend);\r
+                        input.inputID = spendID.toString();\r
+\r
+                        muxSources[i] = new ValueSource(spendID, assetAmount, 0);\r
+                        spends.add(spend);\r
+                        break;\r
+                    }\r
+                    case 0: {\r
+                        if (input.nonce == null) {\r
+                            SecureRandom sr = new SecureRandom();\r
+                            byte[] randBytes = new byte[8];\r
+                            sr.nextBytes(randBytes);\r
+                            input.nonce = Hex.toHexString(randBytes);\r
+                        }\r
+                        Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(input.nonce)));\r
+                        Hash assetDefHash = new Hash(input.assetDefinition);\r
+                        AssetID assetID = new AssetID(input.assetId);\r
+                        AssetAmount value = new AssetAmount(assetID, input.amount);\r
+\r
+                        Issue issuance = new Issue(nonceHash, value, i);\r
+                        Program pro = new Program(input.type, Hex.decode(input.controlProgram));\r
+                        issuance.assetDefinition = new AssetDefinition(assetDefHash, pro);\r
+                        Hash issuanceID = addEntry(entryMap, issuance);\r
+                        input.inputID = issuanceID.toString();\r
+                        muxSources[i] = new ValueSource(issuanceID, value, 0);\r
+                        issuances.add(issuance);\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+            Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51}));\r
+            Hash muxID = addEntry(entryMap, mux);\r
+            for (Spend spend : spends) {\r
+                Output spendOutput = (Output) entryMap.get(spend.spentOutputID);\r
+                spend.setDestination(muxID, spendOutput.source.value, spend.ordinal);\r
+            }\r
+            for (Issue issue : issuances) {\r
+                issue.setDestination(muxID, issue.assetAmount, issue.ordinal);\r
+            }\r
+\r
+            List<Hash> resultIDList = new ArrayList<>();\r
+            for (int i = 0; i < tx.outputs.size(); i++) {\r
+                Transaction.AnnotatedOutput output = tx.outputs.get(i);\r
+\r
+                AssetAmount amount = new AssetAmount(new AssetID(output.assetId), output.amount);\r
+                ValueSource src = new ValueSource(muxID, amount, i);\r
+                Hash resultID;\r
+                if (output.controlProgram.startsWith("6a")) {\r
+                    Retirement retirement = new Retirement(src, i);\r
+                    resultID = addEntry(entryMap, retirement);\r
+                } else {\r
+                    Program prog = new Program(1, Hex.decode(output.controlProgram));\r
+                    Output oup = new Output(src, prog, i);\r
+                    resultID = addEntry(entryMap, oup);\r
+                }\r
+                resultIDList.add(resultID);\r
+                output.id = resultID.toString();\r
+\r
+                ValueDestination destination = new ValueDestination(resultID, src.value, 0);\r
+                mux.witnessDestinations.add(destination);\r
+            }\r
+\r
+            TxHeader txHeader = new TxHeader(tx.version, tx.size, tx.timeRange, resultIDList.toArray(new Hash[]{}));\r
+            Hash txID = addEntry(entryMap, txHeader);\r
+            tx.txID = txID.toString();\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        return tx;\r
+    }\r
+\r
+    private static Hash addEntry(Map<Hash, Entry> entryMap, Entry entry) {\r
+        Hash id = entry.entryID();\r
+        entryMap.put(id, entry);\r
+        return id;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/Receiver.java b/tx-signer/src/main/java/io/bytom/api/Receiver.java
new file mode 100755 (executable)
index 0000000..f13ab85
--- /dev/null
@@ -0,0 +1,48 @@
+package io.bytom.api;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+import io.bytom.common.Utils;\r
+import io.bytom.exception.JSONException;\r
+\r
+/**\r
+ * Receivers are used to facilitate payments between accounts on different\r
+ * cores. They contain a control program and an expiration date. In the future,\r
+ * more payment-related metadata may be placed here.\r
+ * <p>\r
+ * Receivers are typically created under accounts via the\r
+ */\r
+public class Receiver {\r
+\r
+    @SerializedName("address")\r
+    public String address;\r
+    /**\r
+     * Hex-encoded string representation of the control program.\r
+     */\r
+    @SerializedName("control_program")\r
+    public String controlProgram;\r
+\r
+\r
+    /**\r
+     * Serializes the receiver into a form that is safe to transfer over the wire.\r
+     *\r
+     * @return the JSON-serialized representation of the Receiver object\r
+     */\r
+    public String toJson() {\r
+        return Utils.serializer.toJson(this);\r
+    }\r
+\r
+    /**\r
+     * Deserializes a Receiver from JSON.\r
+     *\r
+     * @param json a JSON-serialized Receiver object\r
+     * @return the deserialized Receiver object\r
+     * @throws JSONException Raised if the provided string is not valid JSON.\r
+     */\r
+    public static Receiver fromJson(String json) throws JSONException {\r
+        try {\r
+            return Utils.serializer.fromJson(json, Receiver.class);\r
+        } catch (IllegalStateException e) {\r
+            throw new JSONException("Unable to parse serialized receiver: " + e.getMessage());\r
+        }\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/SignTransaction.java b/tx-signer/src/main/java/io/bytom/api/SignTransaction.java
new file mode 100755 (executable)
index 0000000..cd101f5
--- /dev/null
@@ -0,0 +1,305 @@
+package io.bytom.api;\r
+\r
+import io.bytom.common.DerivePrivateKey;\r
+import io.bytom.common.DeriveXpub;\r
+import io.bytom.common.ExpandedPrivateKey;\r
+import io.bytom.common.Utils;\r
+import org.bouncycastle.jcajce.provider.digest.SHA3;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.math.BigInteger;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+\r
+/**\r
+ * Created by liqiang on 2018/10/24.\r
+ */\r
+public class SignTransaction {\r
+    public String serializeTransaction(Transaction tx) {\r
+        String txSign = null;\r
+        //开始序列化\r
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();\r
+        try {\r
+            stream.write(7);\r
+            // version\r
+            if (null != tx.version)\r
+                Utils.writeVarint(tx.version, stream);\r
+            if (null != tx.timeRange)\r
+                Utils.writeVarint(tx.timeRange, stream);\r
+            //inputs\r
+            if (null != tx.inputs && tx.inputs.size() > 0) {\r
+                Utils.writeVarint(tx.inputs.size(), stream);\r
+                for (Transaction.AnnotatedInput input : tx.inputs) {\r
+                    if (input.type == 1) {\r
+                        //assertVersion\r
+                        Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
+\r
+                        //inputCommitment\r
+                        ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream();\r
+                        //spend type flag\r
+                        Utils.writeVarint(input.type, inputCommitStream);\r
+                        //spendCommitment\r
+                        ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();\r
+                        spendCommitSteam.write(Hex.decode(input.sourceId)); //计算muxID\r
+                        spendCommitSteam.write(Hex.decode(input.assetId));\r
+                        Utils.writeVarint(input.amount, spendCommitSteam);\r
+                        //sourcePosition\r
+                        Utils.writeVarint(input.sourcePosition, spendCommitSteam); //db中获取position\r
+                        //vmVersion\r
+                        Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version\r
+                        //controlProgram\r
+                        Utils.writeVarStr(Hex.decode(input.controlProgram), spendCommitSteam);\r
+\r
+                        byte[] dataSpendCommit = spendCommitSteam.toByteArray();\r
+\r
+                        Utils.writeVarint(dataSpendCommit.length, inputCommitStream);\r
+                        inputCommitStream.write(dataSpendCommit);\r
+                        byte[] dataInputCommit = inputCommitStream.toByteArray();\r
+                        //inputCommit的length\r
+                        Utils.writeVarint(dataInputCommit.length, stream);\r
+                        stream.write(dataInputCommit);\r
+\r
+                        //inputWitness\r
+                        if (null != input.witnessComponent) {\r
+                            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
+                            //arguments\r
+                            int lenSigs = input.witnessComponent.signatures.length;\r
+                            //arguments的length\r
+                            Utils.writeVarint(lenSigs, witnessStream);\r
+                            for (int i = 0; i < lenSigs; i++) {\r
+                                String sig = input.witnessComponent.signatures[i];\r
+                                Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
+                            }\r
+                            byte[] dataWitnessComponets = witnessStream.toByteArray();\r
+                            //witness的length\r
+                            Utils.writeVarint(dataWitnessComponets.length, stream);\r
+                            stream.write(dataWitnessComponets);\r
+                        }\r
+                    }\r
+                    if (input.type == 0) {\r
+                        //assetVersion\r
+                        Utils.writeVarint(01, stream);\r
+                        //写入 type nonce assetId amount\r
+                        ByteArrayOutputStream issueInfo = new ByteArrayOutputStream();\r
+                        //写入 input.type==00 issue\r
+                        Utils.writeVarint(input.type, issueInfo);\r
+                        //写入 8个字节的随机数\r
+                        Utils.writeVarStr(Hex.decode(input.nonce), issueInfo);\r
+                        issueInfo.write(Hex.decode(input.assetId));\r
+                        Utils.writeVarint(input.amount, issueInfo);\r
+                        stream.write(issueInfo.toByteArray().length);\r
+                        stream.write(issueInfo.toByteArray());\r
+\r
+                        ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream();\r
+                        //未知\r
+                        Utils.writeVarint(1, issueInfo1);\r
+                        //写入assetDefine\r
+                        Utils.writeVarStr(Hex.decode(input.assetDefinition), issueInfo1);\r
+                        //vm.version\r
+                        Utils.writeVarint(1, issueInfo1);\r
+                        //controlProgram\r
+                        Utils.writeVarStr(Hex.decode(input.controlProgram), issueInfo1);\r
+\r
+                        //inputWitness\r
+                        if (null != input.witnessComponent) {\r
+                            ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
+                            //arguments\r
+                            int lenSigs = input.witnessComponent.signatures.length;\r
+                            //arguments的length\r
+                            Utils.writeVarint(lenSigs, witnessStream);\r
+                            for (int i = 0; i < lenSigs; i++) {\r
+                                String sig = input.witnessComponent.signatures[i];\r
+                                Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
+                            }\r
+//                            byte[] dataWitnessComponets = witnessStream.toByteArray();\r
+                            //witness的length\r
+//                            Utils.writeVarint(dataWitnessComponets.length, issueInfo1);\r
+\r
+                            issueInfo1.write(witnessStream.toByteArray());\r
+                        }\r
+                        stream.write(issueInfo1.toByteArray().length - 1);\r
+                        stream.write(issueInfo1.toByteArray());\r
+\r
+                    }\r
+                }\r
+            }\r
+\r
+            //outputs\r
+            if (null != tx.outputs && tx.outputs.size() > 0) {\r
+                Utils.writeVarint(tx.outputs.size(), stream);\r
+                for (Transaction.AnnotatedOutput output : tx.outputs) {\r
+                    //assertVersion\r
+                    Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
+                    //outputCommit\r
+                    ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();\r
+                    //assetId\r
+                    outputCommitSteam.write(Hex.decode(output.assetId));\r
+                    //amount\r
+                    Utils.writeVarint(output.amount, outputCommitSteam);\r
+                    //vmVersion\r
+                    Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version\r
+                    //controlProgram\r
+                    Utils.writeVarStr(Hex.decode(output.controlProgram), outputCommitSteam);\r
+\r
+                    byte[] dataOutputCommit = outputCommitSteam.toByteArray();\r
+                    //outputCommit的length\r
+                    Utils.writeVarint(dataOutputCommit.length, stream);\r
+                    stream.write(dataOutputCommit);\r
+\r
+                    //outputWitness\r
+                    Utils.writeVarint(0, stream);\r
+                }\r
+            }\r
+\r
+            byte[] data = stream.toByteArray();\r
+            txSign = Hex.toHexString(data);\r
+\r
+        } catch (IOException e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        return txSign;\r
+    }\r
+\r
+    public Integer getTransactionSize(Transaction tx) {\r
+        String result = serializeTransaction(tx);\r
+        return Hex.decode(result).length;\r
+    }\r
+\r
+    //签名组装witness\r
+    public Transaction buildWitness(Transaction transaction, int index, String priKey) {\r
+\r
+        Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
+        if (null == input.witnessComponent)\r
+\r
+            if (null != input) {\r
+                try {\r
+                    input.witnessComponent = new Transaction.InputWitnessComponent();\r
+                    byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
+                    byte[] key = Hex.decode(priKey);\r
+                    byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(key);\r
+                    byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
+\r
+                    switch (input.type) {\r
+                        case 1: {\r
+                            input.witnessComponent.signatures = new String[2];\r
+                            input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
+                            byte[] deriveXpub = DeriveXpub.deriveXpub(Hex.decode(priKey));\r
+                            String pubKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
+                            input.witnessComponent.signatures[1] = pubKey;\r
+                            break;\r
+                        }\r
+                        case 0: {\r
+                            input.witnessComponent.signatures = new String[1];\r
+                            input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
+                            break;\r
+                        }\r
+                    }\r
+\r
+                } catch (Exception e) {\r
+                    throw new RuntimeException(e);\r
+                }\r
+            } else {\r
+                System.out.println("build witness failed.");\r
+            }\r
+        return transaction;\r
+    }\r
+\r
+    //多签spend\r
+    public void buildWitness(Transaction transaction, int index, String[] privateKeys) {\r
+        Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
+        if (null == input.witnessComponent)\r
+            input.witnessComponent = new Transaction.InputWitnessComponent();\r
+        try {\r
+\r
+            //TODO 多签issue\r
+            StringBuilder sb = new StringBuilder("ae");\r
+            input.witnessComponent.signatures = new String[privateKeys.length + 1];\r
+            for (int i = 0; i < privateKeys.length; i++) {\r
+                byte[] key = DerivePrivateKey.derivePrivateKey(privateKeys[i], 1, false, input.getControlProgramIndex());\r
+                byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
+                byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(key);\r
+                byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
+                input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
+                byte[] deriveXpub = DeriveXpub.deriveXpub(key);\r
+                String publicKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
+                sb.append("20").append(publicKey);\r
+            }\r
+            //签名数跟公钥数相同\r
+            //TODO 签名数跟公钥数量不同\r
+            sb.append("5").append(privateKeys.length).append("5").append(privateKeys.length).append("ad");\r
+            input.witnessComponent.signatures[privateKeys.length] = sb.toString();\r
+\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+\r
+    }\r
+\r
+    private byte[] hashFn(byte[] hashedInputHex, byte[] txID) {\r
+\r
+        SHA3.Digest256 digest256 = new SHA3.Digest256();\r
+        // data = hashedInputHex + txID\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(hashedInputHex, 0, hashedInputHex.length);\r
+        out.write(txID, 0, txID.length);\r
+        byte[] data = out.toByteArray();\r
+        return digest256.digest(data);\r
+    }\r
+\r
+\r
+    public Transaction generateSignatures(Transaction Transaction, BigInteger[] keys) {\r
+        Transaction.AnnotatedInput input = Transaction.inputs.get(0);\r
+        input.witnessComponent.signatures = new String[keys.length];\r
+        for (int i = 0; i < keys.length; i++) {\r
+            if (null != input) {\r
+                try {\r
+                    byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(Transaction.txID));\r
+                    byte[] expandedPrv = Utils.BigIntegerToBytes(keys[i]);\r
+                    byte[] priKey = ExpandedPrivateKey.ExpandedPrivateKey(expandedPrv);\r
+                    byte[] sig = Signer.Ed25519InnerSign(priKey, message);\r
+                    input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
+\r
+                } catch (Exception e) {\r
+                    e.printStackTrace();\r
+                }\r
+            } else {\r
+                System.out.println("generate signatures failed.");\r
+            }\r
+        }\r
+        return Transaction;\r
+    }\r
+\r
+    //根据主私钥对交易签名,生成序列化的rawTransaction\r
+    public String rawTransaction(String rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] privateChild;\r
+        for (int i = 0; i < tx.inputs.size(); i++) {\r
+            if (tx.inputs.get(i).type == 1) {\r
+                privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, 1, tx.inputs.get(i).isChange(), tx.inputs.get(i).getControlProgramIndex());\r
+            } else {\r
+                privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, tx.inputs.get(i).getKeyIndex());\r
+            }\r
+            buildWitness(tx, i, Hex.toHexString(privateChild));\r
+        }\r
+        return serializeTransaction(tx);\r
+    }\r
+\r
+    //多签\r
+    public String rawTransaction(String[] rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String privateChild;\r
+        for (int i = 0; i < tx.inputs.size(); i++) {\r
+            if (tx.inputs.get(i).controlProgram.length() != 44) {\r
+                buildWitness(tx, i, rootPrivateKey);\r
+            }\r
+\r
+        }\r
+        return serializeTransaction(tx);\r
+    }\r
+\r
+\r
+}\r
+\r
+\r
diff --git a/tx-signer/src/main/java/io/bytom/api/Signer.java b/tx-signer/src/main/java/io/bytom/api/Signer.java
new file mode 100755 (executable)
index 0000000..29167e9
--- /dev/null
@@ -0,0 +1,70 @@
+package io.bytom.api;\r
+\r
+import io.bytom.common.DeriveXpub;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+import java.util.Arrays;\r
+\r
+public class Signer {\r
+\r
+    public static byte[] Ed25519InnerSign(byte[] privateKey, byte[] message)\r
+            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
+        MessageDigest md = MessageDigest.getInstance("SHA-512");\r
+        byte[] digestData = new byte[32 + message.length];\r
+        int digestDataIndex = 0;\r
+        for (int i = 32; i < 64; i++) {\r
+            digestData[digestDataIndex] = privateKey[i];\r
+            digestDataIndex++;\r
+        }\r
+        for (int i = 0; i < message.length; i++) {\r
+            digestData[digestDataIndex] = message[i];\r
+            digestDataIndex++;\r
+        }\r
+        md.update(digestData);\r
+        byte[] messageDigest = md.digest();\r
+\r
+        com.google.crypto.tink.subtle.Ed25519.reduce(messageDigest);\r
+        byte[] messageDigestReduced = Arrays.copyOfRange(messageDigest, 0, 32);\r
+        byte[] encodedR = com.google.crypto.tink.subtle.Ed25519.scalarMultWithBaseToBytes(messageDigestReduced);\r
+        byte[] publicKey = DeriveXpub.deriveXpub(privateKey);\r
+\r
+        byte[] hramDigestData = new byte[32 + encodedR.length + message.length];\r
+        int hramDigestIndex = 0;\r
+        for (int i = 0; i < encodedR.length; i++) {\r
+            hramDigestData[hramDigestIndex] = encodedR[i];\r
+            hramDigestIndex++;\r
+        }\r
+        for (int i = 0; i < 32; i++) {\r
+            hramDigestData[hramDigestIndex] = publicKey[i];\r
+            hramDigestIndex++;\r
+        }\r
+        for (int i = 0; i < message.length; i++) {\r
+            hramDigestData[hramDigestIndex] = message[i];\r
+            hramDigestIndex++;\r
+        }\r
+        md.reset();\r
+        md.update(hramDigestData);\r
+        byte[] hramDigest = md.digest();\r
+        com.google.crypto.tink.subtle.Ed25519.reduce(hramDigest);\r
+        byte[] hramDigestReduced = Arrays.copyOfRange(hramDigest, 0, 32);\r
+\r
+        byte[] sk = Arrays.copyOfRange(privateKey, 0, 32);\r
+        byte[] s = new byte[32];\r
+        com.google.crypto.tink.subtle.Ed25519.mulAdd(s, hramDigestReduced, sk, messageDigestReduced);\r
+\r
+        byte[] signature = new byte[64];\r
+        for (int i = 0; i < encodedR.length; i++) {\r
+            signature[i] = encodedR[i];\r
+        }\r
+        int signatureIndex = 32;\r
+        for (int i = 0; i < s.length; i++) {\r
+            signature[signatureIndex] = s[i];\r
+            signatureIndex++;\r
+        }\r
+        return signature;\r
+    }\r
+\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/SubmitTransaction.java b/tx-signer/src/main/java/io/bytom/api/SubmitTransaction.java
new file mode 100755 (executable)
index 0000000..e767428
--- /dev/null
@@ -0,0 +1,28 @@
+package io.bytom.api;\r
+\r
+import io.bytom.common.Utils;\r
+import io.bytom.exception.BytomException;\r
+import io.bytom.http.Client;\r
+\r
+import java.util.HashMap;\r
+\r
+public class SubmitTransaction {\r
+\r
+    public static SubmitResponse submitRawTransaction(Client client, String rawTransaction) throws BytomException {\r
+        HashMap<String, Object> body = new HashMap<>();\r
+        body.put("raw_transaction", rawTransaction);\r
+        return client.request("submit-transaction", body, SubmitResponse.class);\r
+    }\r
+\r
+    public static class SubmitResponse {\r
+        /**\r
+         * The transaction id.\r
+         */\r
+        public String tx_id;\r
+\r
+        public String toJson() {\r
+            return Utils.serializer.toJson(this);\r
+        }\r
+\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/Transaction.java b/tx-signer/src/main/java/io/bytom/api/Transaction.java
new file mode 100755 (executable)
index 0000000..bd81646
--- /dev/null
@@ -0,0 +1,353 @@
+package io.bytom.api;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+import io.bytom.common.ParameterizedTypeImpl;\r
+import io.bytom.common.SuccessRespon;\r
+import io.bytom.common.Utils;\r
+import io.bytom.exception.BytomException;\r
+import io.bytom.http.Client;\r
+import org.apache.log4j.Logger;\r
+\r
+import java.lang.reflect.Type;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Created by liqiang on 2018/10/24.\r
+ */\r
+\r
+public class Transaction {\r
+\r
+    @SerializedName("tx_id")\r
+    public String txID;\r
+    /**\r
+     * version\r
+     */\r
+    public Integer version;\r
+    /**\r
+     * size\r
+     */\r
+    public Integer size;\r
+    /**\r
+     * time_range\r
+     */\r
+    @SerializedName("time_range")\r
+    public Integer timeRange;\r
+\r
+    /**\r
+     * status\r
+     */\r
+    public Integer fee;\r
+\r
+    /**\r
+     * List of specified inputs for a transaction.\r
+     */\r
+    public List<AnnotatedInput> inputs;\r
+\r
+    /**\r
+     * List of specified outputs for a transaction.\r
+     */\r
+    public List<AnnotatedOutput> outputs;\r
+\r
+    //    public InputWitnessComponent inputWitnessComponent;\r
+    private static Logger logger = Logger.getLogger(Transaction.class);\r
+\r
+    public String toJson() {\r
+        return Utils.serializer.toJson(this);\r
+    }\r
+\r
+    public static Transaction fromJson(String json) {\r
+        return Utils.serializer.fromJson(json, Transaction.class);\r
+    }\r
+\r
+    public static Transaction fromSuccessRespon(String json) {\r
+        Type responType = new ParameterizedTypeImpl(SuccessRespon.class, new Class[]{Transaction.class});\r
+        SuccessRespon<Transaction> result = Utils.serializer.fromJson(json, responType);\r
+        return result.dataObject;\r
+    }\r
+\r
+    public static Transaction decode(Client client, String txId) throws BytomException {\r
+        Map<String, Object> req = new HashMap<String, Object>();\r
+        req.put("raw_transaction", txId);\r
+        Transaction Transaction =\r
+                client.request("decode-raw-transaction", req, Transaction.class);\r
+\r
+        logger.info("decode-raw-transaction:");\r
+        logger.info(Transaction.toJson());\r
+\r
+        return Transaction;\r
+    }\r
+\r
+    public static class Builder {\r
+        @SerializedName("tx_id")\r
+        public String txID;\r
+        public Integer version;\r
+        public Integer size;\r
+        @SerializedName("time_range")\r
+        public Integer timeRange;\r
+\r
+        Transaction tx;\r
+        List<AnnotatedInput> inputs;\r
+        List<AnnotatedOutput> outputs;\r
+\r
+        public Builder() {\r
+            this.inputs = new ArrayList<>();\r
+            this.outputs = new ArrayList<>();\r
+        }\r
+\r
+        public Builder addInput(AnnotatedInput input) {\r
+            this.inputs.add(input);\r
+            return this;\r
+        }\r
+\r
+        public Builder addOutput(AnnotatedOutput output) {\r
+            this.outputs.add(output);\r
+            return this;\r
+        }\r
+\r
+\r
+        public Transaction build(Integer version, Integer timeRange, Integer size) {\r
+            tx = new Transaction();\r
+            tx.inputs = this.inputs;\r
+            tx.outputs = this.outputs;\r
+            tx.version = version;\r
+            tx.timeRange = timeRange;\r
+            tx.size = size;\r
+            return tx;\r
+        }\r
+\r
+        public Transaction build(int timeRange) {\r
+            tx = new Transaction();\r
+            tx.inputs = this.inputs;\r
+            tx.outputs = this.outputs;\r
+            tx.version = 1;\r
+            tx.size = 0;\r
+            tx.timeRange = timeRange;\r
+            return tx;\r
+        }\r
+    }\r
+\r
+    public static class AnnotatedInput {\r
+\r
+        @SerializedName("input_id")\r
+        public String inputID;\r
+        /**\r
+         * address\r
+         */\r
+        public String address;\r
+\r
+        /**\r
+         * The number of units of the asset being issued or spent.\r
+         */\r
+        public long amount;\r
+\r
+        //        /**\r
+//         * The definition of the asset being issued or spent (possibly null).\r
+//         */\r
+//        @SerializedName("asset_definition")\r
+//        private Map<String, Object> assetDefinition;\r
+        @SerializedName("asset_definition")\r
+        public String assetDefinition;\r
+\r
+        /**\r
+         * The id of the asset being issued or spent.\r
+         */\r
+        @SerializedName("asset_id")\r
+        public String assetId;\r
+\r
+        /**\r
+         * The control program which must be satisfied to transfer this output.\r
+         */\r
+        @SerializedName("control_program")\r
+        public String controlProgram;\r
+\r
+        /**\r
+         * The id of the output consumed by this input. Null if the input is an\r
+         * issuance.\r
+         */\r
+        @SerializedName("spent_output_id")\r
+        public String spentOutputId;\r
+\r
+        /**\r
+         * The type of the input.<br>\r
+         * Possible values are "issue" and "spend".\r
+         */\r
+        public int type;\r
+\r
+        public String sourceId;\r
+\r
+        public long sourcePosition;\r
+\r
+        public String nonce;\r
+\r
+        private int controlProgramIndex;\r
+        private boolean change;\r
+\r
+        public int keyIndex;\r
+        public String chainPath;\r
+\r
+        @SerializedName("witness_component")\r
+        public InputWitnessComponent witnessComponent;\r
+\r
+        @Override\r
+        public String toString() {\r
+            return Utils.serializer.toJson(this);\r
+        }\r
+\r
+        public int getControlProgramIndex() {\r
+            return controlProgramIndex;\r
+        }\r
+\r
+        public AnnotatedInput setControlProgramIndex(int controlProgramIndex) {\r
+            this.controlProgramIndex = controlProgramIndex;\r
+            return this;\r
+        }\r
+\r
+        public boolean isChange() {\r
+            return change;\r
+        }\r
+\r
+        public AnnotatedInput setChange(boolean change) {\r
+            this.change = change;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setAmount(long amount) {\r
+            this.amount = amount;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setAssetId(String assetId) {\r
+            this.assetId = assetId;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setControlProgram(String controlProgram) {\r
+            this.controlProgram = controlProgram;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setType(int type) {\r
+            this.type = type;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setSourceId(String sourceId) {\r
+            this.sourceId = sourceId;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setSourcePosition(long sourcePosition) {\r
+            this.sourcePosition = sourcePosition;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setNonce(String nonce) {\r
+            this.nonce = nonce;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedInput setAssetDefinition(String assetDefinition) {\r
+            this.assetDefinition = assetDefinition;\r
+            return this;\r
+        }\r
+\r
+        public int getKeyIndex() {\r
+            return keyIndex;\r
+        }\r
+\r
+        public AnnotatedInput setKeyIndex(int keyIndex) {\r
+            this.keyIndex = keyIndex;\r
+            return this;\r
+        }\r
+\r
+    }\r
+\r
+    public static class AnnotatedOutput {\r
+\r
+        /**\r
+         * address\r
+         */\r
+        public String address;\r
+\r
+        /**\r
+         * The number of units of the asset being controlled.\r
+         */\r
+        public long amount;\r
+\r
+        /**\r
+         * The definition of the asset being controlled (possibly null).\r
+         */\r
+        @SerializedName("asset_definition")\r
+        public Map<String, Object> assetDefinition;\r
+\r
+        /**\r
+         * The id of the asset being controlled.\r
+         */\r
+        @SerializedName("asset_id")\r
+        public String assetId;\r
+\r
+        /**\r
+         * The control program which must be satisfied to transfer this output.\r
+         */\r
+        @SerializedName("control_program")\r
+        public String controlProgram;\r
+\r
+        /**\r
+         * The id of the output.\r
+         */\r
+        @SerializedName("id")\r
+        public String id;\r
+\r
+        /**\r
+         * The output's position in a transaction's list of outputs.\r
+         */\r
+        public Integer position;\r
+\r
+        /**\r
+         * The type the output.<br>\r
+         * Possible values are "control" and "retire".\r
+         */\r
+        public String type;\r
+\r
+        public AnnotatedOutput setAddress(String address) {\r
+            this.address = address;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedOutput setAmount(long amount) {\r
+            this.amount = amount;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedOutput setAssetId(String assetId) {\r
+            this.assetId = assetId;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedOutput setControlProgram(String controlProgram) {\r
+            this.controlProgram = controlProgram;\r
+            return this;\r
+        }\r
+\r
+        public AnnotatedOutput setPosition(Integer position) {\r
+            this.position = position;\r
+            return this;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * A single witness component, holding information that will become the input\r
+     * witness.\r
+     */\r
+    public static class InputWitnessComponent {\r
+\r
+        /**\r
+         * The list of signatures made with the specified keys (null unless type is\r
+         * "signature").\r
+         */\r
+        public String[] signatures;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/api/UTXO.java b/tx-signer/src/main/java/io/bytom/api/UTXO.java
new file mode 100755 (executable)
index 0000000..5fef187
--- /dev/null
@@ -0,0 +1,75 @@
+package io.bytom.api;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+import io.bytom.common.Utils;\r
+\r
+public class UTXO {\r
+    /**\r
+     * id : fda38648c553386c56b2f1276b908061b5d812341f0a96921abad8b2b2f28044\r
+     * amount : 1700000\r
+     * address : tm1qhw9q89exmudkf9ecaxtnmv22fd8af0k07jq7u5\r
+     * program : 0014bb8a039726df1b649738e9973db14a4b4fd4becf\r
+     * change : true\r
+     * highest : 139744\r
+     * account_alias : wyjbtm\r
+     * asset_id : ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\r
+     * asset_alias : BTM\r
+     * account_id : 0NNSS39M00A02\r
+     * control_program_index : 26\r
+     * source_id : 34bc595dff3d40c2bd644e0ea0234e843ef8e3aa0720013a2cb712362cc5933f\r
+     * source_pos : 0\r
+     * valid_height : 0\r
+     * derive_rule : 0\r
+     */\r
+\r
+    @SerializedName("id")\r
+    public String id;\r
+    @SerializedName("amount")\r
+    public long amount;\r
+    @SerializedName("address")\r
+    public String address;\r
+    @SerializedName("program")\r
+    public String program;\r
+    @SerializedName("change")\r
+    public boolean change;\r
+    @SerializedName("highest")\r
+    public int highest;\r
+    @SerializedName("account_alias")\r
+    public String accountAlias;\r
+    @SerializedName("asset_id")\r
+    public String assetId;\r
+    @SerializedName("asset_alias")\r
+    public String assetAlias;\r
+    @SerializedName("account_id")\r
+    public String accountId;\r
+    @SerializedName("control_program_index")\r
+    public int controlProgramIndex;\r
+    @SerializedName("source_id")\r
+    public String sourceId;\r
+    @SerializedName("source_pos")\r
+    public int sourcePos;\r
+    @SerializedName("valid_height")\r
+    public int validHeight;\r
+    @SerializedName("derive_rule")\r
+    public int deriveRule;\r
+\r
+    public String toJson() {\r
+        return Utils.serializer.toJson(this);\r
+    }\r
+\r
+    public static UTXO fromJson(String json) {\r
+        return Utils.serializer.fromJson(json, UTXO.class);\r
+    }\r
+\r
+    public static Transaction.AnnotatedInput utxoToAnnotatedInput(UTXO utxo) {\r
+        Transaction.AnnotatedInput annotatedInput = new Transaction.AnnotatedInput();\r
+        annotatedInput.setAmount(utxo.amount);\r
+        annotatedInput.setControlProgram(utxo.program);\r
+        annotatedInput.setChange(utxo.change);\r
+        annotatedInput.setAssetId(utxo.assetId);\r
+        annotatedInput.setControlProgramIndex(utxo.controlProgramIndex);\r
+        annotatedInput.setSourceId(utxo.sourceId);\r
+        annotatedInput.setSourcePosition(utxo.sourcePos);\r
+        return annotatedInput;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/Configuration.java b/tx-signer/src/main/java/io/bytom/common/Configuration.java
new file mode 100755 (executable)
index 0000000..890f35d
--- /dev/null
@@ -0,0 +1,30 @@
+package io.bytom.common;\r
+\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.util.Properties;\r
+\r
+public class Configuration {\r
+\r
+    private static Properties props = new Properties();\r
+\r
+    static {\r
+        try {\r
+            props.load(Thread.currentThread().getContextClassLoader()\r
+                    .getResourceAsStream("config.properties"));\r
+        } catch (FileNotFoundException e) {\r
+            e.printStackTrace();\r
+        } catch (IOException e) {\r
+            e.printStackTrace();\r
+        }\r
+    }\r
+\r
+    public static String getValue(String key) {\r
+        return props.getProperty(key);\r
+    }\r
+\r
+    public static void updateProperties(String key, String value) {\r
+        props.setProperty(key, value);\r
+    }\r
+\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/Constants.java b/tx-signer/src/main/java/io/bytom/common/Constants.java
new file mode 100755 (executable)
index 0000000..3ced44d
--- /dev/null
@@ -0,0 +1,13 @@
+package io.bytom.common;\r
+\r
+/**\r
+ * Created by liqiang on 2018/10/17.\r
+ */\r
+public class Constants {\r
+\r
+    public static int INPUT_TYPE_ISSUANCE = 0;\r
+\r
+    public static int INPUT_TYPE_SPEND = 1;\r
+\r
+    public static int INPUT_TYPE_COINBASE = 2;\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/DerivePrivateKey.java b/tx-signer/src/main/java/io/bytom/common/DerivePrivateKey.java
new file mode 100755 (executable)
index 0000000..9502a41
--- /dev/null
@@ -0,0 +1,35 @@
+package io.bytom.common;\r
+\r
+import io.bytom.util.PathUtil;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class DerivePrivateKey {\r
+    //bip44 派生子秘钥\r
+    //accountIndex 默认为1\r
+    public static byte[] derivePrivateKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] xprv = Hex.decode(rootPriv);\r
+        byte[][] paths = PathUtil.getBip44Path(accountIndex, change, programIndex);\r
+        byte[] res = xprv;\r
+        for (int i = 0; i < paths.length; i++) {\r
+            byte[] xpub = DeriveXpub.deriveXpub(res);\r
+            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+        }\r
+        return res;\r
+    }\r
+\r
+    //BIP32 派生子秘钥 issue 需要用到BIP32派生规则\r
+    public static byte[] derivePrivateKey(String rootPrivateKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] xprv = Hex.decode(rootPrivateKey);\r
+        byte[] res = xprv;\r
+        byte[][] paths = PathUtil.getBip32Path(keyIndex);\r
+        for (int i = 0; i < paths.length; i++) {\r
+            byte[] xpub = DeriveXpub.deriveXpub(res);\r
+            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+        }\r
+        return res;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/DeriveXpub.java b/tx-signer/src/main/java/io/bytom/common/DeriveXpub.java
new file mode 100755 (executable)
index 0000000..1c214b4
--- /dev/null
@@ -0,0 +1,24 @@
+package io.bytom.common;\r
+\r
+import com.google.crypto.tink.subtle.Ed25519;\r
+\r
+public class DeriveXpub {\r
+    public static byte[] deriveXpub(byte[] xprv) {\r
+        byte[] xpub = new byte[xprv.length];\r
+        byte[] scalar = new byte[xprv.length / 2];\r
+//        for (int i = 0; i < xprv.length / 2; i++) {\r
+//            scalar[i] = xprv[i];\r
+//        }\r
+        System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2);\r
+        byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar);\r
+//        for (int i = 0; i < buf.length; i++) {\r
+//            xpub[i] = buf[i];\r
+//        }\r
+        System.arraycopy(buf, 0, xpub, 0, buf.length);\r
+//        for (int i = xprv.length / 2; i < xprv.length; i++) {\r
+//            xpub[i] = xprv[i];\r
+//        }\r
+        System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2);\r
+        return xpub;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/ExpandedPrivateKey.java b/tx-signer/src/main/java/io/bytom/common/ExpandedPrivateKey.java
new file mode 100755 (executable)
index 0000000..2a23df4
--- /dev/null
@@ -0,0 +1,31 @@
+package io.bytom.common;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import javax.crypto.Mac;\r
+import javax.crypto.spec.SecretKeySpec;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class ExpandedPrivateKey {\r
+    public static byte[] HMacSha512(byte[] data, byte[] key)\r
+            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
+        SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");\r
+        Mac mac = Mac.getInstance("HmacSHA512");\r
+        mac.init(signingKey);\r
+        return mac.doFinal(data);\r
+    }\r
+\r
+    public static byte[] ExpandedPrivateKey(byte[] data)\r
+            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {\r
+        // "457870616e64" is "Expand" hex.\r
+        byte[] res = HMacSha512(data, Hex.decode("457870616e64"));\r
+        for (int i = 0; i <= 31; i++) {\r
+            res[i] = data[i];\r
+        }\r
+        return res;\r
+    }\r
+}\r
+\r
+\r
diff --git a/tx-signer/src/main/java/io/bytom/common/FindDst.java b/tx-signer/src/main/java/io/bytom/common/FindDst.java
new file mode 100755 (executable)
index 0000000..4bfd4d5
--- /dev/null
@@ -0,0 +1,26 @@
+package io.bytom.common;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+public class FindDst {\r
+\r
+    public static Logger logger = Logger.getLogger(FindDst.class);\r
+\r
+    public static int find(String[] privateKeys, String xpub) throws Exception {\r
+        // 多签情况下,找到xpub对应的private key的下标 dst\r
+        int dst = -1;\r
+        for (int k = 0; k < privateKeys.length; k++) {\r
+            byte[] tempXpub = DeriveXpub.deriveXpub(Hex.decode(privateKeys[k]));\r
+            if (xpub.equals(Hex.toHexString(tempXpub))) {\r
+                dst = k;\r
+                logger.info("private[dst]: " + privateKeys[dst]);\r
+                break;\r
+            }\r
+        }\r
+        if (dst == -1) {\r
+            throw new Exception("Not a proper private key to sign transaction.");\r
+        }\r
+        return dst;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/NonHardenedChild.java b/tx-signer/src/main/java/io/bytom/common/NonHardenedChild.java
new file mode 100755 (executable)
index 0000000..aebf82f
--- /dev/null
@@ -0,0 +1,85 @@
+package io.bytom.common;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import javax.crypto.Mac;\r
+import javax.crypto.spec.SecretKeySpec;\r
+import java.io.ByteArrayOutputStream;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class NonHardenedChild {\r
+\r
+    private static byte[] HMacSha512(byte[] data, byte[] key)\r
+            throws NoSuchAlgorithmException, InvalidKeyException {\r
+        SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512");\r
+        Mac mac = Mac.getInstance("HmacSHA512");\r
+        mac.init(signingKey);\r
+        return mac.doFinal(data);\r
+    }\r
+\r
+    public static byte[] NHchild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        // begin build data\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write('N');\r
+        out.write(xpub, 0, xpub.length / 2);\r
+        out.write(path, 0, path.length);\r
+        byte[] data = out.toByteArray();\r
+        // end build data\r
+\r
+        // begin build key\r
+        byte[] key = new byte[xpub.length / 2];\r
+        System.arraycopy(xpub, xpub.length / 2, key, 0, xpub.length / 2);\r
+        // end build key\r
+\r
+        // doFinal()\r
+        byte[] res = HMacSha512(data, key);\r
+\r
+        //begin operate res[:32]\r
+        byte[] f = new byte[res.length / 2];\r
+        System.arraycopy(res, 0, f, 0, res.length / 2);\r
+        f = pruneIntermediateScalar(f);\r
+        System.arraycopy(f, 0, res, 0, res.length / 2);\r
+        //end operate res[:32]\r
+\r
+        //begin operate res[:32] again\r
+        int carry = 0;\r
+        int sum = 0;\r
+        for (int i = 0; i < 32; i++) {\r
+            int xprvInt = xprv[i] & 0xFF;\r
+            int resInt = res[i] & 0xFF;\r
+            sum = xprvInt + resInt + carry;\r
+            res[i] = (byte) sum;\r
+            carry = sum >> 8;\r
+        }\r
+        if ((sum >> 8) != 0) {\r
+            System.err.println("sum does not fit in 256-bit int");\r
+        }\r
+        //end operate res[:32] again\r
+        return res;\r
+    }\r
+\r
+    private static byte[] pruneIntermediateScalar(byte[] f) {\r
+        f[0] &= 248; // clear bottom 3 bits\r
+        f[29] &= 1; // clear 7 high bits\r
+        f[30] = 0;  // clear 8 bits\r
+        f[31] = 0;  // clear 8 bits\r
+        return f;\r
+    }\r
+\r
+    public static byte[] child(byte[] xprv, String[] hpaths) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[][] paths = new byte[][]{\r
+                Hex.decode(hpaths[0]),\r
+                Hex.decode(hpaths[1])\r
+        };\r
+        byte[] res = xprv;\r
+        for (int i = 0; i < hpaths.length; i++) {\r
+            byte[] xpub = DeriveXpub.deriveXpub(res);\r
+            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+        }\r
+        return res;\r
+    }\r
+}\r
+\r
+\r
diff --git a/tx-signer/src/main/java/io/bytom/common/ParameterizedTypeImpl.java b/tx-signer/src/main/java/io/bytom/common/ParameterizedTypeImpl.java
new file mode 100755 (executable)
index 0000000..741b792
--- /dev/null
@@ -0,0 +1,30 @@
+package io.bytom.common;\r
+\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+\r
+public class ParameterizedTypeImpl implements ParameterizedType {\r
+\r
+    private final Class raw;\r
+    private final Type[] args;\r
+\r
+    public ParameterizedTypeImpl(Class raw, Type[] args) {\r
+        this.raw = raw;\r
+        this.args = args != null ? args : new Type[0];\r
+    }\r
+\r
+    @Override\r
+    public Type[] getActualTypeArguments() {\r
+        return args;\r
+    }\r
+\r
+    @Override\r
+    public Type getRawType() {\r
+        return raw;\r
+    }\r
+\r
+    @Override\r
+    public Type getOwnerType() {\r
+        return null;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/SuccessRespon.java b/tx-signer/src/main/java/io/bytom/common/SuccessRespon.java
new file mode 100755 (executable)
index 0000000..9dea6a9
--- /dev/null
@@ -0,0 +1,9 @@
+package io.bytom.common;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+\r
+public class SuccessRespon<T> {\r
+    public String status;\r
+    @SerializedName("data")\r
+    public T dataObject;\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/common/Utils.java b/tx-signer/src/main/java/io/bytom/common/Utils.java
new file mode 100755 (executable)
index 0000000..5cb937b
--- /dev/null
@@ -0,0 +1,92 @@
+package io.bytom.common;\r
+\r
+import com.google.gson.Gson;\r
+import com.google.gson.GsonBuilder;\r
+import io.bytom.types.ExpandedKeys;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.math.BigInteger;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+import java.util.Arrays;\r
+\r
+public class Utils {\r
+\r
+    public static String rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";\r
+\r
+    public static final Gson serializer = new GsonBuilder().setDateFormat(rfc3339DateFormat).create();\r
+\r
+    public static int writeVarint(long value, ByteArrayOutputStream stream) throws IOException {\r
+        byte[] varint = new byte[9];\r
+        int n = putUvarint(varint, value);\r
+        byte[] varintTime = Arrays.copyOf(varint, n);\r
+        stream.write(varintTime);\r
+        return n;\r
+    }\r
+\r
+\r
+    public static int writeVarStr(byte[] buf, ByteArrayOutputStream stream) throws IOException {\r
+        int n = writeVarint(buf.length, stream);\r
+        stream.write(buf);\r
+\r
+        return n + (buf.length);\r
+    }\r
+\r
+    public static int getLengthVarInt(long x) {\r
+        byte[] varint = new byte[9];\r
+        int n = putUvarint(varint, x);\r
+        byte[] varintTime = Arrays.copyOf(varint, n);\r
+        return varintTime.length;\r
+    }\r
+\r
+    private static int putUvarint(byte[] buf, long x) {\r
+        int i = 0;\r
+        while (x >= 0x80) {\r
+            buf[i] = (byte) (x | 0x80);\r
+            x >>= 7;\r
+            i++;\r
+        }\r
+        buf[i] = (byte) x;\r
+        return i + 1;\r
+    }\r
+\r
+    public static byte[] BigIntegerToBytes(BigInteger value) {\r
+        if (value == null) {\r
+            return null;\r
+        } else {\r
+            byte[] data = value.toByteArray();\r
+            if (data.length != 1 && data[0] == 0) {\r
+                byte[] tmp = new byte[data.length - 1];\r
+                System.arraycopy(data, 1, tmp, 0, tmp.length);\r
+                data = tmp;\r
+            }\r
+\r
+            return data;\r
+        }\r
+    }\r
+\r
+    public static byte[] pruneIntermediateScalar(byte[] f) {\r
+        f[0] &= 248;\r
+        f[31] &= 31; // clear top 3 bits\r
+        f[31] |= 64; // set second highest bit\r
+        return f;\r
+    }\r
+\r
+    public static ExpandedKeys expandedPriKey(String priKey, String key) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] hashPriKey = ExpandedPrivateKey.HMacSha512(Hex.decode(priKey), key.getBytes());\r
+        //begin operate res[:32]\r
+        byte[] f = new byte[hashPriKey.length / 2];\r
+        System.arraycopy(hashPriKey, 0, f, 0, hashPriKey.length / 2);\r
+        f = pruneIntermediateScalar(f);\r
+        System.arraycopy(f, 0, hashPriKey, 0, hashPriKey.length / 2);\r
+        //end operate res[:32]\r
+        byte[] hashPubKey = DeriveXpub.deriveXpub(hashPriKey);\r
+        ExpandedKeys expandedKeys = new ExpandedKeys();\r
+        expandedKeys.setPriKey(Hex.toHexString(hashPriKey));\r
+        expandedKeys.setPubKey(Hex.toHexString(hashPubKey));\r
+        return expandedKeys;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/APIException.java b/tx-signer/src/main/java/io/bytom/exception/APIException.java
new file mode 100755 (executable)
index 0000000..a02da0a
--- /dev/null
@@ -0,0 +1,178 @@
+package io.bytom.exception;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+\r
+/**\r
+ * APIException wraps errors returned by the API.\r
+ * Each error contains a brief description in addition to a unique error code.<br>\r
+ * The error code can be used by Chain Support to diagnose the exact cause of the error.\r
+ * The mapping of error codes to messages is as follows:<br><br>\r
+ *\r
+ * <h2>General errors</h2>\r
+ * CH001 - Request timed out\r
+ * CH002 - Not found\r
+ * CH003 - Invalid request body\r
+ * CH004 - Invalid request header\r
+ * CH006 - Not found\r
+ *\r
+ * <h2>Account/Asset errors</h2>\r
+ * CH200 - Quorum must be greater than 1 and less than or equal to the length of xpubs\r
+ * CH201 - Invalid xpub format\r
+ * CH202 - At least one xpub is required\r
+ * CH203 - Retrieved type does not match expected type\r
+ *\r
+ * <h2>Access token errors</h2>\r
+ * CH300 - Malformed or empty access token id\r
+ * CH301 - Access tokens must be type client or network\r
+ * CH302 - Access token id is already in use\r
+ * CH310 - The access token used to authenticate this request cannot be deleted\r
+ *\r
+ * <h2>Query errors</h2>\r
+ * CH600 - Malformed pagination parameter `after`\r
+ * CH601 - Incorrect number of parameters to filter\r
+ * CH602 - Malformed query filter\r
+ *\r
+ * <h2>Transaction errors</h2>\r
+ * CH700 - Reference data does not match previous transaction's reference data<br>\r
+ * CH701 - Invalid action type<br>\r
+ * CH702 - Invalid alias on action<br>\r
+ * CH730 - Missing raw transaction<br>\r
+ * CH731 - Too many signing instructions in template for transaction<br>\r
+ * CH732 - Invalid transaction input index<br>\r
+ * CH733 - Invalid signature script component<br>\r
+ * CH734 - Missing signature in template<br>\r
+ * CH735 - Transaction rejected<br>\r
+ * CH760 - Insufficient funds for tx<br>\r
+ * CH761 - Some outputs are reserved; try again<br>\r
+ */\r
+public class APIException extends BytomException {\r
+    /**\r
+     * Unique identifier for the error.\r
+     */\r
+    public String code;\r
+\r
+    /**\r
+     * Message describing the general nature of the error.\r
+     */\r
+    @SerializedName("message")\r
+    public String chainMessage;\r
+\r
+    /**\r
+     * Additional information about the error (possibly null).\r
+     */\r
+    public String detail;\r
+\r
+    /**\r
+     * Specifies whether the error is temporary, or a change to the request is necessary.\r
+     */\r
+    public boolean temporary;\r
+\r
+    /**\r
+     * Unique identifier of the request to the server.\r
+     */\r
+    public String requestId;\r
+\r
+    /**\r
+     * HTTP status code returned by the server.\r
+     */\r
+    public int statusCode;\r
+\r
+    public APIException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    /**\r
+     * Initializes exception with its message and requestId attributes.\r
+     *\r
+     * @param message   error message\r
+     * @param requestId unique identifier of the request\r
+     */\r
+    public APIException(String message, String requestId) {\r
+        super(message);\r
+        this.requestId = requestId;\r
+    }\r
+\r
+    /**\r
+     * Intitializes exception with its code, message, detail &amp; temporary field set.\r
+     *\r
+     * @param code      error code\r
+     * @param message   error message\r
+     * @param detail    additional error information\r
+     * @param temporary unique identifier of the request\r
+     */\r
+    public APIException(String code, String message, String detail, boolean temporary) {\r
+        super(message);\r
+        this.chainMessage = message;\r
+        this.code = code;\r
+        this.detail = detail;\r
+        this.temporary = temporary;\r
+    }\r
+\r
+    /**\r
+     * Initializes exception with its code, message, detail &amp; requestId attributes.\r
+     *\r
+     * @param code      error code\r
+     * @param message   error message\r
+     * @param detail    additional error information\r
+     * @param requestId unique identifier of the request\r
+     */\r
+    public APIException(String code, String message, String detail, String requestId) {\r
+        super(message);\r
+        this.chainMessage = message;\r
+        this.code = code;\r
+        this.detail = detail;\r
+        this.requestId = requestId;\r
+    }\r
+\r
+    /**\r
+     * Initializes exception with all of its attributes.\r
+     *\r
+     * @param code       error code\r
+     * @param message    error message\r
+     * @param detail     additional error information\r
+     * @param temporary  specifies if the error is temporary\r
+     * @param requestId  unique identifier of the request\r
+     * @param statusCode HTTP status code\r
+     */\r
+    public APIException(\r
+            String code,\r
+            String message,\r
+            String detail,\r
+            boolean temporary,\r
+            String requestId,\r
+            int statusCode) {\r
+        super(message);\r
+        this.chainMessage = message;\r
+        this.code = code;\r
+        this.detail = detail;\r
+        this.temporary = temporary;\r
+        this.requestId = requestId;\r
+        this.statusCode = statusCode;\r
+    }\r
+\r
+    /**\r
+     * Constructs a detailed message of the error.\r
+     *\r
+     * @return detailed error message\r
+     */\r
+    @Override\r
+    public String getMessage() {\r
+        String s = "";\r
+\r
+        if (this.code != null && this.code.length() > 0) {\r
+            s += "Code: " + this.code + " ";\r
+        }\r
+\r
+        s += "Message: " + this.chainMessage;\r
+\r
+        if (this.detail != null && this.detail.length() > 0) {\r
+            s += " Detail: " + this.detail;\r
+        }\r
+\r
+        if (this.requestId != null) {\r
+            s += " Request-ID: " + this.requestId;\r
+        }\r
+\r
+        return s;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/BadURLException.java b/tx-signer/src/main/java/io/bytom/exception/BadURLException.java
new file mode 100755 (executable)
index 0000000..a007f56
--- /dev/null
@@ -0,0 +1,15 @@
+package io.bytom.exception;\r
+\r
+/**\r
+ * BadURLException wraps errors due to malformed URLs.\r
+ */\r
+public class BadURLException extends BytomException {\r
+    /**\r
+     * Initializes exception with its message attribute.\r
+     *\r
+     * @param message error message\r
+     */\r
+    public BadURLException(String message) {\r
+        super(message);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/BuildException.java b/tx-signer/src/main/java/io/bytom/exception/BuildException.java
new file mode 100755 (executable)
index 0000000..407a8d4
--- /dev/null
@@ -0,0 +1,49 @@
+package io.bytom.exception;\r
+\r
+import com.google.gson.annotations.SerializedName;\r
+\r
+import java.util.List;\r
+\r
+/**\r
+ * BuildException wraps errors returned by the build-transaction endpoint.\r
+ */\r
+public class BuildException extends APIException {\r
+\r
+    public BuildException(String message, String requestId) {\r
+        super(message, requestId);\r
+    }\r
+\r
+    public static class ActionError extends APIException {\r
+\r
+        public static class Data {\r
+            /**\r
+             * The index of the action that caused this error.\r
+             */\r
+            @SerializedName("index")\r
+            public Integer index;\r
+        }\r
+\r
+        public ActionError(String message, String requestId) {\r
+            super(message, requestId);\r
+        }\r
+\r
+        /**\r
+         * Additional data pertaining to the error.\r
+         */\r
+        public Data data;\r
+    }\r
+\r
+    public static class Data {\r
+        /**\r
+         * A list of errors resulting from building actions.\r
+         */\r
+        @SerializedName("actions")\r
+        public List<ActionError> actionErrors;\r
+    }\r
+\r
+    /**\r
+     * Extra data associated with this error, if any.\r
+     */\r
+    @SerializedName("data")\r
+    public Data data;\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/BytomException.java b/tx-signer/src/main/java/io/bytom/exception/BytomException.java
new file mode 100755 (executable)
index 0000000..aae30ef
--- /dev/null
@@ -0,0 +1,32 @@
+package io.bytom.exception;\r
+\r
+/**\r
+ * Base exception class for the Chain Core SDK.\r
+ */\r
+public class BytomException extends Exception {\r
+    /**\r
+     * Default constructor.\r
+     */\r
+    public BytomException() {\r
+        super();\r
+    }\r
+\r
+    /**\r
+     * Initializes exception with its message attribute.\r
+     *\r
+     * @param message error message\r
+     */\r
+    public BytomException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    /**\r
+     * Initializes a new exception while storing the original cause.\r
+     *\r
+     * @param message the error message\r
+     * @param cause   the cause of the exception\r
+     */\r
+    public BytomException(String message, Throwable cause) {\r
+        super(message, cause);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/ConfigurationException.java b/tx-signer/src/main/java/io/bytom/exception/ConfigurationException.java
new file mode 100755 (executable)
index 0000000..d69de34
--- /dev/null
@@ -0,0 +1,25 @@
+package io.bytom.exception;\r
+\r
+/**\r
+ * ConfigurationException wraps errors during client configuration.\r
+ */\r
+public class ConfigurationException extends BytomException {\r
+    /**\r
+     * Initializes exception with its message attribute.\r
+     *\r
+     * @param message error message\r
+     */\r
+    public ConfigurationException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    /**\r
+     * Initializes new exception while storing original cause.\r
+     *\r
+     * @param message the error message\r
+     * @param cause   the original cause\r
+     */\r
+    public ConfigurationException(String message, Throwable cause) {\r
+        super(message, cause);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/ConnectivityException.java b/tx-signer/src/main/java/io/bytom/exception/ConnectivityException.java
new file mode 100755 (executable)
index 0000000..45df771
--- /dev/null
@@ -0,0 +1,38 @@
+package io.bytom.exception;\r
+\r
+import com.squareup.okhttp.Response;\r
+\r
+import java.io.IOException;\r
+\r
+/**\r
+ * ConnectivityException wraps errors due to connectivity issues with the server.\r
+ */\r
+public class ConnectivityException extends BytomException {\r
+    /**\r
+     * Initializes exception with its message attribute.\r
+     *\r
+     * @param resp the server response used to create error message\r
+     */\r
+    public ConnectivityException(Response resp) {\r
+        super(formatMessage(resp));\r
+    }\r
+\r
+    /**\r
+     * Parses the the server response into a detailed error message.\r
+     *\r
+     * @param resp the server response\r
+     * @return detailed error message\r
+     */\r
+    private static String formatMessage(Response resp) {\r
+        String s =\r
+                "Response HTTP header field Chain-Request-ID is unset. There may be network issues. Please check your local network settings.";\r
+        // TODO(kr): include client-generated reqid here once we have that.\r
+        String body;\r
+        try {\r
+            body = resp.body().string();\r
+        } catch (IOException ex) {\r
+            body = "[unable to read response body: " + ex.toString() + "]";\r
+        }\r
+        return String.format("%s status=%d body=%s", s, resp.code(), body);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/HTTPException.java b/tx-signer/src/main/java/io/bytom/exception/HTTPException.java
new file mode 100755 (executable)
index 0000000..394543c
--- /dev/null
@@ -0,0 +1,25 @@
+package io.bytom.exception;\r
+\r
+/**\r
+ * HTTPException wraps generic HTTP errors.\r
+ */\r
+public class HTTPException extends BytomException {\r
+    /**\r
+     * Initializes exception with its message attribute.\r
+     *\r
+     * @param message error message\r
+     */\r
+    public HTTPException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    /**\r
+     * Initializes new exception while storing original cause.\r
+     *\r
+     * @param message the error message\r
+     * @param cause   the original cause\r
+     */\r
+    public HTTPException(String message, Throwable cause) {\r
+        super(message, cause);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/exception/JSONException.java b/tx-signer/src/main/java/io/bytom/exception/JSONException.java
new file mode 100755 (executable)
index 0000000..13889f9
--- /dev/null
@@ -0,0 +1,39 @@
+package io.bytom.exception;\r
+\r
+/**\r
+ * JSONException wraps errors due to marshaling/unmarshaling json payloads.\r
+ */\r
+public class JSONException extends BytomException {\r
+\r
+    /**\r
+     * Unique indentifier of the request to the server.\r
+     */\r
+    public String requestId;\r
+\r
+    /**\r
+     * Default constructor.\r
+     */\r
+    public JSONException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    /**\r
+     * Initializes exception with its message and requestId attributes.\r
+     * Use this constructor in context of an API call.\r
+     *\r
+     * @param message   error message\r
+     * @param requestId unique identifier of the request\r
+     */\r
+    public JSONException(String message, String requestId) {\r
+        super(message);\r
+        this.requestId = requestId;\r
+    }\r
+\r
+    public String getMessage() {\r
+        String message = "Message: " + super.getMessage();\r
+        if (requestId != null) {\r
+            message += " Request-ID: " + requestId;\r
+        }\r
+        return message;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/http/BatchResponse.java b/tx-signer/src/main/java/io/bytom/http/BatchResponse.java
new file mode 100755 (executable)
index 0000000..f075e9d
--- /dev/null
@@ -0,0 +1,114 @@
+package io.bytom.http;\r
+\r
+import io.bytom.common.Utils;\r
+import io.bytom.exception.BytomException;\r
+\r
+import java.util.*;\r
+\r
+/**\r
+ * BatchResponse provides a convenient interface for handling the results of\r
+ * batched API calls. The response contains one success or error per outgoing\r
+ * request item in the batch. Errors are always of type BytomException.\r
+ */\r
+public class BatchResponse<T> {\r
+    private Map<Integer, T> successesByIndex = new LinkedHashMap<>();\r
+    private Map<Integer, BytomException> errorsByIndex = new LinkedHashMap<>();\r
+\r
+    public String toJson() {\r
+        return Utils.serializer.toJson(this);\r
+    }\r
+\r
+    /**\r
+     * This constructor is used for synthetically generating a batch response\r
+     * object from a map of successes and a map of errors. It ensures that\r
+     * the successes and errors are stored in an order-preserving fashion.\r
+     */\r
+    public BatchResponse(Map<Integer, T> successes, Map<Integer, BytomException> errors) {\r
+        List<Integer> successIndexes = new ArrayList<>();\r
+        Iterator<Integer> successIter = successes.keySet().iterator();\r
+        while (successIter.hasNext()) {\r
+            successIndexes.add(successIter.next());\r
+        }\r
+        Collections.sort(successIndexes);\r
+\r
+        for (int i : successIndexes) {\r
+            successesByIndex.put(i, successes.get(i));\r
+        }\r
+\r
+        List<Integer> errorIndexes = new ArrayList<>();\r
+        Iterator<Integer> errorIter = errors.keySet().iterator();\r
+        while (errorIter.hasNext()) {\r
+            errorIndexes.add(errorIter.next());\r
+        }\r
+        Collections.sort(errorIndexes);\r
+        for (int i : errorIndexes) {\r
+            errorsByIndex.put(i, errors.get(i));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Returns the total number of response objects. This should equal the number\r
+     * of request objects in the batch.\r
+     */\r
+    public int size() {\r
+        return successesByIndex.size() + errorsByIndex.size();\r
+    }\r
+\r
+    /**\r
+     * Returns whether the request object at the given index produced a success.\r
+     *\r
+     * @param index the index of the request object\r
+     */\r
+    public boolean isSuccess(int index) {\r
+        return successesByIndex.containsKey(index);\r
+    }\r
+\r
+    /**\r
+     * Returns whether the request object at the given index produced an error.\r
+     *\r
+     * @param index the index of the request object\r
+     */\r
+    public boolean isError(int index) {\r
+        return errorsByIndex.containsKey(index);\r
+    }\r
+\r
+    /**\r
+     * Returns a list of successful response objects in the batch. The order of\r
+     * the list corresponds to the order of the request objects that produced the\r
+     * successes.\r
+     */\r
+    public List<T> successes() {\r
+        List<T> res = new ArrayList<>();\r
+        res.addAll(successesByIndex.values());\r
+        return res;\r
+    }\r
+\r
+    /**\r
+     * Returns a list of error objects in the batch. The order of the list\r
+     * corresponds to the order of the request objects that produced the\r
+     * errors.\r
+     */\r
+    public List<BytomException> errors() {\r
+        List<BytomException> res = new ArrayList<>();\r
+        res.addAll(errorsByIndex.values());\r
+        return res;\r
+    }\r
+\r
+    /**\r
+     * Returns a map of success responses, keyed by the index of the request\r
+     * object that produced the success. The set of this map's keys is mutually\r
+     * exclusive of the keys returned by errorsByIndex.\r
+     */\r
+    public Map<Integer, T> successesByIndex() {\r
+        return successesByIndex;\r
+    }\r
+\r
+    /**\r
+     * Returns a map of error responses, keyed by the index of the request\r
+     * object that produced the error. The set of this map's keys is mutually\r
+     * exclusive of the keys returned by successByIndex.\r
+     */\r
+    public Map<Integer, BytomException> errorsByIndex() {\r
+        return errorsByIndex;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/http/Client.java b/tx-signer/src/main/java/io/bytom/http/Client.java
new file mode 100755 (executable)
index 0000000..ec0028c
--- /dev/null
@@ -0,0 +1,723 @@
+package io.bytom.http;\r
+\r
+import com.google.gson.Gson;\r
+import com.google.gson.JsonElement;\r
+import com.google.gson.JsonParser;\r
+import com.squareup.okhttp.*;\r
+import io.bytom.common.Configuration;\r
+import io.bytom.common.Utils;\r
+import io.bytom.exception.APIException;\r
+import io.bytom.exception.BytomException;\r
+import io.bytom.exception.ConfigurationException;\r
+import io.bytom.exception.JSONException;\r
+import org.apache.log4j.Logger;\r
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;\r
+\r
+import javax.net.ssl.*;\r
+import java.io.*;\r
+import java.lang.reflect.Type;\r
+import java.net.MalformedURLException;\r
+import java.net.Proxy;\r
+import java.net.URL;\r
+import java.nio.file.Files;\r
+import java.nio.file.Paths;\r
+import java.security.GeneralSecurityException;\r
+import java.security.KeyFactory;\r
+import java.security.KeyStore;\r
+import java.security.cert.Certificate;\r
+import java.security.cert.CertificateFactory;\r
+import java.security.cert.X509Certificate;\r
+import java.security.spec.KeySpec;\r
+import java.security.spec.PKCS8EncodedKeySpec;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Objects;\r
+import java.util.Random;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+/**\r
+ * The Client object contains all information necessary to\r
+ * perform an HTTP request against a remote API. Typically,\r
+ * an application will have a client that makes requests to\r
+ * a Chain Core, and a separate Client that makes requests\r
+ * to an HSM server.\r
+ */\r
+public class Client {\r
+    private String url;\r
+    private String accessToken;\r
+    private OkHttpClient httpClient;\r
+\r
+    // Used to create empty, in-memory key stores.\r
+    private static final char[] DEFAULT_KEYSTORE_PASSWORD = "123456".toCharArray();\r
+    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");\r
+    private static String version = "dev"; // updated in the static initializer\r
+\r
+    public static Logger logger = Logger.getLogger(Client.class);\r
+\r
+    private static class BuildProperties {\r
+        public String version;\r
+    }\r
+\r
+    static {\r
+        InputStream in = Client.class.getClassLoader().getResourceAsStream("properties.json");\r
+        if (in != null) {\r
+            InputStreamReader inr = new InputStreamReader(in);\r
+            version = Utils.serializer.fromJson(inr, BuildProperties.class).version;\r
+        }\r
+    }\r
+\r
+    public static Client generateClient() throws BytomException {\r
+\r
+        String coreURL = Configuration.getValue("bytom.api.url");\r
+        String accessToken = Configuration.getValue("client.access.token");\r
+\r
+        if (coreURL == null || coreURL.isEmpty()) {\r
+            coreURL = "http://47.91.254.104:8888";\r
+        }\r
+\r
+        if (coreURL.endsWith("/")) {\r
+            //split the last char "/"\r
+            coreURL = coreURL.substring(0, coreURL.length() - 1);\r
+            logger.info("check the coreURL is right.");\r
+        }\r
+\r
+        return new Client(coreURL, accessToken);\r
+    }\r
+\r
+    public Client(Builder builder) throws ConfigurationException {\r
+        if (builder.url.endsWith("/")) {\r
+            //split the last char "/"\r
+            builder.url = builder.url.substring(0, builder.url.length() - 1);\r
+        }\r
+        this.url = builder.url;\r
+        this.accessToken = builder.accessToken;\r
+        this.httpClient = buildHttpClient(builder);\r
+    }\r
+\r
+    /**\r
+     * Create a new http Client object using the default development host URL.\r
+     */\r
+    public Client() throws BytomException {\r
+        this(new Builder());\r
+    }\r
+\r
+    /**\r
+     * Create a new http Client object\r
+     *\r
+     * @param url the URL of the Chain Core or HSM\r
+     */\r
+    public Client(String url) throws BytomException {\r
+        this(new Builder().setUrl(url));\r
+    }\r
+\r
+    /**\r
+     * Create a new http Client object\r
+     *\r
+     * @param url         the URL of the Chain Core or HSM\r
+     * @param accessToken a Client API access token\r
+     */\r
+    public Client(String url, String accessToken) throws BytomException {\r
+        this(new Builder().setUrl(url).setAccessToken(accessToken));\r
+    }\r
+\r
+    /**\r
+     * Perform a single HTTP POST request against the API for a specific action.\r
+     *\r
+     * @param action The requested API action\r
+     * @param body   Body payload sent to the API as JSON\r
+     * @param tClass Type of object to be deserialized from the response JSON\r
+     * @return the result of the post request\r
+     * @throws BytomException\r
+     */\r
+    public <T> T request(String action, Object body, final Type tClass) throws BytomException {\r
+        ResponseCreator<T> rc =\r
+                new ResponseCreator<T>() {\r
+                    public T create(Response response, Gson deserializer) throws IOException, BytomException {\r
+                        JsonElement root = new JsonParser().parse(response.body().charStream());\r
+                        JsonElement status = root.getAsJsonObject().get("status");\r
+                        JsonElement data = root.getAsJsonObject().get("data");\r
+                        if (status != null && status.toString().contains("fail")) {\r
+                            throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
+                        } else if (data != null) {\r
+                            return deserializer.fromJson(data, tClass);\r
+                        } else {\r
+                            return deserializer.fromJson(response.body().charStream(), tClass);\r
+                        }\r
+                    }\r
+                };\r
+        return post(action, body, rc);\r
+    }\r
+\r
+    /**\r
+     * Perform a single HTTP POST request against the API for a specific action,\r
+     * ignoring the body of the response.\r
+     *\r
+     * @param action The requested API action\r
+     * @param body   Body payload sent to the API as JSON\r
+     * @throws BytomException\r
+     */\r
+    public void request(String action, Object body) throws BytomException {\r
+        ResponseCreator<Object> rc =\r
+                new ResponseCreator<Object>() {\r
+                    public Object create(Response response, Gson deserializer) throws IOException, BytomException {\r
+                        JsonElement root = new JsonParser().parse(response.body().charStream());\r
+                        JsonElement status = root.getAsJsonObject().get("status");\r
+                        JsonElement data = root.getAsJsonObject().get("data");\r
+                        if (status != null && status.toString().contains("fail")) {\r
+                            throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
+                        }\r
+                        return null;\r
+                    }\r
+                };\r
+        post(action, body, rc);\r
+    }\r
+\r
+    /**\r
+     * return the value of named as key from json\r
+     *\r
+     * @param action\r
+     * @param body\r
+     * @param key\r
+     * @param tClass\r
+     * @param <T>\r
+     * @return\r
+     * @throws BytomException\r
+     */\r
+    public <T> T requestGet(String action, Object body, final String key, final Type tClass)\r
+            throws BytomException {\r
+        ResponseCreator<T> rc = new ResponseCreator<T>() {\r
+            public T create(Response response, Gson deserializer) throws IOException,\r
+                    BytomException {\r
+                JsonElement root = new JsonParser().parse(response.body().charStream());\r
+                JsonElement status = root.getAsJsonObject().get("status");\r
+                JsonElement data = root.getAsJsonObject().get("data");\r
+\r
+                if (status != null && status.toString().contains("fail"))\r
+                    throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
+                else if (data != null)\r
+                    return deserializer.fromJson(data.getAsJsonObject().get(key), tClass);\r
+                else\r
+                    return deserializer.fromJson(response.body().charStream(), tClass);\r
+            }\r
+        };\r
+        return post(action, body, rc);\r
+    }\r
+\r
+    /**\r
+     * Perform a single HTTP POST request against the API for a specific action.\r
+     * Use this method if you want batch semantics, i.e., the endpoint response\r
+     * is an array of valid objects interleaved with arrays, once corresponding to\r
+     * each input object.\r
+     *\r
+     * @param action The requested API action\r
+     * @param body   Body payload sent to the API as JSON\r
+     * @param tClass Type of object to be deserialized from the response JSON\r
+     * @return the result of the post request\r
+     * @throws BytomException\r
+     */\r
+    /*\r
+    public <T> T requestBatch(String action, Object body, final Type tClass) throws BytomException {\r
+        ResponseCreator<T> rc =\r
+                new ResponseCreator<T>() {\r
+                    public T create(Response response, Gson deserializer) throws IOException, BytomException {\r
+                        JsonElement root = new JsonParser().parse(response.body().charStream());\r
+                        JsonElement status = root.getAsJsonObject().get("status");\r
+                        JsonElement data = root.getAsJsonObject().get("data");\r
+                        if (status != null && status.toString().contains("fail")) {\r
+                            throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
+                        } else if (data != null) {\r
+                            return deserializer.fromJson(data, tClass);\r
+                        } else {\r
+                            return deserializer.fromJson(response.body().charStream(), tClass);\r
+                        }\r
+                    }\r
+                };\r
+        //把object转换为json对象数组(有点难)\r
+\r
+        //根据数组的大小循环调用post()方法\r
+\r
+        //重写create()接口方法,对成功和失败做不同的处理\r
+\r
+        //调用BatchResponse(Map<Integer, T> successes, Map<Integer, APIException> errors)\r
+        //构造方法,最后返回BatchResponse实例对象\r
+\r
+        return post(action, body, rc);\r
+    }\r
+    */\r
+\r
+\r
+    /**\r
+     * Returns true if a client access token stored in the client.\r
+     *\r
+     * @return a boolean\r
+     */\r
+    public boolean hasAccessToken() {\r
+        return this.accessToken != null && !this.accessToken.isEmpty();\r
+    }\r
+\r
+    /**\r
+     * Returns the client access token (possibly null).\r
+     *\r
+     * @return the client access token\r
+     */\r
+    public String accessToken() {\r
+        return accessToken;\r
+    }\r
+\r
+    public String getUrl() {\r
+        return url;\r
+    }\r
+\r
+    /**\r
+     * Pins a public key to the HTTP client.\r
+     *\r
+     * @param provider           certificate provider\r
+     * @param subjPubKeyInfoHash public key hash\r
+     */\r
+    public void pinCertificate(String provider, String subjPubKeyInfoHash) {\r
+        CertificatePinner cp =\r
+                new CertificatePinner.Builder().add(provider, subjPubKeyInfoHash).build();\r
+        this.httpClient.setCertificatePinner(cp);\r
+    }\r
+\r
+    /**\r
+     * Sets the default connect timeout for new connections. A value of 0 means no timeout.\r
+     *\r
+     * @param timeout the number of time units for the default timeout\r
+     * @param unit    the unit of time\r
+     */\r
+    public void setConnectTimeout(long timeout, TimeUnit unit) {\r
+        this.httpClient.setConnectTimeout(timeout, unit);\r
+    }\r
+\r
+    /**\r
+     * Sets the default read timeout for new connections. A value of 0 means no timeout.\r
+     *\r
+     * @param timeout the number of time units for the default timeout\r
+     * @param unit    the unit of time\r
+     */\r
+    public void setReadTimeout(long timeout, TimeUnit unit) {\r
+        this.httpClient.setReadTimeout(timeout, unit);\r
+    }\r
+\r
+    /**\r
+     * Sets the default write timeout for new connections. A value of 0 means no timeout.\r
+     *\r
+     * @param timeout the number of time units for the default timeout\r
+     * @param unit    the unit of time\r
+     */\r
+    public void setWriteTimeout(long timeout, TimeUnit unit) {\r
+        this.httpClient.setWriteTimeout(timeout, unit);\r
+    }\r
+\r
+    /**\r
+     * Sets the proxy information for the HTTP client.\r
+     *\r
+     * @param proxy proxy object\r
+     */\r
+    public void setProxy(Proxy proxy) {\r
+        this.httpClient.setProxy(proxy);\r
+    }\r
+\r
+    /**\r
+     * Defines an interface for deserializing HTTP responses into objects.\r
+     *\r
+     * @param <T> the type of object to return\r
+     */\r
+    public interface ResponseCreator<T> {\r
+        /**\r
+         * Deserializes an HTTP response into a Java object of type T.\r
+         *\r
+         * @param response     HTTP response object\r
+         * @param deserializer json deserializer\r
+         * @return an object of type T\r
+         * @throws BytomException\r
+         * @throws IOException\r
+         */\r
+        T create(Response response, Gson deserializer) throws BytomException, IOException;\r
+    }\r
+\r
+    /**\r
+     * Builds and executes an HTTP Post request.\r
+     *\r
+     * @param path        the path to the endpoint\r
+     * @param body        the request body\r
+     * @param respCreator object specifying the response structure\r
+     * @return a response deserialized into type T\r
+     * @throws BytomException\r
+     */\r
+    private <T> T post(String path, Object body, ResponseCreator<T> respCreator)\r
+            throws BytomException {\r
+\r
+        RequestBody requestBody = RequestBody.create(this.JSON, Utils.serializer.toJson(body));\r
+        Request req;\r
+\r
+        BytomException exception = null;\r
+        URL endpointURL = null;\r
+\r
+        try {\r
+            endpointURL = new URL(url + "/" + path);\r
+        } catch (MalformedURLException e) {\r
+            e.printStackTrace();\r
+        }\r
+\r
+        Request.Builder builder =\r
+                new Request.Builder()\r
+                        .header("User-Agent", "bytom-sdk-java/" + version)\r
+                        .url(endpointURL)\r
+                        .method("POST", requestBody);\r
+        if (hasAccessToken()) {\r
+            builder = builder.header("Authorization", buildCredentials());\r
+        }\r
+        req = builder.build();\r
+\r
+        Response resp = null;\r
+\r
+        T object = null;\r
+\r
+        try {\r
+            resp = this.checkError(this.httpClient.newCall(req).execute());\r
+            object = respCreator.create(resp, Utils.serializer);\r
+        } catch (IOException e) {\r
+            e.printStackTrace();\r
+        }\r
+\r
+        return object;\r
+    }\r
+\r
+    private OkHttpClient buildHttpClient(Builder builder) throws ConfigurationException {\r
+        OkHttpClient httpClient = builder.baseHttpClient.clone();\r
+\r
+        try {\r
+            if (builder.trustManagers != null) {\r
+                SSLContext sslContext = SSLContext.getInstance("TLS");\r
+                sslContext.init(builder.keyManagers, builder.trustManagers, null);\r
+                httpClient.setSslSocketFactory(sslContext.getSocketFactory());\r
+            }\r
+        } catch (GeneralSecurityException ex) {\r
+            throw new ConfigurationException("Unable to configure TLS", ex);\r
+        }\r
+        if (builder.readTimeoutUnit != null) {\r
+            httpClient.setReadTimeout(builder.readTimeout, builder.readTimeoutUnit);\r
+        }\r
+        if (builder.writeTimeoutUnit != null) {\r
+            httpClient.setWriteTimeout(builder.writeTimeout, builder.writeTimeoutUnit);\r
+        }\r
+        if (builder.connectTimeoutUnit != null) {\r
+            httpClient.setConnectTimeout(builder.connectTimeout, builder.connectTimeoutUnit);\r
+        }\r
+        if (builder.pool != null) {\r
+            httpClient.setConnectionPool(builder.pool);\r
+        }\r
+        if (builder.proxy != null) {\r
+            httpClient.setProxy(builder.proxy);\r
+        }\r
+        if (builder.cp != null) {\r
+            httpClient.setCertificatePinner(builder.cp);\r
+        }\r
+\r
+        return httpClient;\r
+    }\r
+\r
+    private static final Random randomGenerator = new Random();\r
+    private static final int MAX_RETRIES = 10;\r
+    private static final int RETRY_BASE_DELAY_MILLIS = 40;\r
+\r
+    // the max amount of time cored leader election could take\r
+    private static final int RETRY_MAX_DELAY_MILLIS = 15000;\r
+\r
+    private static int retryDelayMillis(int retryAttempt) {\r
+        // Calculate the max delay as base * 2 ^ (retryAttempt - 1).\r
+        int max = RETRY_BASE_DELAY_MILLIS * (1 << (retryAttempt - 1));\r
+        max = Math.min(max, RETRY_MAX_DELAY_MILLIS);\r
+\r
+        // To incorporate jitter, use a pseudo random delay between [max/2, max] millis.\r
+        return randomGenerator.nextInt(max / 2) + max / 2 + 1;\r
+    }\r
+\r
+    private static final int[] RETRIABLE_STATUS_CODES = {\r
+            408, // Request Timeout\r
+            429, // Too Many Requests\r
+            500, // Internal Server Error\r
+            502, // Bad Gateway\r
+            503, // Service Unavailable\r
+            504, // Gateway Timeout\r
+            509, // Bandwidth Limit Exceeded\r
+    };\r
+\r
+    private static boolean isRetriableStatusCode(int statusCode) {\r
+        for (int i = 0; i < RETRIABLE_STATUS_CODES.length; i++) {\r
+            if (RETRIABLE_STATUS_CODES[i] == statusCode) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private Response checkError(Response response) throws BytomException {\r
+        /*\r
+        String rid = response.headers().get("Bytom-Request-ID");\r
+        if (rid == null || rid.length() == 0) {\r
+            // Header field Bytom-Request-ID is set by the backend\r
+            // API server. If this field is set, then we can expect\r
+            // the body to be well-formed JSON. If it's not set,\r
+            // then we are probably talking to a gateway or proxy.\r
+            throw new ConnectivityException(response);\r
+        } */\r
+\r
+        if ((response.code() / 100) != 2) {\r
+            try {\r
+                APIException err =\r
+                        Utils.serializer.fromJson(response.body().charStream(), APIException.class);\r
+                if (err.code != null) {\r
+                    //err.requestId = rid;\r
+                    err.statusCode = response.code();\r
+                    throw err;\r
+                }\r
+            } catch (IOException ex) {\r
+                //throw new JSONException("Unable to read body. " + ex.getMessage(), rid);\r
+                throw new JSONException("Unable to read body. ");\r
+            }\r
+        }\r
+        return response;\r
+    }\r
+\r
+    private String buildCredentials() {\r
+        String user = "";\r
+        String pass = "";\r
+        if (hasAccessToken()) {\r
+            String[] parts = accessToken.split(":");\r
+            if (parts.length >= 1) {\r
+                user = parts[0];\r
+            }\r
+            if (parts.length >= 2) {\r
+                pass = parts[1];\r
+            }\r
+        }\r
+        return Credentials.basic(user, pass);\r
+    }\r
+\r
+    /**\r
+     * Overrides {@link Object#hashCode()}\r
+     *\r
+     * @return the hash code\r
+     */\r
+    @Override\r
+    public int hashCode() {\r
+        int code = this.url.hashCode();\r
+        if (this.hasAccessToken()) {\r
+            code = code * 31 + this.accessToken.hashCode();\r
+        }\r
+        return code;\r
+    }\r
+\r
+    /**\r
+     * Overrides {@link Object#equals(Object)}\r
+     *\r
+     * @param o the object to compare\r
+     * @return a boolean specifying equality\r
+     */\r
+    @Override\r
+    public boolean equals(Object o) {\r
+        if (o == null) return false;\r
+        if (!(o instanceof Client)) return false;\r
+\r
+        Client other = (Client) o;\r
+        if (!this.url.equalsIgnoreCase(other.url)) {\r
+            return false;\r
+        }\r
+        return Objects.equals(this.accessToken, other.accessToken);\r
+    }\r
+\r
+    /**\r
+     * A builder class for creating client objects\r
+     */\r
+    public static class Builder {\r
+\r
+        private String url;\r
+\r
+        private OkHttpClient baseHttpClient;\r
+        private String accessToken;\r
+        private CertificatePinner cp;\r
+        private KeyManager[] keyManagers;\r
+        private TrustManager[] trustManagers;\r
+        private long connectTimeout;\r
+        private TimeUnit connectTimeoutUnit;\r
+        private long readTimeout;\r
+        private TimeUnit readTimeoutUnit;\r
+        private long writeTimeout;\r
+        private TimeUnit writeTimeoutUnit;\r
+        private Proxy proxy;\r
+        private ConnectionPool pool;\r
+\r
+        public Builder() {\r
+            this.baseHttpClient = new OkHttpClient();\r
+            this.baseHttpClient.setFollowRedirects(false);\r
+            this.setDefaults();\r
+        }\r
+\r
+        public Builder(Client client) {\r
+            this.baseHttpClient = client.httpClient.clone();\r
+            this.url = client.url;\r
+            this.accessToken = client.accessToken;\r
+        }\r
+\r
+        private void setDefaults() {\r
+            this.setReadTimeout(30, TimeUnit.SECONDS);\r
+            this.setWriteTimeout(30, TimeUnit.SECONDS);\r
+            this.setConnectTimeout(30, TimeUnit.SECONDS);\r
+            this.setConnectionPool(50, 2, TimeUnit.MINUTES);\r
+        }\r
+\r
+        public Builder setUrl(String url) {\r
+            this.url = url;\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the access token for the client\r
+         *\r
+         * @param accessToken The access token for the Chain Core or HSM\r
+         */\r
+        public Builder setAccessToken(String accessToken) {\r
+            this.accessToken = accessToken;\r
+            return this;\r
+        }\r
+\r
+\r
+        /**\r
+         * Trusts the given CA certs, and no others. Use this if you are running\r
+         * your own CA, or are using a self-signed server certificate.\r
+         *\r
+         * @param is input stream of the certificates to trust, in PEM format.\r
+         */\r
+        public Builder setTrustedCerts(InputStream is) throws ConfigurationException {\r
+            try {\r
+                // Extract certs from PEM-encoded input.\r
+                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");\r
+                Collection<? extends Certificate> certificates =\r
+                        certificateFactory.generateCertificates(is);\r
+                if (certificates.isEmpty()) {\r
+                    throw new IllegalArgumentException("expected non-empty set of trusted certificates");\r
+                }\r
+\r
+                // Create a new key store and input the cert.\r
+                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());\r
+                keyStore.load(null, DEFAULT_KEYSTORE_PASSWORD);\r
+                int index = 0;\r
+                for (Certificate certificate : certificates) {\r
+                    String certificateAlias = Integer.toString(index++);\r
+                    keyStore.setCertificateEntry(certificateAlias, certificate);\r
+                }\r
+\r
+                // Use key store to build an X509 trust manager.\r
+                KeyManagerFactory keyManagerFactory =\r
+                        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());\r
+                keyManagerFactory.init(keyStore, DEFAULT_KEYSTORE_PASSWORD);\r
+                TrustManagerFactory trustManagerFactory =\r
+                        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\r
+                trustManagerFactory.init(keyStore);\r
+                TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();\r
+                if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {\r
+                    throw new IllegalStateException(\r
+                            "Unexpected default trust managers:" + Arrays.toString(trustManagers));\r
+                }\r
+\r
+                this.trustManagers = trustManagers;\r
+                return this;\r
+            } catch (GeneralSecurityException | IOException ex) {\r
+                throw new ConfigurationException("Unable to configure trusted CA certs", ex);\r
+            }\r
+        }\r
+\r
+        /**\r
+         * Trusts the given CA certs, and no others. Use this if you are running\r
+         * your own CA, or are using a self-signed server certificate.\r
+         *\r
+         * @param path The path of a file containing certificates to trust, in PEM format.\r
+         */\r
+        public Builder setTrustedCerts(String path) throws ConfigurationException {\r
+            try (InputStream is = new FileInputStream(path)) {\r
+                return setTrustedCerts(is);\r
+            } catch (IOException ex) {\r
+                throw new ConfigurationException("Unable to configure trusted CA certs", ex);\r
+            }\r
+        }\r
+\r
+        /**\r
+         * Sets the certificate pinner for the client\r
+         *\r
+         * @param provider           certificate provider\r
+         * @param subjPubKeyInfoHash public key hash\r
+         */\r
+        public Builder pinCertificate(String provider, String subjPubKeyInfoHash) {\r
+            this.cp = new CertificatePinner.Builder().add(provider, subjPubKeyInfoHash).build();\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the connect timeout for the client\r
+         *\r
+         * @param timeout the number of time units for the default timeout\r
+         * @param unit    the unit of time\r
+         */\r
+        public Builder setConnectTimeout(long timeout, TimeUnit unit) {\r
+            this.connectTimeout = timeout;\r
+            this.connectTimeoutUnit = unit;\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the read timeout for the client\r
+         *\r
+         * @param timeout the number of time units for the default timeout\r
+         * @param unit    the unit of time\r
+         */\r
+        public Builder setReadTimeout(long timeout, TimeUnit unit) {\r
+            this.readTimeout = timeout;\r
+            this.readTimeoutUnit = unit;\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the write timeout for the client\r
+         *\r
+         * @param timeout the number of time units for the default timeout\r
+         * @param unit    the unit of time\r
+         */\r
+        public Builder setWriteTimeout(long timeout, TimeUnit unit) {\r
+            this.writeTimeout = timeout;\r
+            this.writeTimeoutUnit = unit;\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the proxy for the client\r
+         *\r
+         * @param proxy\r
+         */\r
+        public Builder setProxy(Proxy proxy) {\r
+            this.proxy = proxy;\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Sets the connection pool for the client\r
+         *\r
+         * @param maxIdle the maximum number of idle http connections in the pool\r
+         * @param timeout the number of time units until an idle http connection in the pool is closed\r
+         * @param unit    the unit of time\r
+         */\r
+        public Builder setConnectionPool(int maxIdle, long timeout, TimeUnit unit) {\r
+            this.pool = new ConnectionPool(maxIdle, unit.toMillis(timeout));\r
+            return this;\r
+        }\r
+\r
+        /**\r
+         * Builds a client with all of the provided parameters.\r
+         */\r
+        public Client build() throws ConfigurationException {\r
+            return new Client(this);\r
+        }\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Asset.java b/tx-signer/src/main/java/io/bytom/types/Asset.java
new file mode 100755 (executable)
index 0000000..1ff2706
--- /dev/null
@@ -0,0 +1,146 @@
+package io.bytom.types;\r
+\r
+\r
+import com.google.gson.Gson;\r
+import com.google.gson.GsonBuilder;\r
+import com.google.gson.reflect.TypeToken;\r
+import io.bytom.common.DerivePrivateKey;\r
+import io.bytom.common.DeriveXpub;\r
+import io.bytom.util.SHA3Util;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+import java.util.LinkedHashMap;\r
+import java.util.Map;\r
+\r
+public class Asset {\r
+\r
+    /**\r
+     * type : asset\r
+     * xpubs : ["135022d3c218e949de69902459ba4233c8a21ecf2fde00ea592876e3138f1bf09dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"]\r
+     * quorum : 1\r
+     * id : 821ab3d068a218e2ec23c28f50786c7f52412394c52bf9f9319d58bd3e459fa3\r
+     * alias : TEST7\r
+     * definition : {"decimals":8,"description":{},"name":"","symbol":""}\r
+     * key_index : 14\r
+     * derive_rule : 0\r
+     * vm_version : 1\r
+     * issue_program : ae202dcda0296db65a471f5e111ed1e61afec158f1ac6be8f8c23c6ccaefa27c5dfa5151ad\r
+     * raw_definition_byte : 7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d\r
+     */\r
+\r
+    public String type;\r
+    public int quorum;\r
+    public String id;\r
+    public String alias;\r
+    public Definition definition;\r
+    public int keyIndex;\r
+    public int deriveRule;\r
+    public int vmVersion;\r
+    public String issueProgram;\r
+    public String rawDefinitionByte;\r
+    public String xpubs;\r
+\r
+    public Asset() {\r
+\r
+    }\r
+\r
+    public Asset(String rawDefinitionByte, int keyIndex, String rootKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String issueProgram = computeIssueProgram(rootKey, keyIndex);\r
+        String assetID = computeAssetID(issueProgram, 1, rawDefinitionByte);\r
+        this.keyIndex = keyIndex;\r
+        this.id = assetID;\r
+        this.rawDefinitionByte = rawDefinitionByte;\r
+        this.issueProgram = issueProgram;\r
+    }\r
+\r
+    public Asset CreateAsset(Definition definitionp, int keyIndex, String rootKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String rawDefinitionByte = computeRawDefinition(definitionp);\r
+        String issueProgram = computeIssueProgram(rootKey, keyIndex);\r
+        String assetID = computeAssetID(issueProgram, 1, rawDefinitionByte);\r
+        this.keyIndex = keyIndex;\r
+        this.id = assetID;\r
+        this.issueProgram = issueProgram;\r
+        this.rawDefinitionByte = rawDefinitionByte;\r
+        return this;\r
+    }\r
+\r
+    private String computeIssueProgram(String rootKey, int keyIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, keyIndex);\r
+        byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey);\r
+        String issueProgram = "ae20" + Hex.toHexString(deriveXpub).substring(0, 64) + "5151ad";\r
+        return issueProgram;\r
+    }\r
+\r
+    private String computeAssetID(String issuanceProgram, int VMVersion, String rawDefinitionByte) {\r
+        byte[] rawDefinitionBytes = SHA3Util.hashSha256(Hex.decode(rawDefinitionByte));\r
+        Hash assetDefineHash = new Hash(rawDefinitionBytes);\r
+        Program pro = new Program(1, Hex.decode(issuanceProgram));\r
+        AssetDefinition assetDefinition = new AssetDefinition(assetDefineHash, pro);\r
+        ByteArrayOutputStream bo = new ByteArrayOutputStream();\r
+        assetDefinition.writeForHash(bo);\r
+        byte[] assetIDBytes = SHA3Util.hashSha256(bo.toByteArray());\r
+        String assetID = Hex.toHexString(assetIDBytes);\r
+        return assetID;\r
+    }\r
+\r
+    private String computeRawDefinition(final Definition definition) {\r
+        Map<String, Object> map = new LinkedHashMap<String, Object>() {\r
+            {\r
+                put("decimals", definition.getDecimals());\r
+                put("description", definition.getDescription());\r
+                put("name", definition.getName());\r
+                put("symbol", definition.getSymbol());\r
+            }\r
+        };\r
+        Gson gson = new GsonBuilder().setPrettyPrinting().create();\r
+        String json = gson.toJson(map, new TypeToken<Map<String, Object>>() {\r
+        }.getType());\r
+        String rawDefinition = Hex.toHexString(json.getBytes());\r
+        return rawDefinition;\r
+    }\r
+\r
+\r
+    public static class Definition {\r
+        private int decimals;\r
+        private Object description;\r
+        private String name;\r
+        private String symbol;\r
+\r
+        public int getDecimals() {\r
+            return decimals;\r
+        }\r
+\r
+        public void setDecimals(int decimals) {\r
+            this.decimals = decimals;\r
+        }\r
+\r
+        public Object getDescription() {\r
+            return description;\r
+        }\r
+\r
+        public void setDescription(Object description) {\r
+            this.description = description;\r
+        }\r
+\r
+        public String getName() {\r
+            return name;\r
+        }\r
+\r
+        public void setName(String name) {\r
+            this.name = name;\r
+        }\r
+\r
+        public String getSymbol() {\r
+            return symbol;\r
+        }\r
+\r
+        public void setSymbol(String symbol) {\r
+            this.symbol = symbol;\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/AssetAmount.java b/tx-signer/src/main/java/io/bytom/types/AssetAmount.java
new file mode 100755 (executable)
index 0000000..eec4e75
--- /dev/null
@@ -0,0 +1,16 @@
+package io.bytom.types;\r
+\r
+public class AssetAmount {\r
+\r
+    public AssetID assetID;\r
+\r
+    public long amount;\r
+\r
+    public AssetAmount() {\r
+    }\r
+\r
+    public AssetAmount(AssetID assetID, long amount) {\r
+        this.assetID = assetID;\r
+        this.amount = amount;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/AssetDefinition.java b/tx-signer/src/main/java/io/bytom/types/AssetDefinition.java
new file mode 100755 (executable)
index 0000000..2a7a84a
--- /dev/null
@@ -0,0 +1,24 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class AssetDefinition extends Entry {\r
+    public Hash assetDefHash;\r
+    public Program program;\r
+\r
+    public AssetDefinition(Hash assetDefHash, Program program) {\r
+        this.assetDefHash = assetDefHash;\r
+        this.program = program;\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "asset";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.program);\r
+        mustWriteForHash(out, this.assetDefHash);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/AssetID.java b/tx-signer/src/main/java/io/bytom/types/AssetID.java
new file mode 100755 (executable)
index 0000000..9d16e17
--- /dev/null
@@ -0,0 +1,26 @@
+package io.bytom.types;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+public class AssetID {\r
+\r
+    private String hexValue;\r
+\r
+    public AssetID() {\r
+    }\r
+\r
+    public AssetID(String hexValue) {\r
+        this.hexValue = hexValue;\r
+    }\r
+\r
+\r
+    public AssetID(byte[] byteArray) {\r
+        this.hexValue = Hex.toHexString(byteArray);\r
+    }\r
+\r
+    public byte[] toByteArray() {\r
+        return Hex.decode(this.hexValue);\r
+    }\r
+\r
+\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Entry.java b/tx-signer/src/main/java/io/bytom/types/Entry.java
new file mode 100755 (executable)
index 0000000..05f677c
--- /dev/null
@@ -0,0 +1,77 @@
+package io.bytom.types;\r
+\r
+import io.bytom.util.OutputUtil;\r
+import org.bouncycastle.jcajce.provider.digest.SHA3;\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.lang.reflect.Field;\r
+\r
+public abstract class Entry {\r
+\r
+    public abstract String typ();\r
+\r
+    public abstract void writeForHash(ByteArrayOutputStream out);\r
+\r
+    public void mustWriteForHash(ByteArrayOutputStream out, Object data) {\r
+        try {\r
+            if (data == null) {\r
+                return;\r
+            }\r
+            if (data instanceof Byte) {\r
+                OutputUtil.writeByte(out, (byte) data);\r
+            } else if (data instanceof Long) {\r
+                OutputUtil.writeLong(out, (long) data);\r
+            } else if (data instanceof byte[]) {\r
+                OutputUtil.writeVarstr(out, (byte[]) data);\r
+            } else if (data instanceof byte[][]) {\r
+                OutputUtil.writeVarstrList(out, (byte[][]) data);\r
+            } else if (data instanceof String) {\r
+                OutputUtil.writeVarstr(out, ((String) data).getBytes());\r
+            } else if (data instanceof Hash) {\r
+                out.write(((Hash) data).toByteArray());\r
+            } else if (data instanceof AssetID) {\r
+                out.write(((AssetID) data).toByteArray());\r
+            } else if (data.getClass().isArray()) {\r
+                Object[] array = (Object[]) data;\r
+                OutputUtil.writeVarint(out, array.length);\r
+                for (Object obj : array) {\r
+                    mustWriteForHash(out, obj);\r
+                }\r
+            } else {\r
+                Class<?> cls = data.getClass();\r
+                Field[] fields = cls.getFields();\r
+                for (Field field : fields) {\r
+                    mustWriteForHash(out, field.get(data));\r
+                }\r
+            }\r
+        } catch (Exception e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+    }\r
+\r
+    public Hash entryID() {\r
+        SHA3.Digest256 digest256 = new SHA3.Digest256();\r
+        ByteArrayOutputStream hasher = new ByteArrayOutputStream();\r
+        ByteArrayOutputStream bh = new ByteArrayOutputStream();\r
+        try {\r
+            hasher.write("entryid:".getBytes());\r
+            hasher.write(this.typ().getBytes());\r
+            hasher.write(":".getBytes());\r
+\r
+            this.writeForHash(bh);\r
+            hasher.write(digest256.digest(bh.toByteArray()));\r
+            return new Hash(digest256.digest(hasher.toByteArray()));\r
+        } catch (IOException e) {\r
+            throw new RuntimeException(e);\r
+        } finally {\r
+            try {\r
+                bh.close();\r
+                hasher.close();\r
+            } catch (IOException e) {\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/ExpandedKeys.java b/tx-signer/src/main/java/io/bytom/types/ExpandedKeys.java
new file mode 100755 (executable)
index 0000000..146211d
--- /dev/null
@@ -0,0 +1,24 @@
+package io.bytom.types;\r
+\r
+public class ExpandedKeys {\r
+\r
+    private String expandedPriKey;\r
+\r
+    private String expandedPubKey;\r
+\r
+    public void setPriKey(String priKey) {\r
+        this.expandedPriKey = priKey;\r
+    }\r
+\r
+    public String getPriKey() {\r
+        return this.expandedPriKey;\r
+    }\r
+\r
+    public void setPubKey(String pubKey) {\r
+        this.expandedPubKey = pubKey;\r
+    }\r
+\r
+    public String getPubKey() {\r
+        return this.expandedPubKey;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Hash.java b/tx-signer/src/main/java/io/bytom/types/Hash.java
new file mode 100755 (executable)
index 0000000..1354fa1
--- /dev/null
@@ -0,0 +1,43 @@
+package io.bytom.types;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.util.Objects;\r
+\r
+public class Hash {\r
+\r
+    private String hexValue;\r
+\r
+    public Hash() {\r
+    }\r
+\r
+    public Hash(String hexValue) {\r
+        this.hexValue = hexValue;\r
+    }\r
+\r
+    public Hash(byte[] byteArray) {\r
+        this.hexValue = Hex.toHexString(byteArray);\r
+    }\r
+\r
+    public byte[] toByteArray() {\r
+        return Hex.decode(this.hexValue);\r
+    }\r
+\r
+    @Override\r
+    public boolean equals(Object o) {\r
+        if (this == o) return true;\r
+        if (o == null || getClass() != o.getClass()) return false;\r
+        Hash hash = (Hash) o;\r
+        return Objects.equals(hexValue, hash.hexValue);\r
+    }\r
+\r
+    @Override\r
+    public int hashCode() {\r
+        return Objects.hash(hexValue);\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+        return this.hexValue;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Issue.java b/tx-signer/src/main/java/io/bytom/types/Issue.java
new file mode 100755 (executable)
index 0000000..e292dd4
--- /dev/null
@@ -0,0 +1,32 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class Issue extends Entry {\r
+    public Hash nonceHash;\r
+    public AssetAmount assetAmount;\r
+    public int ordinal;\r
+    public AssetDefinition assetDefinition;\r
+    public ValueDestination witnessDestination;\r
+\r
+    public Issue(Hash nonceHash, AssetAmount assetAmount, int ordinal) {\r
+        this.nonceHash = nonceHash;\r
+        this.assetAmount = assetAmount;\r
+        this.ordinal = ordinal;\r
+    }\r
+\r
+    public void setDestination(Hash id, AssetAmount val, long pos) {\r
+        this.witnessDestination = new ValueDestination(id, val, pos);\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "issuance1";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.nonceHash);\r
+        mustWriteForHash(out, this.assetAmount);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Mux.java b/tx-signer/src/main/java/io/bytom/types/Mux.java
new file mode 100755 (executable)
index 0000000..5c47cf0
--- /dev/null
@@ -0,0 +1,34 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public class Mux extends Entry {\r
+\r
+    public ValueSource[] sources;\r
+\r
+    public Program program;\r
+\r
+    public List<ValueDestination> witnessDestinations = new ArrayList<>();\r
+\r
+    public Mux() {\r
+    }\r
+\r
+    public Mux(ValueSource[] sources, Program program) {\r
+        this();\r
+        this.sources = sources;\r
+        this.program = program;\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "mux1";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.sources);\r
+        mustWriteForHash(out, this.program);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Output.java b/tx-signer/src/main/java/io/bytom/types/Output.java
new file mode 100755 (executable)
index 0000000..a02b01c
--- /dev/null
@@ -0,0 +1,35 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class Output extends Entry {\r
+\r
+    public ValueSource source;\r
+\r
+    public Program controlProgram;\r
+\r
+    public Integer ordinal;\r
+\r
+    public Output() {\r
+        this.source = new ValueSource();\r
+        this.controlProgram = new Program();\r
+    }\r
+\r
+\r
+    public Output(ValueSource source, Program controlProgram, Integer ordinal) {\r
+        this.source = source;\r
+        this.controlProgram = controlProgram;\r
+        this.ordinal = ordinal;\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "output1";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.source);\r
+        mustWriteForHash(out, this.controlProgram);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Program.java b/tx-signer/src/main/java/io/bytom/types/Program.java
new file mode 100755 (executable)
index 0000000..b2ec30e
--- /dev/null
@@ -0,0 +1,17 @@
+package io.bytom.types;\r
+\r
+\r
+public class Program {\r
+\r
+    public long vmVersion;\r
+\r
+    public byte[] code;\r
+\r
+    public Program() {\r
+    }\r
+\r
+    public Program(long vmVersion, byte[] code) {\r
+        this.vmVersion = vmVersion;\r
+        this.code = code;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Retirement.java b/tx-signer/src/main/java/io/bytom/types/Retirement.java
new file mode 100755 (executable)
index 0000000..7072078
--- /dev/null
@@ -0,0 +1,24 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class Retirement extends Entry {\r
+    public ValueSource valueSource;\r
+    public int ordinal;\r
+\r
+    public Retirement(ValueSource valueSource, int ordinal) {\r
+        this.valueSource = valueSource;\r
+        this.ordinal = ordinal;\r
+    }\r
+\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "retirement1";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, valueSource);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/Spend.java b/tx-signer/src/main/java/io/bytom/types/Spend.java
new file mode 100755 (executable)
index 0000000..283eb61
--- /dev/null
@@ -0,0 +1,33 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class Spend extends Entry {\r
+\r
+    public Hash spentOutputID;\r
+\r
+    public int ordinal;\r
+\r
+    public ValueDestination witnessDestination;\r
+\r
+    public byte[][] witnessArguments;\r
+\r
+    public Spend(Hash spentOutputID, int ordinal) {\r
+        this.spentOutputID = spentOutputID;\r
+        this.ordinal = ordinal;\r
+    }\r
+\r
+    public void setDestination(Hash id, AssetAmount val, long pos) {\r
+        this.witnessDestination = new ValueDestination(id, val, pos);\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "spend1";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.spentOutputID);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/TxHeader.java b/tx-signer/src/main/java/io/bytom/types/TxHeader.java
new file mode 100755 (executable)
index 0000000..2d05b86
--- /dev/null
@@ -0,0 +1,36 @@
+package io.bytom.types;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class TxHeader extends Entry {\r
+\r
+    public long version;\r
+\r
+    public long serializedSize;\r
+\r
+    public long timeRange;\r
+\r
+    public Hash[] resultIDs;\r
+\r
+    public TxHeader() {\r
+    }\r
+\r
+    public TxHeader(long version, long serializedSize, long timeRange, Hash[] resultIDs) {\r
+        this.version = version;\r
+        this.serializedSize = serializedSize;\r
+        this.timeRange = timeRange;\r
+        this.resultIDs = resultIDs;\r
+    }\r
+\r
+    @Override\r
+    public String typ() {\r
+        return "txheader";\r
+    }\r
+\r
+    @Override\r
+    public void writeForHash(ByteArrayOutputStream out) {\r
+        mustWriteForHash(out, this.version);\r
+        mustWriteForHash(out, this.timeRange);\r
+        mustWriteForHash(out, this.resultIDs);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/ValueDestination.java b/tx-signer/src/main/java/io/bytom/types/ValueDestination.java
new file mode 100755 (executable)
index 0000000..9dea32b
--- /dev/null
@@ -0,0 +1,19 @@
+package io.bytom.types;\r
+\r
+public class ValueDestination {\r
+\r
+    public Hash ref;\r
+\r
+    public AssetAmount value;\r
+\r
+    public long position;\r
+\r
+    public ValueDestination() {\r
+    }\r
+\r
+    public ValueDestination(Hash ref, AssetAmount value, long position) {\r
+        this.ref = ref;\r
+        this.value = value;\r
+        this.position = position;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/types/ValueSource.java b/tx-signer/src/main/java/io/bytom/types/ValueSource.java
new file mode 100755 (executable)
index 0000000..7cfd242
--- /dev/null
@@ -0,0 +1,21 @@
+package io.bytom.types;\r
+\r
+\r
+public class ValueSource {\r
+\r
+    public Hash ref;\r
+\r
+    public AssetAmount value;\r
+\r
+    public long position;\r
+\r
+    public ValueSource() {\r
+    }\r
+\r
+    public ValueSource(Hash ref, AssetAmount value, long position) {\r
+        this.ref = ref;\r
+        this.value = value;\r
+        this.position = position;\r
+    }\r
+\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/util/OutputUtil.java b/tx-signer/src/main/java/io/bytom/util/OutputUtil.java
new file mode 100755 (executable)
index 0000000..3c46008
--- /dev/null
@@ -0,0 +1,47 @@
+package io.bytom.util;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+public final class OutputUtil {\r
+\r
+    public static void writeByte(OutputStream out, byte data) throws IOException {\r
+        out.write(data);\r
+    }\r
+\r
+    public static void writeLong(OutputStream out, long data) throws IOException {\r
+        for (int i = 0; i < 8; i++) {\r
+            out.write((byte) data);\r
+            data >>= 8;\r
+        }\r
+    }\r
+\r
+    public static void writeVarint(OutputStream out, long data) throws IOException {\r
+        byte[] buf = new byte[9];\r
+        int n = putUvarint(buf, data);\r
+        out.write(buf, 0, n);\r
+    }\r
+\r
+    public static void writeVarstr(OutputStream out, byte[] str) throws IOException {\r
+        writeVarint(out, str.length);\r
+        out.write(str);\r
+    }\r
+\r
+    public static void writeVarstrList(OutputStream out, byte[][] l) throws IOException {\r
+        writeVarint(out, l.length);\r
+        for (byte[] str : l) {\r
+            writeVarstr(out, str);\r
+        }\r
+    }\r
+\r
+    private static int putUvarint(byte[] buf, long x) {\r
+        int i = 0;\r
+        while (x >= 0x80) {\r
+            buf[i] = (byte) (x | 0x80);\r
+            x >>= 7;\r
+            i++;\r
+        }\r
+        buf[i] = (byte) x;\r
+        return i + 1;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/util/PathUtil.java b/tx-signer/src/main/java/io/bytom/util/PathUtil.java
new file mode 100755 (executable)
index 0000000..5a9b1e7
--- /dev/null
@@ -0,0 +1,54 @@
+package io.bytom.util;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+\r
+import java.nio.ByteBuffer;\r
+import java.nio.ByteOrder;\r
+\r
+public class PathUtil {\r
+    private static String intToPath(int index) {\r
+        byte[] result = new byte[4];\r
+        result[3] = (byte) (index >> 24 & 0xff);\r
+        result[2] = (byte) (index >> 16 & 0xff);\r
+        result[1] = (byte) (index >> 8 & 0xff);\r
+        result[0] = (byte) (index >> 0 & 0xff);\r
+        return Hex.toHexString(result);\r
+    }\r
+\r
+\r
+//  paths = {\r
+////            "2c000000",\r
+////            "99000000",\r
+////            "01000000",  accountIndex\r
+////            "00000000",  change\r
+////            "01000000"   controlProgramIndex\r
+////    }\r
+\r
+    public static byte[][] getBip44Path(int accountIndex, boolean change, int programIndex) {\r
+\r
+        String accountIndexStr = intToPath(accountIndex);\r
+        String changeStr = "00000000";\r
+        String programIndexStr = intToPath(programIndex);\r
+        if (change) {\r
+            changeStr = "01000000";\r
+        }\r
+        byte[][] paths = new byte[][]{\r
+                Hex.decode("2c000000"),\r
+                Hex.decode("99000000"),\r
+                Hex.decode(accountIndexStr),\r
+                Hex.decode(changeStr),\r
+                Hex.decode(programIndexStr),\r
+        };\r
+        return paths;\r
+    }\r
+\r
+    public static byte[][] getBip32Path(int accountIndex) {\r
+        byte[] signerPath = new byte[9];\r
+        byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putInt(accountIndex).array();\r
+        System.arraycopy(path, 0, signerPath, 1, 8);\r
+        byte[][] paths = new byte[][]{\r
+                signerPath\r
+        };\r
+        return paths;\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/java/io/bytom/util/SHA3Util.java b/tx-signer/src/main/java/io/bytom/util/SHA3Util.java
new file mode 100755 (executable)
index 0000000..1e8c1bc
--- /dev/null
@@ -0,0 +1,15 @@
+package io.bytom.util;\r
+\r
+import org.bouncycastle.jcajce.provider.digest.SHA3;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+public class SHA3Util {\r
+    public static byte[] hashSha256(byte[] hash) {\r
+        SHA3.Digest256 digest256 = new SHA3.Digest256();\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        out.write(hash, 0, hash.length);\r
+        byte[] data = out.toByteArray();\r
+        return digest256.digest(data);\r
+    }\r
+}\r
diff --git a/tx-signer/src/main/resources/config.properties b/tx-signer/src/main/resources/config.properties
new file mode 100755 (executable)
index 0000000..6a9d98b
--- /dev/null
@@ -0,0 +1,6 @@
+bytom.api.url=\r
+client.access.token=\r
+# bytom.api.url=http://10.100.7.47:9888/\r
+# client.access.token=wt:3d17dbb953cedd53353bf3f342bb2929e9505105ffeb21670e6bd00abeef3772\r
+#bytom.api.url=http://127.0.0.1:9888/\r
+#client.access.token=sheng:49d1623f5991c62a5094e761477ddd2838dceb49c22fbf84b492a54f1df88123\r
diff --git a/tx-signer/src/main/resources/log4j.properties b/tx-signer/src/main/resources/log4j.properties
new file mode 100755 (executable)
index 0000000..e118e26
--- /dev/null
@@ -0,0 +1,15 @@
+log4j.rootLogger=debug, stdout, R\r
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender\r
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout\r
+log4j.logger.org.apache.commons.httpclient=info\r
+log4j.logger.httpclient.wire.content=info\r
+log4j.logger.httpclient.wire.header=info\r
+# Pattern to output the caller's file name and line number.\r
+log4j.appender.stdout.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}]  %m%n\r
+log4j.appender.R=org.apache.log4j.RollingFileAppender\r
+log4j.appender.R.File=bytom.log\r
+log4j.appender.R.MaxFileSize=1000KB\r
+# Keep one backup file\r
+log4j.appender.R.MaxBackupIndex=1\r
+log4j.appender.R.layout=org.apache.log4j.PatternLayout\r
+log4j.appender.R.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}]  %m%n
\ No newline at end of file
diff --git a/tx-signer/src/test/java/io/bytom/AppTest.java b/tx-signer/src/test/java/io/bytom/AppTest.java
new file mode 100755 (executable)
index 0000000..6bf1fab
--- /dev/null
@@ -0,0 +1,153 @@
+package io.bytom;\r
+\r
+import io.bytom.api.*;\r
+import io.bytom.exception.BytomException;\r
+import io.bytom.http.Client;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class AppTest {\r
+    String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+    String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";\r
+\r
+\r
+    @Test\r
+    public void testSpend() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        Transaction.AnnotatedInput input = btmUtxoToInput();\r
+        Transaction Transaction = new Transaction.Builder()\r
+//                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
+//                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
+                .addInput(input.setType(1))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(170000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("0020fa56ca7d47f8528e68e120d0e052885faeb9d090d238fa4266bdde21b137513c"))\r
+                .build(200000);\r
+        io.bytom.api.Transaction transaction = MapTransaction.mapTx(Transaction);\r
+        SignTransaction signTransaction = new SignTransaction();\r
+        String rawTransaction = signTransaction.rawTransaction(rootKey, transaction);\r
+        System.out.println(rawTransaction);\r
+    }\r
+\r
+    //issue asset\r
+    @Test\r
+    public void testIssue() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+\r
+        String issueAssetId = "a680606d49daae62ef9cb03263ca82a0b1e3184bb6311ea52a5189207f718789";\r
+        String program = "ae204cae24c2cec15491e70fc554026469496e373df9b9970b23acac8b782da0822d5151ad";\r
+        String assetDefine = "7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d";\r
+        Transaction Transaction = new Transaction.Builder()\r
+                .addInput(new Transaction.AnnotatedInput().setType(0).setAssetId(issueAssetId).setControlProgram(program).setAmount(100000000).setAssetDefinition(assetDefine).setChange(false).setKeyIndex(13))\r
+                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
+                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(issueAssetId).setAmount(100000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(870000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
+                .build(2000000);\r
+        MapTransaction.mapTx(Transaction);\r
+        SignTransaction sign = new SignTransaction();\r
+        String rawTransaction = sign.rawTransaction(rootKey, Transaction);\r
+        System.out.println(rawTransaction);\r
+    }\r
+\r
+\r
+    //retire asset\r
+    @Test\r
+    public void testRetire() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String assetId1 = "8e962912423d8aea3409d1754782e7910da81b66c640aece14a6dac238f38e9b";\r
+        Transaction transaction = new Transaction.Builder()\r
+                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(890000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
+                        setChange(false).setControlProgramIndex(2).setSourceId("6026b1acb11f3cbfb5280865ea857117a76d41ba7d60509dd358648424d8496a").setSourcePosition(0))\r
+                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(assetId1).setAmount(100000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
+                        setChange(false).setControlProgramIndex(2).setSourceId("d53e7ccfa06d3b27933a44e5d6e3e288edfc7e38f5396b8552ef44cf58a20347").setSourcePosition(0))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
+                //arbitrary 加在retire control program 后面\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(assetId1).setAmount(100000000).setControlProgram("6a"))\r
+                .build(2000000);\r
+        MapTransaction.mapTx(transaction);\r
+        SignTransaction sign = new SignTransaction();\r
+        String rawTransaction = sign.rawTransaction(rootKey, transaction);\r
+        System.out.println(rawTransaction);\r
+    }\r
+\r
+\r
+    //utxo\r
+    private Transaction.AnnotatedInput btmUtxoToInput() {\r
+        String utxoJson = "{\n" +\r
+                "  \"id\": \"cf2f5c7340490d33d535a680dc8d95bb66fcccbf1045706484621cc067b982ae\",\n" +\r
+                "  \"amount\": 70000000,\n" +\r
+                "  \"address\": \"tm1qf4g97wae3fsz973huwrjqnd68v530nmd7n2pc43zfpw9tvd30qfsd7jl8p\",\n" +\r
+                "  \"program\": \"00204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b17813\",\n" +\r
+                "  \"change\": false,\n" +\r
+                "  \"highest\": 140925,\n" +\r
+                "  \"account_alias\": \"mutiaccout\",\n" +\r
+                "  \"asset_id\": \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\n" +\r
+                "  \"asset_alias\": \"BTM\",\n" +\r
+                "  \"account_id\": \"0PCB0S1GG0A02\",\n" +\r
+                "  \"control_program_index\": 2,\n" +\r
+                "  \"source_id\": \"9b29c72a653f986d5c5a7bf16c0fe63a9f639a0d15f3faeabeb4c14df70bbd91\",\n" +\r
+                "  \"source_pos\": 0,\n" +\r
+                "  \"valid_height\": 0,\n" +\r
+                "  \"derive_rule\": 0\n" +\r
+                "}";\r
+        UTXO utxo = UTXO.fromJson(utxoJson);\r
+        Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo);\r
+        return input;\r
+    }\r
+\r
+    private Transaction.AnnotatedInput otherAssetUtxoToInput() {\r
+        String utxoJson = "\n" +\r
+                "{\n" +\r
+                "  \"id\": \"a1b729714a9ad74108999b6d2f3628c6eea2bd41d7132fca93f56c3f7c12904e\",\n" +\r
+                "  \"amount\": 89900000000,\n" +\r
+                "  \"address\": \"tm1qstadkjjxp7nn3eegdxc38nvur82z37ek2pq2ul\",\n" +\r
+                "  \"program\": \"001482fadb4a460fa738e72869b113cd9c19d428fb36\",\n" +\r
+                "  \"change\": true,\n" +\r
+                "  \"highest\": 139905,\n" +\r
+                "  \"account_alias\": \"wyjbtm\",\n" +\r
+                "  \"asset_id\": \"207265909236260b30942a6b00e30ceb769e0e58156b6482bac64117619c9dcf\",\n" +\r
+                "  \"asset_alias\": \"TEST3\",\n" +\r
+                "  \"account_id\": \"0NNSS39M00A02\",\n" +\r
+                "  \"control_program_index\": 32,\n" +\r
+                "  \"source_id\": \"2886e635442f2f003e1d211f3264520ffb5239944d718bc834925a0cc0798980\",\n" +\r
+                "  \"source_pos\": 1,\n" +\r
+                "  \"valid_height\": 0,\n" +\r
+                "  \"derive_rule\": 0\n" +\r
+                "}";\r
+        UTXO utxo = UTXO.fromJson(utxoJson);\r
+        Transaction.AnnotatedInput input = UTXO.utxoToAnnotatedInput(utxo);\r
+        return input;\r
+    }\r
+\r
+    // submit rawTransaction\r
+    @Test\r
+    public void SubmitTransaction() throws BytomException {\r
+        Client client = TestUtil.generateClient();\r
+        String raw = "0701c09a0c01016b01699b29c72a653f986d5c5a7bf16c0fe63a9f639a0d15f3faeabeb4c14df70bbd91ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80bbb02100012200204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b17813ac020440290bc8593d429d5c7d02f96232cf8035d3776eebf2a7e855c906cdbcf35281f98575624e326fbf1f9e8de70a7047b5af2f43c1d102fcf632d1544cff46aa970540ff237b6d2c5760bf633b640e120600d06e57ab3119d192b33894967a74293531507df473235233033f12c493bc005c8d1e2f858524a431edc7d522891dfada04406ebebe5d380e129fb212a777aca0f971cfe38a2e54cd9b822459609664303aa2e10caa10f5cb8eae4a53688f895a9cbe13e5be085dd289251c63a7c86718730f67ae204e4fcad6d69dfbad1fa83c37e6fd2031476940b44a9165fa79084e5a4d9acaed20415a96ffead835222ee96eda7c16e99a4c87e8e5e43e54d2d493855f707baf78209613d3c8a4aa730bb24e0324a920b2e3c8db7260ded4527f6d6d7d14d86b64385353ad020148ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80e1eb17012200204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b1781300013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ade2040116001414d362694eacfa110dc20dec77d610d22340f95b00";\r
+        SubmitTransaction.SubmitResponse submitResponse = SubmitTransaction.submitRawTransaction(client, raw);\r
+        System.out.println(submitResponse.tx_id);\r
+    }\r
+\r
+\r
+    //单输入多签\r
+    @Test\r
+    public void testMutiSpend(){\r
+        Transaction.AnnotatedInput input = btmUtxoToInput();\r
+        Transaction Transaction = new Transaction.Builder()\r
+//                .addInput(new Transaction.AnnotatedInput().setType(1).setAssetId(btmAssetID).setAmount(880000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b").\r
+//                        setChange(false).setControlProgramIndex(2).setSourceId("fc43933d1c601b2503b033e31d3bacfa5c40ccb2ff0be6e94d8332462e0928a3").setSourcePosition(0))\r
+                .addInput(input.setType(1))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(50000000).setControlProgram("00204d505f3bb98a6022fa37e387204dba3b2917cf6df4d41c5622485c55b1b17813"))\r
+                .addOutput(new Transaction.AnnotatedOutput().setAssetId(btmAssetID).setAmount(10000000).setControlProgram("001414d362694eacfa110dc20dec77d610d22340f95b"))\r
+                .build(200000);\r
+        Transaction transaction = MapTransaction.mapTx(Transaction);\r
+        SignTransaction signTransaction = new SignTransaction();\r
+        String[] rootKeys = new String[3];\r
+        rootKeys[0] = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+        rootKeys[1] = "50a23bf6200b8a98afc049a7d0296a619e2ee27fa0d6d4d271ca244b280b324347627e543cc079614642c7b88c78ce38092430b01d124663e8b84026aefefde1";\r
+        rootKeys[2] = "00e4bf1251fb5aa37aa2a11dec6c0db5cec3f17aa312dbddb30e06957a32ae503ebcdfd4ad5e29be21ee9ec336e939eb72439cf6d99c785268c8f3d71c1be877";\r
+        signTransaction.buildWitness(transaction, 0, rootKeys);\r
+        String raw = signTransaction.serializeTransaction(transaction);\r
+        System.out.println(raw);\r
+    }\r
+}\r
diff --git a/tx-signer/src/test/java/io/bytom/TestUtil.java b/tx-signer/src/test/java/io/bytom/TestUtil.java
new file mode 100755 (executable)
index 0000000..02fefb8
--- /dev/null
@@ -0,0 +1,27 @@
+package io.bytom;\r
+\r
+import io.bytom.common.Configuration;\r
+import io.bytom.exception.BytomException;\r
+import io.bytom.http.Client;\r
+import org.apache.log4j.Logger;\r
+\r
+public class TestUtil {\r
+    public static Logger logger = Logger.getLogger(TestUtil.class);\r
+\r
+    public static Client generateClient() throws BytomException {\r
+\r
+        String coreURL = Configuration.getValue("bytom.api.url");\r
+        String accessToken = Configuration.getValue("client.access.token");\r
+\r
+        if (coreURL == null || coreURL.isEmpty()) {\r
+            coreURL = "http://127.0.0.1:9888";\r
+        }\r
+\r
+        if (coreURL.endsWith("/")) {\r
+            //split the last char "/"\r
+            coreURL = coreURL.substring(0, coreURL.length() - 1);\r
+        }\r
+\r
+        return new Client(coreURL, accessToken);\r
+    }\r
+}\r
diff --git a/tx-signer/src/test/java/io/bytom/api/SignTransactionTest.java b/tx-signer/src/test/java/io/bytom/api/SignTransactionTest.java
new file mode 100755 (executable)
index 0000000..9d6d31e
--- /dev/null
@@ -0,0 +1,132 @@
+\r
+package io.bytom.api;\r
+\r
+import io.bytom.types.*;\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+\r
+/**\r
+ * Created by liqiang on 2018/10/24.\r
+ */\r
+public class SignTransactionTest {\r
+\r
+    //以下为测试用的区块上的交易utxo,即output中第二个输出\r
+    //新交易接收地址为bm1qdpc5sejdkm22uv23jwd8pg6lyqz2trz4trgxh0,需要找零\r
+    /*{\r
+        "id": "3b36453f7dc03b13523d6431afd7e544f60339daed52ba8fca7ebf88cd5e5939",\r
+            "version": 1,\r
+            "size": 330,\r
+            "time_range": 0,\r
+            "inputs": [\r
+        {\r
+            "type": "spend",\r
+                "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",\r
+                "asset_definition": {},\r
+            "amount": 482000000,\r
+                "control_program": "00148da6ccbc216f9019cf80d23fd2083c80e29fcba2",\r
+                "address": "bm1q3knve0ppd7gpnnuq6glayzpusr3fljazzcq0eh",\r
+                "spent_output_id": "d11967ce15741217c650bc0b9dd7a390aaedd8ea5c645266920a7d19d8be681a",\r
+                "input_id": "caae7c37f6cecce6854e6488cc389379e312acd2f7495337633501fc7f72b5f3"\r
+        }\r
+        ],\r
+        "outputs": [\r
+        {\r
+            "type": "control",\r
+                "id": "3110bc8e7d713c17fb3dc3c9deadbfc419a25c25252c8e613d1fa54cc4d05dbd",\r
+                "position": 0,\r
+                "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",\r
+                "asset_definition": {},\r
+            "amount": 281500000,\r
+                "control_program": "00145d6ba5bf0cfdb2487abd594429cd04c2ba566f9f",\r
+                "address": "bm1qt446t0cvlkeys74at9zznngyc2a9vmulcr2xy6"\r
+        },\r
+        {\r
+            "type": "control",\r
+                "id": "db5afebb5b33aec2c46fcebb20b98fffa8c065a101f4c1789fe5491b34dc1b8f",\r
+                "position": 1,\r
+                "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",\r
+                "asset_definition": {},\r
+            "amount": 200000000,\r
+                "control_program": "00140d074bc86bd388a45f1c8911a41b8f0705d9058b",\r
+                "address": "bm1qp5r5hjrt6wy2ghcu3yg6gxu0quzajpvtsm2gnc"\r
+        }\r
+        ],\r
+        "status_fail": false,\r
+            "mux_id": "0e97230a7347967764fd77c8cfa96b38ec6ff08465300a01900c645dfb694f24"\r
+    }*/\r
+\r
+\r
+    @Test\r
+    public void testMustWriteForHash() throws Exception {\r
+        Entry entry = new Entry() {\r
+            @Override\r
+            public String typ() {\r
+                return null;\r
+            }\r
+\r
+            @Override\r
+            public void writeForHash(ByteArrayOutputStream out) {\r
+\r
+            }\r
+        };\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        entry.mustWriteForHash(out, (byte) 2);\r
+        assert Hex.toHexString(out.toByteArray()).equals("02");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, 1L);\r
+        assert Hex.toHexString(out.toByteArray()).equals("0100000000000000");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, 0x3456584738473837L);\r
+        assert Hex.toHexString(out.toByteArray()).equals("3738473847585634");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, new byte[]{0x12, 0x34, (byte) 0x85});\r
+        assert Hex.toHexString(out.toByteArray()).equals("03123485");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, new byte[][]{{0x12, 0x34, (byte) 0x85}, {(byte) 0x86, 0x17, 0x40}});\r
+        assert Hex.toHexString(out.toByteArray()).equals("020312348503861740");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, "hello, 世界");\r
+        assert Hex.toHexString(out.toByteArray()).equals("0d68656c6c6f2c20e4b896e7958c");\r
+\r
+        out.reset();\r
+        entry.mustWriteForHash(out, new String[]{"hi", "你好", "hello"});\r
+        assert Hex.toHexString(out.toByteArray()).equals("0302686906e4bda0e5a5bd0568656c6c6f");\r
+\r
+        out.reset();\r
+        String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f";\r
+        entry.mustWriteForHash(out, new Hash(hash));\r
+        assert Hex.toHexString(out.toByteArray()).equals(hash);\r
+\r
+        out.reset();\r
+        ValueSource valueSource = new ValueSource(new Hash(hash), null, 1);\r
+        Program program = new Program(1, new byte[]{1});\r
+        Mux mux = new Mux();\r
+        mux.sources = new ValueSource[]{valueSource};\r
+        mux.program = program;\r
+        entry.mustWriteForHash(out, mux);\r
+        assert Hex.toHexString(out.toByteArray()).equals("01d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f010000000000000001000000000000000101");\r
+    }\r
+\r
+    @Test\r
+    public void testEntryID() throws Exception {\r
+        String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f";\r
+        ValueSource valueSource = new ValueSource(new Hash(hash), null, 1);\r
+        Program program = new Program(1, new byte[]{1});\r
+        Mux mux = new Mux();\r
+        mux.sources = new ValueSource[]{valueSource};\r
+        mux.program = program;\r
+        String entryID = mux.entryID().toString();\r
+        assert entryID.equals("ebd967df33a3373ab85521fba24c22bf993c73f46fa96254b0c86646093184e9");\r
+    }\r
+\r
+}\r
+\r
+\r
diff --git a/tx-signer/src/test/java/io/bytom/api/SignerTest.java b/tx-signer/src/test/java/io/bytom/api/SignerTest.java
new file mode 100755 (executable)
index 0000000..52ef740
--- /dev/null
@@ -0,0 +1,24 @@
+package io.bytom.api;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class SignerTest {\r
+\r
+    @Test\r
+    public void testEd25519InnerSign() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String rootXprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";\r
+        String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";\r
+        String expandedXprv = "20849f14bbe212d1b8917da2e1eda9afc4c21e9dd0a47f1e169a3326d45ae443236f54b987369e86ed78eb2b0a2def89a69ec69ca1059e2efe045796dc583d91";\r
+        String hashedMessage = "99ab9ebdba106466371467b036d56a0e54ad2a6035e365a6103ba97ab553fd52";\r
+        byte[] sig = Signer.Ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage));\r
+        System.out.println("sig:" + Hex.toHexString(sig));\r
+        //expected: 38b11090e8dd5372018acc24ea4db2c3d82cf01ed5c69a0fae95bff2379c1630f8c8f96937b22685142b4181e6ef5072e7945c101eb81814a20d90cb1d1f0c08\r
+        //          38b11090e8dd5372018acc24ea4db2c3d82cf01ed5c69a0fae95bff2379c1630f8c8f96937b22685142b4181e6ef5072e7945c101eb81814a20d90cb1d1f0c08\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/tx-signer/src/test/java/io/bytom/api/UTXOTest.java b/tx-signer/src/test/java/io/bytom/api/UTXOTest.java
new file mode 100755 (executable)
index 0000000..11249a2
--- /dev/null
@@ -0,0 +1,35 @@
+package io.bytom.api;\r
+\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class UTXOTest {\r
+    @Test\r
+    public void testJson() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String json = "{\n" +\r
+                "  \"id\": \"fda38648c553386c56b2f1276b908061b5d812341f0a96921abad8b2b2f28044\",\n" +\r
+                "  \"amount\": 1700000,\n" +\r
+                "  \"address\": \"tm1qhw9q89exmudkf9ecaxtnmv22fd8af0k07jq7u5\",\n" +\r
+                "  \"program\": \"0014bb8a039726df1b649738e9973db14a4b4fd4becf\",\n" +\r
+                "  \"change\": true,\n" +\r
+                "  \"highest\": 139744,\n" +\r
+                "  \"account_alias\": \"wyjbtm\",\n" +\r
+                "  \"asset_id\": \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\n" +\r
+                "  \"asset_alias\": \"BTM\",\n" +\r
+                "  \"account_id\": \"0NNSS39M00A02\",\n" +\r
+                "  \"control_program_index\": 26,\n" +\r
+                "  \"source_id\": \"34bc595dff3d40c2bd644e0ea0234e843ef8e3aa0720013a2cb712362cc5933f\",\n" +\r
+                "  \"source_pos\": 0,\n" +\r
+                "  \"valid_height\": 0,\n" +\r
+                "  \"derive_rule\": 0\n" +\r
+                "}";\r
+\r
+        UTXO utxo = UTXO.fromJson(json);\r
+        System.out.println(utxo.toJson());\r
+    }\r
+\r
+\r
+}\r
diff --git a/tx-signer/src/test/java/io/bytom/common/DerivePrivateKeyTest.java b/tx-signer/src/test/java/io/bytom/common/DerivePrivateKeyTest.java
new file mode 100755 (executable)
index 0000000..7834aa9
--- /dev/null
@@ -0,0 +1,35 @@
+package io.bytom.common;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class DerivePrivateKeyTest {\r
+    @Test\r
+    public void testBip44Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 1, false, 2);\r
+        System.out.println(Hex.toHexString(derivePrivateKey));\r
+        //expected 48c65f40d860723e71b03988a22edc9ad00ae0deae992e79fb3b812edb5c3e43e78065bf46d0e8ad922cdae600fd2c2a6239b8f1f504f8f255460c6fcce023ff\r
+    }\r
+\r
+    @Test\r
+    public void testBip32Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 2);\r
+        System.out.println(Hex.toHexString(derivePrivateKey));\r
+        //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc\r
+    }\r
+\r
+    @Test\r
+    public void testBip32PublicKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+        byte[] derivePrivateKey = DerivePrivateKey.derivePrivateKey(rootKey, 14);\r
+        byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey);\r
+        System.out.println(Hex.toHexString(deriveXpub).substring(0, 64));\r
+        //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc\r
+    }\r
+}\r
diff --git a/tx-signer/src/test/java/io/bytom/common/DeriveXpubTest.java b/tx-signer/src/test/java/io/bytom/common/DeriveXpubTest.java
new file mode 100755 (executable)
index 0000000..4d6738d
--- /dev/null
@@ -0,0 +1,17 @@
+package io.bytom.common;\r
+\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+public class DeriveXpubTest {\r
+\r
+    @Test\r
+    public void testDeriveXpub() {\r
+        String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";\r
+        byte[] xpub = DeriveXpub.deriveXpub(Hex.decode(hxprv));\r
+        System.out.println("hxpub: " + Hex.toHexString(xpub));\r
+        //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b\r
+        //          d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b\r
+    }\r
+}
\ No newline at end of file
diff --git a/tx-signer/src/test/java/io/bytom/common/ExpandedPrivateKeyTest.java b/tx-signer/src/test/java/io/bytom/common/ExpandedPrivateKeyTest.java
new file mode 100755 (executable)
index 0000000..2e0e4b9
--- /dev/null
@@ -0,0 +1,21 @@
+package io.bytom.common;\r
+\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class ExpandedPrivateKeyTest {\r
+\r
+    @Test\r
+    public void testExpandedKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954";\r
+        byte[] z = ExpandedPrivateKey.ExpandedPrivateKey(Hex.decode(childXprv));\r
+        System.out.println(Hex.toHexString(z));\r
+        //expect: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3\r
+        //        e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3\r
+    }\r
+}
\ No newline at end of file
diff --git a/tx-signer/src/test/java/io/bytom/common/NonHardenedChildTest.java b/tx-signer/src/test/java/io/bytom/common/NonHardenedChildTest.java
new file mode 100755 (executable)
index 0000000..6222cbb
--- /dev/null
@@ -0,0 +1,43 @@
+package io.bytom.common;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+\r
+public class NonHardenedChildTest {\r
+\r
+    @Test\r
+    public void testNHChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";\r
+        byte[] xprv = Hex.decode(hxprv);\r
+        //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b\r
+        String[] hpaths = {"010400000000000000", "0100000000000000"};\r
+        byte[][] paths = new byte[][]{\r
+                Hex.decode(hpaths[0]),\r
+                Hex.decode(hpaths[1])\r
+        };\r
+        byte[] res = xprv;\r
+        for (int i = 0; i < hpaths.length; i++) {\r
+            byte[] xpub = DeriveXpub.deriveXpub(res);\r
+//            System.out.println("xpub: "+Hex.toHexString(xpub));\r
+            res = NonHardenedChild.NHchild(paths[i], res, xpub);\r
+        }\r
+        System.out.println("res: " + Hex.toHexString(res));\r
+        //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
+        //          e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
+    }\r
+\r
+    @Test\r
+    public void testChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b";\r
+        String[] hpaths = {"010400000000000000", "0100000000000000"};\r
+        byte[] childXprv = NonHardenedChild.child(Hex.decode(hxprv), hpaths);\r
+        System.out.println("childXprv: " + Hex.toHexString(childXprv));\r
+        //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
+        //          e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954\r
+    }\r
+}
\ No newline at end of file
diff --git a/tx-signer/src/test/java/io/bytom/types/AssetTest.java b/tx-signer/src/test/java/io/bytom/types/AssetTest.java
new file mode 100755 (executable)
index 0000000..8eea2c5
--- /dev/null
@@ -0,0 +1,36 @@
+package io.bytom.types;\r
+\r
+import com.google.gson.Gson;\r
+import org.junit.Test;\r
+\r
+import java.security.InvalidKeyException;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.SignatureException;\r
+\r
+public class AssetTest {\r
+    String rootkey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4";\r
+\r
+    @Test\r
+    public void testCreateAsset() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
+        Object o = new Object();\r
+        Asset.Definition definition = new Asset.Definition();\r
+\r
+//        "definition": {\r
+//            "decimals": 8,\r
+//            "description": "Bytom Official Issue",\r
+//            "name": "BTM",\r
+//            "symbol": "BTM"\r
+//        }\r
+        definition.setDecimals(8);\r
+        //{}\r
+        definition.setDescription(o);\r
+        definition.setName("");\r
+        definition.setSymbol("");\r
+\r
+        Asset asset = new Asset();\r
+        asset.CreateAsset(definition, 2, rootkey);\r
+        Gson gson = new Gson();\r
+        String s = gson.toJson(asset);\r
+        System.out.println(s);\r
+    }\r
+}\r
diff --git a/tx-signer/src/test/java/io/bytom/util/SHA3256Test.java b/tx-signer/src/test/java/io/bytom/util/SHA3256Test.java
new file mode 100755 (executable)
index 0000000..b712d16
--- /dev/null
@@ -0,0 +1,17 @@
+package io.bytom.util;\r
+\r
+import org.bouncycastle.util.encoders.Hex;\r
+import org.junit.Test;\r
+\r
+public class SHA3256Test {\r
+    @Test\r
+    public void testSHA3Hash() {\r
+        String rawTransaction = "7b0a202022646563696d616c73223a20382c0a202022646573637" +\r
+                "2697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d";\r
+        byte[] bytes = SHA3Util.hashSha256(Hex.decode(rawTransaction));\r
+        System.out.println(Hex.toHexString(bytes));\r
+        //expected 69ab19b3907f40e4f264dbd3f71967654e0e93f836026918af8861932ed0409b\r
+    }\r
+\r
+\r
+}\r