--- /dev/null
+<?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
</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>
--- /dev/null
+# 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
--- /dev/null
+<?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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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 & 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 & 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+\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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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