├── .gitignore ├── README.md ├── java-sdk ├── README.md ├── doc │ ├── index.md │ └── transactions.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── bytom │ │ │ ├── api │ │ │ ├── AccessToken.java │ │ │ ├── Account.java │ │ │ ├── Asset.java │ │ │ ├── Balance.java │ │ │ ├── Block.java │ │ │ ├── CoreConfig.java │ │ │ ├── Key.java │ │ │ ├── Message.java │ │ │ ├── RawTransaction.java │ │ │ ├── Receiver.java │ │ │ ├── Transaction.java │ │ │ ├── UnconfirmedTransaction.java │ │ │ ├── UnspentOutput.java │ │ │ └── Wallet.java │ │ │ ├── common │ │ │ ├── Configuration.java │ │ │ ├── ParameterizedTypeImpl.java │ │ │ └── Utils.java │ │ │ ├── exception │ │ │ ├── APIException.java │ │ │ ├── BadURLException.java │ │ │ ├── BuildException.java │ │ │ ├── BytomException.java │ │ │ ├── ConfigurationException.java │ │ │ ├── ConnectivityException.java │ │ │ ├── HTTPException.java │ │ │ └── JSONException.java │ │ │ └── http │ │ │ ├── BatchResponse.java │ │ │ ├── Client.java │ │ │ └── SuccessMessage.java │ └── resources │ │ ├── config.properties │ │ └── log4j.properties │ └── test │ └── java │ └── io │ └── bytom │ ├── AppTest.java │ ├── TestUtils.java │ └── integration │ ├── AccessTokenTest.java │ ├── AccountTest.java │ ├── AssetTest.java │ ├── BalanceTest.java │ ├── BlockTest.java │ ├── CoreConfigTest.java │ ├── KeyTest.java │ ├── MessageTest.java │ ├── RawTransactionTest.java │ ├── TransactionTest.java │ ├── UTXOTest.java │ ├── UnconfirmedTransactionTest.java │ └── WalletTest.java ├── pom.xml └── tx-signer ├── README.md ├── README_zh.md ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── google │ │ │ └── crypto │ │ │ └── tink │ │ │ └── subtle │ │ │ ├── Bytes.java │ │ │ ├── Curve25519.java │ │ │ ├── Ed25519.java │ │ │ ├── Ed25519Constants.java │ │ │ └── Field25519.java │ └── io │ │ └── bytom │ │ └── offline │ │ ├── api │ │ ├── BIPProtocol.java │ │ ├── BaseInput.java │ │ ├── IssuanceInput.java │ │ ├── Output.java │ │ ├── SpendInput.java │ │ ├── Transaction.java │ │ └── WitnessComponent.java │ │ ├── common │ │ ├── DerivePrivateKey.java │ │ ├── DeriveXpub.java │ │ ├── ExpandedPrivateKey.java │ │ ├── NonHardenedChild.java │ │ ├── Signer.java │ │ ├── Utils.java │ │ └── VMUtil.java │ │ ├── exception │ │ ├── MapTransactionException.java │ │ ├── SerializeTransactionException.java │ │ ├── SignTransactionException.java │ │ └── WriteForHashException.java │ │ ├── types │ │ ├── AssetAmount.java │ │ ├── AssetDefinition.java │ │ ├── AssetID.java │ │ ├── Entry.java │ │ ├── ExpandedKeys.java │ │ ├── Hash.java │ │ ├── InputEntry.java │ │ ├── Issue.java │ │ ├── Mux.java │ │ ├── OutputEntry.java │ │ ├── Program.java │ │ ├── Retirement.java │ │ ├── Spend.java │ │ ├── TxHeader.java │ │ ├── ValueDestination.java │ │ └── ValueSource.java │ │ └── util │ │ ├── OutputUtil.java │ │ ├── PathUtil.java │ │ └── SHA3Util.java └── resources │ ├── config.properties │ └── log4j.properties └── test └── java └── io └── bytom └── offline ├── api ├── AppTest.java ├── SignTransactionTest.java └── SignerTest.java ├── common ├── DerivePrivateKeyTest.java ├── DeriveXpubTest.java ├── ExpandedPrivateKeyTest.java └── NonHardenedChildTest.java └── util └── SHA3256Test.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target/ 4 | bytom.log 5 | bytom.log.* 6 | javadoc/ 7 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Bytom Java SDK 2 | 3 | Bytom java sdk contains two modules: 4 | 5 | 1. java-sdk encapsulates of the request data and return value of the bytom api,The signature of the transaction needs to be completed online.See the [java-sdk documentation](java-sdk/README.md) for details. 6 | 7 | 2. tx-signer implementation offline signature of transaction, see the [tx-signer documentation](tx-signer/README.md) for details. 8 | 9 | -------------------------------------------------------------------------------- /java-sdk/README.md: -------------------------------------------------------------------------------- 1 | # Bytom java-sdk 2 | 3 | This page will document the API classes and ways to properly use the API. 4 | Subsequent new releases also maintain backward compatibility with this class 5 | approach. For more information, please see Bytom API reference documentation 6 | at [Bytom wiki](https://github.com/Bytom/bytom/wiki/API-Reference) 7 | 8 | ## Installation 9 | 10 | There are various ways to install and use this sdk. We'll provide three ways to get it. Note that the bytom-sdk requires JAVA 7 or newer. 11 | 12 | ### Apache Maven 13 | 14 | ```xml 15 | 16 | io.bytom 17 | java-sdk 18 | 1.0.2 19 | 20 | ``` 21 | 22 | ### Gradle/Grails 23 | ```xml 24 | compile 'io.bytom:bytom-sdk:1.0.2' 25 | ``` 26 | 27 | ### Building from source code 28 | 29 | To clone, compile, and install in your local maven repository (or copy the artifacts from the target/ directory to wherever you need them): 30 | 31 | ```shell 32 | git clone https://github.com/Bytom/bytom-java-sdk.git 33 | cd java-sdk 34 | mvn package -Dmaven.test.skip=true 35 | mvn install 36 | ``` 37 | 38 | ## Basic Usage 39 | 40 | ```java 41 | public static Client generateClient() throws BytomException { 42 | String coreURL = Configuration.getValue("bytom.api.url"); 43 | String accessToken = Configuration.getValue("client.access.token"); 44 | if (coreURL == null || coreURL.isEmpty()) { 45 | coreURL = "http://127.0.0.1:9888/"; 46 | } 47 | return new Client(coreURL, accessToken); 48 | } 49 | 50 | Client client = Client.generateClient(); 51 | ``` 52 | > Note: you can touch a file named ```config.properties``` in resources folder to config ```bytom.api.url``` and ```client.access.token``` by custom. 53 | 54 | ## Usage 55 | 56 | * [`Step 1: Create a key`](#create-a-key) 57 | * [`Step 2: Create an account`](#create-an-account) 58 | * [`Step 3: Create an receiver`](#create-an-receiver) 59 | * [`Step 4: Create an asset`](#create-an-asset) 60 | * [`Step 5: Issue asset`](#issue-asset) 61 | * [`Firstly build the transaction`](#firstly-build-the-transaction) 62 | * [`Secondly sign the transaction`](#secondly-sign-the-transaction) 63 | * [`Finally submit the transaction`](#finally-submit-the-transaction) 64 | 65 | > For more details, see [API methods](https://github.com/Bytom/java-sdk/blob/master/doc/index.md#api-methods) 66 | 67 | ### Create a key 68 | 69 | ```java 70 | String alias = "test"; 71 | String password = "123456"; 72 | 73 | Key.Builder builder = new Key.Builder().setAlias(alias).setPassword(password); 74 | Key key = Key.create(client, builder); 75 | ``` 76 | 77 | ### Create an account 78 | 79 | ```java 80 | String alias = "sender-account"; 81 | Integer quorum = 1; 82 | List root_xpubs = new ArrayList(); 83 | root_xpubs.add(senderKey.xpub); 84 | 85 | Account.Builder builder = new Account.Builder().setAlias(alias).setQuorum(quorum).setRootXpub(root_xpubs); 86 | 87 | Account account = Account.create(client, builder); 88 | ``` 89 | 90 | ### Create an receiver 91 | 92 | ```java 93 | String alias = receiverAccount.alias; 94 | String id = receiverAccount.id; 95 | 96 | Account.ReceiverBuilder receiverBuilder = new Account.ReceiverBuilder().setAccountAlias(alias).setAccountId(id); 97 | Receiver receiver = receiverBuilder.create(client); 98 | ``` 99 | 100 | ### Create an asset 101 | 102 | ```java 103 | String alias = "receiver-asset"; 104 | 105 | List xpubs = receiverAccount.xpubs; 106 | 107 | Asset.Builder builder = new Asset.Builder() 108 | .setAlias(alias) 109 | .setQuorum(1) 110 | .setRootXpubs(xpubs); 111 | receiverAsset = builder.create(client); 112 | ``` 113 | 114 | ### Issue asset 115 | 116 | For more transaction details, see [transactions](https://github.com/Bytom/java-sdk/blob/master/doc/transactions.md) 117 | 118 | #### Firstly build the transaction 119 | 120 | ```java 121 | Transaction.Template controlAddress = new Transaction.Builder() 122 | .addAction( 123 | new Transaction.Action.SpendFromAccount() 124 | .setAccountId(senderAccount.id) 125 | .setAssetId(senderAsset.id) 126 | .setAmount(300000000) 127 | ) 128 | .addAction( 129 | new Transaction.Action.ControlWithAddress() 130 | .setAddress(receiverAddress.address) 131 | .setAssetId(senderAsset.id) 132 | .setAmount(200000000) 133 | ).build(client); 134 | ``` 135 | 136 | #### Secondly sign the transaction 137 | 138 | ```java 139 | Transaction.Template singer = new Transaction.SignerBuilder().sign(client, 140 | controlAddress, "123456"); 141 | ``` 142 | 143 | #### Finally submit the transaction 144 | 145 | ```java 146 | Transaction.SubmitResponse txs = Transaction.submit(client, singer); 147 | ``` 148 | 149 | 150 | ### All usage examples 151 | 152 | For more details you can see [doc](https://github.com/Bytom/bytom-java-sdk/blob/master/doc/index.md#api-methods). And you can find Test Cases at [Junit Test Cases](https://github.com/Bytom/bytom-java-sdk/tree/master/src/test/java/io/bytom/integration) 153 | 154 | ## Support and Feedback 155 | 156 | If you find a bug, please submit the issue in Github directly by using [Issues](https://github.com/Bytom/bytom-java-sdk/issues) 157 | 158 | ## License 159 | 160 | Bytom JAVA SDK is based on the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) protocol. 161 | -------------------------------------------------------------------------------- /java-sdk/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bytom-sdk 7 | io.bytom 8 | 1.0.2 9 | 10 | 4.0.0 11 | 12 | java-sdk 13 | 1.0.2 14 | 15 | 16 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/AccessToken.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.ParameterizedTypeImpl; 5 | import io.bytom.common.Utils; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.apache.log4j.Logger; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | *

AccessToken Class

17 | */ 18 | public class AccessToken { 19 | /** 20 | * Token id 21 | */ 22 | public String id; 23 | /** 24 | * Token token 25 | */ 26 | public String token; 27 | /** 28 | * Token type 29 | */ 30 | public String type; 31 | /** 32 | * create time of token 33 | */ 34 | @SerializedName(value = "created_at", alternate = {"create"}) 35 | public String createTime; 36 | 37 | private static Logger logger = Logger.getLogger(AccessToken.class); 38 | 39 | /** 40 | * Serializes the AccessToken into a form that is safe to transfer over the wire. 41 | * 42 | * @return the JSON-serialized representation of the AccessToken object 43 | */ 44 | public String toJson() { 45 | return Utils.serializer.toJson(this); 46 | } 47 | 48 | public static class Builder { 49 | /** 50 | * id of Token 51 | */ 52 | public String id; 53 | /** 54 | * type of Token 55 | */ 56 | public String type; 57 | 58 | public Builder() { 59 | } 60 | 61 | /** 62 | * @param id the id to set 63 | * @return Builder 64 | */ 65 | public Builder setId(String id) { 66 | this.id = id; 67 | return this; 68 | } 69 | 70 | /** 71 | * @param type the type to set, possibly null 72 | * @return Builder 73 | */ 74 | public Builder setType(String type) { 75 | this.type = type; 76 | return this; 77 | } 78 | 79 | /** 80 | * Call create-access-token api 81 | * 82 | * @param client client object that makes requests to the core 83 | * @return AccessToken object 84 | * @throws BytomException 85 | */ 86 | public AccessToken create(Client client) throws BytomException { 87 | AccessToken accessToken = client.request("create-access-token", this, AccessToken.class); 88 | 89 | logger.info("create-access-token:"); 90 | logger.info(accessToken.toJson()); 91 | 92 | return accessToken; 93 | } 94 | } 95 | 96 | /** 97 | * Call check-access-token api 98 | * 99 | * @param client client object that makes requests to the core 100 | * @param id id 101 | * @param secret secret 102 | * @throws BytomException 103 | */ 104 | public static void check(Client client, String id, String secret) throws BytomException { 105 | Map req = new HashMap(); 106 | req.put("id", id); 107 | req.put("secret", secret); 108 | // add a native control 109 | if (client.getUrl().equals("http://127.0.0.1:9888") || 110 | client.getUrl().equals("http://127.0.0.1:9888/")) { 111 | client.request("check-access-token", req); 112 | logger.info("check-access-token successfully."); 113 | } else { 114 | logger.info("this is a native method."); 115 | } 116 | } 117 | 118 | /** 119 | * Call delete-access-token api 120 | * native method, can't rpc 121 | * 122 | * @param client client object that makes requests to the core 123 | * @param id id 124 | * @throws BytomException 125 | */ 126 | public static void delete(Client client, String id) throws BytomException { 127 | Map req = new HashMap(); 128 | req.put("id", id); 129 | // add a native control 130 | if (client.getUrl().equals("http://127.0.0.1:9888") || 131 | client.getUrl().equals("http://127.0.0.1:9888/")) { 132 | client.request("delete-access-token", req); 133 | logger.info("delete-access-token."); 134 | } else { 135 | logger.info("this is a native method."); 136 | } 137 | } 138 | 139 | /** 140 | * Call list-access-tokens api.
141 | * native method, can't rpc 142 | * 143 | * @param client client object that makes requests to the core 144 | * @return list of AccessToken objects 145 | * @throws BytomException 146 | */ 147 | public static List list(Client client) throws BytomException { 148 | Type listType = new ParameterizedTypeImpl(List.class, new Class[]{AccessToken.class}); 149 | List accessTokenList = null; 150 | if (client.getUrl().equals("http://127.0.0.1:9888") || 151 | client.getUrl().equals("http://127.0.0.1:9888/")) { 152 | accessTokenList = client.request("list-access-tokens", null, listType); 153 | 154 | logger.info("list-access-tokens:"); 155 | logger.info("size of accessTokenList:" + accessTokenList.size()); 156 | logger.info(accessTokenList.get(0).toJson()); 157 | } else { 158 | logger.info("this is a native method."); 159 | } 160 | 161 | return accessTokenList; 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Balance.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.ParameterizedTypeImpl; 5 | import io.bytom.common.Utils; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.apache.log4j.Logger; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class Balance { 16 | 17 | /** 18 | * account id 19 | */ 20 | @SerializedName("account_id") 21 | public String accountId; 22 | 23 | /** 24 | * name of account 25 | */ 26 | @SerializedName("account_alias") 27 | public String accountAlias; 28 | 29 | /** 30 | * sum of the unspent outputs. 31 | * specified asset balance of account. 32 | */ 33 | public long amount; 34 | 35 | /** 36 | * asset id 37 | */ 38 | @SerializedName("asset_id") 39 | public String assetId; 40 | 41 | /** 42 | * name of asset 43 | */ 44 | @SerializedName("asset_alias") 45 | public String assetAlias; 46 | 47 | @SerializedName("asset_definition") 48 | public Map definition; 49 | 50 | private static Logger logger = Logger.getLogger(Balance.class); 51 | 52 | /** 53 | * Serializes the Balance into a form that is safe to transfer over the wire. 54 | * 55 | * @return the JSON-serialized representation of the Receiver object 56 | */ 57 | public String toJson() { 58 | return Utils.serializer.toJson(this); 59 | } 60 | 61 | 62 | public static class QueryBuilder { 63 | 64 | /** 65 | * Call list-Balances api 66 | * 67 | * @param client 68 | * @return 69 | * @throws BytomException 70 | */ 71 | public List list(Client client) throws BytomException { 72 | 73 | Type listType = new ParameterizedTypeImpl(List.class, new Class[]{Balance.class}); 74 | List balanceList = client.request("list-balances", null, listType); 75 | logger.info("list-balances:"); 76 | logger.info("size of :" + balanceList.size()); 77 | for (Balance result : balanceList) { 78 | logger.info(result.toJson()); 79 | } 80 | 81 | return balanceList; 82 | } 83 | 84 | /** 85 | * sum of all Asset alias amount 86 | * 87 | * @param client 88 | * @param assetAlias 89 | * @return 90 | * @throws BytomException 91 | */ 92 | public Balance listByAssetAlias(Client client, String assetAlias) throws BytomException { 93 | List balanceList = list(client); 94 | Balance assetBalance = new Balance(); 95 | assetBalance.assetAlias = assetAlias; 96 | long amount = 0; 97 | for (Balance result : balanceList) { 98 | if (result.assetAlias.equals(assetAlias)) { 99 | amount += result.amount; 100 | assetBalance.assetId = result.assetId; 101 | } 102 | } 103 | assetBalance.amount = amount; 104 | 105 | logger.info(assetBalance.toJson()); 106 | 107 | return assetBalance; 108 | } 109 | 110 | /** 111 | * sum of all Account alias amount 112 | * 113 | * @param client 114 | * @param accountAlias 115 | * @return 116 | * @throws BytomException 117 | */ 118 | public List listByAccountAlias(Client client, String accountAlias) throws BytomException { 119 | List balanceList = list(client); 120 | List accountBalance = new ArrayList<>(); 121 | for (Balance result : balanceList) { 122 | if (result.accountAlias.equals(accountAlias)) { 123 | accountBalance.add(result); 124 | logger.info(result.toJson()); 125 | } 126 | } 127 | return accountBalance; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Block.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class Block { 13 | 14 | public String hash; 15 | 16 | public Integer size; 17 | 18 | public Integer version; 19 | 20 | public Integer height; 21 | 22 | @SerializedName("previous_block_hash") 23 | public String previousBlockHash; 24 | 25 | public Integer timestamp; 26 | 27 | public Integer nonce; 28 | 29 | public long bits; 30 | 31 | public String difficulty; 32 | 33 | @SerializedName("transaction_merkle_root") 34 | public String transactionsMerkleRoot; 35 | 36 | @SerializedName("transaction_status_hash") 37 | public String transactionStatusHash; 38 | 39 | public List transactions; 40 | 41 | 42 | private static Logger logger = Logger.getLogger(Block.class); 43 | 44 | public String toJson() { 45 | return Utils.serializer.toJson(this); 46 | } 47 | 48 | /** 49 | * Call get-block-count api 50 | * 51 | * @param client 52 | * @return 53 | * @throws BytomException 54 | */ 55 | public static Integer getBlockCount(Client client) throws BytomException { 56 | Integer blockCount = 57 | client.requestGet("get-block-count", null, "block_count", Integer.class); 58 | 59 | logger.info("get-block-count:"+blockCount); 60 | return blockCount; 61 | } 62 | 63 | /** 64 | * Call get-block-hash api 65 | * 66 | * @param client 67 | * @return 68 | * @throws BytomException 69 | */ 70 | public static String getBlockHash(Client client) throws BytomException { 71 | String blockHash = 72 | client.requestGet("get-block-hash", null, "block_hash", String.class); 73 | 74 | logger.info("get-block-hash:"+blockHash); 75 | 76 | return blockHash; 77 | } 78 | 79 | public static class QueryBuilder { 80 | 81 | /** 82 | * block_height, height of block. 83 | */ 84 | @SerializedName("block_height") 85 | public int blockHeight; 86 | /** 87 | * block_hash, hash of block. 88 | */ 89 | @SerializedName("block_hash") 90 | public String blockHash; 91 | 92 | public QueryBuilder setBlockHeight(int blockHeight) { 93 | this.blockHeight = blockHeight; 94 | return this; 95 | } 96 | 97 | public QueryBuilder setBlockHash(String blockHash) { 98 | this.blockHash = blockHash; 99 | return this; 100 | } 101 | 102 | /** 103 | * Call get-block api 104 | * 105 | * @param client 106 | * @return 107 | * @throws BytomException 108 | */ 109 | public Block getBlock(Client client) throws BytomException { 110 | 111 | Block block = client.request("get-block", this, Block.class); 112 | 113 | logger.info("get-block:"); 114 | logger.info(block.toJson()); 115 | 116 | return block; 117 | } 118 | 119 | /** 120 | * Call get-block-header api 121 | * 122 | * @param client 123 | * @return 124 | * @throws BytomException 125 | */ 126 | public BlockHeader getBlockHeader(Client client) throws BytomException { 127 | BlockHeader blockHeader = 128 | client.request("get-block-header", this, BlockHeader.class); 129 | 130 | logger.info("get-block-header:"); 131 | logger.info(blockHeader.toJson()); 132 | 133 | return blockHeader; 134 | } 135 | 136 | /** 137 | * Call get-difficulty api 138 | * 139 | * @param client 140 | * @return 141 | * @throws BytomException 142 | */ 143 | public BlockDifficulty getBlockDifficulty(Client client) throws BytomException { 144 | BlockDifficulty blockDifficulty = 145 | client.request("get-difficulty", this, BlockDifficulty.class); 146 | 147 | logger.info("get-difficulty:"); 148 | logger.info(blockDifficulty.toJson()); 149 | 150 | return blockDifficulty; 151 | } 152 | 153 | /** 154 | * Call get-hash-rate api 155 | * 156 | * @param client 157 | * @return 158 | * @throws BytomException 159 | */ 160 | public BlockHashRate getHashRate(Client client) throws BytomException { 161 | BlockHashRate blockHashRate = 162 | client.request("get-hash-rate", this, BlockHashRate.class); 163 | 164 | logger.info("get-hash-rate:"); 165 | logger.info(blockHashRate.toJson()); 166 | 167 | return blockHashRate; 168 | } 169 | 170 | } 171 | 172 | public static class BlockHeader { 173 | 174 | @SerializedName("block_header") 175 | public String blockHeader; 176 | 177 | @SerializedName("reward") 178 | public Integer reward; 179 | 180 | public String toJson() { 181 | return Utils.serializer.toJson(this); 182 | } 183 | 184 | } 185 | 186 | public static class BlockDifficulty { 187 | public String hash; 188 | public Integer height; 189 | public Integer bits; 190 | public String difficulty; 191 | 192 | public String toJson() { 193 | return Utils.serializer.toJson(this); 194 | } 195 | 196 | 197 | } 198 | 199 | public static class BlockHashRate { 200 | public String hash; 201 | public Integer height; 202 | public Integer hash_rate; 203 | 204 | public String toJson() { 205 | return Utils.serializer.toJson(this); 206 | } 207 | 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/CoreConfig.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | public class CoreConfig { 10 | 11 | 12 | private static Logger logger = Logger.getLogger(CoreConfig.class); 13 | 14 | /** 15 | * Call net-info api 16 | * 17 | * @param client 18 | * @return 19 | * @throws BytomException 20 | */ 21 | public static NetInfo getNetInfo(Client client) throws BytomException { 22 | NetInfo netInfo = client.request("net-info", null, NetInfo.class); 23 | 24 | logger.info("net-info:"); 25 | logger.info(netInfo.toJson()); 26 | 27 | return netInfo; 28 | } 29 | 30 | /** 31 | * Call gas-rate api 32 | * 33 | * @param client 34 | * @return 35 | * @throws BytomException 36 | */ 37 | public static Integer getGasRate(Client client) throws BytomException { 38 | Integer gas = client.requestGet("gas-rate", null, "gas_rate", Integer.class); 39 | 40 | logger.info("gas-rate:"); 41 | logger.info(gas); 42 | 43 | return gas; 44 | } 45 | 46 | public static class NetInfo { 47 | /** 48 | * listening, whether the node is listening. 49 | */ 50 | public boolean listening; 51 | 52 | /** 53 | * syncing, whether the node is syncing. 54 | */ 55 | public boolean syncing; 56 | 57 | /** 58 | * mining, whether the node is mining. 59 | */ 60 | public boolean mining; 61 | 62 | /** 63 | * peer_count, current count of connected peers. 64 | */ 65 | @SerializedName("peer_count") 66 | public int peerCount; 67 | 68 | /** 69 | * current_block, current block height in the node's blockchain. 70 | */ 71 | @SerializedName("current_block") 72 | public long currentBlock; 73 | 74 | /** 75 | * highest_block, current highest block of the connected peers. 76 | */ 77 | @SerializedName("highest_block") 78 | public long highestBlock; 79 | 80 | /** 81 | * network_id, network id. 82 | */ 83 | @SerializedName("network_id") 84 | public String networkID; 85 | 86 | /** 87 | * version, bytom version. 88 | */ 89 | @SerializedName("version") 90 | public String version; 91 | 92 | public String toJson() { 93 | return Utils.serializer.toJson(this); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Key.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.ParameterizedTypeImpl; 5 | import io.bytom.common.Utils; 6 | import io.bytom.exception.*; 7 | import io.bytom.http.Client; 8 | import org.apache.log4j.Logger; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | *

Key Class

17 | * 18 | * @version 1.0 19 | * @since 2018-05-18 20 | */ 21 | public class Key { 22 | 23 | @SerializedName("alias") 24 | public String alias; 25 | 26 | @SerializedName("xpub") 27 | public String xpub; 28 | 29 | @SerializedName("file") 30 | public String file; 31 | 32 | private static Logger logger = Logger.getLogger(Key.class); 33 | 34 | public String toJson() { 35 | return Utils.serializer.toJson(this); 36 | } 37 | 38 | /** 39 | * Create a key object 40 | * 41 | * @param client client object that makes requests to the core 42 | * @param builder Key.Builder object that make parameters 43 | * @return Key a key object 44 | * @throws BytomException BytomException 45 | */ 46 | public static Key create(Client client, Builder builder) throws BytomException { 47 | Key key = client.request("create-key", builder, Key.class); 48 | return key; 49 | } 50 | 51 | /** 52 | * List all key objects 53 | * 54 | * @param client client object that makes requests to the core 55 | * @return a list of key object 56 | * @throws BytomException BytomException 57 | */ 58 | public static List list(Client client) throws BytomException { 59 | Type listType = new ParameterizedTypeImpl(List.class, new Class[]{Key.class}); 60 | List keyList = client.request("list-keys", null, listType); 61 | 62 | logger.info("list-key:"); 63 | logger.info("size of key:"+keyList.size()); 64 | logger.info(keyList); 65 | 66 | return keyList; 67 | } 68 | 69 | /** 70 | * delete a key 71 | * 72 | * @param client client object that makes requests to the core 73 | * @param xpub the xpub is given when creates key 74 | * @param password the password is given when creates key 75 | * @throws BytomException BytomException 76 | */ 77 | public static void delete(Client client, String xpub, String password) throws BytomException { 78 | Map req = new HashMap(); 79 | req.put("xpub", xpub); 80 | req.put("password", password); 81 | client.request("delete-key", req); 82 | logger.info("delete-key successfully."); 83 | } 84 | 85 | /** 86 | * reset password 87 | * 88 | * @param client client object that makes requests to the core 89 | * @param xpub the xpub is given when creates key 90 | * @param oldPwd the old password is given when creates key 91 | * @param newPwd new password used to set 92 | * @throws BytomException BytomException 93 | */ 94 | public static void resetPassword(Client client, String xpub, String oldPwd, String newPwd) throws BytomException { 95 | Map req = new HashMap<>(); 96 | req.put("xpub", xpub); 97 | req.put("old_password", oldPwd); 98 | req.put("new_password", newPwd); 99 | client.request("reset-key-password", req); 100 | } 101 | 102 | /** 103 | *

Key.Builder Class

104 | */ 105 | public static class Builder { 106 | /** 107 | * User specified, unique identifier. 108 | */ 109 | public String alias; 110 | 111 | /** 112 | * User specified. 113 | */ 114 | public String password; 115 | 116 | /** 117 | * Sets the alias on the builder object. 118 | * 119 | * @param alias alias 120 | * @return updated builder object 121 | */ 122 | public Builder setAlias(String alias) { 123 | this.alias = alias; 124 | return this; 125 | } 126 | 127 | /** 128 | * Sets the alias on the builder object. 129 | * 130 | * @param password password 131 | * @return updated builder object 132 | */ 133 | public Builder setPassword(String password) { 134 | this.password = password; 135 | return this; 136 | } 137 | 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return "Key{" + 143 | "alias='" + alias + '\'' + 144 | ", xpub='" + xpub + '\'' + 145 | ", file='" + file + '\'' + 146 | '}'; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Message.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | public class Message { 10 | 11 | @SerializedName("derived_xpub") 12 | public String derivedXpub; 13 | @SerializedName("signature") 14 | public String signature; 15 | 16 | private static Logger logger = Logger.getLogger(Message.class); 17 | 18 | public String toJson() { 19 | return Utils.serializer.toJson(this); 20 | } 21 | 22 | public static class SignBuilder { 23 | 24 | public String address; 25 | public String message; 26 | public String password; 27 | 28 | public SignBuilder setAddress(String address) { 29 | this.address = address; 30 | return this; 31 | } 32 | 33 | public SignBuilder setMessage(String message) { 34 | this.message = message; 35 | return this; 36 | } 37 | 38 | public SignBuilder setPassword(String password) { 39 | this.password = password; 40 | return this; 41 | } 42 | 43 | /** 44 | * Call sign-message api 45 | * 46 | * @param client 47 | * @return 48 | * @throws BytomException 49 | */ 50 | public Message sign(Client client) throws BytomException { 51 | Message message = client.request("sign-message", this, Message.class); 52 | 53 | logger.info("sign-message:"); 54 | logger.info(message.toJson()); 55 | 56 | return message; 57 | } 58 | 59 | } 60 | 61 | public static class VerifyBuilder { 62 | 63 | /** 64 | * address, address for account. 65 | */ 66 | public String address; 67 | 68 | /** 69 | * derived_xpub, derived xpub. 70 | */ 71 | @SerializedName("derived_xpub") 72 | public String derivedXpub; 73 | 74 | /** 75 | * message, message for signature by derived_xpub. 76 | */ 77 | public String message; 78 | 79 | /** 80 | * signature, signature for message. 81 | */ 82 | public String signature; 83 | 84 | 85 | public VerifyBuilder setAddress(String address) { 86 | this.address = address; 87 | return this; 88 | } 89 | 90 | public VerifyBuilder setDerivedXpub(String derivedXpub) { 91 | this.derivedXpub = derivedXpub; 92 | return this; 93 | } 94 | 95 | public VerifyBuilder setMessage(String message) { 96 | this.message = message; 97 | return this; 98 | } 99 | 100 | public VerifyBuilder setSignature(String signature) { 101 | this.signature = signature; 102 | return this; 103 | } 104 | 105 | /** 106 | * Call verify-message api 107 | * @param client 108 | * @return 109 | * @throws BytomException 110 | */ 111 | public Boolean verifyMessage(Client client) throws BytomException { 112 | Boolean result = client.requestGet("verify-message", this, "result", Boolean.class); 113 | 114 | logger.info("verify-message:"+result); 115 | 116 | return result; 117 | } 118 | 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/RawTransaction.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class RawTransaction { 14 | /** 15 | * version 16 | */ 17 | public Integer version; 18 | /** 19 | * size 20 | */ 21 | public Integer size; 22 | /** 23 | * time_range 24 | */ 25 | @SerializedName("time_range") 26 | public Integer timeRange; 27 | 28 | /** 29 | * status 30 | */ 31 | public Integer fee; 32 | 33 | /** 34 | * List of specified inputs for a transaction. 35 | */ 36 | public List inputs; 37 | 38 | /** 39 | * List of specified outputs for a transaction. 40 | */ 41 | public List outputs; 42 | 43 | private static Logger logger = Logger.getLogger(RawTransaction.class); 44 | 45 | public String toJson() { 46 | return Utils.serializer.toJson(this); 47 | } 48 | 49 | 50 | public static RawTransaction decode(Client client, String txId) throws BytomException { 51 | Map req = new HashMap(); 52 | req.put("raw_transaction", txId); 53 | RawTransaction rawTransaction = 54 | client.request("decode-raw-transaction", req, RawTransaction.class); 55 | 56 | logger.info("decode-raw-transaction:"); 57 | logger.info(rawTransaction.toJson()); 58 | 59 | return rawTransaction; 60 | } 61 | 62 | public static class AnnotatedInput { 63 | 64 | /** 65 | * address 66 | */ 67 | private String address; 68 | 69 | /** 70 | * The number of units of the asset being issued or spent. 71 | */ 72 | private long amount; 73 | 74 | /** 75 | * The definition of the asset being issued or spent (possibly null). 76 | */ 77 | @SerializedName("asset_definition") 78 | private Map assetDefinition; 79 | 80 | /** 81 | * The id of the asset being issued or spent. 82 | */ 83 | @SerializedName("asset_id") 84 | private String assetId; 85 | 86 | /** 87 | * The control program which must be satisfied to transfer this output. 88 | */ 89 | @SerializedName("control_program") 90 | private String controlProgram; 91 | 92 | /** 93 | * The id of the output consumed by this input. Null if the input is an 94 | * issuance. 95 | */ 96 | @SerializedName("spent_output_id") 97 | private String spentOutputId; 98 | 99 | /** 100 | * The type of the input.
101 | * Possible values are "issue" and "spend". 102 | */ 103 | private String type; 104 | } 105 | 106 | public static class AnnotatedOutput { 107 | 108 | /** 109 | * address 110 | */ 111 | private String address; 112 | 113 | /** 114 | * The number of units of the asset being controlled. 115 | */ 116 | private long amount; 117 | 118 | /** 119 | * The definition of the asset being controlled (possibly null). 120 | */ 121 | @SerializedName("asset_definition") 122 | private Map assetDefinition; 123 | 124 | /** 125 | * The id of the asset being controlled. 126 | */ 127 | @SerializedName("asset_id") 128 | public String assetId; 129 | 130 | /** 131 | * The control program which must be satisfied to transfer this output. 132 | */ 133 | @SerializedName("control_program") 134 | private String controlProgram; 135 | 136 | /** 137 | * The id of the output. 138 | */ 139 | @SerializedName("id") 140 | private String id; 141 | 142 | /** 143 | * The output's position in a transaction's list of outputs. 144 | */ 145 | private Integer position; 146 | 147 | /** 148 | * The type the output.
149 | * Possible values are "control" and "retire". 150 | */ 151 | private String type; 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Receiver.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.JSONException; 6 | 7 | /** 8 | * Receivers are used to facilitate payments between accounts on different 9 | * cores. They contain a control program and an expiration date. In the future, 10 | * more payment-related metadata may be placed here. 11 | *

12 | * Receivers are typically created under accounts via the 13 | * {@link Account.ReceiverBuilder} class. 14 | */ 15 | public class Receiver { 16 | 17 | @SerializedName("address") 18 | public String address; 19 | /** 20 | * Hex-encoded string representation of the control program. 21 | */ 22 | @SerializedName("control_program") 23 | public String controlProgram; 24 | 25 | 26 | /** 27 | * Serializes the receiver into a form that is safe to transfer over the wire. 28 | * 29 | * @return the JSON-serialized representation of the Receiver object 30 | */ 31 | public String toJson() { 32 | return Utils.serializer.toJson(this); 33 | } 34 | 35 | /** 36 | * Deserializes a Receiver from JSON. 37 | * 38 | * @param json a JSON-serialized Receiver object 39 | * @return the deserialized Receiver object 40 | * @throws JSONException Raised if the provided string is not valid JSON. 41 | */ 42 | public static Receiver fromJson(String json) throws JSONException { 43 | try { 44 | return Utils.serializer.fromJson(json, Receiver.class); 45 | } catch (IllegalStateException e) { 46 | throw new JSONException("Unable to parse serialized receiver: " + e.getMessage()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/UnconfirmedTransaction.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | import java.lang.reflect.Type; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class UnconfirmedTransaction { 15 | /** 16 | * Unique identifier, or transaction hash, of a transaction. 17 | */ 18 | private String id; 19 | 20 | /** 21 | * version 22 | */ 23 | private Integer version; 24 | 25 | /** 26 | * size 27 | */ 28 | private Integer size; 29 | /** 30 | * time_range 31 | */ 32 | @SerializedName("time_range") 33 | private Integer timeRange; 34 | 35 | /** 36 | * status 37 | */ 38 | @SerializedName("status_fail") 39 | private boolean statusFail; 40 | 41 | /** 42 | * List of specified inputs for a transaction. 43 | */ 44 | private List inputs; 45 | 46 | /** 47 | * List of specified outputs for a transaction. 48 | */ 49 | private List outputs; 50 | 51 | private static Logger logger = Logger.getLogger(UnconfirmedTransaction.class); 52 | 53 | /** 54 | * Serializes the UnconfirmedTransaction into a form that is safe to transfer over the wire. 55 | * 56 | * @return the JSON-serialized representation of the UnconfirmedTransaction object 57 | */ 58 | public String toJson() { 59 | return Utils.serializer.toJson(this); 60 | } 61 | 62 | /** 63 | * Call get-unconfirmed-transaction api 64 | * 65 | * @param client 66 | * @param txId 67 | * @return 68 | * @throws BytomException 69 | */ 70 | public static UnconfirmedTransaction get(Client client, String txId) throws BytomException { 71 | Map req = new HashMap(); 72 | req.put("tx_id", txId); 73 | UnconfirmedTransaction UCTX = client.request("get-unconfirmed-transaction", 74 | req, UnconfirmedTransaction.class); 75 | 76 | logger.info("get-unconfirmed-transaction:"); 77 | logger.info(UCTX.toJson()); 78 | 79 | return UCTX; 80 | } 81 | 82 | public static UTXResponse list(Client client) throws BytomException { 83 | UTXResponse utxResponse = 84 | client.request("list-unconfirmed-transactions", null, UTXResponse.class); 85 | 86 | logger.info("list-unconfirmed-transactions:"); 87 | logger.info(utxResponse.toJson()); 88 | 89 | return utxResponse; 90 | } 91 | 92 | public static class UTXResponse { 93 | 94 | @SerializedName("total") 95 | public Integer total; 96 | 97 | @SerializedName("tx_ids") 98 | public List txIds; 99 | 100 | public String toJson() { 101 | return Utils.serializer.toJson(this); 102 | } 103 | } 104 | 105 | public static class AnnotatedInput { 106 | 107 | /** 108 | * address 109 | */ 110 | private String address; 111 | 112 | /** 113 | * The number of units of the asset being issued or spent. 114 | */ 115 | private long amount; 116 | 117 | /** 118 | * The definition of the asset being issued or spent (possibly null). 119 | */ 120 | @SerializedName("asset_definition") 121 | private Map assetDefinition; 122 | 123 | /** 124 | * The id of the asset being issued or spent. 125 | */ 126 | @SerializedName("asset_id") 127 | private String assetId; 128 | 129 | /** 130 | * The control program which must be satisfied to transfer this output. 131 | */ 132 | @SerializedName("control_program") 133 | private String controlProgram; 134 | 135 | /** 136 | * The id of the output consumed by this input. Null if the input is an 137 | * issuance. 138 | */ 139 | @SerializedName("spent_output_id") 140 | private String spentOutputId; 141 | 142 | /** 143 | * The type of the input.
144 | * Possible values are "issue" and "spend". 145 | */ 146 | private String type; 147 | } 148 | 149 | public static class AnnotatedOutput { 150 | 151 | /** 152 | * address 153 | */ 154 | private String address; 155 | 156 | /** 157 | * The number of units of the asset being controlled. 158 | */ 159 | private long amount; 160 | 161 | /** 162 | * The definition of the asset being controlled (possibly null). 163 | */ 164 | @SerializedName("asset_definition") 165 | private Map assetDefinition; 166 | 167 | /** 168 | * The id of the asset being controlled. 169 | */ 170 | @SerializedName("asset_id") 171 | public String assetId; 172 | 173 | /** 174 | * The control program which must be satisfied to transfer this output. 175 | */ 176 | @SerializedName("control_program") 177 | private String controlProgram; 178 | 179 | /** 180 | * The id of the output. 181 | */ 182 | @SerializedName("id") 183 | private String id; 184 | 185 | /** 186 | * The output's position in a transaction's list of outputs. 187 | */ 188 | private Integer position; 189 | 190 | /** 191 | * The type the output.
192 | * Possible values are "control" and "retire". 193 | */ 194 | private String type; 195 | 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/UnspentOutput.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.ParameterizedTypeImpl; 5 | import io.bytom.common.Utils; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.apache.log4j.Logger; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.List; 12 | 13 | public class UnspentOutput { 14 | 15 | /** 16 | * The id of the asset being controlled. 17 | */ 18 | @SerializedName("asset_id") 19 | public String assetId; 20 | 21 | /** 22 | * The alias of the asset being controlled. 23 | */ 24 | @SerializedName("asset_alias") 25 | public String assetAlias; 26 | 27 | /** 28 | * The number of units of the asset being controlled. 29 | */ 30 | public long amount; 31 | 32 | /** 33 | * address of account 34 | */ 35 | public String address; 36 | 37 | /** 38 | * whether the account address is change 39 | */ 40 | public boolean change; 41 | 42 | /** 43 | * The ID of the output. 44 | */ 45 | @SerializedName("id") 46 | public String id; 47 | 48 | /** 49 | * The control program which must be satisfied to transfer this output. 50 | */ 51 | @SerializedName("program") 52 | public String program; 53 | 54 | @SerializedName("control_program_index") 55 | public String controlProgramIndex; 56 | 57 | /** 58 | * source unspent output id 59 | */ 60 | @SerializedName("source_id") 61 | public String sourceId; 62 | 63 | /** 64 | * position of source unspent output id in block 65 | */ 66 | @SerializedName("source_pos") 67 | public int sourcePos; 68 | 69 | /** 70 | * The definition of the asset being controlled (possibly null). 71 | */ 72 | @SerializedName("valid_height") 73 | public int validHeight; 74 | 75 | private static Logger logger = Logger.getLogger(UnspentOutput.class); 76 | 77 | /** 78 | * Serializes the Address into a form that is safe to transfer over the wire. 79 | * 80 | * @return the JSON-serialized representation of the Receiver object 81 | */ 82 | public String toJson() { 83 | return Utils.serializer.toJson(this); 84 | } 85 | 86 | public static class QueryBuilder { 87 | 88 | /** 89 | * id of unspent output. 90 | */ 91 | public String id; 92 | 93 | /** 94 | * The id of the account controlling this output (possibly null if a control program 95 | * is specified). 96 | */ 97 | @SerializedName("account_id") 98 | public String accountId; 99 | 100 | /** 101 | * The alias of the account controlling this output (possibly null if a control 102 | * program is specified). 103 | */ 104 | @SerializedName("account_alias") 105 | public String accountAlias; 106 | 107 | public Boolean unconfirmed; 108 | 109 | @SerializedName("smart_contract") 110 | public Boolean smartContract; 111 | 112 | public Integer from; 113 | 114 | public Integer count; 115 | 116 | public QueryBuilder setId(String id) { 117 | this.id = id; 118 | return this; 119 | } 120 | 121 | public QueryBuilder setAccountId(String accountId) { 122 | this.accountId = accountId; 123 | return this; 124 | } 125 | 126 | public QueryBuilder setAccountAlias(String accountAlias) { 127 | this.accountAlias = accountAlias; 128 | return this; 129 | } 130 | 131 | public QueryBuilder setSmartContract(boolean smartContract) { 132 | this.smartContract = smartContract; 133 | return this; 134 | } 135 | 136 | public QueryBuilder setUnconfirmed(boolean unconfirmed) { 137 | this.unconfirmed = unconfirmed; 138 | return this; 139 | } 140 | 141 | public QueryBuilder setFrom(Integer from) { 142 | this.from = from; 143 | return this; 144 | } 145 | 146 | public QueryBuilder setCount(Integer count) { 147 | this.count = count; 148 | return this; 149 | } 150 | 151 | /** 152 | * call list-unspent-outputs api 153 | * 154 | * @param client client object that makes requests to the core 155 | * @return 156 | * @throws BytomException BytomException 157 | */ 158 | public List list(Client client) throws BytomException { 159 | 160 | Type listType = new ParameterizedTypeImpl(List.class, new Class[]{UnspentOutput.class}); 161 | List unspentOutputList = client.request("list-unspent-outputs", this, listType); 162 | logger.info("list-unspent-outputs:"); 163 | logger.info("size of unspentOutputList:" + unspentOutputList.size()); 164 | for (UnspentOutput UTXO : unspentOutputList) { 165 | logger.info(UTXO.toJson()); 166 | } 167 | 168 | return unspentOutputList; 169 | } 170 | 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/api/Wallet.java: -------------------------------------------------------------------------------- 1 | package io.bytom.api; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.bytom.common.Utils; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.apache.log4j.Logger; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | *

Wallet Class

15 | */ 16 | public class Wallet { 17 | 18 | @SerializedName("account_image") 19 | public AccountImage accountImage; 20 | 21 | @SerializedName("asset_image") 22 | public AssetImage assetImage; 23 | 24 | @SerializedName("key_images") 25 | public KeyImages keyImages; 26 | 27 | private static Logger logger = Logger.getLogger(Wallet.class); 28 | 29 | /** 30 | * Serializes the Address into a form that is safe to transfer over the wire. 31 | * 32 | * @return the JSON-serialized representation of the Receiver object 33 | */ 34 | public String toJson() { 35 | return Utils.serializer.toJson(this); 36 | } 37 | 38 | /** 39 | * Call backup-wallet api 40 | * 41 | * @param client 42 | * @return 43 | * @throws BytomException 44 | */ 45 | public static Wallet backupWallet(Client client) throws BytomException { 46 | Wallet wallet = client.request("backup-wallet", null, Wallet.class); 47 | 48 | logger.info("backup-wallet:"); 49 | logger.info(wallet.toJson()); 50 | 51 | return wallet; 52 | } 53 | 54 | /** 55 | * Call restore-wallet api 56 | * 57 | * @param client 58 | * @param accountImage 59 | * @param assetImage 60 | * @param keyImages 61 | * @throws BytomException 62 | */ 63 | public static void restoreWallet(Client client ,Object accountImage, Object assetImage , Object keyImages) throws BytomException{ 64 | Map body = new HashMap(); 65 | body.put("account_image", accountImage); 66 | body.put("asset_image", assetImage); 67 | body.put("key_images", keyImages); 68 | 69 | logger.info("restore-wallet:"); 70 | logger.info(body.toString()); 71 | 72 | client.request("restore-wallet", body); 73 | } 74 | 75 | public static class AccountImage { 76 | 77 | public Slices[] slices; 78 | 79 | public static class Slices { 80 | 81 | @SerializedName("contract_index") 82 | public int contractIndex; 83 | 84 | public Account account; 85 | 86 | public static class Account { 87 | 88 | public String type; 89 | 90 | public List xpubs; 91 | 92 | public int quorum; 93 | 94 | @SerializedName("key_index") 95 | public int keyIndex; 96 | 97 | public String id; 98 | 99 | public String alias; 100 | 101 | } 102 | 103 | } 104 | } 105 | 106 | public static class AssetImage { 107 | 108 | public Assets[] assets; 109 | 110 | public static class Assets { 111 | public String type; 112 | public List xpubs; 113 | public int quorum; 114 | public String id; 115 | public String alias; 116 | public Map definition; 117 | @SerializedName("key_index") 118 | public int keyIndex; 119 | @SerializedName("vm_version") 120 | public int vmVersion; 121 | @SerializedName("asset_image") 122 | public String issueProgram; 123 | @SerializedName("raw_definition_byte") 124 | public String rawDefinitionByte; 125 | } 126 | } 127 | 128 | public static class KeyImages { 129 | 130 | public Xkeys[] xkeys; 131 | 132 | public static class Xkeys { 133 | 134 | public Crypto crypto; 135 | public String id; 136 | public String type; 137 | public int version; 138 | public String alias; 139 | public String xpub; 140 | 141 | public static class Crypto { 142 | public String cipher; 143 | public String ciphertext; 144 | public Map cipherparams; 145 | public String kdf; 146 | public Map kdfparams; 147 | public String mac; 148 | } 149 | 150 | } 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/common/Configuration.java: -------------------------------------------------------------------------------- 1 | package io.bytom.common; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.Properties; 6 | 7 | public class Configuration { 8 | 9 | private static Properties props = new Properties(); 10 | static { 11 | try { 12 | props.load(Thread.currentThread().getContextClassLoader() 13 | .getResourceAsStream("config.properties")); 14 | } catch (FileNotFoundException e) { 15 | e.printStackTrace(); 16 | } catch (IOException e) { 17 | e.printStackTrace(); 18 | } 19 | } 20 | 21 | public static String getValue(String key) { 22 | return props.getProperty(key); 23 | } 24 | 25 | public static void updateProperties(String key, String value) { 26 | props.setProperty(key, value); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/common/ParameterizedTypeImpl.java: -------------------------------------------------------------------------------- 1 | package io.bytom.common; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | import java.lang.reflect.Type; 5 | 6 | public class ParameterizedTypeImpl implements ParameterizedType { 7 | 8 | private final Class raw; 9 | private final Type[] args; 10 | 11 | public ParameterizedTypeImpl(Class raw, Type[] args) { 12 | this.raw = raw; 13 | this.args = args != null ? args : new Type[0]; 14 | } 15 | 16 | @Override 17 | public Type[] getActualTypeArguments() { 18 | return args; 19 | } 20 | 21 | @Override 22 | public Type getRawType() { 23 | return raw; 24 | } 25 | 26 | @Override 27 | public Type getOwnerType() { 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/common/Utils.java: -------------------------------------------------------------------------------- 1 | package io.bytom.common; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | public class Utils { 7 | public static String rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; 8 | public static final Gson serializer = new GsonBuilder().setDateFormat(rfc3339DateFormat).create(); 9 | } 10 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/APIException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * APIException wraps errors returned by the API. 7 | * Each error contains a brief description in addition to a unique error code.
8 | * The error code can be used by Chain Support to diagnose the exact cause of the error. 9 | * The mapping of error codes to messages is as follows:

10 | * 11 | *

General errors

12 | * CH001 - Request timed out 13 | * CH002 - Not found 14 | * CH003 - Invalid request body 15 | * CH004 - Invalid request header 16 | * CH006 - Not found 17 | * 18 | *

Account/Asset errors

19 | * CH200 - Quorum must be greater than 1 and less than or equal to the length of xpubs 20 | * CH201 - Invalid xpub format 21 | * CH202 - At least one xpub is required 22 | * CH203 - Retrieved type does not match expected type 23 | * 24 | *

Access token errors

25 | * CH300 - Malformed or empty access token id 26 | * CH301 - Access tokens must be type client or network 27 | * CH302 - Access token id is already in use 28 | * CH310 - The access token used to authenticate this request cannot be deleted 29 | * 30 | *

Query errors

31 | * CH600 - Malformed pagination parameter `after` 32 | * CH601 - Incorrect number of parameters to filter 33 | * CH602 - Malformed query filter 34 | * 35 | *

Transaction errors

36 | * CH700 - Reference data does not match previous transaction's reference data
37 | * CH701 - Invalid action type
38 | * CH702 - Invalid alias on action
39 | * CH730 - Missing raw transaction
40 | * CH731 - Too many signing instructions in template for transaction
41 | * CH732 - Invalid transaction input index
42 | * CH733 - Invalid signature script component
43 | * CH734 - Missing signature in template
44 | * CH735 - Transaction rejected
45 | * CH760 - Insufficient funds for tx
46 | * CH761 - Some outputs are reserved; try again
47 | */ 48 | public class APIException extends BytomException { 49 | /** 50 | * Unique identifier for the error. 51 | */ 52 | public String code; 53 | 54 | /** 55 | * Message describing the general nature of the error. 56 | */ 57 | @SerializedName("message") 58 | public String chainMessage; 59 | 60 | /** 61 | * Additional information about the error (possibly null). 62 | */ 63 | public String detail; 64 | 65 | /** 66 | * Specifies whether the error is temporary, or a change to the request is necessary. 67 | */ 68 | public boolean temporary; 69 | 70 | /** 71 | * Unique identifier of the request to the server. 72 | */ 73 | public String requestId; 74 | 75 | /** 76 | * HTTP status code returned by the server. 77 | */ 78 | public int statusCode; 79 | 80 | /** 81 | * Initializes exception with its message and requestId attributes. 82 | * @param message error message 83 | * @param requestId unique identifier of the request 84 | */ 85 | public APIException(String message, String requestId) { 86 | super(message); 87 | this.requestId = requestId; 88 | } 89 | 90 | /** 91 | * Intitializes exception with its code, message, detail & temporary field set. 92 | * @param code error code 93 | * @param message error message 94 | * @param detail additional error information 95 | * @param temporary unique identifier of the request 96 | */ 97 | public APIException(String code, String message, String detail, boolean temporary) { 98 | super(message); 99 | this.chainMessage = message; 100 | this.code = code; 101 | this.detail = detail; 102 | this.temporary = temporary; 103 | } 104 | 105 | /** 106 | * Initializes exception with its code, message, detail & requestId attributes. 107 | * @param code error code 108 | * @param message error message 109 | * @param detail additional error information 110 | * @param requestId unique identifier of the request 111 | */ 112 | public APIException(String code, String message, String detail, String requestId) { 113 | super(message); 114 | this.chainMessage = message; 115 | this.code = code; 116 | this.detail = detail; 117 | this.requestId = requestId; 118 | } 119 | 120 | /** 121 | * Initializes exception with all of its attributes. 122 | * @param code error code 123 | * @param message error message 124 | * @param detail additional error information 125 | * @param temporary specifies if the error is temporary 126 | * @param requestId unique identifier of the request 127 | * @param statusCode HTTP status code 128 | */ 129 | public APIException( 130 | String code, 131 | String message, 132 | String detail, 133 | boolean temporary, 134 | String requestId, 135 | int statusCode) { 136 | super(message); 137 | this.chainMessage = message; 138 | this.code = code; 139 | this.detail = detail; 140 | this.temporary = temporary; 141 | this.requestId = requestId; 142 | this.statusCode = statusCode; 143 | } 144 | 145 | /** 146 | * Constructs a detailed message of the error. 147 | * @return detailed error message 148 | */ 149 | @Override 150 | public String getMessage() { 151 | String s = ""; 152 | 153 | if (this.code != null && this.code.length() > 0) { 154 | s += "Code: " + this.code + " "; 155 | } 156 | 157 | s += "Message: " + this.chainMessage; 158 | 159 | if (this.detail != null && this.detail.length() > 0) { 160 | s += " Detail: " + this.detail; 161 | } 162 | 163 | if (this.requestId != null) { 164 | s += " Request-ID: " + this.requestId; 165 | } 166 | 167 | return s; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/BadURLException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | /** 4 | * BadURLException wraps errors due to malformed URLs. 5 | */ 6 | public class BadURLException extends BytomException { 7 | /** 8 | * Initializes exception with its message attribute. 9 | * @param message error message 10 | */ 11 | public BadURLException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/BuildException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * BuildException wraps errors returned by the build-transaction endpoint. 9 | */ 10 | public class BuildException extends APIException { 11 | 12 | public BuildException(String message, String requestId) { 13 | super(message, requestId); 14 | } 15 | 16 | public static class ActionError extends APIException { 17 | 18 | public static class Data { 19 | /** 20 | * The index of the action that caused this error. 21 | */ 22 | @SerializedName("index") 23 | public Integer index; 24 | } 25 | 26 | public ActionError(String message, String requestId) { 27 | super(message, requestId); 28 | } 29 | 30 | /** 31 | * Additional data pertaining to the error. 32 | */ 33 | public Data data; 34 | } 35 | 36 | public static class Data { 37 | /** 38 | * A list of errors resulting from building actions. 39 | */ 40 | @SerializedName("actions") 41 | public List actionErrors; 42 | } 43 | 44 | /** 45 | * Extra data associated with this error, if any. 46 | */ 47 | @SerializedName("data") 48 | public Data data; 49 | } 50 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/BytomException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | /** 4 | * Base exception class for the Chain Core SDK. 5 | */ 6 | public class BytomException extends Exception { 7 | /** 8 | * Default constructor. 9 | */ 10 | public BytomException() { 11 | super(); 12 | } 13 | 14 | /** 15 | * Initializes exception with its message attribute. 16 | * 17 | * @param message error message 18 | */ 19 | public BytomException(String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Initializes a new exception while storing the original cause. 25 | * 26 | * @param message the error message 27 | * @param cause the cause of the exception 28 | */ 29 | public BytomException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/ConfigurationException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | /** 4 | * ConfigurationException wraps errors during client configuration. 5 | */ 6 | public class ConfigurationException extends BytomException { 7 | /** 8 | * Initializes exception with its message attribute. 9 | * @param message error message 10 | */ 11 | public ConfigurationException(String message) { 12 | super(message); 13 | } 14 | 15 | /** 16 | * Initializes new exception while storing original cause. 17 | * @param message the error message 18 | * @param cause the original cause 19 | */ 20 | public ConfigurationException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/ConnectivityException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | import com.squareup.okhttp.Response; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * ConnectivityException wraps errors due to connectivity issues with the server. 9 | */ 10 | public class ConnectivityException extends BytomException { 11 | /** 12 | * Initializes exception with its message attribute. 13 | * @param resp the server response used to create error message 14 | */ 15 | public ConnectivityException(Response resp) { 16 | super(formatMessage(resp)); 17 | } 18 | 19 | /** 20 | * Parses the the server response into a detailed error message. 21 | * @param resp the server response 22 | * @return detailed error message 23 | */ 24 | private static String formatMessage(Response resp) { 25 | String s = 26 | "Response HTTP header field Chain-Request-ID is unset. There may be network issues. Please check your local network settings."; 27 | // TODO(kr): include client-generated reqid here once we have that. 28 | String body; 29 | try { 30 | body = resp.body().string(); 31 | } catch (IOException ex) { 32 | body = "[unable to read response body: " + ex.toString() + "]"; 33 | } 34 | return String.format("%s status=%d body=%s", s, resp.code(), body); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/HTTPException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | /** 4 | * HTTPException wraps generic HTTP errors. 5 | */ 6 | public class HTTPException extends BytomException { 7 | /** 8 | * Initializes exception with its message attribute. 9 | * @param message error message 10 | */ 11 | public HTTPException(String message) { 12 | super(message); 13 | } 14 | 15 | /** 16 | * Initializes new exception while storing original cause. 17 | * @param message the error message 18 | * @param cause the original cause 19 | */ 20 | public HTTPException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/exception/JSONException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.exception; 2 | 3 | /** 4 | * JSONException wraps errors due to marshaling/unmarshaling json payloads. 5 | */ 6 | public class JSONException extends BytomException { 7 | 8 | /** 9 | * Unique indentifier of the request to the server. 10 | */ 11 | public String requestId; 12 | 13 | /** 14 | * Default constructor. 15 | */ 16 | public JSONException(String message) { 17 | super(message); 18 | } 19 | 20 | /** 21 | * Initializes exception with its message and requestId attributes. 22 | * Use this constructor in context of an API call. 23 | * 24 | * @param message error message 25 | * @param requestId unique identifier of the request 26 | */ 27 | public JSONException(String message, String requestId) { 28 | super(message); 29 | this.requestId = requestId; 30 | } 31 | 32 | public String getMessage() { 33 | String message = "Message: " + super.getMessage(); 34 | if (requestId != null) { 35 | message += " Request-ID: " + requestId; 36 | } 37 | return message; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/http/BatchResponse.java: -------------------------------------------------------------------------------- 1 | package io.bytom.http; 2 | 3 | import io.bytom.exception.*; 4 | import com.google.gson.Gson; 5 | import com.google.gson.JsonArray; 6 | import com.google.gson.JsonElement; 7 | import com.google.gson.JsonParser; 8 | import com.squareup.okhttp.Response; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.Type; 12 | import java.util.*; 13 | 14 | /** 15 | * BatchResponse provides a convenient interface for handling the results of 16 | * batched API calls. The response contains one success or error per outgoing 17 | * request item in the batch. Errors are always of type APIExcpetion. 18 | */ 19 | public class BatchResponse { 20 | private Response response; 21 | private Map successesByIndex = new LinkedHashMap<>(); 22 | private Map errorsByIndex = new LinkedHashMap<>(); 23 | 24 | /** 25 | * This constructor is used when deserializing a response from an API call. 26 | */ 27 | public BatchResponse(Response response, Gson serializer, Type tClass, Type eClass) 28 | throws BytomException, IOException { 29 | this.response = response; 30 | 31 | try { 32 | JsonArray root = new JsonParser().parse(response.body().charStream()).getAsJsonArray(); 33 | for (int i = 0; i < root.size(); i++) { 34 | JsonElement elem = root.get(i); 35 | 36 | // Test for interleaved errors 37 | APIException err = serializer.fromJson(elem, eClass); 38 | if (err.code != null) { 39 | errorsByIndex.put(i, err); 40 | continue; 41 | } 42 | 43 | successesByIndex.put(i, (T) serializer.fromJson(elem, tClass)); 44 | } 45 | } catch (IllegalStateException e) { 46 | throw new JSONException( 47 | "Unable to read body: " + e.getMessage(), response.headers().get("Chain-Request-ID")); 48 | } 49 | } 50 | 51 | /** 52 | * This constructor is used for synthetically generating a batch response 53 | * object from a map of successes and a map of errors. It ensures that 54 | * the successes and errors are stored in an order-preserving fashion. 55 | */ 56 | public BatchResponse(Map successes, Map errors) { 57 | List successIndexes = new ArrayList<>(); 58 | Iterator successIter = successes.keySet().iterator(); 59 | while (successIter.hasNext()) successIndexes.add(successIter.next()); 60 | Collections.sort(successIndexes); 61 | for (int i : successIndexes) successesByIndex.put(i, successes.get(i)); 62 | 63 | List errorIndexes = new ArrayList<>(); 64 | Iterator errorIter = errors.keySet().iterator(); 65 | while (errorIter.hasNext()) errorIndexes.add(errorIter.next()); 66 | Collections.sort(errorIndexes); 67 | for (int i : errorIndexes) errorsByIndex.put(i, errors.get(i)); 68 | } 69 | 70 | /** 71 | * Returns the internal response object. 72 | */ 73 | public Response response() { 74 | return response; 75 | } 76 | 77 | /** 78 | * Returns the total number of response objects. This should equal the number 79 | * of request objects in the batch. 80 | */ 81 | public int size() { 82 | return successesByIndex.size() + errorsByIndex.size(); 83 | } 84 | 85 | /** 86 | * Returns whether the request object at the given index produced a success. 87 | * @param index the index of the request object 88 | */ 89 | public boolean isSuccess(int index) { 90 | return successesByIndex.containsKey(index); 91 | } 92 | 93 | /** 94 | * Returns whether the request object at the given index produced an error. 95 | * @param index the index of the request object 96 | */ 97 | public boolean isError(int index) { 98 | return errorsByIndex.containsKey(index); 99 | } 100 | 101 | /** 102 | * Returns a list of successful response objects in the batch. The order of 103 | * the list corresponds to the order of the request objects that produced the 104 | * successes. 105 | */ 106 | public List successes() { 107 | List res = new ArrayList<>(); 108 | res.addAll(successesByIndex.values()); 109 | return res; 110 | } 111 | 112 | /** 113 | * Returns a list of error objects in the batch. The order of the list 114 | * corresponds to the order of the request objects that produced the 115 | * errors. 116 | */ 117 | public List errors() { 118 | List res = new ArrayList<>(); 119 | res.addAll(errorsByIndex.values()); 120 | return res; 121 | } 122 | 123 | /** 124 | * Returns a map of success responses, keyed by the index of the request 125 | * object that produced the success. The set of this map's keys is mutually 126 | * exclusive of the keys returned by errorsByIndex. 127 | */ 128 | public Map successesByIndex() { 129 | return successesByIndex; 130 | } 131 | 132 | /** 133 | * Returns a map of error responses, keyed by the index of the request 134 | * object that produced the error. The set of this map's keys is mutually 135 | * exclusive of the keys returned by successByIndex. 136 | */ 137 | public Map errorsByIndex() { 138 | return errorsByIndex; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /java-sdk/src/main/java/io/bytom/http/SuccessMessage.java: -------------------------------------------------------------------------------- 1 | package io.bytom.http; 2 | 3 | /** 4 | * This class represents RPC success responses whose content is not meaningful. 5 | */ 6 | public class SuccessMessage { 7 | public String message; 8 | } 9 | -------------------------------------------------------------------------------- /java-sdk/src/main/resources/config.properties: -------------------------------------------------------------------------------- 1 | bytom.api.url= 2 | client.access.token= 3 | 4 | # bytom.api.url=http://10.100.7.47:9888/ 5 | # client.access.token=wt:3d17dbb953cedd53353bf3f342bb2929e9505105ffeb21670e6bd00abeef3772 6 | 7 | #bytom.api.url=http://127.0.0.1:9888/ 8 | #client.access.token=sheng:49d1623f5991c62a5094e761477ddd2838dceb49c22fbf84b492a54f1df88123 9 | -------------------------------------------------------------------------------- /java-sdk/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=debug, stdout, R 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 4 | 5 | log4j.logger.org.apache.commons.httpclient=info 6 | log4j.logger.httpclient.wire.content=info 7 | log4j.logger.httpclient.wire.header=info 8 | 9 | # Pattern to output the caller's file name and line number. 10 | log4j.appender.stdout.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n 11 | 12 | log4j.appender.R=org.apache.log4j.RollingFileAppender 13 | log4j.appender.R.File=bytom.log 14 | log4j.appender.R.MaxFileSize= 1000KB 15 | 16 | # Keep one backup file 17 | log4j.appender.R.MaxBackupIndex=1 18 | 19 | log4j.appender.R.layout=org.apache.log4j.PatternLayout 20 | log4j.appender.R.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/AppTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom; 2 | 3 | import com.google.gson.Gson; 4 | import com.squareup.okhttp.*; 5 | import io.bytom.api.Block; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Unit test for simple App. 12 | */ 13 | public class AppTest { 14 | private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); 15 | 16 | private final OkHttpClient client = new OkHttpClient(); 17 | 18 | 19 | @Test 20 | public void testListAccounts() throws IOException { 21 | 22 | String postBody = "{}"; 23 | 24 | Request request = new Request.Builder() 25 | .url("http://127.0.0.1:9888/list-accounts") 26 | .post(RequestBody.create(JSON, postBody)) 27 | .build(); 28 | 29 | Response response = client.newCall(request).execute(); 30 | 31 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 32 | 33 | System.out.println(response.body().string()); 34 | } 35 | 36 | 37 | @Test 38 | public void testCreateAccount() throws IOException { 39 | 40 | String postBody = "{\n" + 41 | " \"root_xpubs\": [\n" + 42 | " \"012454d25928d52d42e3ee1f2bebe0916974d958f9ec08c9a028043ffe3dd95630c1b788c947b8c07ede2a4b5e3e3bbe0e305bab4526a7bc67b21e1d051e74ef\"\n" + 43 | " ], \n" + 44 | " \"quorum\": 1, \n" + 45 | " \"alias\": \"sheng\"\n" + 46 | "}"; 47 | 48 | Request request = new Request.Builder() 49 | .url("http://127.0.0.1:9888/create-account") 50 | .post(RequestBody.create(JSON, postBody)) 51 | .build(); 52 | 53 | Response response = client.newCall(request).execute(); 54 | 55 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 56 | 57 | System.out.println(response.body().string()); 58 | } 59 | 60 | @Test 61 | public void testBlockGet() throws Exception { 62 | String postBody = "{\n" + 63 | " \"hash\": \"37eabf4d321f43b930f52b3af2e53b2ad2dbb234949e778213a4a54e4be04ea8\",\n" + 64 | " \"size\": 388,\n" + 65 | " \"version\": 1,\n" + 66 | " \"height\": 158,\n" + 67 | " \"previous_block_hash\": \"2cfa73d6ed14a7fcb3a8ccc2d4c45b0fd4c7754ec6fafaa0964045c23e2131cd\",\n" + 68 | " \"timestamp\": 1527241075,\n" + 69 | " \"nonce\": 0,\n" + 70 | " \"bits\": 2305843009214532812,\n" + 71 | " \"difficulty\": \"5789598940468732338727519302629705571043137272394850465663635070376277442560\",\n" + 72 | " \"transaction_merkle_root\": \"62755c4770374e696c4fecd94e022ccf6f2adc00f409ac694a5cb92fe02353eb\",\n" + 73 | " \"transaction_status_hash\": \"c9c377e5192668bc0a367e4a4764f11e7c725ecced1d7b6a492974fab1b6d5bc\",\n" + 74 | " \"transactions\": [\n" + 75 | " {\n" + 76 | " \"id\": \"5c38b8107d53cbfebd19adbd11f2839d914099a74e491f1cf75f8b18320c84e2\",\n" + 77 | " \"version\": 1,\n" + 78 | " \"size\": 77,\n" + 79 | " \"time_range\": 0,\n" + 80 | " \"inputs\": [\n" + 81 | " {\n" + 82 | " \"type\": \"coinbase\",\n" + 83 | " \"asset_id\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n" + 84 | " \"asset_definition\": {},\n" + 85 | " \"amount\": 0,\n" + 86 | " \"arbitrary\": \"c29e\"\n" + 87 | " }\n" + 88 | " ],\n" + 89 | " \"outputs\": [\n" + 90 | " {\n" + 91 | " \"type\": \"control\",\n" + 92 | " \"id\": \"9acda4f2bd83c7114a8837b596860df773a7e61b1d6b0a774addda1473361ad4\",\n" + 93 | " \"position\": 0,\n" + 94 | " \"asset_id\": \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\n" + 95 | " \"asset_definition\": {},\n" + 96 | " \"amount\": 41250000000,\n" + 97 | " \"control_program\": \"001412a47b53b3bbdaf9f3510e1d8c19d69f81ca9142\",\n" + 98 | " \"address\": \"sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h\"\n" + 99 | " }\n" + 100 | " ],\n" + 101 | " \"status_fail\": false\n" + 102 | " }\n" + 103 | " ]\n" + 104 | " }"; 105 | Gson gson = new Gson(); 106 | Block block = gson.fromJson(postBody, Block.class); 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/TestUtils.java: -------------------------------------------------------------------------------- 1 | package io.bytom; 2 | 3 | import io.bytom.common.Configuration; 4 | import io.bytom.exception.BytomException; 5 | import io.bytom.http.Client; 6 | import org.apache.log4j.Logger; 7 | 8 | /** 9 | * TestUtils provides a simplified api for testing. 10 | */ 11 | public class TestUtils { 12 | 13 | public static Logger logger = Logger.getLogger(TestUtils.class); 14 | 15 | public static Client generateClient() throws BytomException { 16 | 17 | String coreURL = Configuration.getValue("bytom.api.url"); 18 | String accessToken = Configuration.getValue("client.access.token"); 19 | 20 | if (coreURL == null || coreURL.isEmpty()) { 21 | coreURL = "http://127.0.0.1:9888"; 22 | } 23 | 24 | if (coreURL.endsWith("/")) { 25 | //split the last char "/" 26 | coreURL = coreURL.substring(0, coreURL.length()-1); 27 | } 28 | 29 | return new Client(coreURL, accessToken); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/AccessTokenTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.AccessToken; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | public class AccessTokenTest { 12 | 13 | static Client client; 14 | 15 | static { 16 | try { 17 | client = TestUtils.generateClient(); 18 | } catch (BytomException e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | 23 | @Test 24 | public void testTokenCreate() throws Exception { 25 | AccessToken accessToken = new AccessToken.Builder().setId("sheng").create(client); 26 | } 27 | 28 | 29 | @Test 30 | public void testTokenList() throws Exception { 31 | List tokenList = AccessToken.list(client); 32 | } 33 | 34 | @Test 35 | public void testTokenCheck() throws Exception { 36 | String secret = "5e37378eb59de6b10e60f2247ebf71c4955002e75e0cd31ede3bf48813a0a799"; 37 | AccessToken.check(client, "sheng", secret); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/AccountTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Account; 5 | import io.bytom.api.Receiver; 6 | import io.bytom.http.Client; 7 | import org.junit.Test; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNotNull; 14 | 15 | public class AccountTest { 16 | 17 | static Client client; 18 | static Account account; 19 | 20 | @Test 21 | public void testAccountCreate() throws Exception { 22 | client = TestUtils.generateClient(); 23 | 24 | String alias = "AccountTest.testAccountCreate.002"; 25 | Integer quorum = 1; 26 | List root_xpubs = new ArrayList<>(); 27 | root_xpubs.add("c4b25825e92cd8623de4fd6a35952ad0efb2ed215fdb1b40754f0ed12eff7827d147d1e8b003601ba2f78a4a84dcc77e93ed282633f2679048c5d5ac5ea10cb5"); 28 | 29 | 30 | // Key.Builder builder = new Key.Builder().setAlias(alias).setPassword(password); 31 | // key = Key.create(client, builder); 32 | 33 | Account.Builder builder = new Account.Builder().setAlias(alias).setQuorum(quorum).setRootXpub(root_xpubs); 34 | account = Account.create(client, builder); 35 | 36 | assertNotNull(account.id); 37 | assertEquals(alias.toLowerCase(), account.alias); 38 | } 39 | 40 | @Test 41 | public void testAccountList() throws Exception { 42 | client = TestUtils.generateClient(); 43 | List accountList = Account.list(client); 44 | } 45 | 46 | @Test 47 | public void testAccountDelete() throws Exception { 48 | client = TestUtils.generateClient(); 49 | List accountList = Account.list(client); 50 | String alias = accountList.get(accountList.size()-1).alias; 51 | //delete the last Account Object 52 | Account.delete(client, alias); 53 | } 54 | 55 | @Test 56 | public void testReceiverCreate() throws Exception { 57 | client = TestUtils.generateClient(); 58 | List accountList = Account.list(client); 59 | String alias = accountList.get(accountList.size()-1).alias; 60 | String id = accountList.get(accountList.size()-1).id; 61 | 62 | Account.ReceiverBuilder receiverBuilder = new Account.ReceiverBuilder().setAccountAlias(alias).setAccountId(id); 63 | Receiver receiver = receiverBuilder.create(client); 64 | 65 | assertNotNull(receiver.address); 66 | assertNotNull(receiver.controlProgram); 67 | } 68 | 69 | @Test 70 | public void testAddressList() throws Exception { 71 | client = TestUtils.generateClient(); 72 | List accountList = Account.list(client); 73 | String alias = accountList.get(accountList.size()-1).alias; 74 | String id = accountList.get(accountList.size()-1).id; 75 | 76 | Account.AddressBuilder addressBuilder = new Account.AddressBuilder().setAccountId(id).setAccountAlias(alias); 77 | List addressList = addressBuilder.list(client); 78 | 79 | assertNotNull(addressList); 80 | } 81 | 82 | @Test 83 | public void testAddressValidate() throws Exception { 84 | client = TestUtils.generateClient(); 85 | 86 | List accountList = Account.list(client); 87 | String alias = accountList.get(accountList.size()-1).alias; 88 | String id = accountList.get(accountList.size()-1).id; 89 | 90 | Account.AddressBuilder addressBuilder = new Account.AddressBuilder().setAccountId(id).setAccountAlias(alias); 91 | List addressList = addressBuilder.list(client); 92 | 93 | Account.Address address = addressBuilder.validate(client, addressList.get(0).address); 94 | assertEquals(true, address.is_local); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/AssetTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Account; 5 | import io.bytom.api.Asset; 6 | import io.bytom.http.Client; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertNotNull; 13 | 14 | public class AssetTest { 15 | 16 | static Client client; 17 | static Account account; 18 | static Asset asset; 19 | 20 | @Test 21 | public void testAssetCreate() throws Exception { 22 | client = TestUtils.generateClient(); 23 | 24 | List accountList = Account.list(client); 25 | String alias = "GOLD"; 26 | 27 | List xpubs = accountList.get(0).xpubs; 28 | 29 | Asset.Builder builder = new Asset.Builder() 30 | .setAlias(alias) 31 | .setQuorum(1) 32 | .setRootXpubs(xpubs); 33 | asset = builder.create(client); 34 | assertNotNull(asset); 35 | } 36 | 37 | @Test 38 | public void testAssetGet() throws Exception { 39 | client = TestUtils.generateClient(); 40 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder(); 41 | String id = queryBuilder.list(client).get(1).id; 42 | queryBuilder.setId(id); 43 | Asset asset = queryBuilder.get(client); 44 | } 45 | 46 | @Test 47 | public void testAssetList() throws Exception { 48 | client = TestUtils.generateClient(); 49 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder(); 50 | List assetList = queryBuilder.list(client); 51 | assertEquals(2, assetList.size()); 52 | } 53 | 54 | @Test 55 | public void testUpdateAssetAlias() throws Exception { 56 | client = TestUtils.generateClient(); 57 | 58 | Asset.QueryBuilder queryBuilder = new Asset.QueryBuilder(); 59 | String id = queryBuilder.list(client).get(1).id; 60 | 61 | String alias = "HELLOWORLD"; 62 | 63 | 64 | Asset.AliasUpdateBuilder aliasUpdateBuilder = 65 | new Asset.AliasUpdateBuilder() 66 | .setAlias(alias) 67 | .setAssetId(id); 68 | aliasUpdateBuilder.update(client); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/BalanceTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Balance; 5 | import io.bytom.api.Wallet; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.apache.log4j.Logger; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | import java.util.List; 13 | 14 | public class BalanceTest { 15 | 16 | static Client client; 17 | 18 | static { 19 | try { 20 | client = TestUtils.generateClient(); 21 | } catch (BytomException e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | static Balance balance; 27 | 28 | @Test 29 | public void testBalanceList() throws Exception { 30 | List balanceList = new Balance.QueryBuilder().list(client); 31 | Assert.assertNotNull(balanceList); 32 | } 33 | 34 | @Test 35 | public void testBalanceByAssetAlias() throws Exception { 36 | Balance balance = new Balance.QueryBuilder().listByAssetAlias(client, "BTM"); 37 | Assert.assertNotNull(balance); 38 | } 39 | 40 | @Test 41 | public void testBalanceByAccountAlias() throws Exception { 42 | List balanceList = new Balance.QueryBuilder().listByAccountAlias(client, "test"); 43 | Assert.assertNotNull(balanceList); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/BlockTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Asset; 5 | import io.bytom.api.Block; 6 | import io.bytom.api.Wallet; 7 | import io.bytom.exception.BytomException; 8 | import io.bytom.http.Client; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | public class BlockTest { 13 | 14 | static Client client; 15 | 16 | static { 17 | try { 18 | client = TestUtils.generateClient(); 19 | } catch (BytomException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | 24 | static Block block; 25 | static Block.BlockHeader blockHeader; 26 | static Block.BlockDifficulty blockDifficulty; 27 | static Block.BlockHashRate blockHashRate; 28 | 29 | @Test 30 | public void testBlockCountGet() throws Exception { 31 | int count = Block.getBlockCount(client); 32 | Assert.assertEquals(158, count); 33 | } 34 | 35 | @Test 36 | public void testBlockHashGet() throws Exception { 37 | String blockHash = Block.getBlockHash(client); 38 | Assert.assertNotNull(blockHash); 39 | } 40 | 41 | @Test 42 | public void testBlockGet() throws Exception { 43 | int height = Block.getBlockCount(client); 44 | String blockHash = Block.getBlockHash(client); 45 | 46 | block = new Block.QueryBuilder() 47 | .setBlockHeight(height) 48 | .setBlockHash(blockHash) 49 | .getBlock(client); 50 | } 51 | 52 | @Test 53 | public void testBlockHeader() throws Exception { 54 | int height = Block.getBlockCount(client); 55 | String blockHash = Block.getBlockHash(client); 56 | 57 | blockHeader = new Block.QueryBuilder() 58 | .setBlockHeight(height) 59 | .setBlockHash(blockHash) 60 | .getBlockHeader(client); 61 | } 62 | 63 | @Test 64 | public void testBlockDifficulty() throws Exception { 65 | int height = Block.getBlockCount(client); 66 | String blockHash = Block.getBlockHash(client); 67 | 68 | blockDifficulty = new Block.QueryBuilder() 69 | .setBlockHeight(height) 70 | .setBlockHash(blockHash) 71 | .getBlockDifficulty(client); 72 | } 73 | 74 | @Test 75 | public void testBlockHashRate() throws Exception { 76 | int height = Block.getBlockCount(client); 77 | String blockHash = Block.getBlockHash(client); 78 | 79 | blockHashRate = new Block.QueryBuilder() 80 | .setBlockHeight(height) 81 | .setBlockHash(blockHash) 82 | .getHashRate(client); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/CoreConfigTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.CoreConfig; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class CoreConfigTest { 11 | 12 | static Client client; 13 | 14 | static { 15 | try { 16 | client = TestUtils.generateClient(); 17 | } catch (BytomException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | 22 | static CoreConfig.NetInfo netInfo; 23 | 24 | static Integer gasRate; 25 | 26 | @Test 27 | public void testNetInfo() throws Exception { 28 | netInfo = CoreConfig.getNetInfo(client); 29 | Assert.assertNotNull(netInfo); 30 | } 31 | 32 | @Test 33 | public void testGasRate() throws Exception { 34 | gasRate = CoreConfig.getGasRate(client); 35 | Assert.assertNotNull(gasRate); 36 | } 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/KeyTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Key; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertNotEquals; 13 | import static org.junit.Assert.assertNotNull; 14 | 15 | public class KeyTest { 16 | 17 | static Client client; 18 | static Key key; 19 | 20 | @Test 21 | public void testClientKeyCreate() throws Exception { 22 | client = TestUtils.generateClient(); 23 | 24 | String alias = "KeyTest.testKeyCreate.successli004"; 25 | String password = "123456"; 26 | 27 | Key.Builder builder = new Key.Builder().setAlias(alias).setPassword(password); 28 | key = Key.create(client, builder); 29 | 30 | assertNotNull(key.xpub); 31 | assertEquals(alias.toLowerCase(), key.alias); 32 | } 33 | 34 | @Test 35 | public void testClientKeyList() throws Exception { 36 | //client = TestUtils.generateClient(); 37 | client = new Client("http://127.0.0.1:9888/"); 38 | List keyList = Key.list(client); 39 | } 40 | 41 | @Test 42 | public void testClientKeyDelete() throws Exception { 43 | client = TestUtils.generateClient(); 44 | List keyList = Key.list(client); 45 | String xpub = keyList.get(keyList.size()-1).xpub; 46 | //delete the last Key Object 47 | Key.delete(client, xpub, "123456"); 48 | } 49 | 50 | @Test 51 | public void testClientKeyResetPassword() throws BytomException { 52 | client = TestUtils.generateClient(); 53 | List keyList = Key.list(client); 54 | String xpub = keyList.get(keyList.size()-1).xpub; 55 | Key.resetPassword(client, xpub, "123456", "123456789"); 56 | Key.delete(client, xpub, "123456789"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/MessageTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Message; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class MessageTest { 11 | 12 | static Client client; 13 | 14 | static { 15 | try { 16 | client = TestUtils.generateClient(); 17 | } catch (BytomException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | 22 | static Message message; 23 | 24 | @Test 25 | public void testMessageSign() throws Exception { 26 | 27 | message = new Message.SignBuilder() 28 | .setAddress("sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h") 29 | .setMessage("test") 30 | .setPassword("123456") 31 | .sign(client); 32 | 33 | Assert.assertNotNull(message); 34 | } 35 | 36 | @Test 37 | public void testMessageVerify() throws Exception { 38 | String derived_xpub = "6e1efce70e2b29efa348aec7c148edc2beb72edc0d4422a03cfb0f40e6e4cfc6e6050b5863bbe84c44131280dff68614e0308a4d081e8b677d0f7f09fb3390c4"; 39 | String signature = "0d840d5b6a6df028013260e94e871c1443686c446a65db4ee93005c5395c3607feb0ac5bf583a3139c8a3d0afe757448ff49fa17ffd2377831ce5f925c846b0b"; 40 | 41 | Boolean verify = new Message.VerifyBuilder() 42 | .setDerivedXpub(derived_xpub) 43 | .setSignature(signature) 44 | .setAddress("sm1qz2j8k5anh0d0nu63pcwccxwkn7qu4y2zjwaj5h") 45 | .setMessage("test") 46 | .verifyMessage(client); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/RawTransactionTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.RawTransaction; 5 | import io.bytom.api.Wallet; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | public class RawTransactionTest { 12 | 13 | static Client client; 14 | 15 | static { 16 | try { 17 | client = TestUtils.generateClient(); 18 | } catch (BytomException e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | 23 | static RawTransaction rawTransaction; 24 | 25 | 26 | @Test 27 | public void testRawTxDecode() throws Exception { 28 | String rawTxId = "070100010161015f30e052cd50e385951936c08fb5642bd12b727da958960249ddad8c9a77e5371fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d59901000116001412a47b53b3bbdaf9f3510e1d8c19d69f81ca91426302405a8b5adebd2ab63ba1ac55a7bbadc1fe246806c37732df0442b827fa4e06058137711d9d012becdc9de507a8ad0de0dd50780c0503c0dcff2dc03d7592e31a08206e1efce70e2b29efa348aec7c148edc2beb72edc0d4422a03cfb0f40e6e4cfc602013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d3bdc6980101160014a9c0ea4abe4d09546197bac3c86f4dd39fde1afb00013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084af5f011600144cc6edb1f4077c740e0201bb3688e6efba4a098c00"; 29 | rawTransaction = RawTransaction.decode(client, rawTxId); 30 | Assert.assertNotNull(rawTransaction); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/UTXOTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.UnspentOutput; 5 | import io.bytom.exception.BytomException; 6 | import io.bytom.http.Client; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.util.List; 11 | 12 | public class UTXOTest { 13 | 14 | static Client client; 15 | 16 | static { 17 | try { 18 | client = TestUtils.generateClient(); 19 | } catch (BytomException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | 24 | 25 | @Test 26 | public void testBalanceList() throws Exception { 27 | List UTXOList = new UnspentOutput.QueryBuilder().list(client); 28 | Assert.assertNotNull(UTXOList); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/UnconfirmedTransactionTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.UnconfirmedTransaction; 5 | import io.bytom.api.Wallet; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | public class UnconfirmedTransactionTest { 12 | 13 | static Client client; 14 | 15 | static { 16 | try { 17 | client = TestUtils.generateClient(); 18 | } catch (BytomException e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | 23 | static UnconfirmedTransaction unconfirmedTransaction; 24 | static UnconfirmedTransaction.UTXResponse utxResponse; 25 | 26 | @Test 27 | public void testUCTXList() throws Exception { 28 | utxResponse = UnconfirmedTransaction.list(client); 29 | Assert.assertNotNull(utxResponse); 30 | } 31 | 32 | @Test 33 | public void testUCTXGet() throws Exception { 34 | utxResponse = UnconfirmedTransaction.list(client); 35 | unconfirmedTransaction = UnconfirmedTransaction.get(client, utxResponse.txIds.get(0)); 36 | Assert.assertNotNull(unconfirmedTransaction); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /java-sdk/src/test/java/io/bytom/integration/WalletTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.integration; 2 | 3 | import io.bytom.TestUtils; 4 | import io.bytom.api.Key; 5 | import io.bytom.api.Wallet; 6 | import io.bytom.exception.BytomException; 7 | import io.bytom.http.Client; 8 | import org.junit.Test; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | public class WalletTest { 14 | 15 | static Client client; 16 | 17 | static { 18 | try { 19 | client = TestUtils.generateClient(); 20 | } catch (BytomException e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | 25 | static Wallet wallet; 26 | 27 | @Test 28 | public void testWalletBackUp() throws Exception { 29 | 30 | wallet = Wallet.backupWallet(client); 31 | } 32 | 33 | @Test 34 | public void testWallerRestore() throws Exception { 35 | // get wallet object 36 | wallet = Wallet.backupWallet(client); 37 | //restore 38 | Wallet.restoreWallet(client, wallet.accountImage, wallet.assetImage, wallet.keyImages); 39 | } 40 | 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | io.bytom 8 | bytom-sdk 9 | 1.0.2 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 7 17 | 7 18 | 19 | 20 | 21 | 22 | 23 | java-sdk 24 | tx-signer 25 | 26 | pom 27 | 28 | bytom-sdk-java 29 | https://github.com/Bytom 30 | SDK for Bytom project in github. 31 | 32 | 33 | 34 | Apache License, Version 2.0 35 | http://www.apache.org/licenses/LICENSE-2.0.txt 36 | repo 37 | 38 | 39 | 40 | 41 | 42 | https://github.com/Bytom/bytom-java-sdk/issues 43 | GitHub Issues 44 | 45 | 46 | 47 | 48 | successli 49 | successli@outlook.com 50 | https://github.com/successli 51 | 52 | 53 | 54 | 55 | scm:git:git@github.com:Bytom/bytom-java-sdk.git 56 | scm:git:git@github.com:Bytom/bytom-java-sdk.git 57 | git@github.com:Bytom/bytom-java-sdk.git 58 | 59 | 60 | 61 | UTF-8 62 | 63 | 64 | 65 | 66 | junit 67 | junit 68 | 4.12 69 | test 70 | 71 | 72 | log4j 73 | log4j 74 | 1.2.17 75 | 76 | 77 | com.squareup.okhttp 78 | okhttp 79 | 2.5.0 80 | 81 | 82 | com.squareup.okhttp 83 | mockwebserver 84 | 2.5.0 85 | test 86 | 87 | 88 | com.google.code.gson 89 | gson 90 | 2.8.0 91 | 92 | 93 | org.bouncycastle 94 | bcpkix-jdk15on 95 | 1.56 96 | 97 | 98 | 99 | 100 | 101 | release 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-source-plugin 107 | 2.2.1 108 | 109 | 110 | attach-sources 111 | 112 | jar-no-fork 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-javadoc-plugin 120 | 2.9.1 121 | 122 | 123 | attach-javadocs 124 | 125 | jar 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-gpg-plugin 133 | 1.5 134 | 135 | 136 | sign-artifacts 137 | verify 138 | 139 | sign 140 | 141 | 142 | 143 | 144 | 145 | maven-assembly-plugin 146 | 2.3 147 | 148 | 149 | jar-with-dependencies 150 | 151 | 152 | 153 | 154 | package 155 | 156 | single 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | oss 169 | https://oss.sonatype.org/content/repositories/snapshots 170 | 171 | 172 | oss 173 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /tx-signer/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # tx-signer 4 | 5 | Java implementation of signing transaction offline. 6 | 7 | ## Pre 8 | 9 | #### Add dependency to your project 10 | 11 | 1. first get source code 12 | 13 | ``` 14 | git clone https://github.com/Bytom/bytom-java-sdk.git 15 | ``` 16 | 17 | 2. install to maven repository 18 | 19 | ``` 20 | $ mvn clean install -DskipTests 21 | ``` 22 | 23 | 3. add dependency 24 | ```xml 25 | 26 | io.bytom 27 | tx-signer 28 | 1.0.2 29 | 30 | ``` 31 | 32 | ## Example 33 | 34 | #### build transaction with spend input 35 | ``` 36 | String btmAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; 37 | 38 | // create spend input 39 | SpendInput input = new SpendInput(); 40 | input.setAssetId(btmAssetID); 41 | input.setAmount(9800000000L); 42 | 43 | // control program of spend utxo 44 | input.setProgram("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"); 45 | 46 | // source position of spend utxo 47 | input.setSourcePosition(2); 48 | 49 | // source id of spend utxo 50 | input.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea"); 51 | 52 | // is the spend utxo used for change 53 | input.setChange(true); 54 | 55 | // BIP protocol for derived paths, default BIP44 56 | input.setBipProtocol(BIPProtocol.BIP32); 57 | 58 | // contorl program index of spend utxo 59 | input.setControlProgramIndex(457); 60 | 61 | // account index 62 | input.setKeyIndex(1); 63 | 64 | // provide a root private key for signing 65 | input.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); 66 | 67 | // build transaction with signature 68 | Transaction tx = new Transaction.Builder() 69 | .addInput(input) 70 | .addOutput(new Output(btmAssetID, 8800000000L, "0014a82f02bc37bc5ed87d5f9fca02f8a6a7d89cdd5c")) 71 | .addOutput(new Output(btmAssetID, 900000000L, "00200824e931fb806bd77fdcd291aad3bd0a4493443a4120062bd659e64a3e0bac66")) 72 | .setTimeRange(0) 73 | .build(); 74 | 75 | String rawTransaction = tx.rawTransaction(); 76 | ``` 77 | 78 | #### build transaction with issuance input 79 | ``` 80 | IssuanceInput issuanceInput = new IssuanceInput(); 81 | issuanceInput.setAssetId("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14"); 82 | issuanceInput.setAmount(100000000000L); 83 | 84 | // issuance program 85 | issuanceInput.setProgram("ae2054a71277cc162eb3eb21b5bd9fe54402829a53b294deaed91692a2cd8a081f9c5151ad"); 86 | issuanceInput.setNonce("ac9d5a527f5ab00a"); 87 | 88 | // asset index 89 | issuanceInput.setKeyIndex(5); 90 | 91 | // raw asset definition 92 | issuanceInput.setRawAssetDefinition("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d"); 93 | 94 | // provide a root private key for signing 95 | issuanceInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); 96 | 97 | SpendInput spendInput = new SpendInput(); 98 | spendInput.setAssetId(btmAssetID); 99 | spendInput.setAmount(9800000000L); 100 | spendInput.setProgram("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"); 101 | spendInput.setKeyIndex(1); 102 | spendInput.setChange(true); 103 | spendInput.setSourceID("4b5cb973f5bef4eadde4c89b92ee73312b940e84164da0594149554cc8a2adea"); 104 | spendInput.setSourcePosition(2); 105 | spendInput.setControlProgramIndex(457); 106 | spendInput.setRootPrivateKey("4864bae85cf38bfbb347684abdbc01e311a24f99e2c7fe94f3c071d9c83d8a5a349722316972e382c339b79b7e1d83a565c6b3e7cf46847733a47044ae493257"); 107 | 108 | Transaction tx = new Transaction.Builder() 109 | .addInput(issuanceInput) 110 | .addInput(spendInput) 111 | .addOutput(new Output("7b38dc897329a288ea31031724f5c55bcafec80468a546955023380af2faad14", 100000000000L, "001437e1aec83a4e6587ca9609e4e5aa728db7007449")) 112 | .addOutput(new Output(btmAssetID, 9700000000L, "00148be1104e04734e5edaba5eea2e85793896b77c56")) 113 | .setTimeRange(0) 114 | .build(); 115 | 116 | String rawTx = tx.rawTransaction(); 117 | 118 | ``` -------------------------------------------------------------------------------- /tx-signer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bytom-sdk 7 | io.bytom 8 | 1.0.2 9 | 10 | 4.0.0 11 | 12 | tx-signer 13 | 1.0.2 14 | 15 | 16 | 17 | 18 | com.google.crypto.tink 19 | tink 20 | 1.1.1 21 | 22 | 23 | com.google.protobuf 24 | protobuf-java 25 | 26 | 27 | 28 | 29 | 30 | org.bouncycastle 31 | bcprov-ext-jdk15on 32 | 1.55 33 | 34 | 35 | 36 | org.bouncycastle 37 | bcprov-jdk15on 38 | 1.59 39 | 40 | 41 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/com/google/crypto/tink/subtle/Bytes.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.crypto.tink.subtle; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.security.GeneralSecurityException; 21 | import java.util.Arrays; 22 | 23 | /** 24 | * Helper methods that deal with byte arrays. 25 | * 26 | * @since 1.0.0 27 | */ 28 | public final class Bytes { 29 | /** 30 | * Best effort fix-timing array comparison. 31 | * 32 | * @return true if two arrays are equal. 33 | */ 34 | public static final boolean equal(final byte[] x, final byte[] y) { 35 | if (x == null || y == null) { 36 | return false; 37 | } 38 | if (x.length != y.length) { 39 | return false; 40 | } 41 | int res = 0; 42 | for (int i = 0; i < x.length; i++) { 43 | res |= x[i] ^ y[i]; 44 | } 45 | return res == 0; 46 | } 47 | 48 | /** 49 | * Returns the concatenation of the input arrays in a single array. For example, {@code concat(new 50 | * byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, c}}. 51 | * 52 | * @return a single array containing all the values from the source arrays, in order 53 | */ 54 | public static byte[] concat(byte[]... chunks) throws GeneralSecurityException { 55 | int length = 0; 56 | for (byte[] chunk : chunks) { 57 | if (length > Integer.MAX_VALUE - chunk.length) { 58 | throw new GeneralSecurityException("exceeded size limit"); 59 | } 60 | length += chunk.length; 61 | } 62 | byte[] res = new byte[length]; 63 | int pos = 0; 64 | for (byte[] chunk : chunks) { 65 | System.arraycopy(chunk, 0, res, pos, chunk.length); 66 | pos += chunk.length; 67 | } 68 | return res; 69 | } 70 | 71 | /** 72 | * Computes the xor of two byte arrays, specifying offsets and the length to xor. 73 | * 74 | * @return a new byte[] of length len. 75 | */ 76 | public static final byte[] xor( 77 | final byte[] x, int offsetX, final byte[] y, int offsetY, int len) { 78 | if (len < 0 || x.length - len < offsetX || y.length - len < offsetY) { 79 | throw new IllegalArgumentException( 80 | "That combination of buffers, offsets and length to xor result in out-of-bond accesses."); 81 | } 82 | byte[] res = new byte[len]; 83 | for (int i = 0; i < len; i++) { 84 | res[i] = (byte) (x[i + offsetX] ^ y[i + offsetY]); 85 | } 86 | return res; 87 | } 88 | 89 | /** 90 | * Computes the xor of two byte buffers, specifying the length to xor, and 91 | * stores the result to {@code output}. 92 | * 93 | * @return a new byte[] of length len. 94 | */ 95 | public static final void xor(ByteBuffer output, ByteBuffer x, ByteBuffer y, int len) { 96 | if (len < 0 || x.remaining() < len || y.remaining() < len || output.remaining() < len) { 97 | throw new IllegalArgumentException( 98 | "That combination of buffers, offsets and length to xor result in out-of-bond accesses."); 99 | } 100 | for (int i = 0; i < len; i++) { 101 | output.put((byte) (x.get() ^ y.get())); 102 | } 103 | } 104 | 105 | /** 106 | * Computes the xor of two byte arrays of equal size. 107 | * 108 | * @return a new byte[] of length x.length. 109 | */ 110 | public static final byte[] xor(final byte[] x, final byte[] y) { 111 | if (x.length != y.length) { 112 | throw new IllegalArgumentException("The lengths of x and y should match."); 113 | } 114 | return xor(x, 0, y, 0, x.length); 115 | } 116 | 117 | /** 118 | * xors b to the end of a. 119 | * 120 | * @return a new byte[] of length x.length. 121 | */ 122 | public static final byte[] xorEnd(final byte[] a, final byte[] b) { 123 | if (a.length < b.length) { 124 | throw new IllegalArgumentException("xorEnd requires a.length >= b.length"); 125 | } 126 | int paddingLength = a.length - b.length; 127 | byte[] res = Arrays.copyOf(a, a.length); 128 | for (int i = 0; i < b.length; i++) { 129 | res[paddingLength + i] ^= b[i]; 130 | } 131 | return res; 132 | } 133 | 134 | // TODO(thaidn): add checks for boundary conditions/overflows. 135 | 136 | /** 137 | * Transforms a passed value to a LSB first byte array with the size of the specified capacity 138 | * 139 | * @param capacity size of the resulting byte array 140 | * @param value that should be represented as a byte array 141 | */ 142 | public static byte[] intToByteArray(int capacity, int value) { 143 | final byte[] result = new byte[capacity]; 144 | for (int i = 0; i < capacity; i++) { 145 | result[i] = (byte) ((value >> (8 * i)) & 0xFF); 146 | } 147 | return result; 148 | } 149 | 150 | /** 151 | * Transforms a passed LSB first byte array to an int 152 | * 153 | * @param bytes that should be transformed to a byte array 154 | */ 155 | public static int byteArrayToInt(byte[] bytes) { 156 | return byteArrayToInt(bytes, bytes.length); 157 | } 158 | 159 | /** 160 | * Transforms a passed LSB first byte array to an int 161 | * 162 | * @param bytes that should be transformed to a byte array 163 | * @param length amount of the passed {@code bytes} that should be transformed 164 | */ 165 | public static int byteArrayToInt(byte[] bytes, int length) { 166 | return byteArrayToInt(bytes, 0, length); 167 | } 168 | 169 | /** 170 | * Transforms a passed LSB first byte array to an int 171 | * 172 | * @param bytes that should be transformed to a byte array 173 | * @param offset start index to start the transformation 174 | * @param length amount of the passed {@code bytes} that should be transformed 175 | */ 176 | public static int byteArrayToInt(byte[] bytes, int offset, int length) { 177 | int value = 0; 178 | for (int i = 0; i < length; i++) { 179 | value += (bytes[i + offset] & 0xFF) << (i * 8); 180 | } 181 | return value; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/com/google/crypto/tink/subtle/Ed25519Constants.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.crypto.tink.subtle; 18 | 19 | import java.math.BigInteger; 20 | 21 | /** 22 | * Constants used in {@link Ed25519}. 23 | */ 24 | final class Ed25519Constants { 25 | 26 | // d = -121665 / 121666 mod 2^255-19 27 | static final long[] D; 28 | // 2d 29 | static final long[] D2; 30 | // 2^((p-1)/4) mod p where p = 2^255-19 31 | static final long[] SQRTM1; 32 | 33 | /** 34 | * Base point for the Edwards twisted curve = (x, 4/5) and its exponentiations. B_TABLE[i][j] = 35 | * (j+1)*256^i*B for i in [0, 32) and j in [0, 8). Base point B = B_TABLE[0][0] 36 | */ 37 | static final Ed25519.CachedXYT[][] B_TABLE; 38 | static final Ed25519.CachedXYT[] B2; 39 | 40 | private static final BigInteger P_BI = 41 | BigInteger.valueOf(2).pow(255).subtract(BigInteger.valueOf(19)); 42 | private static final BigInteger D_BI = 43 | BigInteger.valueOf(-121665).multiply(BigInteger.valueOf(121666).modInverse(P_BI)).mod(P_BI); 44 | private static final BigInteger D2_BI = BigInteger.valueOf(2).multiply(D_BI).mod(P_BI); 45 | private static final BigInteger SQRTM1_BI = 46 | BigInteger.valueOf(2).modPow(P_BI.subtract(BigInteger.ONE).divide(BigInteger.valueOf(4)), P_BI); 47 | 48 | private static class Point { 49 | private BigInteger x; 50 | private BigInteger y; 51 | } 52 | 53 | private static BigInteger recoverX(BigInteger y) { 54 | // x^2 = (y^2 - 1) / (d * y^2 + 1) mod 2^255-19 55 | BigInteger xx = 56 | y.pow(2) 57 | .subtract(BigInteger.ONE) 58 | .multiply(D_BI.multiply(y.pow(2)).add(BigInteger.ONE).modInverse(P_BI)); 59 | BigInteger x = xx.modPow(P_BI.add(BigInteger.valueOf(3)).divide(BigInteger.valueOf(8)), P_BI); 60 | if (!x.pow(2).subtract(xx).mod(P_BI).equals(BigInteger.ZERO)) { 61 | x = x.multiply(SQRTM1_BI).mod(P_BI); 62 | } 63 | if (x.testBit(0)) { 64 | x = P_BI.subtract(x); 65 | } 66 | return x; 67 | } 68 | 69 | private static Point edwards(Point a, Point b) { 70 | Point o = new Point(); 71 | BigInteger xxyy = D_BI.multiply(a.x.multiply(b.x).multiply(a.y).multiply(b.y)).mod(P_BI); 72 | o.x = 73 | (a.x.multiply(b.y).add(b.x.multiply(a.y))) 74 | .multiply(BigInteger.ONE.add(xxyy).modInverse(P_BI)) 75 | .mod(P_BI); 76 | o.y = 77 | (a.y.multiply(b.y).add(a.x.multiply(b.x))) 78 | .multiply(BigInteger.ONE.subtract(xxyy).modInverse(P_BI)) 79 | .mod(P_BI); 80 | return o; 81 | } 82 | 83 | private static byte[] toLittleEndian(BigInteger n) { 84 | byte[] b = new byte[32]; 85 | byte[] nBytes = n.toByteArray(); 86 | System.arraycopy(nBytes, 0, b, 32 - nBytes.length, nBytes.length); 87 | for (int i = 0; i < b.length / 2; i++) { 88 | byte t = b[i]; 89 | b[i] = b[b.length - i - 1]; 90 | b[b.length - i - 1] = t; 91 | } 92 | return b; 93 | } 94 | 95 | private static Ed25519.CachedXYT getCachedXYT(Point p) { 96 | return new Ed25519.CachedXYT( 97 | Field25519.expand(toLittleEndian(p.y.add(p.x).mod(P_BI))), 98 | Field25519.expand(toLittleEndian(p.y.subtract(p.x).mod(P_BI))), 99 | Field25519.expand(toLittleEndian(D2_BI.multiply(p.x).multiply(p.y).mod(P_BI)))); 100 | } 101 | 102 | static { 103 | Point b = new Point(); 104 | b.y = BigInteger.valueOf(4).multiply(BigInteger.valueOf(5).modInverse(P_BI)).mod(P_BI); 105 | b.x = recoverX(b.y); 106 | 107 | D = Field25519.expand(toLittleEndian(D_BI)); 108 | D2 = Field25519.expand(toLittleEndian(D2_BI)); 109 | SQRTM1 = Field25519.expand(toLittleEndian(SQRTM1_BI)); 110 | 111 | Point bi = b; 112 | B_TABLE = new Ed25519.CachedXYT[32][8]; 113 | for (int i = 0; i < 32; i++) { 114 | Point bij = bi; 115 | for (int j = 0; j < 8; j++) { 116 | B_TABLE[i][j] = getCachedXYT(bij); 117 | bij = edwards(bij, bi); 118 | } 119 | for (int j = 0; j < 8; j++) { 120 | bi = edwards(bi, bi); 121 | } 122 | } 123 | bi = b; 124 | Point b2 = edwards(b, b); 125 | B2 = new Ed25519.CachedXYT[8]; 126 | for (int i = 0; i < 8; i++) { 127 | B2[i] = getCachedXYT(bi); 128 | bi = edwards(bi, b2); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/BIPProtocol.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | public enum BIPProtocol { 4 | 5 | BIP32, 6 | BIP44 7 | } 8 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/BaseInput.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.Utils; 4 | import io.bytom.offline.types.*; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | public abstract class BaseInput { 11 | 12 | static final int ISSUANCE_INPUT_TYPE = 0; 13 | static final int SPEND_INPUT_TYPE = 1; 14 | 15 | static final byte AssetKeySpace = 0; 16 | static final byte AccountKeySpace = 1; 17 | 18 | private String inputID; 19 | 20 | /** 21 | * The number of units of the asset being issued or spent. 22 | */ 23 | private Long amount; 24 | 25 | /** 26 | * The id of the asset being issued or spent. 27 | */ 28 | private String assetId; 29 | 30 | /** 31 | * The program which must be satisfied to transfer this output. 32 | * it represents control program when is SpendInput, it represents issuance program when is IssuanceInput 33 | */ 34 | private String program; 35 | 36 | private int vmVersion = 1; 37 | 38 | /** 39 | * used for bip32 derived path. 40 | * it represents accountIndex when is SpendInput, it represents assetIndex when is IssuanceInput 41 | */ 42 | private Integer keyIndex; 43 | 44 | WitnessComponent witnessComponent = new WitnessComponent(); 45 | 46 | public BaseInput() {} 47 | 48 | public abstract InputEntry toInputEntry(Map entryMap, int index); 49 | public abstract void buildWitness(String txID) throws Exception; 50 | public abstract byte[] serializeInputCommitment() throws IOException; 51 | public abstract byte[] serializeInputWitness() throws IOException; 52 | 53 | public byte[] serializeInput() throws IOException { 54 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 55 | // assert version 56 | Utils.writeVarint(1, stream); 57 | Utils.writeExtensibleString(serializeInputCommitment(), stream); 58 | Utils.writeExtensibleString(serializeInputWitness(), stream); 59 | return stream.toByteArray(); 60 | } 61 | 62 | 63 | public void validate() { 64 | if (assetId == null) { 65 | throw new IllegalArgumentException("the asset id of input must be specified."); 66 | } 67 | if (amount == null) { 68 | throw new IllegalArgumentException("the amount id of input must be specified."); 69 | } 70 | if (program == null) { 71 | throw new IllegalArgumentException("the program id of input must be specified."); 72 | } 73 | if (keyIndex == null) { 74 | throw new IllegalArgumentException("the key index of input must be specified."); 75 | } 76 | if (witnessComponent.getRootPrivateKey() == null) { 77 | throw new IllegalArgumentException("the root private key of input must be specified."); 78 | } 79 | } 80 | 81 | public AssetAmount getAssetAmount() { 82 | return new AssetAmount(new AssetID(this.assetId), this.amount); 83 | } 84 | 85 | public void setRootPrivateKey(String prvKey) { 86 | witnessComponent.setRootPrivateKey(prvKey); 87 | } 88 | 89 | public String getInputID() { 90 | return inputID; 91 | } 92 | 93 | public void setInputID(String inputID) { 94 | this.inputID = inputID; 95 | } 96 | 97 | public long getAmount() { 98 | return amount; 99 | } 100 | 101 | public void setAmount(long amount) { 102 | this.amount = amount; 103 | } 104 | 105 | public String getAssetId() { 106 | return assetId; 107 | } 108 | 109 | public void setAssetId(String assetId) { 110 | this.assetId = assetId; 111 | } 112 | 113 | public String getProgram() { 114 | return program; 115 | } 116 | 117 | public void setProgram(String program) { 118 | this.program = program; 119 | } 120 | 121 | public int getVmVersion() { 122 | return vmVersion; 123 | } 124 | 125 | public void setVmVersion(int vmVersion) { 126 | this.vmVersion = vmVersion; 127 | } 128 | 129 | public int getKeyIndex() { 130 | return keyIndex; 131 | } 132 | 133 | public void setKeyIndex(int keyIndex) { 134 | this.keyIndex = keyIndex; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/IssuanceInput.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.DerivePrivateKey; 4 | import io.bytom.offline.common.ExpandedPrivateKey; 5 | import io.bytom.offline.common.Signer; 6 | import io.bytom.offline.common.Utils; 7 | import io.bytom.offline.types.*; 8 | import io.bytom.offline.util.SHA3Util; 9 | import org.bouncycastle.util.encoders.Hex; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.security.SecureRandom; 13 | import java.util.Map; 14 | 15 | public class IssuanceInput extends BaseInput { 16 | 17 | private String nonce; 18 | 19 | private String rawAssetDefinition; 20 | 21 | public IssuanceInput() {} 22 | 23 | public IssuanceInput(String assetID, Long amount, String issuanceProgram) { 24 | this.setAssetId(assetID); 25 | this.setAmount(amount); 26 | this.setProgram(issuanceProgram); 27 | } 28 | 29 | public IssuanceInput(String assetID, Long amount, String issuanceProgram, String nonce, String rawAssetDefinition) { 30 | this(assetID, amount, issuanceProgram); 31 | this.nonce = nonce; 32 | this.rawAssetDefinition = rawAssetDefinition; 33 | } 34 | 35 | @Override 36 | public InputEntry toInputEntry(Map entryMap, int index) { 37 | if (this.nonce == null) { 38 | SecureRandom sr = new SecureRandom(); 39 | byte[] randBytes = new byte[8]; 40 | sr.nextBytes(randBytes); 41 | this.nonce = Hex.toHexString(randBytes); 42 | } 43 | 44 | Hash nonceHash = new Hash(SHA3Util.hashSha256(Hex.decode(this.nonce))); 45 | Hash assetDefHash = new Hash(this.rawAssetDefinition); 46 | AssetAmount value = this.getAssetAmount(); 47 | 48 | Program program = new Program(this.getVmVersion(), Hex.decode(this.getProgram())); 49 | return new Issue(nonceHash, value, index, new AssetDefinition(assetDefHash, program)); 50 | } 51 | 52 | @Override 53 | public void buildWitness(String txID) throws Exception { 54 | String rootPrivateKey = witnessComponent.getRootPrivateKey(); 55 | byte[] childPrivateKey = DerivePrivateKey.bip32derivePrvKey(rootPrivateKey, getKeyIndex(), AssetKeySpace); 56 | 57 | byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID)); 58 | byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(childPrivateKey); 59 | byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message); 60 | 61 | witnessComponent.appendWitness(Hex.toHexString(sig)); 62 | } 63 | 64 | @Override 65 | public byte[] serializeInputCommitment() throws IOException { 66 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 67 | Utils.writeVarint(ISSUANCE_INPUT_TYPE, stream); 68 | Utils.writeVarStr(Hex.decode(nonce), stream); 69 | stream.write(Hex.decode(getAssetId())); 70 | Utils.writeVarint(getAmount(), stream); 71 | return stream.toByteArray(); 72 | } 73 | 74 | @Override 75 | public byte[] serializeInputWitness() throws IOException { 76 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 77 | Utils.writeVarStr(Hex.decode(rawAssetDefinition), stream); 78 | // vm version 79 | Utils.writeVarint(1, stream); 80 | Utils.writeVarStr(Hex.decode(getProgram()), stream); 81 | Utils.writeVarList(witnessComponent.toByteArray(), stream); 82 | return stream.toByteArray(); 83 | } 84 | 85 | @Override 86 | public void validate() { 87 | super.validate(); 88 | if (nonce == null) { 89 | throw new IllegalArgumentException("the nonce of issuance input must be specified."); 90 | } 91 | if (rawAssetDefinition == null) { 92 | throw new IllegalArgumentException("the nonce of issuance input must be specified."); 93 | } 94 | } 95 | 96 | public String getNonce() { 97 | return nonce; 98 | } 99 | 100 | public void setNonce(String nonce) { 101 | this.nonce = nonce; 102 | } 103 | 104 | public String getRawAssetDefinition() { 105 | return rawAssetDefinition; 106 | } 107 | 108 | public void setRawAssetDefinition(String rawAssetDefinition) { 109 | this.rawAssetDefinition = rawAssetDefinition; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/Output.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.Utils; 4 | import io.bytom.offline.common.VMUtil; 5 | import org.bouncycastle.util.encoders.Hex; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | 9 | public class Output { 10 | 11 | /** 12 | * The number of units of the asset being controlled. 13 | */ 14 | private Long amount; 15 | 16 | /** 17 | * The id of the asset being controlled. 18 | */ 19 | private String assetId; 20 | 21 | /** 22 | * The control program which must be satisfied to transfer this output. 23 | */ 24 | private String controlProgram; 25 | 26 | /** 27 | * The id of the output. 28 | */ 29 | private String id; 30 | 31 | /** 32 | * The output's position in a transaction's list of outputs. 33 | */ 34 | private Integer position; 35 | 36 | public Output(String assetID, long amount, String controlProgram) { 37 | this.assetId = assetID; 38 | this.amount = amount; 39 | this.controlProgram = controlProgram; 40 | } 41 | 42 | public static Output newRetireOutput(String assetID, long amount, String arbitrary) { 43 | String retireProgram = Hex.toHexString(new byte[]{VMUtil.OP_FAIL}) + Hex.toHexString(VMUtil.pushDataBytes(Hex.decode(arbitrary))); 44 | return new Output(assetID, amount, retireProgram); 45 | } 46 | 47 | public byte[] serializeOutput() throws IOException { 48 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 49 | 50 | //assetVersion 51 | Utils.writeVarint(1, stream); //AssetVersion是否默认为1 52 | //outputCommit 53 | ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream(); 54 | //assetId 55 | outputCommitSteam.write(Hex.decode(assetId)); 56 | //amount 57 | Utils.writeVarint(amount, outputCommitSteam); 58 | //vmVersion 59 | Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version 60 | //controlProgram 61 | Utils.writeVarStr(Hex.decode(controlProgram), outputCommitSteam); 62 | 63 | Utils.writeExtensibleString(outputCommitSteam.toByteArray(), stream); 64 | 65 | //outputWitness 66 | Utils.writeVarint(0, stream); 67 | return stream.toByteArray(); 68 | } 69 | 70 | public Long getAmount() { 71 | return amount; 72 | } 73 | 74 | public void setAmount(Long amount) { 75 | this.amount = amount; 76 | } 77 | 78 | public String getAssetId() { 79 | return assetId; 80 | } 81 | 82 | public void setAssetId(String assetId) { 83 | this.assetId = assetId; 84 | } 85 | 86 | public String getControlProgram() { 87 | return controlProgram; 88 | } 89 | 90 | public void setControlProgram(String controlProgram) { 91 | this.controlProgram = controlProgram; 92 | } 93 | 94 | public String getId() { 95 | return id; 96 | } 97 | 98 | public void setId(String id) { 99 | this.id = id; 100 | } 101 | 102 | public Integer getPosition() { 103 | return position; 104 | } 105 | 106 | public void setPosition(Integer position) { 107 | this.position = position; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/SpendInput.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.*; 4 | import io.bytom.offline.types.*; 5 | import org.bouncycastle.util.encoders.Hex; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | public class SpendInput extends BaseInput { 11 | 12 | private String sourceID; 13 | 14 | private Integer sourcePosition; 15 | 16 | private Boolean change; 17 | 18 | private Integer controlProgramIndex; 19 | 20 | private BIPProtocol bipProtocol = BIPProtocol.BIP44; 21 | 22 | public SpendInput() {} 23 | 24 | public SpendInput(String assetID, long amount, String controlProgram) { 25 | this.setAssetId(assetID); 26 | this.setAmount(amount); 27 | this.setProgram(controlProgram); 28 | } 29 | 30 | public SpendInput(String assetID, long amount, String controlProgram, String sourceID, Integer sourcePosition) { 31 | this(assetID, amount, controlProgram); 32 | this.sourceID = sourceID; 33 | this.sourcePosition = sourcePosition; 34 | } 35 | 36 | @Override 37 | public InputEntry toInputEntry(Map entryMap, int index) { 38 | Program pro = new Program(this.getVmVersion(), Hex.decode(this.getProgram())); 39 | AssetAmount assetAmount = this.getAssetAmount(); 40 | Hash sourceID = new Hash(this.sourceID); 41 | ValueSource src = new ValueSource(sourceID, assetAmount, this.sourcePosition); 42 | 43 | OutputEntry prevOut = new OutputEntry(src, pro, 0); 44 | Hash prevOutID = prevOut.entryID(); 45 | entryMap.put(prevOutID, prevOut); 46 | return new Spend(prevOutID, index); 47 | } 48 | 49 | @Override 50 | public void buildWitness(String txID) throws Exception { 51 | String rootPrvKey = witnessComponent.getRootPrivateKey(); 52 | 53 | byte[] privateChild; 54 | if (bipProtocol == BIPProtocol.BIP44) { 55 | privateChild = DerivePrivateKey.bip44derivePrvKey(rootPrvKey, getKeyIndex(), change, controlProgramIndex); 56 | } else { 57 | privateChild = DerivePrivateKey.bip32derivePrvKey(rootPrvKey, getKeyIndex(), AccountKeySpace, controlProgramIndex); 58 | } 59 | 60 | byte[] message = Utils.hashFn(Hex.decode(getInputID()), Hex.decode(txID)); 61 | byte[] expandedPrivateKey = ExpandedPrivateKey.expandedPrivateKey(privateChild); 62 | byte[] sig = Signer.ed25519InnerSign(expandedPrivateKey, message); 63 | 64 | witnessComponent.appendWitness(Hex.toHexString(sig)); 65 | 66 | byte[] deriveXpub = DeriveXpub.deriveXpub(privateChild); 67 | String pubKey = Hex.toHexString(deriveXpub).substring(0, 64); 68 | 69 | witnessComponent.appendWitness(pubKey); 70 | } 71 | 72 | @Override 73 | public byte[] serializeInputCommitment() throws IOException { 74 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 75 | Utils.writeVarint(SPEND_INPUT_TYPE, stream); 76 | 77 | ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream(); 78 | spendCommitSteam.write(Hex.decode(sourceID)); 79 | spendCommitSteam.write(Hex.decode(getAssetId())); 80 | Utils.writeVarint(getAmount(), spendCommitSteam); 81 | Utils.writeVarint(sourcePosition, spendCommitSteam); 82 | // vm version 83 | Utils.writeVarint(1, spendCommitSteam); 84 | Utils.writeVarStr(Hex.decode(getProgram()), spendCommitSteam); 85 | Utils.writeExtensibleString(spendCommitSteam.toByteArray(), stream); 86 | return stream.toByteArray(); 87 | } 88 | 89 | @Override 90 | public byte[] serializeInputWitness() throws IOException { 91 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 92 | Utils.writeVarList(witnessComponent.toByteArray(), stream); 93 | return stream.toByteArray(); 94 | } 95 | 96 | @Override 97 | public void validate() { 98 | super.validate(); 99 | if (sourceID == null) { 100 | throw new IllegalArgumentException("the source id of spend input must be specified."); 101 | } 102 | if (sourcePosition == null) { 103 | throw new IllegalArgumentException("the source position of spend input must be specified."); 104 | } 105 | if (change == null) { 106 | throw new IllegalArgumentException("the change of spend input must be specified."); 107 | } 108 | if (controlProgramIndex == null) { 109 | throw new IllegalArgumentException("the control program index of spend input must be specified."); 110 | } 111 | } 112 | 113 | public void setSourceID(String sourceID) { 114 | this.sourceID = sourceID; 115 | } 116 | 117 | public void setSourcePosition(int sourcePosition) { 118 | this.sourcePosition = sourcePosition; 119 | } 120 | 121 | public void setChange(boolean change) { 122 | this.change = change; 123 | } 124 | 125 | public void setControlProgramIndex(int controlProgramIndex) { 126 | this.controlProgramIndex = controlProgramIndex; 127 | } 128 | 129 | public void setBipProtocol(BIPProtocol bipProtocol) { 130 | this.bipProtocol = bipProtocol; 131 | } 132 | } -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/Transaction.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.Utils; 4 | import io.bytom.offline.exception.MapTransactionException; 5 | import io.bytom.offline.exception.SerializeTransactionException; 6 | import io.bytom.offline.exception.SignTransactionException; 7 | import io.bytom.offline.types.*; 8 | import org.bouncycastle.util.encoders.Hex; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * Created by liqiang on 2018/10/24. 18 | */ 19 | 20 | public class Transaction { 21 | 22 | private String txID; 23 | /** 24 | * version 25 | */ 26 | private Integer version; 27 | /** 28 | * size 29 | */ 30 | private Integer size; 31 | /** 32 | * time_range 33 | */ 34 | private Integer timeRange; 35 | 36 | /** 37 | * List of specified inputs for a transaction. 38 | */ 39 | private List inputs; 40 | 41 | /** 42 | * List of specified outputs for a transaction. 43 | */ 44 | private List outputs; 45 | 46 | public Transaction(Builder builder) { 47 | this.inputs = builder.inputs; 48 | this.outputs = builder.outputs; 49 | this.version = builder.version; 50 | this.size = builder.size; 51 | this.timeRange = builder.timeRange; 52 | 53 | this.validate(); 54 | this.mapTx(); 55 | this.sign(); 56 | } 57 | 58 | public static class Builder { 59 | 60 | private Integer version = 1; 61 | 62 | private Integer size = 0; 63 | 64 | private Integer timeRange; 65 | 66 | private List inputs; 67 | private List outputs; 68 | 69 | public Builder() { 70 | this.inputs = new ArrayList<>(); 71 | this.outputs = new ArrayList<>(); 72 | } 73 | 74 | public Builder addInput(BaseInput input) { 75 | this.inputs.add(input); 76 | return this; 77 | } 78 | 79 | public Builder addOutput(Output output) { 80 | this.outputs.add(output); 81 | return this; 82 | } 83 | 84 | public Builder setTimeRange(int timeRange) { 85 | this.timeRange = timeRange; 86 | return this; 87 | } 88 | 89 | public Transaction build() { 90 | return new Transaction(this); 91 | } 92 | } 93 | 94 | private void sign() { 95 | for (BaseInput input : inputs) { 96 | try { 97 | input.buildWitness(txID); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | throw new SignTransactionException(e); 101 | } 102 | } 103 | } 104 | 105 | public String rawTransaction() { 106 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 107 | try { 108 | stream.write(7); 109 | 110 | Utils.writeVarint(version, stream); 111 | 112 | Utils.writeVarint(timeRange, stream); 113 | 114 | Utils.writeVarint(inputs.size(), stream); 115 | for (BaseInput input : inputs) { 116 | stream.write(input.serializeInput()); 117 | } 118 | 119 | Utils.writeVarint(outputs.size(), stream); 120 | for (Output output : outputs) { 121 | stream.write(output.serializeOutput()); 122 | } 123 | } catch (IOException e) { 124 | e.printStackTrace(); 125 | throw new SerializeTransactionException(e); 126 | } 127 | return Hex.toHexString(stream.toByteArray()); 128 | } 129 | 130 | private void validate() { 131 | if (version == null) { 132 | throw new IllegalArgumentException("the version of transaction must be specified."); 133 | } 134 | if (timeRange == null) { 135 | throw new IllegalArgumentException("the time range of transaction must be specified."); 136 | } 137 | if (size == null) { 138 | throw new IllegalArgumentException("the size range of transaction must be specified."); 139 | } 140 | 141 | for (BaseInput input : inputs) { 142 | input.validate(); 143 | } 144 | } 145 | 146 | private void mapTx() { 147 | Map entryMap = new HashMap<>(); 148 | ValueSource[] muxSources = new ValueSource[inputs.size()]; 149 | List inputEntries = new ArrayList<>(); 150 | 151 | try { 152 | for (int i = 0; i < inputs.size(); i++) { 153 | BaseInput input = inputs.get(i); 154 | InputEntry inputEntry = input.toInputEntry(entryMap, i); 155 | Hash spendID = addEntry(entryMap, inputEntry); 156 | input.setInputID(spendID.toString()); 157 | 158 | muxSources[i] = new ValueSource(spendID, input.getAssetAmount(), 0); 159 | inputEntries.add(inputEntry); 160 | } 161 | 162 | Mux mux = new Mux(muxSources, new Program(1, new byte[]{0x51})); 163 | Hash muxID = addEntry(entryMap, mux); 164 | for (InputEntry inputEntry : inputEntries) { 165 | inputEntry.setDestination(muxID, inputEntry.getOrdinal(), entryMap); 166 | } 167 | 168 | List resultIDList = new ArrayList<>(); 169 | for (int i = 0; i < outputs.size(); i++) { 170 | Output output = outputs.get(i); 171 | 172 | AssetAmount amount = new AssetAmount(new AssetID(output.getAssetId()), output.getAmount()); 173 | ValueSource src = new ValueSource(muxID, amount, i); 174 | 175 | Hash resultID; 176 | if (output.getControlProgram().startsWith("6a")) { 177 | Retirement retirement = new Retirement(src, i); 178 | resultID = addEntry(entryMap, retirement); 179 | } else { 180 | Program program = new Program(1, Hex.decode(output.getControlProgram())); 181 | OutputEntry oup = new OutputEntry(src, program, i); 182 | resultID = addEntry(entryMap, oup); 183 | } 184 | 185 | resultIDList.add(resultID); 186 | output.setId(resultID.toString()); 187 | 188 | ValueDestination destination = new ValueDestination(resultID, src.getValue(), 0); 189 | mux.getWitnessDestinations().add(destination); 190 | } 191 | 192 | TxHeader txHeader = new TxHeader(version, size, timeRange, resultIDList.toArray(new Hash[]{})); 193 | Hash txID = addEntry(entryMap, txHeader); 194 | this.txID = txID.toString(); 195 | 196 | } catch (Exception e) { 197 | e.printStackTrace(); 198 | throw new MapTransactionException(e); 199 | } 200 | } 201 | 202 | private Hash addEntry(Map entryMap, Entry entry) { 203 | Hash id = entry.entryID(); 204 | entryMap.put(id, entry); 205 | return id; 206 | } 207 | 208 | public String getTxID() { 209 | return txID; 210 | } 211 | 212 | public Integer getVersion() { 213 | return version; 214 | } 215 | 216 | public Integer getSize() { 217 | return size; 218 | } 219 | 220 | public Integer getTimeRange() { 221 | return timeRange; 222 | } 223 | 224 | public List getInputs() { 225 | return inputs; 226 | } 227 | 228 | public List getOutputs() { 229 | return outputs; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/api/WitnessComponent.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * A single witness component, holding information that will become the input 10 | * witness. 11 | */ 12 | public class WitnessComponent { 13 | 14 | /** 15 | * The list of witnesses made with the specified keys (null unless type is 16 | * "signature"). 17 | */ 18 | private List witnesses; 19 | 20 | private String rootPrivateKey; 21 | 22 | public WitnessComponent() { 23 | witnesses = new ArrayList<>(); 24 | } 25 | 26 | public byte[][] toByteArray() { 27 | byte[][] byteArray = new byte[witnesses.size()][]; 28 | for (int i = 0; i < witnesses.size(); i++) { 29 | byteArray[i] = Hex.decode(witnesses.get(i)); 30 | } 31 | return byteArray; 32 | } 33 | 34 | public String getWitness(int index) { 35 | return witnesses.get(index); 36 | } 37 | 38 | public void appendWitness(String witness) { 39 | witnesses.add(witness); 40 | } 41 | 42 | public String getRootPrivateKey() { 43 | return rootPrivateKey; 44 | } 45 | 46 | public void setRootPrivateKey(String rootPrivateKey) { 47 | this.rootPrivateKey = rootPrivateKey; 48 | } 49 | } -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/DerivePrivateKey.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import io.bytom.offline.util.PathUtil; 4 | import org.bouncycastle.util.encoders.Hex; 5 | 6 | import java.security.InvalidKeyException; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.security.SignatureException; 9 | 10 | public class DerivePrivateKey { 11 | //bip44 派生子秘钥 12 | //accountIndex 默认为1 13 | public static byte[] bip44derivePrvKey(String rootPriv, int accountIndex, boolean change, int programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 14 | byte[][] paths = PathUtil.getBip44Path(accountIndex, change, programIndex); 15 | byte[] res = Hex.decode(rootPriv); 16 | for (int i = 0; i < paths.length; i++) { 17 | byte[] xpub = DeriveXpub.deriveXpub(res); 18 | res = NonHardenedChild.nhChild(paths[i], res, xpub); 19 | } 20 | return res; 21 | } 22 | 23 | //BIP32 派生子秘钥 issue 需要用到BIP32派生规则 24 | public static byte[] bip32derivePrvKey(String rootPrivateKey, int accountIndex, byte keySpace, long ...programIndex) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 25 | byte[] res = Hex.decode(rootPrivateKey); 26 | byte[][] paths = PathUtil.getBip32Path(keySpace, accountIndex, programIndex); 27 | for (int i = 0; i < paths.length; i++) { 28 | byte[] xpub = DeriveXpub.deriveXpub(res); 29 | res = NonHardenedChild.nhChild(paths[i], res, xpub); 30 | } 31 | return res; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/DeriveXpub.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import com.google.crypto.tink.subtle.Ed25519; 4 | 5 | public class DeriveXpub { 6 | public static byte[] deriveXpub(byte[] xprv) { 7 | byte[] xpub = new byte[xprv.length]; 8 | byte[] scalar = new byte[xprv.length / 2]; 9 | 10 | System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2); 11 | byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar); 12 | 13 | System.arraycopy(buf, 0, xpub, 0, buf.length); 14 | System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2); 15 | return xpub; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/ExpandedPrivateKey.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | import javax.crypto.Mac; 5 | import javax.crypto.spec.SecretKeySpec; 6 | import java.security.InvalidKeyException; 7 | import java.security.NoSuchAlgorithmException; 8 | 9 | public class ExpandedPrivateKey { 10 | public static byte[] hmacSha512(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException { 11 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512"); 12 | Mac mac = Mac.getInstance("HmacSHA512"); 13 | mac.init(signingKey); 14 | return mac.doFinal(data); 15 | } 16 | 17 | public static byte[] expandedPrivateKey(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException { 18 | // "457870616e64" is "Expand" hex. 19 | byte[] res = hmacSha512(data, Hex.decode("457870616e64")); 20 | for (int i = 0; i <= 31; i++) { 21 | res[i] = data[i]; 22 | } 23 | return res; 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/NonHardenedChild.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.io.ByteArrayOutputStream; 8 | import java.security.InvalidKeyException; 9 | import java.security.NoSuchAlgorithmException; 10 | 11 | public class NonHardenedChild { 12 | 13 | private static byte[] hmacSha512(byte[] data, byte[] key) 14 | throws NoSuchAlgorithmException, InvalidKeyException { 15 | SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA512"); 16 | Mac mac = Mac.getInstance("HmacSHA512"); 17 | mac.init(signingKey); 18 | return mac.doFinal(data); 19 | } 20 | 21 | public static byte[] nhChild(byte[] path, byte[] xprv, byte[] xpub) throws NoSuchAlgorithmException, InvalidKeyException { 22 | // begin build data 23 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 24 | out.write('N'); 25 | out.write(xpub, 0, xpub.length / 2); 26 | out.write(path, 0, path.length); 27 | byte[] data = out.toByteArray(); 28 | // end build data 29 | 30 | // begin build key 31 | byte[] key = new byte[xpub.length / 2]; 32 | System.arraycopy(xpub, xpub.length / 2, key, 0, xpub.length / 2); 33 | // end build key 34 | 35 | // doFinal() 36 | byte[] res = hmacSha512(data, key); 37 | 38 | //begin operate res[:32] 39 | byte[] f = new byte[res.length / 2]; 40 | System.arraycopy(res, 0, f, 0, res.length / 2); 41 | f = pruneIntermediateScalar(f); 42 | System.arraycopy(f, 0, res, 0, res.length / 2); 43 | //end operate res[:32] 44 | 45 | //begin operate res[:32] again 46 | int carry = 0; 47 | int sum = 0; 48 | for (int i = 0; i < 32; i++) { 49 | int xprvInt = xprv[i] & 0xFF; 50 | int resInt = res[i] & 0xFF; 51 | sum = xprvInt + resInt + carry; 52 | res[i] = (byte) sum; 53 | carry = sum >> 8; 54 | } 55 | if ((sum >> 8) != 0) { 56 | System.err.println("sum does not fit in 256-bit int"); 57 | } 58 | //end operate res[:32] again 59 | return res; 60 | } 61 | 62 | private static byte[] pruneIntermediateScalar(byte[] f) { 63 | f[0] &= 248; // clear bottom 3 bits 64 | f[29] &= 1; // clear 7 high bits 65 | f[30] = 0; // clear 8 bits 66 | f[31] = 0; // clear 8 bits 67 | return f; 68 | } 69 | 70 | public static byte[] child(byte[] xprv, String[] hpaths) throws NoSuchAlgorithmException, InvalidKeyException { 71 | byte[][] paths = new byte[][]{ 72 | Hex.decode(hpaths[0]), 73 | Hex.decode(hpaths[1]) 74 | }; 75 | byte[] res = xprv; 76 | for (int i = 0; i < hpaths.length; i++) { 77 | byte[] xpub = DeriveXpub.deriveXpub(res); 78 | res = NonHardenedChild.nhChild(paths[i], res, xpub); 79 | } 80 | return res; 81 | } 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/Signer.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Arrays; 6 | 7 | public class Signer { 8 | 9 | public static byte[] ed25519InnerSign(byte[] privateKey, byte[] message) throws NoSuchAlgorithmException { 10 | MessageDigest md = MessageDigest.getInstance("SHA-512"); 11 | byte[] digestData = new byte[32 + message.length]; 12 | int digestDataIndex = 0; 13 | for (int i = 32; i < 64; i++) { 14 | digestData[digestDataIndex] = privateKey[i]; 15 | digestDataIndex++; 16 | } 17 | for (int i = 0; i < message.length; i++) { 18 | digestData[digestDataIndex] = message[i]; 19 | digestDataIndex++; 20 | } 21 | md.update(digestData); 22 | byte[] messageDigest = md.digest(); 23 | 24 | com.google.crypto.tink.subtle.Ed25519.reduce(messageDigest); 25 | byte[] messageDigestReduced = Arrays.copyOfRange(messageDigest, 0, 32); 26 | byte[] encodedR = com.google.crypto.tink.subtle.Ed25519.scalarMultWithBaseToBytes(messageDigestReduced); 27 | byte[] publicKey = DeriveXpub.deriveXpub(privateKey); 28 | 29 | byte[] hramDigestData = new byte[32 + encodedR.length + message.length]; 30 | int hramDigestIndex = 0; 31 | for (int i = 0; i < encodedR.length; i++) { 32 | hramDigestData[hramDigestIndex] = encodedR[i]; 33 | hramDigestIndex++; 34 | } 35 | for (int i = 0; i < 32; i++) { 36 | hramDigestData[hramDigestIndex] = publicKey[i]; 37 | hramDigestIndex++; 38 | } 39 | for (int i = 0; i < message.length; i++) { 40 | hramDigestData[hramDigestIndex] = message[i]; 41 | hramDigestIndex++; 42 | } 43 | md.reset(); 44 | md.update(hramDigestData); 45 | byte[] hramDigest = md.digest(); 46 | com.google.crypto.tink.subtle.Ed25519.reduce(hramDigest); 47 | byte[] hramDigestReduced = Arrays.copyOfRange(hramDigest, 0, 32); 48 | 49 | byte[] sk = Arrays.copyOfRange(privateKey, 0, 32); 50 | byte[] s = new byte[32]; 51 | com.google.crypto.tink.subtle.Ed25519.mulAdd(s, hramDigestReduced, sk, messageDigestReduced); 52 | 53 | byte[] signature = new byte[64]; 54 | for (int i = 0; i < encodedR.length; i++) { 55 | signature[i] = encodedR[i]; 56 | } 57 | int signatureIndex = 32; 58 | for (int i = 0; i < s.length; i++) { 59 | signature[signatureIndex] = s[i]; 60 | signatureIndex++; 61 | } 62 | return signature; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/Utils.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import io.bytom.offline.types.ExpandedKeys; 6 | import org.bouncycastle.jcajce.provider.digest.SHA3; 7 | import org.bouncycastle.util.encoders.Hex; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.math.BigInteger; 12 | import java.security.InvalidKeyException; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.security.SignatureException; 15 | import java.util.Arrays; 16 | 17 | public class Utils { 18 | 19 | public static String rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; 20 | 21 | public static final Gson serializer = new GsonBuilder().setDateFormat(rfc3339DateFormat).create(); 22 | 23 | public static int writeVarint(long value, ByteArrayOutputStream stream) throws IOException { 24 | byte[] varint = new byte[9]; 25 | int n = putUvarint(varint, value); 26 | byte[] varintTime = Arrays.copyOf(varint, n); 27 | stream.write(varintTime); 28 | return n; 29 | } 30 | 31 | 32 | public static int writeVarStr(byte[] buf, ByteArrayOutputStream stream) throws IOException { 33 | int n = writeVarint(buf.length, stream); 34 | stream.write(buf); 35 | 36 | return n + (buf.length); 37 | } 38 | 39 | public static int writeVarList(byte[][] list, ByteArrayOutputStream stream) throws IOException { 40 | int n = writeVarint(list.length, stream); 41 | for (byte[] bytes : list) { 42 | n += writeVarStr(bytes, stream); 43 | } 44 | return n; 45 | } 46 | 47 | public static int writeExtensibleString(byte[] bytes, ByteArrayOutputStream stream) throws IOException { 48 | int n = writeVarint(bytes.length, stream); 49 | stream.write(bytes); 50 | return n + bytes.length; 51 | } 52 | 53 | public static int getLengthVarInt(long x) { 54 | byte[] varint = new byte[9]; 55 | int n = putUvarint(varint, x); 56 | byte[] varintTime = Arrays.copyOf(varint, n); 57 | return varintTime.length; 58 | } 59 | 60 | private static int putUvarint(byte[] buf, long x) { 61 | int i = 0; 62 | while (x >= 0x80) { 63 | buf[i] = (byte) (x | 0x80); 64 | x >>= 7; 65 | i++; 66 | } 67 | buf[i] = (byte) x; 68 | return i + 1; 69 | } 70 | 71 | public static byte[] BigIntegerToBytes(BigInteger value) { 72 | if (value == null) { 73 | return null; 74 | } else { 75 | byte[] data = value.toByteArray(); 76 | if (data.length != 1 && data[0] == 0) { 77 | byte[] tmp = new byte[data.length - 1]; 78 | System.arraycopy(data, 1, tmp, 0, tmp.length); 79 | data = tmp; 80 | } 81 | 82 | return data; 83 | } 84 | } 85 | 86 | public static byte[] pruneIntermediateScalar(byte[] f) { 87 | f[0] &= 248; 88 | f[31] &= 31; // clear top 3 bits 89 | f[31] |= 64; // set second highest bit 90 | return f; 91 | } 92 | 93 | public static ExpandedKeys expandedPriKey(String priKey, String key) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 94 | byte[] hashPriKey = ExpandedPrivateKey.hmacSha512(Hex.decode(priKey), key.getBytes()); 95 | //begin operate res[:32] 96 | byte[] f = new byte[hashPriKey.length / 2]; 97 | System.arraycopy(hashPriKey, 0, f, 0, hashPriKey.length / 2); 98 | f = pruneIntermediateScalar(f); 99 | System.arraycopy(f, 0, hashPriKey, 0, hashPriKey.length / 2); 100 | //end operate res[:32] 101 | byte[] hashPubKey = DeriveXpub.deriveXpub(hashPriKey); 102 | ExpandedKeys expandedKeys = new ExpandedKeys(); 103 | expandedKeys.setPriKey(Hex.toHexString(hashPriKey)); 104 | expandedKeys.setPubKey(Hex.toHexString(hashPubKey)); 105 | return expandedKeys; 106 | } 107 | 108 | public static String pushDataInt(int n) { 109 | if (n==0){ 110 | return "00"; 111 | }else if (n>=1&&n<=15){ 112 | return "5"+Integer.toString(n,16); 113 | }else if(n==16){ 114 | return "60"; 115 | } 116 | return null; 117 | 118 | } 119 | 120 | public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) { 121 | 122 | SHA3.Digest256 digest256 = new SHA3.Digest256(); 123 | // data = hashedInputHex + txID 124 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 125 | out.write(hashedInputHex, 0, hashedInputHex.length); 126 | out.write(txID, 0, txID.length); 127 | byte[] data = out.toByteArray(); 128 | return digest256.digest(data); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/common/VMUtil.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | 6 | public class VMUtil { 7 | 8 | public static final byte OP_0 = (byte) 0x00; 9 | public static final byte OP_1 = (byte) 0x51; 10 | public static final byte OP_PUSHDATA1 = (byte) 0x4c; 11 | public static final byte OP_PUSHDATA2 = (byte) 0x4d; 12 | public static final byte OP_PUSHDATA4 = (byte) 0x43; 13 | public static final byte OP_TXSIGHASH = (byte) 0xae; 14 | public static final byte OP_CHECKMULTISIG = (byte) 0xad; 15 | public static final byte OP_FAIL = (byte) 0x6a; 16 | 17 | public static byte[] p2spMultiSigProgram(byte[][] pubKeys, int nRequired) { 18 | checkMultiSigParams(nRequired, pubKeys.length); 19 | ByteArrayOutputStream program = new ByteArrayOutputStream(); 20 | program.write(OP_TXSIGHASH); 21 | 22 | for (byte[] pubKey : pubKeys) { 23 | try { 24 | program.write(pushDataBytes(pubKey)); 25 | program.write(pushDataInt64(nRequired)); 26 | program.write(pushDataInt64(pubKeys.length)); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | program.write(OP_CHECKMULTISIG); 32 | return program.toByteArray(); 33 | } 34 | 35 | private static void checkMultiSigParams(int nRequired, int nPubkeys) { 36 | if (nRequired < 0 || nPubkeys < 0 || nRequired > nPubkeys || (nRequired == 0 && nPubkeys > 0)) { 37 | throw new IllegalArgumentException(); 38 | } 39 | } 40 | 41 | public static byte[] pushDataBytes(byte[] data) { 42 | int len = data.length; 43 | if (len == 0) { 44 | return new byte[] {OP_0}; 45 | } 46 | if (len <= 75) { 47 | byte[] dest = new byte[1 + len]; 48 | dest[0] = (byte) len; 49 | System.arraycopy(data, 0, dest, 1, len); 50 | return dest; 51 | } 52 | if (len < 256) { 53 | byte[] dest = new byte[2 + len]; 54 | dest[0] = OP_PUSHDATA1; 55 | dest[1] = (byte) len; 56 | System.arraycopy(data, 0, dest, 2, len); 57 | return dest; 58 | } 59 | if (len < 65536) { 60 | byte[] dest = new byte[3 + len]; 61 | dest[0] = OP_PUSHDATA2; 62 | dest[1] = (byte) len; 63 | dest[2] = (byte) (len >> 8); 64 | System.arraycopy(data, 0, dest, 3, len); 65 | return dest; 66 | } 67 | byte[] dest = new byte[5 + len]; 68 | dest[0] = OP_PUSHDATA4; 69 | dest[1] = (byte) len; 70 | dest[2] = (byte) (len >> 8); 71 | dest[3] = (byte) (len >> 16); 72 | dest[4] = (byte) (len >> 24); 73 | System.arraycopy(data, 0, dest, 5, len); 74 | return dest; 75 | } 76 | 77 | private static byte[] pushDataInt64(long n) { 78 | if (n == 0) { 79 | return new byte[] {OP_0}; 80 | } 81 | if (n >= 1 && n <= 16) { 82 | return new byte[] {(byte) (OP_1 + (byte) n - 1)}; 83 | } 84 | return pushDataBytes(int64Bytes(n)); 85 | } 86 | 87 | private static byte[] int64Bytes(long n) { 88 | byte[] bytes = new byte[8]; 89 | int i = 0; 90 | while (n != 0) { 91 | bytes[i] = (byte) n; 92 | n >>= 8; 93 | i++; 94 | } 95 | byte[] res = new byte[i]; 96 | System.arraycopy(bytes, 0, res, 0, i); 97 | return res; 98 | } 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/exception/MapTransactionException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.exception; 2 | 3 | public class MapTransactionException extends RuntimeException { 4 | 5 | public MapTransactionException() { 6 | super(); 7 | } 8 | 9 | public MapTransactionException(String message) { 10 | super(message); 11 | } 12 | 13 | public MapTransactionException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | 18 | public MapTransactionException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/exception/SerializeTransactionException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.exception; 2 | 3 | public class SerializeTransactionException extends RuntimeException { 4 | 5 | public SerializeTransactionException() { 6 | super(); 7 | } 8 | 9 | public SerializeTransactionException(String message) { 10 | super(message); 11 | } 12 | 13 | public SerializeTransactionException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | 18 | public SerializeTransactionException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/exception/SignTransactionException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.exception; 2 | 3 | public class SignTransactionException extends RuntimeException { 4 | 5 | public SignTransactionException() { 6 | super(); 7 | } 8 | 9 | public SignTransactionException(String message) { 10 | super(message); 11 | } 12 | 13 | public SignTransactionException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | 18 | public SignTransactionException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/exception/WriteForHashException.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.exception; 2 | 3 | public class WriteForHashException extends RuntimeException { 4 | 5 | public WriteForHashException() { 6 | super(); 7 | } 8 | 9 | public WriteForHashException(String message) { 10 | super(message); 11 | } 12 | 13 | public WriteForHashException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | 18 | public WriteForHashException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/AssetAmount.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | public class AssetAmount { 4 | 5 | private AssetID assetID; 6 | 7 | private Long amount; 8 | 9 | public AssetAmount() {} 10 | 11 | public AssetAmount(AssetID assetID, long amount) { 12 | this.assetID = assetID; 13 | this.amount = amount; 14 | } 15 | 16 | public AssetID getAssetID() { 17 | return assetID; 18 | } 19 | 20 | public void setAssetID(AssetID assetID) { 21 | this.assetID = assetID; 22 | } 23 | 24 | public Long getAmount() { 25 | return amount; 26 | } 27 | 28 | public void setAmount(Long amount) { 29 | this.amount = amount; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/AssetDefinition.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | public class AssetDefinition extends Entry { 6 | 7 | private Hash assetDefHash; 8 | private Program program; 9 | 10 | public AssetDefinition(Hash assetDefHash, Program program) { 11 | this.assetDefHash = assetDefHash; 12 | this.program = program; 13 | } 14 | 15 | @Override 16 | public String typ() { 17 | return "asset"; 18 | } 19 | 20 | @Override 21 | public void writeForHash(ByteArrayOutputStream out) { 22 | mustWriteForHash(out, this.program); 23 | mustWriteForHash(out, this.assetDefHash); 24 | } 25 | 26 | public Hash getAssetDefHash() { 27 | return assetDefHash; 28 | } 29 | 30 | public Program getProgram() { 31 | return program; 32 | } 33 | 34 | public void setAssetDefHash(Hash assetDefHash) { 35 | this.assetDefHash = assetDefHash; 36 | } 37 | 38 | public void setProgram(Program program) { 39 | this.program = program; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/AssetID.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | public class AssetID { 6 | 7 | private String hexValue; 8 | 9 | public AssetID() {} 10 | 11 | public AssetID(String hexValue) { 12 | this.hexValue = hexValue; 13 | } 14 | 15 | 16 | public AssetID(byte[] byteArray) { 17 | this.hexValue = Hex.toHexString(byteArray); 18 | } 19 | 20 | public byte[] toByteArray() { 21 | return Hex.decode(this.hexValue); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Entry.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import io.bytom.offline.exception.WriteForHashException; 4 | import io.bytom.offline.util.OutputUtil; 5 | import org.bouncycastle.jcajce.provider.digest.SHA3; 6 | import java.beans.PropertyDescriptor; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.Method; 11 | 12 | public abstract class Entry { 13 | 14 | public abstract String typ(); 15 | 16 | public abstract void writeForHash(ByteArrayOutputStream out); 17 | 18 | public void mustWriteForHash(ByteArrayOutputStream out, Object data) { 19 | try { 20 | if (data == null) { 21 | throw new WriteForHashException("The field needs to be written to hash is null"); 22 | } 23 | if (data instanceof Byte) { 24 | OutputUtil.writeByte(out, (byte) data); 25 | } else if (data instanceof Long) { 26 | OutputUtil.writeLong(out, (long) data); 27 | } else if (data instanceof byte[]) { 28 | OutputUtil.writeVarstr(out, (byte[]) data); 29 | } else if (data instanceof byte[][]) { 30 | OutputUtil.writeVarstrList(out, (byte[][]) data); 31 | } else if (data instanceof String) { 32 | OutputUtil.writeVarstr(out, ((String) data).getBytes()); 33 | } else if (data instanceof Hash) { 34 | out.write(((Hash) data).toByteArray()); 35 | } else if (data instanceof AssetID) { 36 | out.write(((AssetID) data).toByteArray()); 37 | } else if (data.getClass().isArray()) { 38 | Object[] array = (Object[]) data; 39 | OutputUtil.writeVarint(out, array.length); 40 | for (Object obj : array) { 41 | mustWriteForHash(out, obj); 42 | } 43 | } else { 44 | Class cls = data.getClass(); 45 | Field[] fields = cls.getDeclaredFields(); 46 | for (Field field : fields) { 47 | PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), cls); 48 | Method readMethod = descriptor.getReadMethod(); 49 | mustWriteForHash(out, readMethod.invoke(data)); 50 | } 51 | } 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | throw new WriteForHashException(e); 55 | } 56 | } 57 | 58 | public Hash entryID() { 59 | SHA3.Digest256 digest256 = new SHA3.Digest256(); 60 | ByteArrayOutputStream hasher = new ByteArrayOutputStream(); 61 | ByteArrayOutputStream bh = new ByteArrayOutputStream(); 62 | try { 63 | hasher.write("entryid:".getBytes()); 64 | hasher.write(this.typ().getBytes()); 65 | hasher.write(":".getBytes()); 66 | 67 | this.writeForHash(bh); 68 | hasher.write(digest256.digest(bh.toByteArray())); 69 | return new Hash(digest256.digest(hasher.toByteArray())); 70 | } catch (IOException e) { 71 | e.printStackTrace(); 72 | throw new RuntimeException(e); 73 | } finally { 74 | try { 75 | bh.close(); 76 | hasher.close(); 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/ExpandedKeys.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | public class ExpandedKeys { 4 | 5 | private String expandedPriKey; 6 | 7 | private String expandedPubKey; 8 | 9 | public void setPriKey(String priKey) { 10 | this.expandedPriKey = priKey; 11 | } 12 | 13 | public String getPriKey() { 14 | return this.expandedPriKey; 15 | } 16 | 17 | public void setPubKey(String pubKey) { 18 | this.expandedPubKey = pubKey; 19 | } 20 | 21 | public String getPubKey() { 22 | return this.expandedPubKey; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Hash.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import java.util.Objects; 6 | 7 | public class Hash { 8 | 9 | private String hexValue; 10 | 11 | public Hash() {} 12 | 13 | public Hash(String hexValue) { 14 | this.hexValue = hexValue; 15 | } 16 | 17 | public Hash(byte[] byteArray) { 18 | this.hexValue = Hex.toHexString(byteArray); 19 | } 20 | 21 | public byte[] toByteArray() { 22 | return Hex.decode(this.hexValue); 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) return true; 28 | if (o == null || getClass() != o.getClass()) return false; 29 | Hash hash = (Hash) o; 30 | return Objects.equals(hexValue, hash.hexValue); 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | return Objects.hash(hexValue); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return this.hexValue; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/InputEntry.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.util.Map; 4 | 5 | public abstract class InputEntry extends Entry { 6 | 7 | protected int ordinal; 8 | 9 | public abstract void setDestination(Hash id, long pos, Map entryMap); 10 | 11 | public int getOrdinal() { 12 | return ordinal; 13 | } 14 | 15 | public void setOrdinal(int ordinal) { 16 | this.ordinal = ordinal; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Issue.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.Map; 5 | 6 | public class Issue extends InputEntry { 7 | 8 | private Hash nonceHash; 9 | private AssetAmount assetAmount; 10 | private AssetDefinition assetDefinition; 11 | private ValueDestination witnessDestination; 12 | 13 | public Issue(Hash nonceHash, AssetAmount assetAmount, int ordinal, AssetDefinition assetDefinition) { 14 | this.nonceHash = nonceHash; 15 | this.assetAmount = assetAmount; 16 | this.ordinal = ordinal; 17 | this.assetDefinition = assetDefinition; 18 | } 19 | 20 | @Override 21 | public void setDestination(Hash id, long pos, Map entryMap) { 22 | this.witnessDestination = new ValueDestination(id, this.assetAmount, pos); 23 | } 24 | 25 | @Override 26 | public String typ() { 27 | return "issuance1"; 28 | } 29 | 30 | @Override 31 | public void writeForHash(ByteArrayOutputStream out) { 32 | mustWriteForHash(out, this.nonceHash); 33 | mustWriteForHash(out, this.assetAmount); 34 | } 35 | 36 | public Hash getNonceHash() { 37 | return nonceHash; 38 | } 39 | 40 | public void setNonceHash(Hash nonceHash) { 41 | this.nonceHash = nonceHash; 42 | } 43 | 44 | public AssetAmount getAssetAmount() { 45 | return assetAmount; 46 | } 47 | 48 | public void setAssetAmount(AssetAmount assetAmount) { 49 | this.assetAmount = assetAmount; 50 | } 51 | 52 | public AssetDefinition getAssetDefinition() { 53 | return assetDefinition; 54 | } 55 | 56 | public void setAssetDefinition(AssetDefinition assetDefinition) { 57 | this.assetDefinition = assetDefinition; 58 | } 59 | 60 | public ValueDestination getWitnessDestination() { 61 | return witnessDestination; 62 | } 63 | 64 | public void setWitnessDestination(ValueDestination witnessDestination) { 65 | this.witnessDestination = witnessDestination; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Mux.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class Mux extends Entry { 8 | 9 | private ValueSource[] sources; 10 | 11 | private Program program; 12 | 13 | private List witnessDestinations = new ArrayList<>(); 14 | 15 | public Mux() {} 16 | 17 | public Mux(ValueSource[] sources, Program program) { 18 | this.sources = sources; 19 | this.program = program; 20 | } 21 | 22 | @Override 23 | public String typ() { 24 | return "mux1"; 25 | } 26 | 27 | @Override 28 | public void writeForHash(ByteArrayOutputStream out) { 29 | mustWriteForHash(out, this.sources); 30 | mustWriteForHash(out, this.program); 31 | } 32 | 33 | public ValueSource[] getSources() { 34 | return sources; 35 | } 36 | 37 | public void setSources(ValueSource[] sources) { 38 | this.sources = sources; 39 | } 40 | 41 | public Program getProgram() { 42 | return program; 43 | } 44 | 45 | public void setProgram(Program program) { 46 | this.program = program; 47 | } 48 | 49 | public List getWitnessDestinations() { 50 | return witnessDestinations; 51 | } 52 | 53 | public void setWitnessDestinations(List witnessDestinations) { 54 | this.witnessDestinations = witnessDestinations; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/OutputEntry.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | public class OutputEntry extends Entry { 6 | 7 | private ValueSource source; 8 | 9 | private Program controlProgram; 10 | 11 | private Integer ordinal; 12 | 13 | public OutputEntry() { 14 | this.source = new ValueSource(); 15 | this.controlProgram = new Program(); 16 | } 17 | 18 | 19 | public OutputEntry(ValueSource source, Program controlProgram, Integer ordinal) { 20 | this.source = source; 21 | this.controlProgram = controlProgram; 22 | this.ordinal = ordinal; 23 | } 24 | 25 | @Override 26 | public String typ() { 27 | return "output1"; 28 | } 29 | 30 | @Override 31 | public void writeForHash(ByteArrayOutputStream out) { 32 | mustWriteForHash(out, this.source); 33 | mustWriteForHash(out, this.controlProgram); 34 | } 35 | 36 | public ValueSource getSource() { 37 | return source; 38 | } 39 | 40 | public void setSource(ValueSource source) { 41 | this.source = source; 42 | } 43 | 44 | public Program getControlProgram() { 45 | return controlProgram; 46 | } 47 | 48 | public void setControlProgram(Program controlProgram) { 49 | this.controlProgram = controlProgram; 50 | } 51 | 52 | public Integer getOrdinal() { 53 | return ordinal; 54 | } 55 | 56 | public void setOrdinal(Integer ordinal) { 57 | this.ordinal = ordinal; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Program.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | 4 | public class Program { 5 | 6 | private long vmVersion; 7 | 8 | private byte[] code; 9 | 10 | public Program() {} 11 | 12 | public Program(long vmVersion, byte[] code) { 13 | this.vmVersion = vmVersion; 14 | this.code = code; 15 | } 16 | 17 | public long getVmVersion() { 18 | return vmVersion; 19 | } 20 | 21 | public void setVmVersion(long vmVersion) { 22 | this.vmVersion = vmVersion; 23 | } 24 | 25 | public byte[] getCode() { 26 | return code; 27 | } 28 | 29 | public void setCode(byte[] code) { 30 | this.code = code; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Retirement.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | public class Retirement extends Entry { 6 | 7 | private ValueSource valueSource; 8 | private int ordinal; 9 | 10 | public Retirement(ValueSource valueSource, int ordinal) { 11 | this.valueSource = valueSource; 12 | this.ordinal = ordinal; 13 | } 14 | 15 | 16 | @Override 17 | public String typ() { 18 | return "retirement1"; 19 | } 20 | 21 | @Override 22 | public void writeForHash(ByteArrayOutputStream out) { 23 | mustWriteForHash(out, valueSource); 24 | } 25 | 26 | public ValueSource getValueSource() { 27 | return valueSource; 28 | } 29 | 30 | public void setValueSource(ValueSource valueSource) { 31 | this.valueSource = valueSource; 32 | } 33 | 34 | public int getOrdinal() { 35 | return ordinal; 36 | } 37 | 38 | public void setOrdinal(int ordinal) { 39 | this.ordinal = ordinal; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/Spend.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.Map; 5 | 6 | public class Spend extends InputEntry { 7 | 8 | private Hash spentOutputID; 9 | 10 | private int ordinal; 11 | 12 | private ValueDestination witnessDestination; 13 | 14 | private byte[][] witnessArguments; 15 | 16 | public Spend(Hash spentOutputID, int ordinal) { 17 | this.spentOutputID = spentOutputID; 18 | this.ordinal = ordinal; 19 | } 20 | 21 | @Override 22 | public void setDestination(Hash id, long pos, Map entryMap) { 23 | OutputEntry spendOutput = (OutputEntry) entryMap.get(this.spentOutputID); 24 | this.witnessDestination = new ValueDestination(id, spendOutput.getSource().getValue(), pos); 25 | } 26 | 27 | @Override 28 | public String typ() { 29 | return "spend1"; 30 | } 31 | 32 | @Override 33 | public void writeForHash(ByteArrayOutputStream out) { 34 | mustWriteForHash(out, this.spentOutputID); 35 | } 36 | 37 | public Hash getSpentOutputID() { 38 | return spentOutputID; 39 | } 40 | 41 | public void setSpentOutputID(Hash spentOutputID) { 42 | this.spentOutputID = spentOutputID; 43 | } 44 | 45 | @Override 46 | public int getOrdinal() { 47 | return ordinal; 48 | } 49 | 50 | @Override 51 | public void setOrdinal(int ordinal) { 52 | this.ordinal = ordinal; 53 | } 54 | 55 | public ValueDestination getWitnessDestination() { 56 | return witnessDestination; 57 | } 58 | 59 | public void setWitnessDestination(ValueDestination witnessDestination) { 60 | this.witnessDestination = witnessDestination; 61 | } 62 | 63 | public byte[][] getWitnessArguments() { 64 | return witnessArguments; 65 | } 66 | 67 | public void setWitnessArguments(byte[][] witnessArguments) { 68 | this.witnessArguments = witnessArguments; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/TxHeader.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | public class TxHeader extends Entry { 6 | 7 | private long version; 8 | 9 | private long serializedSize; 10 | 11 | private long timeRange; 12 | 13 | private Hash[] resultIDs; 14 | 15 | public TxHeader() {} 16 | 17 | public TxHeader(long version, long serializedSize, long timeRange, Hash[] resultIDs) { 18 | this.version = version; 19 | this.serializedSize = serializedSize; 20 | this.timeRange = timeRange; 21 | this.resultIDs = resultIDs; 22 | } 23 | 24 | @Override 25 | public String typ() { 26 | return "txheader"; 27 | } 28 | 29 | @Override 30 | public void writeForHash(ByteArrayOutputStream out) { 31 | mustWriteForHash(out, this.version); 32 | mustWriteForHash(out, this.timeRange); 33 | mustWriteForHash(out, this.resultIDs); 34 | } 35 | 36 | public long getVersion() { 37 | return version; 38 | } 39 | 40 | public void setVersion(long version) { 41 | this.version = version; 42 | } 43 | 44 | public long getSerializedSize() { 45 | return serializedSize; 46 | } 47 | 48 | public void setSerializedSize(long serializedSize) { 49 | this.serializedSize = serializedSize; 50 | } 51 | 52 | public long getTimeRange() { 53 | return timeRange; 54 | } 55 | 56 | public void setTimeRange(long timeRange) { 57 | this.timeRange = timeRange; 58 | } 59 | 60 | public Hash[] getResultIDs() { 61 | return resultIDs; 62 | } 63 | 64 | public void setResultIDs(Hash[] resultIDs) { 65 | this.resultIDs = resultIDs; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/ValueDestination.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | public class ValueDestination { 4 | 5 | private Hash ref; 6 | 7 | private AssetAmount value; 8 | 9 | private long position; 10 | 11 | public ValueDestination() {} 12 | 13 | public ValueDestination(Hash ref, AssetAmount value, long position) { 14 | this.ref = ref; 15 | this.value = value; 16 | this.position = position; 17 | } 18 | 19 | public Hash getRef() { 20 | return ref; 21 | } 22 | 23 | public void setRef(Hash ref) { 24 | this.ref = ref; 25 | } 26 | 27 | public AssetAmount getValue() { 28 | return value; 29 | } 30 | 31 | public void setValue(AssetAmount value) { 32 | this.value = value; 33 | } 34 | 35 | public long getPosition() { 36 | return position; 37 | } 38 | 39 | public void setPosition(long position) { 40 | this.position = position; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/types/ValueSource.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.types; 2 | 3 | 4 | public class ValueSource { 5 | 6 | private Hash ref; 7 | 8 | private AssetAmount value; 9 | 10 | private long position; 11 | 12 | public ValueSource() {} 13 | 14 | public ValueSource(Hash ref, AssetAmount value, long position) { 15 | this.ref = ref; 16 | this.value = value; 17 | this.position = position; 18 | } 19 | 20 | public Hash getRef() { 21 | return ref; 22 | } 23 | 24 | public void setRef(Hash ref) { 25 | this.ref = ref; 26 | } 27 | 28 | public AssetAmount getValue() { 29 | return value; 30 | } 31 | 32 | public void setValue(AssetAmount value) { 33 | this.value = value; 34 | } 35 | 36 | public long getPosition() { 37 | return position; 38 | } 39 | 40 | public void setPosition(long position) { 41 | this.position = position; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/util/OutputUtil.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.util; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public final class OutputUtil { 7 | 8 | public static void writeByte(OutputStream out, byte data) throws IOException { 9 | out.write(data); 10 | } 11 | 12 | public static void writeLong(OutputStream out, long data) throws IOException { 13 | for (int i = 0; i < 8; i++) { 14 | out.write((byte) data); 15 | data >>= 8; 16 | } 17 | } 18 | 19 | public static void writeVarint(OutputStream out, long data) throws IOException { 20 | byte[] buf = new byte[9]; 21 | int n = putUvarint(buf, data); 22 | out.write(buf, 0, n); 23 | } 24 | 25 | public static void writeVarstr(OutputStream out, byte[] str) throws IOException { 26 | writeVarint(out, str.length); 27 | out.write(str); 28 | } 29 | 30 | public static void writeVarstrList(OutputStream out, byte[][] l) throws IOException { 31 | writeVarint(out, l.length); 32 | for (byte[] str : l) { 33 | writeVarstr(out, str); 34 | } 35 | } 36 | 37 | private static int putUvarint(byte[] buf, long x) { 38 | int i = 0; 39 | while (x >= 0x80) { 40 | buf[i] = (byte) (x | 0x80); 41 | x >>= 7; 42 | i++; 43 | } 44 | buf[i] = (byte) x; 45 | return i + 1; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/util/PathUtil.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.util; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | public class PathUtil { 9 | private static String intToPath(int index) { 10 | byte[] result = new byte[4]; 11 | result[3] = (byte) (index >> 24 & 0xff); 12 | result[2] = (byte) (index >> 16 & 0xff); 13 | result[1] = (byte) (index >> 8 & 0xff); 14 | result[0] = (byte) (index >> 0 & 0xff); 15 | return Hex.toHexString(result); 16 | } 17 | 18 | 19 | // paths = { 20 | //// "2c000000", 21 | //// "99000000", 22 | //// "01000000", accountIndex 23 | //// "00000000", change 24 | //// "01000000" controlProgramIndex 25 | //// } 26 | 27 | public static byte[][] getBip44Path(int accountIndex, boolean change, int programIndex) { 28 | 29 | String accountIndexStr = intToPath(accountIndex); 30 | String changeStr = "00000000"; 31 | String programIndexStr = intToPath(programIndex); 32 | if (change) { 33 | changeStr = "01000000"; 34 | } 35 | return new byte[][]{ 36 | Hex.decode("2c000000"), 37 | Hex.decode("99000000"), 38 | Hex.decode(accountIndexStr), 39 | Hex.decode(changeStr), 40 | Hex.decode(programIndexStr), 41 | }; 42 | } 43 | 44 | public static byte[][] getBip32Path(byte keySpace, long accountIndex, long ...itemIndexes) { 45 | byte[] signerPath = new byte[9]; 46 | signerPath[0] = keySpace; 47 | byte[] path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(accountIndex).array(); 48 | System.arraycopy(path, 0, signerPath, 1, 8); 49 | 50 | byte[][] res = new byte[1 + itemIndexes.length][]; 51 | res[0] = signerPath; 52 | for (int i = 0; i < itemIndexes.length; i++) { 53 | path = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(itemIndexes[i]).array(); 54 | res[i + 1] = path; 55 | } 56 | return res; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tx-signer/src/main/java/io/bytom/offline/util/SHA3Util.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.util; 2 | 3 | import org.bouncycastle.jcajce.provider.digest.SHA3; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | 7 | public class SHA3Util { 8 | public static byte[] hashSha256(byte[] hash) { 9 | SHA3.Digest256 digest256 = new SHA3.Digest256(); 10 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 11 | out.write(hash, 0, hash.length); 12 | byte[] data = out.toByteArray(); 13 | return digest256.digest(data); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tx-signer/src/main/resources/config.properties: -------------------------------------------------------------------------------- 1 | bytom.api.url= 2 | client.access.token= 3 | # bytom.api.url=http://10.100.7.47:9888/ 4 | # client.access.token=wt:3d17dbb953cedd53353bf3f342bb2929e9505105ffeb21670e6bd00abeef3772 5 | #bytom.api.url=http://127.0.0.1:9888/ 6 | #client.access.token=sheng:49d1623f5991c62a5094e761477ddd2838dceb49c22fbf84b492a54f1df88123 7 | -------------------------------------------------------------------------------- /tx-signer/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=debug, stdout, R 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 4 | log4j.logger.org.apache.commons.httpclient=info 5 | log4j.logger.httpclient.wire.content=info 6 | log4j.logger.httpclient.wire.header=info 7 | # Pattern to output the caller's file name and line number. 8 | log4j.appender.stdout.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n 9 | log4j.appender.R=org.apache.log4j.RollingFileAppender 10 | log4j.appender.R.File=bytom.log 11 | log4j.appender.R.MaxFileSize=1000KB 12 | # Keep one backup file 13 | log4j.appender.R.MaxBackupIndex=1 14 | log4j.appender.R.layout=org.apache.log4j.PatternLayout 15 | log4j.appender.R.layout.ConversionPattern=%-4r %-5p [%d{yyyy-MM-dd HH:mm:ss}] %m%n -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/api/SignTransactionTest.java: -------------------------------------------------------------------------------- 1 | 2 | package io.bytom.offline.api; 3 | 4 | import io.bytom.offline.types.*; 5 | import org.bouncycastle.util.encoders.Hex; 6 | import org.junit.Test; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | 10 | /** 11 | * Created by liqiang on 2018/10/24. 12 | */ 13 | public class SignTransactionTest { 14 | 15 | //以下为测试用的区块上的交易utxo,即output中第二个输出 16 | //新交易接收地址为bm1qdpc5sejdkm22uv23jwd8pg6lyqz2trz4trgxh0,需要找零 17 | /*{ 18 | "id": "3b36453f7dc03b13523d6431afd7e544f60339daed52ba8fca7ebf88cd5e5939", 19 | "version": 1, 20 | "size": 330, 21 | "time_range": 0, 22 | "inputs": [ 23 | { 24 | "type": "spend", 25 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 26 | "asset_definition": {}, 27 | "amount": 482000000, 28 | "control_program": "00148da6ccbc216f9019cf80d23fd2083c80e29fcba2", 29 | "address": "bm1q3knve0ppd7gpnnuq6glayzpusr3fljazzcq0eh", 30 | "spent_output_id": "d11967ce15741217c650bc0b9dd7a390aaedd8ea5c645266920a7d19d8be681a", 31 | "input_id": "caae7c37f6cecce6854e6488cc389379e312acd2f7495337633501fc7f72b5f3" 32 | } 33 | ], 34 | "outputs": [ 35 | { 36 | "type": "control", 37 | "id": "3110bc8e7d713c17fb3dc3c9deadbfc419a25c25252c8e613d1fa54cc4d05dbd", 38 | "position": 0, 39 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 40 | "asset_definition": {}, 41 | "amount": 281500000, 42 | "control_program": "00145d6ba5bf0cfdb2487abd594429cd04c2ba566f9f", 43 | "address": "bm1qt446t0cvlkeys74at9zznngyc2a9vmulcr2xy6" 44 | }, 45 | { 46 | "type": "control", 47 | "id": "db5afebb5b33aec2c46fcebb20b98fffa8c065a101f4c1789fe5491b34dc1b8f", 48 | "position": 1, 49 | "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 50 | "asset_definition": {}, 51 | "amount": 200000000, 52 | "control_program": "00140d074bc86bd388a45f1c8911a41b8f0705d9058b", 53 | "address": "bm1qp5r5hjrt6wy2ghcu3yg6gxu0quzajpvtsm2gnc" 54 | } 55 | ], 56 | "status_fail": false, 57 | "mux_id": "0e97230a7347967764fd77c8cfa96b38ec6ff08465300a01900c645dfb694f24" 58 | }*/ 59 | 60 | 61 | @Test 62 | public void testMustWriteForHash() throws Exception { 63 | Entry entry = new Entry() { 64 | @Override 65 | public String typ() { 66 | return null; 67 | } 68 | 69 | @Override 70 | public void writeForHash(ByteArrayOutputStream out) { 71 | 72 | } 73 | }; 74 | 75 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 76 | entry.mustWriteForHash(out, (byte) 2); 77 | assert Hex.toHexString(out.toByteArray()).equals("02"); 78 | 79 | out.reset(); 80 | entry.mustWriteForHash(out, 1L); 81 | assert Hex.toHexString(out.toByteArray()).equals("0100000000000000"); 82 | 83 | out.reset(); 84 | entry.mustWriteForHash(out, 0x3456584738473837L); 85 | assert Hex.toHexString(out.toByteArray()).equals("3738473847585634"); 86 | 87 | out.reset(); 88 | entry.mustWriteForHash(out, new byte[]{0x12, 0x34, (byte) 0x85}); 89 | assert Hex.toHexString(out.toByteArray()).equals("03123485"); 90 | 91 | out.reset(); 92 | entry.mustWriteForHash(out, new byte[][]{{0x12, 0x34, (byte) 0x85}, {(byte) 0x86, 0x17, 0x40}}); 93 | assert Hex.toHexString(out.toByteArray()).equals("020312348503861740"); 94 | 95 | out.reset(); 96 | entry.mustWriteForHash(out, "hello, 世界"); 97 | assert Hex.toHexString(out.toByteArray()).equals("0d68656c6c6f2c20e4b896e7958c"); 98 | 99 | out.reset(); 100 | entry.mustWriteForHash(out, new String[]{"hi", "你好", "hello"}); 101 | assert Hex.toHexString(out.toByteArray()).equals("0302686906e4bda0e5a5bd0568656c6c6f"); 102 | 103 | out.reset(); 104 | String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f"; 105 | entry.mustWriteForHash(out, new Hash(hash)); 106 | assert Hex.toHexString(out.toByteArray()).equals(hash); 107 | 108 | out.reset(); 109 | ValueSource valueSource = new ValueSource(new Hash(hash), null, 1); 110 | Program program = new Program(1, new byte[]{1}); 111 | Mux mux = new Mux(new ValueSource[]{valueSource}, program); 112 | entry.mustWriteForHash(out, mux); 113 | assert Hex.toHexString(out.toByteArray()).equals("01d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f010000000000000001000000000000000101"); 114 | } 115 | 116 | @Test 117 | public void testEntryID() throws Exception { 118 | String hash = "d8ab56a5c9296f591db071a8b63f395e1485b12d4b105b49fee287c03888331f"; 119 | ValueSource valueSource = new ValueSource(new Hash(hash), null, 1); 120 | Program program = new Program(1, new byte[]{1}); 121 | Mux mux = new Mux(new ValueSource[]{valueSource}, program); 122 | String entryID = mux.entryID().toString(); 123 | assert entryID.equals("ebd967df33a3373ab85521fba24c22bf993c73f46fa96254b0c86646093184e9"); 124 | } 125 | 126 | } 127 | 128 | 129 | -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/api/SignerTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.api; 2 | 3 | import io.bytom.offline.common.Signer; 4 | import org.bouncycastle.util.encoders.Hex; 5 | import org.junit.Test; 6 | 7 | import java.security.InvalidKeyException; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.security.SignatureException; 10 | 11 | public class SignerTest { 12 | 13 | @Test 14 | public void testEd25519InnerSign() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 15 | String rootXprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"; 16 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954"; 17 | String expandedXprv = "20849f14bbe212d1b8917da2e1eda9afc4c21e9dd0a47f1e169a3326d45ae443236f54b987369e86ed78eb2b0a2def89a69ec69ca1059e2efe045796dc583d91"; 18 | String hashedMessage = "99ab9ebdba106466371467b036d56a0e54ad2a6035e365a6103ba97ab553fd52"; 19 | byte[] sig = Signer.ed25519InnerSign(Hex.decode(expandedXprv), Hex.decode(hashedMessage)); 20 | System.out.println("sig:" + Hex.toHexString(sig)); 21 | //expected: e628e980c690d9ef4ca8a2edee1654a6b401edc4f1af7bda3ffd97fe412522c3bab671dd4e51d0aeeb64f8d761fbdb03e296ab0c1dcbed4eafa504f412a98100 22 | } 23 | } -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/common/DerivePrivateKeyTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | import org.junit.Test; 5 | 6 | import java.security.InvalidKeyException; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.security.SignatureException; 9 | 10 | public class DerivePrivateKeyTest { 11 | @Test 12 | public void testBip44Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 13 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; 14 | byte[] derivePrivateKey = DerivePrivateKey.bip44derivePrvKey(rootKey, 1, false, 2); 15 | System.out.println(Hex.toHexString(derivePrivateKey)); 16 | //expected 48c65f40d860723e71b03988a22edc9ad00ae0deae992e79fb3b812edb5c3e43e78065bf46d0e8ad922cdae600fd2c2a6239b8f1f504f8f255460c6fcce023ff 17 | } 18 | 19 | @Test 20 | public void testBip32Key() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 21 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; 22 | byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 2, (byte) 0); 23 | System.out.println(Hex.toHexString(derivePrivateKey)); 24 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc 25 | } 26 | 27 | @Test 28 | public void testBip32PublicKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 29 | String rootKey = "38d2c44314c401b3ea7c23c54e12c36a527aee46a7f26b82443a46bf40583e439dea25de09b0018b35a741d8cd9f6ec06bc11db49762617485f5309ab72a12d4"; 30 | byte[] derivePrivateKey = DerivePrivateKey.bip32derivePrvKey(rootKey, 14, (byte) 0); 31 | byte[] deriveXpub = DeriveXpub.deriveXpub(derivePrivateKey); 32 | System.out.println(Hex.toHexString(deriveXpub).substring(0, 64)); 33 | //expected 2806f655e862ffc550c2dcaa5057e02e0a1c7c714a4c5a1823bd83f060593e43f5fef4fced766de36cb7ea8ca51cebac7830d54dca1929e669a4a7c3dc7b9dcc 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/common/DeriveXpubTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | 4 | import org.bouncycastle.util.encoders.Hex; 5 | import org.junit.Test; 6 | 7 | public class DeriveXpubTest { 8 | 9 | @Test 10 | public void testDeriveXpub() { 11 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"; 12 | byte[] xpub = DeriveXpub.deriveXpub(Hex.decode(hxprv)); 13 | System.out.println("hxpub: " + Hex.toHexString(xpub)); 14 | //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b 15 | // d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b 16 | } 17 | } -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/common/ExpandedPrivateKeyTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | 4 | import org.bouncycastle.util.encoders.Hex; 5 | import org.junit.Test; 6 | 7 | import java.security.InvalidKeyException; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.security.SignatureException; 10 | 11 | public class ExpandedPrivateKeyTest { 12 | 13 | @Test 14 | public void testExpandedKey() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 15 | String childXprv = "e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954"; 16 | byte[] z = ExpandedPrivateKey.expandedPrivateKey(Hex.decode(childXprv)); 17 | System.out.println(Hex.toHexString(z)); 18 | //expect: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3 19 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a633535b899d45316cd83e027913d3ff3dc52f6a951a686fd2b750099e1f7c70cb98c3 20 | } 21 | } -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/common/NonHardenedChildTest.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.common; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | import org.junit.Test; 5 | 6 | import java.security.InvalidKeyException; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.security.SignatureException; 9 | 10 | 11 | public class NonHardenedChildTest { 12 | 13 | @Test 14 | public void testNHChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 15 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"; 16 | byte[] xprv = Hex.decode(hxprv); 17 | //expected: d9c7b41f030a398dada343096040c675be48278046623849977cb0fd01d395a51c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b 18 | String[] hpaths = {"010400000000000000", "0100000000000000"}; 19 | byte[][] paths = new byte[][]{ 20 | Hex.decode(hpaths[0]), 21 | Hex.decode(hpaths[1]) 22 | }; 23 | byte[] res = xprv; 24 | for (int i = 0; i < hpaths.length; i++) { 25 | byte[] xpub = DeriveXpub.deriveXpub(res); 26 | // System.out.println("xpub: "+Hex.toHexString(xpub)); 27 | res = NonHardenedChild.nhChild(paths[i], res, xpub); 28 | } 29 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 30 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 31 | } 32 | 33 | @Test 34 | public void testChild() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { 35 | String hxprv = "10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b"; 36 | String[] hpaths = {"010400000000000000", "0100000000000000"}; 37 | byte[] childXprv = NonHardenedChild.child(Hex.decode(hxprv), hpaths); 38 | //expected: e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 39 | // e8c0965af60563c4cabcf2e947b1cd955c4f501eb946ffc8c3447e5ec8a6335398a3720b3f96077fa187fdde48fe7dc293984b196f5e292ef8ed78fdbd8ed954 40 | } 41 | } -------------------------------------------------------------------------------- /tx-signer/src/test/java/io/bytom/offline/util/SHA3256Test.java: -------------------------------------------------------------------------------- 1 | package io.bytom.offline.util; 2 | 3 | import org.bouncycastle.util.encoders.Hex; 4 | import org.junit.Test; 5 | 6 | public class SHA3256Test { 7 | @Test 8 | public void testSHA3Hash() { 9 | String rawTransaction = "7b0a202022646563696d616c73223a20382c0a202022646573637" + 10 | "2697074696f6e223a207b7d2c0a2020226e616d65223a2022222c0a20202273796d626f6c223a2022220a7d"; 11 | byte[] bytes = SHA3Util.hashSha256(Hex.decode(rawTransaction)); 12 | System.out.println(Hex.toHexString(bytes)); 13 | //expected 69ab19b3907f40e4f264dbd3f71967654e0e93f836026918af8861932ed0409b 14 | } 15 | 16 | 17 | } 18 | --------------------------------------------------------------------------------