├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── gxchain │ │ └── client │ │ ├── GXChainCallBack.java │ │ ├── GXChainClient.java │ │ ├── domian │ │ ├── KeyPair.java │ │ ├── TransactionResult.java │ │ └── params │ │ │ └── GetTableRowsParams.java │ │ ├── exception │ │ ├── GXChainApiException.java │ │ └── HttpAccessFailException.java │ │ ├── graphenej │ │ ├── Address.java │ │ ├── AssetOptions.java │ │ ├── BIP39.java │ │ ├── BrainKey.java │ │ ├── BrainKeyTest.java │ │ ├── Chains.java │ │ ├── Converter.java │ │ ├── FileBin.java │ │ ├── Invoice.java │ │ ├── KeyTest.java │ │ ├── LineItem.java │ │ ├── MarketTrade.java │ │ ├── OrderBook.java │ │ ├── Price.java │ │ ├── PublicKey.java │ │ ├── PublicKeyTest.java │ │ ├── RPC.java │ │ ├── TestAccounts.java │ │ ├── Util.java │ │ ├── UtilTest.java │ │ ├── Varint.java │ │ ├── brainkeydict.txt │ │ ├── crypto │ │ │ ├── AndroidRandomSource.java │ │ │ ├── EntropySource.java │ │ │ ├── RandomSource.java │ │ │ ├── SecureRandomGenerator.java │ │ │ └── SecureRandomStrengthener.java │ │ ├── enums │ │ │ ├── AuthorityType.java │ │ │ ├── ObjectType.java │ │ │ └── OperationType.java │ │ ├── errors │ │ │ ├── ChecksumException.java │ │ │ ├── IncompatibleOperation.java │ │ │ ├── IncompleteAssetError.java │ │ │ ├── MalformedAddressException.java │ │ │ ├── MalformedOperationException.java │ │ │ ├── MalformedTransactionException.java │ │ │ └── RepeatedRequestIdException.java │ │ ├── interfaces │ │ │ ├── ByteSerializable.java │ │ │ ├── GrapheneSerializable.java │ │ │ ├── JsonSerializable.java │ │ │ ├── NodeErrorListener.java │ │ │ ├── SubscriptionHub.java │ │ │ ├── SubscriptionListener.java │ │ │ └── WitnessResponseListener.java │ │ ├── models │ │ │ ├── AccountBalanceUpdate.java │ │ │ ├── AccountProperties.java │ │ │ ├── ApiCall.java │ │ │ ├── AssetFeed.java │ │ │ ├── AssetHolderCount.java │ │ │ ├── BaseResponse.java │ │ │ ├── BitAssetData.java │ │ │ ├── Block.java │ │ │ ├── BlockHeader.java │ │ │ ├── BroadcastedTransaction.java │ │ │ ├── BucketObject.java │ │ │ ├── DynamicGlobalProperties.java │ │ │ ├── HistoricalTransfer.java │ │ │ ├── SubscriptionResponse.java │ │ │ ├── WitnessResponse.java │ │ │ ├── backup │ │ │ │ ├── LinkedAccount.java │ │ │ │ ├── PrivateKeyBackup.java │ │ │ │ ├── Wallet.java │ │ │ │ └── WalletBackup.java │ │ │ └── contract │ │ │ │ ├── Abi.java │ │ │ │ ├── Action.java │ │ │ │ ├── ContractAccountProperties.java │ │ │ │ ├── Field.java │ │ │ │ ├── Struct.java │ │ │ │ └── Table.java │ │ ├── objects │ │ │ ├── AccountOptions.java │ │ │ ├── Asset.java │ │ │ ├── AssetAmount.java │ │ │ ├── AssetTest.java │ │ │ ├── Authority.java │ │ │ ├── BlockData.java │ │ │ ├── BroadcastRequestParams.java │ │ │ ├── Extensions.java │ │ │ ├── GrapheneObject.java │ │ │ ├── LimitOrder.java │ │ │ ├── Memo.java │ │ │ ├── Optional.java │ │ │ ├── Transaction.java │ │ │ ├── UserAccount.java │ │ │ └── Vote.java │ │ ├── operations │ │ │ ├── AccountCreateOperation.java │ │ │ ├── AccountUpdateOperation.java │ │ │ ├── AccountUpdateOperationBuilder.java │ │ │ ├── BaseOperation.java │ │ │ ├── BaseOperationBuilder.java │ │ │ ├── BroadcastOperation.java │ │ │ ├── CallContractOperation.java │ │ │ ├── DiyOperation.java │ │ │ ├── LimitOrderCancelOperation.java │ │ │ ├── LimitOrderCreateOperation.java │ │ │ ├── TransferOperation.java │ │ │ └── TransferOperationBuilder.java │ │ └── test │ │ │ └── NaiveSSLContext.java │ │ ├── rpc │ │ ├── GXChainApiRestClient.java │ │ ├── GXChainClientFactory.java │ │ ├── api │ │ │ ├── GXChainApiService.java │ │ │ ├── GxbApiFactory.java │ │ │ ├── GxbGsonConverterFactory.java │ │ │ ├── GxbGsonRequestBodyConverter.java │ │ │ └── GxbGsonResponseBodyConverter.java │ │ └── impl │ │ │ └── GXChainApiRestClientImpl.java │ │ └── util │ │ ├── BeanUtils.java │ │ ├── GXGsonUtil.java │ │ └── TxSerializerUtil.java └── resources │ └── js │ └── tx_serializer.min.js └── test └── java └── com └── gxchain └── client ├── GXChainClientTest.java └── util └── GXGsonUtilTest.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | .idea 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 4.0.0 8 | 9 | com.gxchain.common 10 | gxchain-client 11 | 2.0.7-RELEASE 12 | 13 | UTF-8 14 | 2.5.0 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.12 21 | 22 | 23 | org.tukaani 24 | xz 25 | 1.8 26 | 27 | 28 | com.squareup.retrofit2 29 | retrofit 30 | ${com.squareup.retrofit2.version} 31 | 32 | 33 | com.squareup.retrofit2 34 | converter-jackson 35 | ${com.squareup.retrofit2.version} 36 | 37 | 38 | cglib 39 | cglib 40 | 3.1 41 | 42 | 43 | org.bitcoinj 44 | bitcoinj-core 45 | 0.14.3 46 | 47 | 48 | org.bouncycastle 49 | bcprov-jdk15on 50 | 1.59 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | 1.16.18 56 | 57 | 58 | com.google.code.gson 59 | gson 60 | 2.8.2 61 | 62 | 63 | org.apache.commons 64 | commons-lang3 65 | 3.4 66 | 67 | 68 | commons-codec 69 | commons-codec 70 | 1.10 71 | 72 | 73 | commons-collections 74 | commons-collections 75 | 3.2.2 76 | 77 | 78 | joda-time 79 | joda-time 80 | 2.9.1 81 | 82 | 83 | org.slf4j 84 | slf4j-api 85 | 1.7.5 86 | 87 | 88 | ch.qos.logback 89 | logback-core 90 | 1.0.13 91 | 92 | 93 | ch.qos.logback 94 | logback-classic 95 | 1.0.13 96 | 97 | 98 | com.gxchain.common 99 | common-signature 100 | 1.1.1-RELEASE 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-compiler-plugin 109 | 3.1 110 | 111 | 1.8 112 | 1.8 113 | UTF-8 114 | true 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-source-plugin 120 | 2.4 121 | 122 | 123 | attach-sources 124 | 125 | jar-no-fork 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-surefire-plugin 133 | 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | nexus-releases 143 | 144 | http://repo.gxchain.cn/repository/maven-releases/ 145 | 146 | 147 | nexus-snapshots 148 | http://192.168.1.122:8081/repository/maven-snapshots/ 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/GXChainCallBack.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client; 2 | 3 | import com.google.gson.JsonArray; 4 | 5 | /** 6 | * @author liruobin 7 | * @since 2018/7/5 下午4:49 8 | */ 9 | @FunctionalInterface 10 | public interface GXChainCallBack { 11 | void response(long blockHeight, String txid, JsonArray operation); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/domian/KeyPair.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.domian; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author liruobin 10 | * @since 2018/7/5 下午3:30 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Builder 16 | public class KeyPair { 17 | /** 18 | * 脑key 19 | */ 20 | private String brainKey; 21 | /** 22 | * 私钥 23 | */ 24 | private String privateKey; 25 | /** 26 | * 公钥 27 | */ 28 | private String publicKey; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/domian/TransactionResult.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.domian; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.gxchain.client.graphenej.objects.Transaction; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | 8 | /** 9 | * @author liruobin 10 | * @since 2019/2/16 3:04 PM 11 | */ 12 | @Getter 13 | @AllArgsConstructor 14 | public class TransactionResult { 15 | private Transaction transaction; 16 | 17 | private JsonElement result; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/domian/params/GetTableRowsParams.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.domian.params; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author liruobin 11 | * @since 2019/2/16 5:49 PM 12 | */ 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class GetTableRowsParams { 18 | /** 19 | * 查询时指定的key最小值, 默认为0 20 | */ 21 | @SerializedName("lower_bound") 22 | private Number lowerBound; 23 | /** 24 | * 查询时指定的key最大值,默认为-1,即最大的无符号整形 25 | */ 26 | @SerializedName("upper_bound") 27 | private Number upperBound; 28 | /** 29 | * 查询时指定的index,默认为1,即第1个索引 30 | */ 31 | @SerializedName("index_position") 32 | private Number indexPosition; 33 | /** 34 | * 查询时指定返回limit条,默认返回10条 35 | */ 36 | private Number limit; 37 | /** 38 | * 查询结果按key的倒序输出,默认为0,即按key从小到大输出 39 | */ 40 | private Boolean reverse; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/exception/GXChainApiException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.exception; 2 | 3 | import com.gxchain.client.graphenej.models.BaseResponse; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author liruobin 8 | * @since 2018/7/5 上午10:20 9 | */ 10 | public class GXChainApiException extends RuntimeException { 11 | @Getter 12 | private BaseResponse.Error error; 13 | 14 | public GXChainApiException() { 15 | super(); 16 | } 17 | 18 | 19 | public GXChainApiException(String message) { 20 | super(message); 21 | } 22 | 23 | public GXChainApiException(BaseResponse.Error error) { 24 | this.error = error; 25 | } 26 | 27 | public GXChainApiException(Throwable cause) { 28 | super(cause); 29 | } 30 | 31 | 32 | public GXChainApiException(String message, Throwable cause) { 33 | super(message, cause); 34 | } 35 | 36 | @Override 37 | public String getMessage() { 38 | if (error != null) { 39 | return error.getMessage(); 40 | } 41 | return super.getMessage(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/exception/HttpAccessFailException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.exception; 2 | 3 | public class HttpAccessFailException extends RuntimeException { 4 | 5 | public HttpAccessFailException() { 6 | super(); 7 | } 8 | 9 | public HttpAccessFailException(String message) { 10 | super(message); 11 | } 12 | 13 | public HttpAccessFailException(Throwable cause) { 14 | super(cause); 15 | } 16 | 17 | 18 | public HttpAccessFailException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/Address.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.primitives.Bytes; 5 | import com.gxchain.client.graphenej.errors.MalformedAddressException; 6 | import org.bitcoinj.core.Base58; 7 | import org.bitcoinj.core.ECKey; 8 | import org.spongycastle.crypto.digests.RIPEMD160Digest; 9 | 10 | import java.util.Arrays; 11 | 12 | /** 13 | * Class used to encapsulate address-related operations. 14 | */ 15 | public class Address { 16 | 17 | public final static String BITSHARES_PREFIX = "GXC"; 18 | 19 | private PublicKey publicKey; 20 | private String prefix; 21 | 22 | public Address(ECKey key) { 23 | this.publicKey = new PublicKey(key); 24 | this.prefix = BITSHARES_PREFIX; 25 | } 26 | 27 | public Address(ECKey key, String prefix) { 28 | this.publicKey = new PublicKey(key); 29 | this.prefix = prefix; 30 | } 31 | 32 | public Address(String address) throws MalformedAddressException { 33 | this.prefix = address.substring(0, 3); 34 | byte[] decoded = Base58.decode(address.substring(3, address.length())); 35 | byte[] pubKey = Arrays.copyOfRange(decoded, 0, decoded.length - 4); 36 | byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length); 37 | publicKey = new PublicKey(ECKey.fromPublicOnly(pubKey)); 38 | byte[] calculatedChecksum = calculateChecksum(pubKey); 39 | Preconditions.checkArgument(Arrays.deepEquals(new byte[][] {checksum}, new byte[][] {calculatedChecksum}), "checkSum error"); 40 | } 41 | 42 | public PublicKey getPublicKey(){ 43 | return this.publicKey; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | byte[] pubKey = this.publicKey.toBytes(); 49 | byte[] checksum = calculateChecksum(pubKey); 50 | byte[] pubKeyChecksummed = Bytes.concat(pubKey, checksum); 51 | return this.prefix + Base58.encode(pubKeyChecksummed); 52 | } 53 | 54 | private byte[] calculateChecksum(byte[] data){ 55 | byte[] checksum = new byte[160 / 8]; 56 | RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); 57 | ripemd160Digest.update(data, 0, data.length); 58 | ripemd160Digest.doFinal(checksum, 0); 59 | return Arrays.copyOfRange(checksum, 0, 4); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/AssetOptions.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.google.common.primitives.UnsignedLong; 4 | 5 | /** 6 | * Created by nelson on 12/13/16. 7 | */ 8 | public class AssetOptions { 9 | // TODO: Use these constants instead of using cryptic constants like 128 and 511 10 | public static final int CHARGE_MARKET_FEE = 0x01; 11 | public static final int WHITE_LIST = 0x02; 12 | public static final int OVERRIDE_AUTHORITY = 0x04; 13 | public static final int TRANSFER_RESTRICTED = 0x08; 14 | public static final int DISABLE_FORCE_SETTLE = 0x10; 15 | public static final int GLOBAL_SETTLE = 0x20; 16 | public static final int DISABLE_CONFIDENTIAL = 0x40; 17 | public static final int WITNESS_FED_ASSET = 0x80; 18 | public static final int COMITEE_FED_ASSET = 0x100; 19 | 20 | private UnsignedLong max_supply; 21 | private float market_fee_percent; 22 | private UnsignedLong max_market_fee; 23 | private long issuer_permissions; 24 | private int flags; 25 | private Price core_exchange_rate; 26 | //TODO: Implement whitelist_authorities, blacklist_authorities, whitelist_markets, blacklist_markets and extensions 27 | private String description; 28 | 29 | public UnsignedLong getMaxSupply() { 30 | return max_supply; 31 | } 32 | 33 | public void setMaxSupply(UnsignedLong max_supply) { 34 | this.max_supply = max_supply; 35 | } 36 | 37 | public float getMarketFeePercent() { 38 | return market_fee_percent; 39 | } 40 | 41 | public void setMarketFeePercent(float market_fee_percent) { 42 | this.market_fee_percent = market_fee_percent; 43 | } 44 | 45 | public UnsignedLong getMaxMarketFee() { 46 | return max_market_fee; 47 | } 48 | 49 | public void setMaxMarketFee(UnsignedLong max_market_fee) { 50 | this.max_market_fee = max_market_fee; 51 | } 52 | 53 | public long getIssuerPermissions() { 54 | return issuer_permissions; 55 | } 56 | 57 | public void setIssuerPermissions(long issuer_permissions) { 58 | this.issuer_permissions = issuer_permissions; 59 | } 60 | 61 | public int getFlags() { 62 | return flags; 63 | } 64 | 65 | public void setFlags(int flags) { 66 | this.flags = flags; 67 | } 68 | 69 | public Price getCoreExchangeRate() { 70 | return core_exchange_rate; 71 | } 72 | 73 | public void setCoreExchangeRate(Price core_exchange_rate) { 74 | this.core_exchange_rate = core_exchange_rate; 75 | } 76 | 77 | public String getDescription() { 78 | return description; 79 | } 80 | 81 | public void setDescription(String description) { 82 | this.description = description; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/BIP39.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import org.bitcoinj.core.Base58; 4 | import org.bitcoinj.core.ECKey; 5 | import org.bitcoinj.crypto.HDKeyDerivation; 6 | import org.bitcoinj.crypto.MnemonicCode; 7 | import org.spongycastle.crypto.digests.RIPEMD160Digest; 8 | import org.spongycastle.crypto.digests.SHA512Digest; 9 | 10 | import java.util.Arrays; 11 | 12 | /** 13 | * 14 | * @author hvarona 15 | */ 16 | public class BIP39 { 17 | 18 | private final ECKey mPrivateKey; 19 | 20 | public BIP39(String words, String passphrase) { 21 | 22 | byte[] seed = MnemonicCode.toSeed(Arrays.asList(words.split(" ")), passphrase); 23 | mPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed); 24 | 25 | } 26 | 27 | public String getUncompressedAddress() { 28 | RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); 29 | SHA512Digest sha512Digest = new SHA512Digest(); 30 | sha512Digest.update(mPrivateKey.decompress().getPubKey(), 0, mPrivateKey.decompress().getPubKey().length); 31 | byte[] intermediate = new byte[512 / 8]; 32 | sha512Digest.doFinal(intermediate, 0); 33 | ripemd160Digest.update(intermediate, 0, intermediate.length); 34 | byte[] output = new byte[160 / 8]; 35 | ripemd160Digest.doFinal(output, 0); 36 | String encoded = Base58.encode(output); 37 | byte[] checksum = new byte[(160 / 8) + 4]; 38 | System.arraycopy(calculateChecksum(output), 0, checksum, checksum.length - 4, 4); 39 | System.arraycopy(output, 0, checksum, 0, output.length); 40 | 41 | return ("BTS" + Base58.encode(checksum)); 42 | } 43 | 44 | public String getAddress() { 45 | RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); 46 | SHA512Digest sha512Digest = new SHA512Digest(); 47 | sha512Digest.update(mPrivateKey.getPubKey(), 0, mPrivateKey.getPubKey().length); 48 | byte[] intermediate = new byte[512 / 8]; 49 | sha512Digest.doFinal(intermediate, 0); 50 | ripemd160Digest.update(intermediate, 0, intermediate.length); 51 | byte[] output = new byte[160 / 8]; 52 | ripemd160Digest.doFinal(output, 0); 53 | String encoded = Base58.encode(output); 54 | byte[] checksum = new byte[(160 / 8) + 4]; 55 | System.arraycopy(calculateChecksum(output), 0, checksum, checksum.length - 4, 4); 56 | System.arraycopy(output, 0, checksum, 0, output.length); 57 | 58 | return ("BTS" + Base58.encode(checksum)); 59 | } 60 | 61 | public byte[] calculateChecksum(byte[] input) { 62 | byte[] answer = new byte[4]; 63 | RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); 64 | ripemd160Digest.update(input, 0, input.length); 65 | byte[] output = new byte[160 / 8]; 66 | ripemd160Digest.doFinal(output, 0); 67 | System.arraycopy(output, 0, answer, 0, 4); 68 | return answer; 69 | } 70 | 71 | public byte[] getPublicKey() { 72 | return mPrivateKey.getPubKey(); 73 | } 74 | 75 | public ECKey getPrivateKey() { 76 | return mPrivateKey; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/BrainKey.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.gxchain.client.graphenej.crypto.SecureRandomGenerator; 4 | import org.bitcoinj.core.DumpedPrivateKey; 5 | import org.bitcoinj.core.ECKey; 6 | import org.bitcoinj.core.NetworkParameters; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.UnsupportedEncodingException; 11 | import java.security.MessageDigest; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.security.SecureRandom; 14 | import java.util.ArrayList; 15 | 16 | /** 17 | * Class used to encapsulate all BrainKey-related operations. 18 | */ 19 | public class BrainKey { 20 | private static final Logger LOGGER = LoggerFactory.getLogger(BrainKey.class); 21 | 22 | // The size of the word dictionary 23 | public static final int DICT_WORD_COUNT = 49744; 24 | 25 | /* The required number of words */ 26 | public static final int BRAINKEY_WORD_COUNT = 12; 27 | 28 | /* The default sequence number is zero */ 29 | public static final int DEFAULT_SEQUENCE_NUMBER = 0; 30 | 31 | /* The corresponding private key derivated from the brain key */ 32 | private ECKey mPrivateKey; 33 | 34 | /* The actual words from this brain key + the sequence number */ 35 | private String mBrainKey; 36 | 37 | /* The sequence number */ 38 | private int sequenceNumber; 39 | 40 | /** 41 | * Method that will generate a random brain key 42 | * 43 | * @param words The list of words from the graphene specification 44 | * dictionary. 45 | * @return A random sequence of words 46 | */ 47 | public static String suggest(String words) { 48 | String[] wordArray = words.split(","); 49 | ArrayList suggestedBrainKey = new ArrayList(); 50 | assert (wordArray.length == DICT_WORD_COUNT); 51 | SecureRandom secureRandom = SecureRandomGenerator.getSecureRandom(); 52 | int index; 53 | for (int i = 0; i < BRAINKEY_WORD_COUNT; i++) { 54 | index = secureRandom.nextInt(DICT_WORD_COUNT - 1); 55 | suggestedBrainKey.add(wordArray[index].toUpperCase()); 56 | } 57 | StringBuilder stringBuilder = new StringBuilder(); 58 | for(String word : suggestedBrainKey){ 59 | stringBuilder.append(word); 60 | stringBuilder.append(" "); 61 | } 62 | return stringBuilder.toString().trim(); 63 | } 64 | /** 65 | * BrainKey constructor that takes as argument a specific brain key word 66 | * sequence and generates the private key and address from that. 67 | * 68 | * @param words The brain key specifying the private key 69 | * @param sequence Sequence number 70 | */ 71 | public BrainKey(String words, int sequence) { 72 | this.mBrainKey = words; 73 | this.sequenceNumber = sequence; 74 | String encoded = String.format("%s %d", words, sequence); 75 | try { 76 | MessageDigest md = MessageDigest.getInstance("SHA-512"); 77 | byte[] bytes = md.digest(encoded.getBytes("UTF-8")); 78 | MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 79 | byte[] result = sha256.digest(bytes); 80 | mPrivateKey = ECKey.fromPrivate(result); 81 | 82 | } catch (NoSuchAlgorithmException e) { 83 | LOGGER.info("NoSuchAlgotithmException. Msg: " + e.getMessage()); 84 | } catch (UnsupportedEncodingException e) { 85 | LOGGER.info("UnsupportedEncodingException. Msg: " + e.getMessage()); 86 | } 87 | } 88 | 89 | /** 90 | * Gets the array of bytes representing the public key. 91 | * @return 92 | */ 93 | public byte[] getPublicKey() { 94 | return mPrivateKey.getPubKey(); 95 | } 96 | 97 | /** 98 | * Returns the private key as an instance of the ECKey class. 99 | * @return 100 | */ 101 | public ECKey getPrivateKey() { 102 | return mPrivateKey; 103 | } 104 | 105 | /** 106 | * Returns the private key in the Wallet Import Format for the uncompressed private key. 107 | * @see WIF 108 | * @return 109 | */ 110 | public String getWalletImportFormat(){ 111 | DumpedPrivateKey wif = this.mPrivateKey.decompress().getPrivateKeyEncoded(NetworkParameters.fromID(NetworkParameters.ID_MAINNET)); 112 | return wif.toString(); 113 | } 114 | 115 | /** 116 | * Returns the public address derived from this brain key 117 | * @param prefix: The prefix to use in this address. 118 | * @return An instance of the {@link Address} class 119 | */ 120 | public Address getPublicAddress(String prefix){ 121 | return new Address(ECKey.fromPublicOnly(getPublicKey()), prefix); 122 | } 123 | 124 | /** 125 | * Brain key words getter 126 | * @return: The word sequence that comprises this brain key 127 | */ 128 | public String getBrainKey(){ 129 | return mBrainKey; 130 | } 131 | 132 | /** 133 | * Sequence number getter 134 | * @return: The sequence number used alongside with the brain key words in order 135 | * to derive the private key 136 | */ 137 | public int getSequenceNumber(){ 138 | return sequenceNumber; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/BrainKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import junit.framework.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Created by nelson on 4/18/17. 9 | */ 10 | public class BrainKeyTest { 11 | public final String TEST_BRAINKEY = "BARIC BICKERN LITZ TIPFUL JINGLED POOL TUMBAK PURIST APOPYLE DURAIN SATLIJK FAUCAL"; 12 | private BrainKey mBrainKey; 13 | 14 | @Before 15 | public void setup(){ 16 | mBrainKey = new BrainKey(TEST_BRAINKEY, BrainKey.DEFAULT_SEQUENCE_NUMBER); 17 | } 18 | 19 | @Test 20 | public void testAddress(){ 21 | Address address = mBrainKey.getPublicAddress(Address.BITSHARES_PREFIX); 22 | Assert.assertEquals("Assert that the address created is the expected one", 23 | "BTS61UqqgE3ARuTGcckzARsdQm4EMFdBEwYyi1pbwyHrZZWrCDhT2", 24 | address.toString()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/Chains.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | /** 4 | * Created by nelson on 11/8/16. 5 | */ 6 | public class Chains { 7 | public static class BITSHARES { 8 | public static final String CHAIN_ID = "bc59e6e7f500fa56504ce7101f7df8eb74151398f62167567adcf18a026928d1"; 9 | } 10 | public static class GRAPHENE { 11 | public static final String CHAIN_ID = "b8d1603965b3eb1acba27e62ff59f74efa3154d43a4188d381088ac7cdf35539"; 12 | } 13 | public static class TEST { 14 | public static final String CHAIN_ID = "39f5e2ede1f8bc1a3a54a7914414e3779e33193f1f5693510e73cb7a87617447"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/Converter.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.gxchain.client.graphenej.errors.IncompleteAssetError; 4 | import com.gxchain.client.graphenej.models.BucketObject; 5 | import com.gxchain.client.graphenej.objects.Asset; 6 | import com.gxchain.client.graphenej.objects.AssetAmount; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.MathContext; 10 | 11 | /** 12 | * Generic converter class used to translate the market information contained in a BucketObject and/or Price instances. 13 | * 14 | * Created by nelson on 12/23/16. 15 | */ 16 | public class Converter { 17 | private final String TAG = this.getClass().getName(); 18 | public static final int OPEN_VALUE = 0; 19 | public static final int CLOSE_VALUE = 1; 20 | public static final int HIGH_VALUE = 2; 21 | public static final int LOW_VALUE = 3; 22 | 23 | public static final int BASE_TO_QUOTE = 100; 24 | public static final int QUOTE_TO_BASE = 101; 25 | 26 | private Asset base; 27 | private Asset quote; 28 | private BucketObject bucket; 29 | 30 | /** 31 | * Constructor meant to be used trying to perform a conversion and in possession of a Price object. 32 | */ 33 | public Converter(){} 34 | 35 | /** 36 | * Constructor meant to be used when trying to perform a conversion and in possession of 37 | * a BucketObject, typically resulting from a 'get_market_history' API call. 38 | * @param base 39 | * @param quote 40 | * @param bucket 41 | */ 42 | public Converter(Asset base, Asset quote, BucketObject bucket){ 43 | this.base = base; 44 | this.quote = quote; 45 | this.bucket = bucket; 46 | } 47 | 48 | /** 49 | * Method used to obtain the equivalence between two assets considering their precisions 50 | * and given the specific time bucket passed in the constructor. 51 | * 52 | * The resulting double value will tell us how much of a given asset, a unit of 53 | * its pair is worth. 54 | * 55 | * The second argument is used to specify which of the assets should 56 | * be taken as a unit reference. 57 | * 58 | * For instance if used with the BASE_TO_QUOTE constant, this method will tell us how 59 | * many of the quote asset will make up for a unit of the base asset. And the opposite 60 | * is true for the QUOTE_TO_BASE contant. 61 | * 62 | * @param bucketAttribute: The desired bucket attribute to take in consideration. Can 63 | * be any of the following: OPEN_VALUE, CLOSE_VALUE, HIGH_VALUE or 64 | * LOW_VALUE. 65 | * @param direction: One of two constants 'BASE_TO_QUOTE' or 'QUOTE_TO_BASE' used to specify 66 | * which of the two assets is the one used as a unitary reference. 67 | * @return: double value representing how much of one asset, a unit of the paired asset 68 | * was worth at the point in time specified by the time bucket and the bucket parameter. 69 | */ 70 | public double getConversionRate(int bucketAttribute, int direction){ 71 | if(this.base.getPrecision() == -1 || this.quote.getPrecision() == -1){ 72 | throw new IncompleteAssetError(); 73 | } 74 | BigDecimal baseValue; 75 | BigDecimal quoteValue; 76 | switch (bucketAttribute){ 77 | case OPEN_VALUE: 78 | baseValue = bucket.open_base; 79 | quoteValue = bucket.open_quote; 80 | break; 81 | case CLOSE_VALUE: 82 | baseValue = bucket.close_base; 83 | quoteValue = bucket.close_quote; 84 | break; 85 | case HIGH_VALUE: 86 | baseValue = bucket.high_base; 87 | quoteValue = bucket.high_quote; 88 | break; 89 | case LOW_VALUE: 90 | baseValue = bucket.low_base; 91 | quoteValue = bucket.low_quote; 92 | break; 93 | default: 94 | baseValue = bucket.close_base; 95 | quoteValue = bucket.close_quote; 96 | } 97 | double basePrecisionAdjusted = baseValue.divide(BigDecimal.valueOf((long) Math.pow(10, base.getPrecision()))).doubleValue(); 98 | double quotePrecisionAdjusted = quoteValue.divide(BigDecimal.valueOf((long) Math.pow(10, quote.getPrecision()))).doubleValue(); 99 | if(direction == QUOTE_TO_BASE){ 100 | return basePrecisionAdjusted / quotePrecisionAdjusted; 101 | }else{ 102 | return quotePrecisionAdjusted / basePrecisionAdjusted; 103 | } 104 | } 105 | 106 | /** 107 | * Converts a given asset amount to the corresponding pair used when creating this class. 108 | * @param assetAmount: The asset to convert from. 109 | * @param bucketAttribute: The bucket attribute to use as a reference. Possible values are OPEN_VALUE, 110 | * CLOSE_VALUE, HIGH_VALUE or LOW_VALUE. 111 | * @return: The converted value in base units, that is the number of a unit x 10^precision 112 | */ 113 | public long convert(AssetAmount assetAmount, int bucketAttribute) { 114 | double conversionRate = 0; 115 | double precisionFactor = 0.0; 116 | if(assetAmount.getAsset().equals(this.base)){ 117 | conversionRate = this.getConversionRate(bucketAttribute, BASE_TO_QUOTE); 118 | precisionFactor = Math.pow(10, this.quote.getPrecision()) / Math.pow(10, this.base.getPrecision()); 119 | }else if(assetAmount.getAsset().equals(this.quote)){ 120 | conversionRate = this.getConversionRate(bucketAttribute, QUOTE_TO_BASE); 121 | precisionFactor = Math.pow(10, this.base.getPrecision()) / Math.pow(10, this.quote.getPrecision()); 122 | } 123 | long assetAmountValue = assetAmount.getAmount().longValue(); 124 | long convertedBaseValue = (long) (assetAmountValue * conversionRate * precisionFactor); 125 | return convertedBaseValue; 126 | } 127 | 128 | /** 129 | * Method used to obtain the conversion rate between two assets given in a Price instance as recovered by the 130 | * 'get_limit_orders' API call. 131 | * 132 | * The same rules that apply for the {@link #getConversionRate(int bucketAttribute, int direction) getConversionRate} 133 | * are valid for the 'direction' argument. 134 | * 135 | * @param price: The Price object instance 136 | * @param direction: The direction from which to perform the conversion, can be only one of BASE_TO_QUOTE or 137 | * QUOTE_TO_BASE. 138 | * @return: A double representing the exchange rate. 139 | */ 140 | public double getConversionRate(Price price, int direction){ 141 | Asset base = price.base.getAsset(); 142 | Asset quote = price.quote.getAsset(); 143 | if(base.getPrecision() == -1 || quote.getPrecision() == -1){ 144 | throw new IncompleteAssetError("The given asset instance must provide precision information"); 145 | } 146 | double conversionRate = 0; 147 | double precisionFactor = 0.0; 148 | MathContext mathContext = new MathContext(Math.max(base.getPrecision(), quote.getPrecision())); 149 | BigDecimal baseValue = BigDecimal.valueOf(price.base.getAmount().longValue()); 150 | BigDecimal quoteValue = BigDecimal.valueOf(price.quote.getAmount().doubleValue()); 151 | // LOGGER.info(String.format("base: %d, quote: %d", baseValue.longValue(), quoteValue.longValue())); 152 | if(direction == BASE_TO_QUOTE){ 153 | conversionRate = quoteValue.divide(baseValue, mathContext).doubleValue(); 154 | precisionFactor = Math.pow(10, base.getPrecision()) / Math.pow(10, quote.getPrecision()); 155 | }else{ 156 | conversionRate = baseValue.divide(quoteValue, mathContext).doubleValue(); 157 | precisionFactor = Math.pow(10, quote.getPrecision()) / Math.pow(10, base.getPrecision()); 158 | } 159 | // LOGGER.info(String.format("conversion rate: %.4f, precision factor: %.2f", conversionRate, precisionFactor)); 160 | return conversionRate * precisionFactor; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.gxchain.client.graphenej.interfaces.JsonSerializable; 6 | import org.bitcoinj.core.Base58; 7 | 8 | /** 9 | * Class used to handle invoice generation, compression and QR-Code data derivation, 10 | * as detailed in this link. 11 | * @author Nelson R. Pérez 12 | */ 13 | public class Invoice implements JsonSerializable { 14 | private String to; 15 | private String to_label; 16 | private String memo; 17 | private String currency; 18 | private LineItem[] line_items; 19 | private String note; 20 | private String callback; 21 | 22 | public Invoice(String to, String to_label, String memo, String currency, LineItem[] items, String note, String callback){ 23 | this.to = to; 24 | this.to_label = to_label; 25 | this.memo = memo; 26 | this.currency = currency; 27 | this.line_items = items; 28 | this.note = note; 29 | this.callback = callback; 30 | } 31 | 32 | public String getToLabel() { 33 | return to_label; 34 | } 35 | 36 | public void setToLabel(String to_label) { 37 | this.to_label = to_label; 38 | } 39 | 40 | public String getNote() { 41 | return note; 42 | } 43 | 44 | public void setNote(String note) { 45 | this.note = note; 46 | } 47 | 48 | public String getTo() { 49 | return to; 50 | } 51 | 52 | public void setTo(String to) { 53 | this.to = to; 54 | } 55 | 56 | public String getMemo() { 57 | return memo; 58 | } 59 | 60 | public void setMemo(String memo) { 61 | this.memo = memo; 62 | } 63 | 64 | public String getCurrency() { 65 | return currency; 66 | } 67 | 68 | public void setCurrency(String currency) { 69 | this.currency = currency; 70 | } 71 | 72 | public LineItem[] getLineItems() { 73 | return line_items; 74 | } 75 | 76 | public void setLineItems(LineItem[] line_items) { 77 | this.line_items = line_items; 78 | } 79 | 80 | public String getCallback() { 81 | return callback; 82 | } 83 | 84 | public void setCallback(String callback) { 85 | this.callback = callback; 86 | } 87 | 88 | @Override 89 | public String toJsonString() { 90 | Gson gson = new Gson(); 91 | return gson.toJson(this); 92 | } 93 | 94 | @Override 95 | public JsonElement toJsonObject() { 96 | return null; 97 | } 98 | 99 | public static String toQrCode(Invoice invoice){ 100 | String json = invoice.toJsonString(); 101 | return Base58.encode(Util.compress(json.getBytes(), Util.LZMA)); 102 | } 103 | 104 | public static Invoice fromQrCode(String encoded){ 105 | String json = new String(Util.decompress(Base58.decode(encoded), Util.LZMA)); 106 | Gson gson = new Gson(); 107 | return gson.fromJson(json, Invoice.class); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/KeyTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import junit.framework.Assert; 4 | import org.bitcoinj.core.DumpedPrivateKey; 5 | import org.bitcoinj.core.ECKey; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Created by nelson on 11/2/17. 10 | */ 11 | 12 | public class KeyTest { 13 | 14 | /** 15 | * Testing key to address derivation 16 | */ 17 | @Test 18 | public void testKeyToAddress(){ 19 | String wif = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM"; 20 | ECKey sourcePrivate = DumpedPrivateKey.fromBase58(null, wif).getKey(); 21 | Address address = new Address(ECKey.fromPublicOnly(sourcePrivate.getPubKey())); 22 | Assert.assertEquals("Generated address matches expected one", "BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY", address.toString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/LineItem.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | /** 4 | * Created by nelson on 1/11/17. 5 | */ 6 | public class LineItem { 7 | private String label; 8 | private int quantity; 9 | private double price; 10 | 11 | public LineItem(String label, int quantity, double price){ 12 | this.label = label; 13 | this.quantity = quantity; 14 | this.price = price; 15 | } 16 | 17 | public int getQuantity() { 18 | return quantity; 19 | } 20 | 21 | public void setQuantity(int quantity) { 22 | this.quantity = quantity; 23 | } 24 | 25 | public double getPrice() { 26 | return price; 27 | } 28 | 29 | public void setPrice(double price) { 30 | this.price = price; 31 | } 32 | 33 | public String getLabel(){ 34 | return label; 35 | } 36 | 37 | public void setLabel(String label) { 38 | this.label = label; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/MarketTrade.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | /** 4 | * 5 | * @author henry 6 | */ 7 | public class MarketTrade { 8 | public String date; 9 | public double price; 10 | public double amount; 11 | public double value; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/OrderBook.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.google.common.math.DoubleMath; 4 | import com.google.common.primitives.UnsignedLong; 5 | import com.gxchain.client.graphenej.objects.Asset; 6 | import com.gxchain.client.graphenej.objects.AssetAmount; 7 | import com.gxchain.client.graphenej.objects.LimitOrder; 8 | import com.gxchain.client.graphenej.objects.UserAccount; 9 | import com.gxchain.client.graphenej.operations.LimitOrderCreateOperation; 10 | 11 | import java.math.RoundingMode; 12 | import java.util.List; 13 | 14 | /** 15 | * This class will maintain a snapshot of the order book between two assets. 16 | * 17 | * It also provides a handy method that should return the appropriate LimitOrderCreateOperation 18 | * object needed in case the user wants to perform market-priced operations. 19 | * 20 | * It is important to keep the order book updated, ideally by listening to blockchain events, and calling the 'update' method. 21 | * 22 | * Created by nelson on 3/25/17. 23 | */ 24 | public class OrderBook { 25 | private List limitOrders; 26 | 27 | public OrderBook(List limitOrders){ 28 | this.limitOrders = limitOrders; 29 | } 30 | 31 | /** 32 | * Replaces the current limit order by the list provided as parameter. 33 | * @param limitOrders: New list of orders 34 | */ 35 | public void update(List limitOrders){ 36 | this.limitOrders = limitOrders; 37 | } 38 | 39 | public void update(LimitOrder limitOrder){ 40 | //TODO: Implement the method that will update a single limit order from the order book 41 | } 42 | 43 | /** 44 | * High level method used to exchange a specific amount of an asset (The base) for another 45 | * one (The quote) at market value. 46 | * 47 | * It should analyze the order book and figure out the optimal amount of the base asset to give 48 | * away in order to obtain the desired amount of the quote asset. 49 | * 50 | * @param seller: User account of the seller, used to build the limit order create operation 51 | * @param myBaseAsset: The asset the user is willing to give away 52 | * @param myQuoteAmount: The amount of a given asset the user wants 53 | * @param expiration: The expiration time of the limit order 54 | * 55 | * @return An instance of the LimitOrderCreateOperation class, which is ready to be broadcasted. 56 | */ 57 | public LimitOrderCreateOperation exchange(UserAccount seller, Asset myBaseAsset, AssetAmount myQuoteAmount, int expiration){ 58 | AssetAmount toSell = new AssetAmount(UnsignedLong.valueOf(calculateRequiredBase(myQuoteAmount)), myBaseAsset); 59 | AssetAmount toReceive = myQuoteAmount; 60 | LimitOrderCreateOperation buyOrder = new LimitOrderCreateOperation(seller, toSell, toReceive, expiration, true); 61 | 62 | return buyOrder; 63 | } 64 | 65 | /** 66 | * Given a specific amount of a desired asset, this method will calculate how much of the corresponding 67 | * asset we need to offer to perform a successful transaction with the current order book. 68 | * @param quoteAmount: The amount of the desired asset. 69 | * @return: The minimum amount of the base asset that we need to give away 70 | */ 71 | public long calculateRequiredBase(AssetAmount quoteAmount){ 72 | long totalBought = 0; 73 | long totalSold = 0; 74 | for(int i = 0; i < this.limitOrders.size() && totalBought < quoteAmount.getAmount().longValue(); i++){ 75 | LimitOrder order = this.limitOrders.get(i); 76 | 77 | // If the base asset is the same as our quote asset, we have a match 78 | if(order.getSellPrice().base.getAsset().getObjectId().equals(quoteAmount.getAsset().getObjectId())){ 79 | // My quote amount, is the order's base amount 80 | long orderAmount = order.getForSale(); 81 | 82 | // The amount of the quote asset we still need 83 | long stillNeed = quoteAmount.getAmount().longValue() - totalBought; 84 | 85 | // If the offered amount is greater than what we still need, we exchange just what we need 86 | if(orderAmount >= stillNeed) { 87 | totalBought += stillNeed; 88 | double additionalRatio = (double) stillNeed / (double) order.getSellPrice().base.getAmount().longValue(); 89 | double additionalAmount = order.getSellPrice().quote.getAmount().longValue() * additionalRatio; 90 | long longAdditional = DoubleMath.roundToLong(additionalAmount, RoundingMode.HALF_UP); 91 | totalSold += longAdditional; 92 | }else{ 93 | // If the offered amount is less than what we need, we exchange the whole order 94 | totalBought += orderAmount; 95 | 96 | // The amount specified in the price ratio is not always all for sale. So in order to calculate 97 | // the amount actually sold we have to do: 98 | // actually_sold = for_sale * quote / base 99 | double sellRatio = ((double) orderAmount) / ((double) order.getSellPrice().base.getAmount().longValue()); 100 | 101 | totalSold += Math.floor(order.getSellPrice().quote.getAmount().doubleValue() * sellRatio); 102 | } 103 | } 104 | } 105 | return totalSold; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/Price.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.gxchain.client.graphenej.objects.AssetAmount; 4 | 5 | /** 6 | * The price struct stores asset prices in the Graphene system. 7 | * 8 | * A price is defined as a ratio between two assets, and represents a possible exchange rate 9 | * between those two assets. prices are generally not stored in any simplified form, i.e. a price 10 | * of (1000 CORE)/(20 USD) is perfectly normal. 11 | 12 | * The assets within a price are labeled base and quote. Throughout the Graphene code base, 13 | * the convention used is that the base asset is the asset being sold, and the quote asset is 14 | * the asset being purchased, where the price is represented as base/quote, so in the example 15 | * price above the seller is looking to sell CORE asset and get USD in return. 16 | * 17 | * Note: Taken from the Graphene doxygen. 18 | * Created by nelson on 12/16/16. 19 | */ 20 | public class Price { 21 | public AssetAmount base; 22 | public AssetAmount quote; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/PublicKey.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 4 | import org.bitcoinj.core.ECKey; 5 | import org.spongycastle.math.ec.ECPoint; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Created by nelson on 11/30/16. 11 | */ 12 | public class PublicKey implements ByteSerializable, Serializable { 13 | private ECKey publicKey; 14 | 15 | public PublicKey(ECKey key) { 16 | if(key.hasPrivKey()){ 17 | throw new IllegalStateException("Passing a private key to PublicKey constructor"); 18 | } 19 | this.publicKey = key; 20 | } 21 | 22 | public ECKey getKey(){ 23 | return publicKey; 24 | } 25 | 26 | @Override 27 | public byte[] toBytes() { 28 | if(publicKey.isCompressed()) { 29 | return publicKey.getPubKey(); 30 | }else{ 31 | publicKey = ECKey.fromPublicOnly(ECKey.compressPoint(publicKey.getPubKeyPoint())); 32 | return publicKey.getPubKey(); 33 | } 34 | } 35 | 36 | public String getAddress(){ 37 | ECKey pk = ECKey.fromPublicOnly(publicKey.getPubKey()); 38 | if(!pk.isCompressed()){ 39 | ECPoint point = ECKey.compressPoint(pk.getPubKeyPoint()); 40 | pk = ECKey.fromPublicOnly(point); 41 | } 42 | return new Address(pk).toString(); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return publicKey.hashCode(); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object obj) { 52 | PublicKey other = (PublicKey) obj; 53 | return this.publicKey.equals(other.getKey()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/PublicKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertNotEquals; 11 | 12 | /** 13 | * Created by nelson on 12/16/16. 14 | */ 15 | public class PublicKeyTest { 16 | private static final Logger LOGGER = LoggerFactory.getLogger(PublicKeyTest.class); 17 | @Before 18 | public void setUp() throws Exception { 19 | 20 | } 21 | 22 | @Test 23 | public void equals() throws Exception { 24 | Address address1 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); 25 | Address address2 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY"); 26 | Address address3 = new Address("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYp00"); 27 | PublicKey pk1 = address1.getPublicKey(); 28 | PublicKey pk2 = address2.getPublicKey(); 29 | PublicKey pk3 = address3.getPublicKey(); 30 | assertEquals("Public keys must be equal", pk1, pk2); 31 | assertNotEquals("Public keys must not be equal", pk1, pk3); 32 | } 33 | 34 | @Test 35 | public void testGxc() throws Exception { 36 | Address address1 = new Address("GXC5Yu6M75wt1HP87wpqqPrrNDANFMkgvA9djiT8N73D6Rq7zNraQ"); 37 | PublicKey pk1 = address1.getPublicKey(); 38 | LOGGER.info(pk1.toString()); 39 | } 40 | 41 | @After 42 | public void tearDown() throws Exception { 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/RPC.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | /** 4 | * Created by nelson on 11/16/16. 5 | */ 6 | public class RPC { 7 | public static final String VERSION = "2.0"; 8 | public static final String CALL_LOGIN = "login"; 9 | public static final String CALL_NETWORK_BROADCAST = "network_broadcast"; 10 | public static final String CALL_HISTORY = "history"; 11 | public static final String CALL_DATABASE = "database"; 12 | public static final String CALL_ASSET = "asset"; 13 | public static final String CALL_SET_SUBSCRIBE_CALLBACK = "set_subscribe_callback"; 14 | public static final String CALL_CANCEL_ALL_SUBSCRIPTIONS = "cancel_all_subscriptions"; 15 | public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name"; 16 | public static final String CALL_GET_ACCOUNTS = "get_accounts"; 17 | public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties"; 18 | public static final String CALL_BROADCAST_TRANSACTION = "broadcast_transaction"; 19 | public static final String CALL_GET_REQUIRED_FEES = "get_required_fees"; 20 | public static final String CALL_GET_KEY_REFERENCES = "get_key_references"; 21 | public static final String CALL_GET_RELATIVE_ACCOUNT_HISTORY = "get_relative_account_history"; 22 | public static final String CALL_LOOKUP_ACCOUNTS = "lookup_accounts"; 23 | public static final String CALL_LIST_ASSETS = "list_assets"; 24 | public static final String GET_OBJECTS = "get_objects"; 25 | public static final String GET_ACCOUNT_BALANCES = "get_account_balances"; 26 | public static final String CALL_LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols"; 27 | public static final String CALL_GET_BLOCK_HEADER = "get_block_header"; 28 | public static final String CALL_GET_LIMIT_ORDERS = "get_limit_orders"; 29 | public static final String CALL_GET_TRADE_HISTORY = "get_trade_history"; 30 | public static final String CALL_GET_MARKET_HISTORY = "get_market_history"; 31 | public static final String CALL_GET_ALL_ASSET_HOLDERS = "get_all_asset_holders"; 32 | public static final String CALL_GET_CHAIN_ID = "get_chain_id"; 33 | public static final String NETWORK_BROADCAST = "network_broadcast"; 34 | public static final String CALL_GET_BLOCK = "get_block"; 35 | 36 | public static final String BROADCAST_TRANSACTION = "broadcast_transaction"; 37 | 38 | public static final String GET_KEY_REFERENCES = "get_key_references"; 39 | 40 | public static final String LOOKUP_ASSET_SYMBOLS = "lookup_asset_symbols"; 41 | 42 | public static final String BROADCAST_TRANSACTION_SYNCHRONOUS = "broadcast_transaction_synchronous"; 43 | public static final String GET_TABLE_ROWS = "get_table_rows"; 44 | public static final String GET_TABLE_ROWS_EX = "get_table_rows_ex"; 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/TestAccounts.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | /** 4 | * Created by nelson on 11/2/17. 5 | */ 6 | 7 | public class TestAccounts { 8 | 9 | public class Bilthon5 { 10 | public static final String BRAINKEY = "TIGROID BUTLER GALLOWS PAKEHA MOONJAH ACARI LUPUS BAE ACRISIA ABBACY BASSOON AKUND"; 11 | public static final String WIF = "5Jr84yteNbomGgLq5nvH2Y6WYJCBTsjugPouCtggfS3RguqxDo8"; 12 | } 13 | 14 | public class Bilthon7 { 15 | public static final String BRAINKEY = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH"; 16 | public static final String WIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM"; 17 | } 18 | 19 | public class Bilthon16 { 20 | public static final String BRAINKEY = "BARIC BICKERN LITZ TIPFUL JINGLED POOL TUMBAK PURIST APOPYLE DURAIN SATLIJK FAUCAL"; 21 | public static final String WIF = "5JNxigtdkjkjM1dVGBBLk2axnUZmHrNLK4CqnUjWDzeX18HnyoD"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/UtilTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej; 2 | 3 | import com.google.common.primitives.UnsignedLong; 4 | import junit.framework.Assert; 5 | import org.junit.Test; 6 | 7 | /** 8 | * Class used to test Util methods 9 | */ 10 | 11 | public class UtilTest { 12 | 13 | @Test 14 | public void testRevertUnsignedLong(){ 15 | UnsignedLong unsignedLong = UnsignedLong.valueOf("12179241258665439971"); 16 | byte[] reversed = Util.revertUnsignedLong(unsignedLong); 17 | Assert.assertEquals("e3f28878655b05a9", Util.bytesToHex(reversed)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/crypto/AndroidRandomSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research and Development GmbH 3 | * 4 | * Licensed under the Microsoft Reference Source License (MS-RSL) 5 | * 6 | * This license governs use of the accompanying software. If you use the software, you accept this license. 7 | * If you do not accept the license, do not use the software. 8 | * 9 | * 1. Definitions 10 | * The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law. 11 | * "You" means the licensee of the software. 12 | * "Your company" means the company you worked for when you downloaded the software. 13 | * "Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes 14 | * of debugging your products, maintaining your products, or enhancing the interoperability of your products with the 15 | * software, and specifically excludes the right to distribute the software outside of your company. 16 | * "Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor 17 | * under this license. 18 | * 19 | * 2. Grant of Rights 20 | * (A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, 21 | * worldwide, royalty-free copyright license to reproduce the software for reference use. 22 | * (B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, 23 | * worldwide, royalty-free patent license under licensed patents for reference use. 24 | * 25 | * 3. Limitations 26 | * (A) No Trademark License- This license does not grant you any rights to use the Licensor’s name, logo, or trademarks. 27 | * (B) If you begin patent litigation against the Licensor over patents that you think may apply to the software 28 | * (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically. 29 | * (C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties, 30 | * guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot 31 | * change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability, 32 | * fitness for a particular purpose and non-infringement. 33 | */ 34 | 35 | package com.gxchain.client.graphenej.crypto; 36 | 37 | import java.io.DataInputStream; 38 | import java.io.File; 39 | import java.io.FileInputStream; 40 | import java.io.IOException; 41 | import java.nio.ByteBuffer; 42 | 43 | 44 | public class AndroidRandomSource implements RandomSource, EntropySource { 45 | 46 | @Override 47 | public synchronized void nextBytes(byte[] bytes) { 48 | // On Android we use /dev/urandom for providing random data 49 | File file = new File("/dev/urandom"); 50 | if (!file.exists()) { 51 | throw new RuntimeException("Unable to generate random bytes on this Android device"); 52 | } 53 | try { 54 | FileInputStream stream = new FileInputStream(file); 55 | DataInputStream dis = new DataInputStream(stream); 56 | dis.readFully(bytes); 57 | dis.close(); 58 | } catch (IOException e) { 59 | throw new RuntimeException("Unable to generate random bytes on this Android device", e); 60 | } 61 | } 62 | 63 | @Override 64 | public ByteBuffer provideEntropy() { 65 | byte[] buffer = new byte[ 256 / 8]; 66 | nextBytes(buffer); 67 | ByteBuffer byteBuffer = ByteBuffer.allocate(buffer.length); 68 | byteBuffer.put(buffer, 0, buffer.length); 69 | return byteBuffer; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/crypto/EntropySource.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.crypto; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * A simple interface that can be used to retrieve entropy from any source. 7 | * 8 | * @author owlstead 9 | */ 10 | public interface EntropySource { 11 | /** 12 | * Retrieves the entropy. 13 | * The position of the ByteBuffer must be advanced to the limit by any users calling this method. 14 | * The values of the bytes between the position and limit should be set to zero by any users calling this method. 15 | * 16 | * @return entropy within the position and limit of the given buffer 17 | */ 18 | ByteBuffer provideEntropy(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/crypto/RandomSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013, 2014 Megion Research & Development GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gxchain.client.graphenej.crypto; 17 | 18 | public interface RandomSource { 19 | /** 20 | * Generates a user specified number of random bytes 21 | * 22 | * @param bytes 23 | * The array to fill with random bytes 24 | */ 25 | void nextBytes(byte[] bytes); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/crypto/SecureRandomGenerator.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.crypto; 2 | 3 | import java.security.SecureRandom; 4 | 5 | /** 6 | * Created by nelson on 12/20/16. 7 | */ 8 | public class SecureRandomGenerator { 9 | 10 | public static SecureRandom getSecureRandom(){ 11 | SecureRandomStrengthener randomStrengthener = SecureRandomStrengthener.getInstance(); 12 | // randomStrengthener.addEntropySource(new AndroidRandomSource()); 13 | return randomStrengthener.generateAndSeedRandomNumberGenerator(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/crypto/SecureRandomStrengthener.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.crypto; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.security.DigestException; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.security.SecureRandom; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | /** 12 | * A strengthener that can be used to generate and re-seed random number 13 | * generators that do not seed themselves appropriately. 14 | * 15 | * @author owlstead 16 | */ 17 | public class SecureRandomStrengthener { 18 | private static final String DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR = "SHA1PRNG"; 19 | 20 | private static final EntropySource mTimeEntropySource = new EntropySource() { 21 | 22 | final ByteBuffer timeBuffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE 23 | * 2); 24 | 25 | @Override 26 | public ByteBuffer provideEntropy() { 27 | this.timeBuffer.clear(); 28 | this.timeBuffer.putLong(System.currentTimeMillis()); 29 | this.timeBuffer.putLong(System.nanoTime()); 30 | this.timeBuffer.flip(); 31 | return this.timeBuffer; 32 | } 33 | }; 34 | 35 | private final String algorithm; 36 | private final List entropySources = new LinkedList(); 37 | private final MessageDigest digest; 38 | private final ByteBuffer seedBuffer; 39 | 40 | /** 41 | * Generates an instance of a {@link SecureRandomStrengthener} that 42 | * generates and re-seeds instances of {@code "SHA1PRNG"}. 43 | * 44 | * @return the strengthener, never null 45 | */ 46 | public static SecureRandomStrengthener getInstance() { 47 | return new SecureRandomStrengthener( 48 | DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR); 49 | } 50 | 51 | /** 52 | * Generates an instance of a {@link SecureRandomStrengthener} that 53 | * generates instances of the given argument. Note that the availability of 54 | * the given algorithm arguments in not tested until generation. 55 | * 56 | * @param algorithm 57 | * the algorithm indicating the {@link SecureRandom} instance to 58 | * use 59 | * @return the strengthener, never null 60 | */ 61 | public static SecureRandomStrengthener getInstance(final String algorithm) { 62 | return new SecureRandomStrengthener(algorithm); 63 | } 64 | 65 | private SecureRandomStrengthener(final String algorithm) { 66 | if (algorithm == null || algorithm.length() == 0) { 67 | throw new IllegalArgumentException( 68 | "Please provide a PRNG algorithm string such as SHA1PRNG"); 69 | } 70 | 71 | this.algorithm = algorithm; 72 | try { 73 | this.digest = MessageDigest.getInstance("SHA1"); 74 | } catch (final NoSuchAlgorithmException e) { 75 | throw new IllegalStateException( 76 | "MessageDigest to create seed not available", e); 77 | } 78 | this.seedBuffer = ByteBuffer.allocate(this.digest.getDigestLength()); 79 | } 80 | 81 | /** 82 | * Add an entropy source, which will be called for each generation and 83 | * re-seeding of the given random number generator. 84 | * 85 | * @param source 86 | * the source of entropy 87 | */ 88 | public void addEntropySource(final EntropySource source) { 89 | if (source == null) { 90 | throw new IllegalArgumentException( 91 | "EntropySource should not be null"); 92 | } 93 | this.entropySources.add(source); 94 | } 95 | 96 | /** 97 | * Generates and seeds a random number generator of the configured 98 | * algorithm. Calls the {@link EntropySource#provideEntropy()} method of all 99 | * added sources of entropy. 100 | * 101 | * @return the random number generator 102 | */ 103 | public SecureRandom generateAndSeedRandomNumberGenerator() { 104 | final SecureRandom secureRandom; 105 | try { 106 | secureRandom = SecureRandom.getInstance(this.algorithm); 107 | } catch (final NoSuchAlgorithmException e) { 108 | throw new IllegalStateException("PRNG is not available", e); 109 | } 110 | 111 | reseed(secureRandom); 112 | return secureRandom; 113 | } 114 | 115 | /** 116 | * Re-seeds the random number generator. Calls the 117 | * {@link EntropySource#provideEntropy()} method of all added sources of 118 | * entropy. 119 | * 120 | * @param secureRandom 121 | * the random number generator to re-seed 122 | */ 123 | public void reseed(final SecureRandom secureRandom) { 124 | this.seedBuffer.clear(); 125 | secureRandom.nextBytes(this.seedBuffer.array()); 126 | 127 | for (final EntropySource source : this.entropySources) { 128 | final ByteBuffer entropy = source.provideEntropy(); 129 | if (entropy == null) { 130 | continue; 131 | } 132 | 133 | final ByteBuffer wipeBuffer = entropy.duplicate(); 134 | this.digest.update(entropy); 135 | wipe(wipeBuffer); 136 | } 137 | 138 | this.digest.update(mTimeEntropySource.provideEntropy()); 139 | this.digest.update(this.seedBuffer); 140 | this.seedBuffer.clear(); 141 | // remove data from seedBuffer so it won't be retrievable 142 | 143 | // reuse 144 | 145 | try { 146 | this.digest.digest(this.seedBuffer.array(), 0, 147 | this.seedBuffer.capacity()); 148 | } catch (final DigestException e) { 149 | throw new IllegalStateException( 150 | "DigestException should not be thrown", e); 151 | } 152 | secureRandom.setSeed(this.seedBuffer.array()); 153 | 154 | wipe(this.seedBuffer); 155 | } 156 | 157 | private void wipe(final ByteBuffer buf) { 158 | while (buf.hasRemaining()) { 159 | buf.put((byte) 0); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/enums/AuthorityType.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.enums; 2 | 3 | /** 4 | * Enum-type used to specify the different roles of an authority. 5 | * 6 | * @see Authority 7 | */ 8 | 9 | public enum AuthorityType { 10 | OWNER, 11 | ACTIVE, 12 | MEMO 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/enums/ObjectType.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.enums; 2 | 3 | import com.gxchain.client.graphenej.objects.GrapheneObject; 4 | 5 | /** 6 | * Enum type used to list all possible object types and obtain their space + type id 7 | */ 8 | 9 | public enum ObjectType { 10 | BASE_OBJECT, 11 | ACCOUNT_OBJECT, 12 | ASSET_OBJECT, 13 | FORCE_SETTLEMENT_OBJECT, 14 | COMMITTEE_MEMBER_OBJECT, 15 | WITNESS_OBJECT, 16 | LIMIT_ORDER_OBJECT, 17 | CALL_ORDER_OBJECT, 18 | CUSTOM_OBJECT, 19 | PROPOSAL_OBJECT, 20 | OPERATION_HISTORY_OBJECT, 21 | WITHDRAW_PERMISSION_OBJECT, 22 | VESTING_BALANCE_OBJECT, 23 | WORKER_OBJECT, 24 | BALANCE_OBJECT, 25 | GLOBAL_PROPERTY_OBJECT, 26 | DYNAMIC_GLOBAL_PROPERTY_OBJECT, 27 | ASSET_DYNAMIC_DATA, 28 | ASSET_BITASSET_DATA, 29 | ACCOUNT_BALANCE_OBJECT, 30 | ACCOUNT_STATISTICS_OBJECT, 31 | TRANSACTION_OBJECT, 32 | BLOCK_SUMMARY_OBJECT, 33 | ACCOUNT_TRANSACTION_HISTORY_OBJECT, 34 | BLINDED_BALANCE_OBJECT, 35 | CHAIN_PROPERTY_OBJECT, 36 | WITNESS_SCHEDULE_OBJECT, 37 | BUDGET_RECORD_OBJECT, 38 | SPECIAL_AUTHORITY_OBJECT; 39 | 40 | private int getSpace(){ 41 | int space = 1; 42 | switch(this){ 43 | case BASE_OBJECT: 44 | case ACCOUNT_OBJECT: 45 | case ASSET_OBJECT: 46 | case FORCE_SETTLEMENT_OBJECT: 47 | case COMMITTEE_MEMBER_OBJECT: 48 | case WITNESS_OBJECT: 49 | case LIMIT_ORDER_OBJECT: 50 | case CALL_ORDER_OBJECT: 51 | case CUSTOM_OBJECT: 52 | case PROPOSAL_OBJECT: 53 | case OPERATION_HISTORY_OBJECT: 54 | case WITHDRAW_PERMISSION_OBJECT: 55 | case VESTING_BALANCE_OBJECT: 56 | case WORKER_OBJECT: 57 | case BALANCE_OBJECT: 58 | space = 1; 59 | break; 60 | case GLOBAL_PROPERTY_OBJECT: 61 | case DYNAMIC_GLOBAL_PROPERTY_OBJECT: 62 | case ASSET_DYNAMIC_DATA: 63 | case ASSET_BITASSET_DATA: 64 | case ACCOUNT_BALANCE_OBJECT: 65 | case ACCOUNT_STATISTICS_OBJECT: 66 | case TRANSACTION_OBJECT: 67 | case BLOCK_SUMMARY_OBJECT: 68 | case ACCOUNT_TRANSACTION_HISTORY_OBJECT: 69 | case BLINDED_BALANCE_OBJECT: 70 | case CHAIN_PROPERTY_OBJECT: 71 | case WITNESS_SCHEDULE_OBJECT: 72 | case BUDGET_RECORD_OBJECT: 73 | case SPECIAL_AUTHORITY_OBJECT: 74 | space = 2; 75 | break; 76 | } 77 | return space; 78 | } 79 | 80 | private int getType(){ 81 | int type = 0; 82 | switch(this){ 83 | case BASE_OBJECT: 84 | type = 1; 85 | break; 86 | case ACCOUNT_OBJECT: 87 | type = 2; 88 | break; 89 | case ASSET_OBJECT: 90 | type = 3; 91 | break; 92 | case FORCE_SETTLEMENT_OBJECT: 93 | type = 4; 94 | break; 95 | case COMMITTEE_MEMBER_OBJECT: 96 | type = 5; 97 | break; 98 | case WITNESS_OBJECT: 99 | type = 6; 100 | break; 101 | case LIMIT_ORDER_OBJECT: 102 | type = 7; 103 | break; 104 | case CALL_ORDER_OBJECT: 105 | type = 8; 106 | break; 107 | case CUSTOM_OBJECT: 108 | type = 9; 109 | break; 110 | case PROPOSAL_OBJECT: 111 | type = 10; 112 | break; 113 | case OPERATION_HISTORY_OBJECT: 114 | type = 11; 115 | break; 116 | case WITHDRAW_PERMISSION_OBJECT: 117 | type = 12; 118 | break; 119 | case VESTING_BALANCE_OBJECT: 120 | type = 13; 121 | break; 122 | case WORKER_OBJECT: 123 | type = 14; 124 | break; 125 | case BALANCE_OBJECT: 126 | type = 15; 127 | break; 128 | case GLOBAL_PROPERTY_OBJECT: 129 | type = 0; 130 | break; 131 | case DYNAMIC_GLOBAL_PROPERTY_OBJECT: 132 | type = 1; 133 | break; 134 | case ASSET_DYNAMIC_DATA: 135 | type = 3; 136 | break; 137 | case ASSET_BITASSET_DATA: 138 | type = 4; 139 | break; 140 | case ACCOUNT_BALANCE_OBJECT: 141 | type = 5; 142 | break; 143 | case ACCOUNT_STATISTICS_OBJECT: 144 | type = 6; 145 | break; 146 | case TRANSACTION_OBJECT: 147 | type = 7; 148 | break; 149 | case BLOCK_SUMMARY_OBJECT: 150 | type = 8; 151 | break; 152 | case ACCOUNT_TRANSACTION_HISTORY_OBJECT: 153 | type = 9; 154 | break; 155 | case BLINDED_BALANCE_OBJECT: 156 | type = 10; 157 | break; 158 | case CHAIN_PROPERTY_OBJECT: 159 | type = 11; 160 | break; 161 | case WITNESS_SCHEDULE_OBJECT: 162 | type = 12; 163 | break; 164 | case BUDGET_RECORD_OBJECT: 165 | type = 13; 166 | break; 167 | case SPECIAL_AUTHORITY_OBJECT: 168 | type = 14; 169 | } 170 | return type; 171 | } 172 | 173 | /** 174 | * This method is used to return the generic object type in the form space.type.0. 175 | * 176 | * Not to be confused with {@link GrapheneObject#getObjectId()}, which will return 177 | * the full object id in the form space.type.id. 178 | * 179 | * @return: The generic object type 180 | */ 181 | public String getGenericObjectId(){ 182 | return String.format("%d.%d.0", getSpace(), getType()); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/enums/OperationType.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Enum type used to keep track of all the operation types and their corresponding ids. 9 | * 10 | * Source 11 | * 12 | * Created by nelson on 11/6/16. 13 | */ 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public enum OperationType { 17 | TRANSFER_OPERATION(0,"转账"), 18 | DIY_OPERATION(35,"自定义操作"), 19 | BROADCAST_STORE_DATA(73,"代理转账"), 20 | CREATE_CONTRACT(74,"创建智能合约"), 21 | CALL_CONTRACT(75,"执行智能合约"), 22 | UPDATE_CONTRACT(76,"更新智能合约"), 23 | 24 | 25 | LIMIT_ORDER_CREATE_OPERATION(-1,""), 26 | LIMIT_ORDER_CANCEL_OPERATION(-1,""), 27 | CALL_ORDER_UPDATE_OPERATION(-1,""), 28 | FILL_ORDER_OPERATION(-1,""), // VIRTUAL 29 | ACCOUNT_CREATE_OPERATION(5,""), 30 | ACCOUNT_UPDATE_OPERATION(6,""), 31 | ACCOUNT_WHITELIST_OPERATION(-1,""), 32 | ACCOUNT_UPGRADE_OPERATION(-1,""), 33 | ACCOUNT_TRANSFER_OPERATION(-1,""), 34 | ASSET_CREATE_OPERATION(-1,""), 35 | ASSET_UPDATE_OPERATION(-1,""), 36 | ASSET_UPDATE_BITASSET_OPERATION(-1,""), 37 | ASSET_UPDATE_FEED_PRODUCERS_OPERATION(-1,""), 38 | ASSET_ISSUE_OPERATION(-1,""), 39 | ASSET_RESERVE_OPERATION(-1,""), 40 | ASSET_FUND_FEE_POOL_OPERATION(-1,""), 41 | ASSET_SETTLE_OPERATION(-1,""), 42 | ASSET_GLOBAL_SETTLE_OPERATION(-1,""), 43 | ASSET_PUBLISH_FEED_OPERATION(-1,""), 44 | WITNESS_CREATE_OPERATION(-1,""), 45 | WITNESS_UPDATE_OPERATION(-1,""), 46 | PROPOSAL_CREATE_OPERATION(-1,""), 47 | PROPOSAL_UPDATE_OPERATION(-1,""), 48 | PROPOSAL_DELETE_OPERATION(-1,""), 49 | WITHDRAW_PERMISSION_CREATE_OPERATION(-1,""), 50 | WITHDRAW_PERMISSION_UPDATE_OPERATION(-1,""), 51 | WITHDRAW_PERMISSION_CLAIM_OPERATION(-1,""), 52 | WITHDRAW_PERMISSION_DELETE_OPERATION(-1,""), 53 | COMMITTEE_MEMBER_CREATE_OPERATION(-1,""), 54 | COMMITTEE_MEMBER_UPDATE_OPERATION(-1,""), 55 | COMMITTEE_MEMBER_UPDATE_GLOBAL_PARAMETERS_OPERATION(-1,""), 56 | VESTING_BALANCE_CREATE_OPERATION(-1,""), 57 | VESTING_BALANCE_WITHDRAW_OPERATION(-1,""), 58 | WORKER_CREATE_OPERATION(-1,""), 59 | CUSTOM_OPERATION(-1,""), 60 | ASSERT_OPERATION(-1,""), 61 | BALANCE_CLAIM_OPERATION(-1,""), 62 | OVERRIDE_TRANSFER_OPERATION(-1,""), 63 | TRANSFER_TO_BLIND_OPERATION(-1,""), 64 | BLIND_TRANSFER_OPERATION(-1,""), 65 | TRANSFER_FROM_BLIND_OPERATION(-1,""), 66 | ASSET_SETTLE_CANCEL_OPERATION(-1,""), // VIRTUAL 67 | ASSET_CLAIM_FEES_OPERATION(-1,""); 68 | 69 | @Getter private int code; 70 | @Getter private String sub; 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/ChecksumException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 12/20/16. 5 | */ 6 | public class ChecksumException extends Exception { 7 | public ChecksumException(String message){ 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/IncompatibleOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 1/18/17. 5 | */ 6 | public class IncompatibleOperation extends RuntimeException { 7 | 8 | public IncompatibleOperation(String message){ 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/IncompleteAssetError.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 12/25/16. 5 | */ 6 | public class IncompleteAssetError extends RuntimeException{ 7 | 8 | public IncompleteAssetError(String message){ 9 | super(message); 10 | } 11 | 12 | public IncompleteAssetError(){ 13 | super("The asset used in this method is probably incomplete, Assets instances can be created with just its id information but this context requires more information"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/MalformedAddressException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 12/1/16. 5 | */ 6 | public class MalformedAddressException extends Exception { 7 | public MalformedAddressException(String message){ 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/MalformedOperationException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 3/1/17. 5 | */ 6 | public class MalformedOperationException extends RuntimeException { 7 | 8 | public MalformedOperationException(String msg){ 9 | super(msg); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/MalformedTransactionException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 11/14/16. 5 | */ 6 | public class MalformedTransactionException extends Exception { 7 | 8 | public MalformedTransactionException(String message){ 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/errors/RepeatedRequestIdException.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.errors; 2 | 3 | /** 4 | * Created by nelson on 6/27/17. 5 | */ 6 | 7 | public class RepeatedRequestIdException extends Exception { 8 | 9 | public RepeatedRequestIdException(String message){ 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/ByteSerializable.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | /** 4 | * Interface implemented by all entities for which makes sense to have a byte-array representation. 5 | */ 6 | public interface ByteSerializable { 7 | 8 | byte[] toBytes(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/GrapheneSerializable.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | /** 4 | * Interface used to group both ByteSerializable and JsonSerializable interfaces. 5 | */ 6 | public interface GrapheneSerializable extends ByteSerializable, JsonSerializable { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/JsonSerializable.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Interface to be implemented by any entity for which makes sense to 9 | * have a JSON-formatted string and object representation. 10 | */ 11 | public interface JsonSerializable extends Serializable { 12 | 13 | String toJsonString(); 14 | 15 | JsonElement toJsonObject(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/NodeErrorListener.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | import com.gxchain.client.graphenej.models.BaseResponse; 4 | 5 | /** 6 | * Interface to be implemented by any listener to network errors. 7 | */ 8 | public interface NodeErrorListener { 9 | void onError(BaseResponse.Error error); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/SubscriptionHub.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Interface to be implemented by any class that hosts a SubscriptionResponseDeserializer and wants to 7 | * expose an interface for its management of its listeners. 8 | * 9 | * Created by nelson on 1/30/17. 10 | */ 11 | public interface SubscriptionHub { 12 | 13 | /** 14 | * Adds a given listener to the list of subscription listeners. 15 | * @param listener: The SubscriptionListener to add. 16 | */ 17 | void addSubscriptionListener(SubscriptionListener listener); 18 | 19 | /** 20 | * Removes a given listener from the list. 21 | * @param listener: The SubscriptionListener to remove. 22 | */ 23 | void removeSubscriptionListener(SubscriptionListener listener); 24 | 25 | /** 26 | * Retrieves a list of all subscription listeners. 27 | * @return 28 | */ 29 | List getSubscriptionListeners(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/SubscriptionListener.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | import com.gxchain.client.graphenej.enums.ObjectType; 4 | import com.gxchain.client.graphenej.models.SubscriptionResponse; 5 | 6 | /** 7 | * Generic interface that must be implemented by any class that wants to be informed about a specific 8 | * event notification. 9 | * 10 | * Created by nelson on 1/26/17. 11 | */ 12 | public interface SubscriptionListener { 13 | 14 | /** 15 | * Every subscription listener must implement a method that returns the type of object it is 16 | * interested in. 17 | * @return: Instance of the ObjectType enum class. 18 | */ 19 | ObjectType getInterestObjectType(); 20 | 21 | 22 | /** 23 | * Method called whenever there is an update that might be of interest for this listener. 24 | * Note however that the objects returned inside the SubscriptionResponse are not guaranteed to be 25 | * only of the object type requested by this class in the getInterestObjectType. 26 | * 27 | * @param response: SubscriptionResponse instance, which may or may not contain an object of interest. 28 | */ 29 | void onSubscriptionUpdate(SubscriptionResponse response); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/interfaces/WitnessResponseListener.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.interfaces; 2 | 3 | import com.gxchain.client.graphenej.models.BaseResponse; 4 | import com.gxchain.client.graphenej.models.WitnessResponse; 5 | 6 | /** 7 | * Class used to represent any listener to network requests. 8 | */ 9 | public interface WitnessResponseListener { 10 | 11 | void onSuccess(WitnessResponse response); 12 | 13 | void onError(BaseResponse.Error error); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/AccountBalanceUpdate.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.objects.GrapheneObject; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Created by nelson on 1/12/17. 9 | */ 10 | 11 | public class AccountBalanceUpdate extends GrapheneObject implements Serializable { 12 | public static final String KEY_OWNER = "owner"; 13 | public static final String KEY_ASSET_TYPE = "asset_type"; 14 | public static final String KEY_BALANCE = "balance"; 15 | 16 | public String owner; 17 | public String asset_type; 18 | public long balance; 19 | 20 | public AccountBalanceUpdate(String id) { 21 | super(id); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/AccountProperties.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.models.contract.Abi; 4 | import com.gxchain.client.graphenej.objects.AccountOptions; 5 | import com.gxchain.client.graphenej.objects.Authority; 6 | import lombok.Data; 7 | 8 | /** 9 | * Created by nelson on 11/15/16. 10 | * 11 | * Details of Dynamic Account specs can be found at 12 | * https://bitshares.org/technology/dynamic-account-permissions/ 13 | * 14 | */ 15 | @Data 16 | public class AccountProperties { 17 | private String id; 18 | private String membership_expiration_date; 19 | private String registrar; 20 | private String referrer; 21 | private String lifetime_referrer; 22 | private long network_fee_percentage; 23 | private long lifetime_referrer_fee_percentage; 24 | private long referrer_rewards_percentage; 25 | private String name; 26 | private Authority owner; 27 | private Authority active; 28 | private AccountOptions options; 29 | private String statistics; 30 | private String[] whitelisting_accounts; 31 | private String[] blacklisting_accounts; 32 | private String[] whitelisted_accounts; 33 | private String[] blacklisted_accounts; 34 | private Object[] owner_special_authority; 35 | private Object[] active_special_authority; 36 | private long top_n_control_flags; 37 | private Abi abi; 38 | 39 | /** 40 | * active public key 41 | * @return 42 | */ 43 | public String getActivePublicKey(){ 44 | return getActive().getKeyAuthList().get(0).getAddress(); 45 | } 46 | 47 | /** 48 | * memo public key 49 | * @return 50 | */ 51 | public String getMemoPublicKey(){ 52 | return getOptions().getMemoKey().getAddress(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/ApiCall.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.google.gson.*; 4 | import com.gxchain.client.graphenej.interfaces.JsonSerializable; 5 | import com.gxchain.client.util.GXGsonUtil; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.lang.reflect.Type; 11 | 12 | /** 13 | * Class used to build a Graphene websocket API call. 14 | * 15 | * @see Websocket Calls & Notifications 16 | */ 17 | public class ApiCall implements JsonSerializable { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(ApiCall.class); 19 | public static final String KEY_SEQUENCE_ID = "id"; 20 | public static final String KEY_METHOD = "method"; 21 | public static final String KEY_PARAMS = "params"; 22 | public static final String KEY_JSON_RPC = "jsonrpc"; 23 | 24 | public String method; 25 | public String methodToCall; 26 | public String jsonrpc; 27 | public JsonArray params; 28 | public Integer apiId; 29 | public Integer sequenceId; 30 | 31 | public ApiCall(Integer apiId, String methodToCall, JsonArray params, String jsonrpc, Integer sequenceId) { 32 | this.apiId = apiId; 33 | this.method = "call"; 34 | this.methodToCall = methodToCall; 35 | this.jsonrpc = jsonrpc; 36 | this.params = params; 37 | this.sequenceId = sequenceId; 38 | } 39 | 40 | public ApiCall(String method, JsonArray params, String jsonrpc, Integer sequenceId) { 41 | this.method = method; 42 | this.jsonrpc = jsonrpc; 43 | this.params = params; 44 | this.sequenceId = sequenceId; 45 | } 46 | 47 | @Override 48 | public String toJsonString() { 49 | return GXGsonUtil.toJson(this); 50 | } 51 | 52 | @Override 53 | public JsonElement toJsonObject() { 54 | JsonObject obj = new JsonObject(); 55 | obj.addProperty(KEY_SEQUENCE_ID, this.sequenceId); 56 | obj.addProperty(KEY_METHOD, this.method); 57 | JsonArray paramsArray = new JsonArray(); 58 | if (this.apiId != null && StringUtils.isNotBlank(this.methodToCall)) { 59 | paramsArray.add(this.apiId); 60 | paramsArray.add(this.methodToCall); 61 | paramsArray.add(this.params); 62 | } else { 63 | paramsArray = this.params; 64 | } 65 | obj.add(KEY_PARAMS, paramsArray); 66 | obj.addProperty(KEY_JSON_RPC, this.jsonrpc); 67 | return obj; 68 | } 69 | 70 | public static class ApiCallSerializer implements JsonSerializer { 71 | 72 | @Override 73 | public JsonElement serialize(ApiCall apiCall, Type type, JsonSerializationContext jsonSerializationContext) { 74 | return apiCall.toJsonObject(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/AssetFeed.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.Price; 4 | 5 | /** 6 | * Created by nelson on 1/9/17. 7 | */ 8 | public class AssetFeed { 9 | public Price settlement_price; 10 | public long maintenance_collateral_ratio; 11 | public long maximum_short_squeeze_ratio; 12 | public Price core_exchange_rate; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/AssetHolderCount.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.google.gson.*; 4 | import com.gxchain.client.graphenej.objects.Asset; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * Created by nelson on 1/25/17. 10 | */ 11 | public class AssetHolderCount { 12 | public static final String KEY_ASSET_ID = "asset_id"; 13 | public static final String KEY_COUNT = "count"; 14 | 15 | public Asset asset; 16 | public long count; 17 | 18 | public static class HoldersCountDeserializer implements JsonDeserializer { 19 | 20 | @Override 21 | public AssetHolderCount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 22 | JsonObject jsonObject = json.getAsJsonObject(); 23 | AssetHolderCount holdersCount = new AssetHolderCount(); 24 | holdersCount.asset = new Asset(jsonObject.get(KEY_ASSET_ID).getAsString()); 25 | holdersCount.count = jsonObject.get(KEY_COUNT).getAsLong(); 26 | return holdersCount; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * Created by nelson on 11/12/16. 8 | */ 9 | @Data public class BaseResponse { 10 | public long id; 11 | public Error error; 12 | 13 | 14 | @Data @NoArgsConstructor public static class Error { 15 | public ErrorData data; 16 | public int code; 17 | public String message; 18 | 19 | public Error(String message) { 20 | this.message = message; 21 | } 22 | } 23 | 24 | 25 | @Data @NoArgsConstructor public static class ErrorData { 26 | public int code; 27 | public String name; 28 | public String message; 29 | 30 | public ErrorData(String message) { 31 | this.message = message; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/BitAssetData.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.Price; 4 | import com.gxchain.client.graphenej.objects.GrapheneObject; 5 | 6 | /** 7 | * This is the representation of the response from the 'get_objects' call with 8 | * a 2.4.x id, which will retrieve a 'impl_asset_bitasset_data_type'. 9 | * 10 | * Created by nelson on 1/8/17. 11 | */ 12 | public class BitAssetData extends GrapheneObject { 13 | public Object[] feeds; 14 | public AssetFeed current_feed; 15 | public String current_feed_publication_time; 16 | public Object options; 17 | public long force_settled_volume; 18 | public boolean is_prediction_market; 19 | public Price settlement_price; 20 | public long settlement_fund; 21 | 22 | public BitAssetData(String id) { 23 | super(id); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/Block.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.objects.Transaction; 4 | 5 | /** 6 | * @author liruobin 7 | * @since 2018/7/5 下午4:36 8 | */ 9 | public class Block { 10 | public String previous; 11 | public String timestamp; 12 | public String witness; 13 | public String transaction_merkle_root; 14 | public Object[] extension; 15 | public String witness_signature; 16 | public String block_id; 17 | public String signing_key; 18 | public String[] transaction_ids; 19 | public Transaction[] transactions; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/BlockHeader.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | /** 4 | * Created by nelson on 12/13/16. 5 | */ 6 | public class BlockHeader { 7 | public String previous; 8 | public String timestamp; 9 | public String witness; 10 | public String transaction_merkle_root; 11 | public Object[] extension; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/BroadcastedTransaction.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.objects.GrapheneObject; 4 | import com.gxchain.client.graphenej.objects.Transaction; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Created by nelson on 1/28/17. 10 | */ 11 | public class BroadcastedTransaction extends GrapheneObject implements Serializable { 12 | public static final String KEY_TRX = "trx"; 13 | public static final String KEY_TRX_ID = "trx_id"; 14 | 15 | private Transaction trx; 16 | private String trx_id; 17 | 18 | public BroadcastedTransaction(String id){ 19 | super(id); 20 | } 21 | 22 | public void setTransaction(Transaction t){ 23 | this.trx = t; 24 | } 25 | 26 | public Transaction getTransaction() { 27 | return trx; 28 | } 29 | 30 | public void setTransactionId(String id){ 31 | this.trx_id = id; 32 | } 33 | 34 | public String getTransactionId() { 35 | return trx_id; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/BucketObject.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.google.gson.*; 4 | import com.gxchain.client.graphenej.objects.Asset; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.lang.reflect.Type; 9 | import java.math.BigDecimal; 10 | import java.text.ParseException; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | 14 | /** 15 | * Created by nelson on 12/22/16. 16 | */ 17 | public class BucketObject { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(BucketObject.class); 19 | public static final String KEY_HIGH_BASE = "high_base"; 20 | public static final String KEY_HIGH_QUOTE = "high_quote"; 21 | public static final String KEY_LOW_BASE = "low_base"; 22 | public static final String KEY_LOW_QUOTE = "low_quote"; 23 | public static final String KEY_OPEN_BASE = "open_base"; 24 | public static final String KEY_OPEN_QUOTE = "open_quote"; 25 | public static final String KEY_CLOSE_BASE = "close_base"; 26 | public static final String KEY_CLOSE_QUOTE = "close_quote"; 27 | public static final String KEY_BASE_VOLUME = "base_volume"; 28 | public static final String KEY_QUOTE_VOLUME = "quote_volume"; 29 | public static final String KEY_BASE = "base"; 30 | public static final String KEY_QUOTE = "quote"; 31 | public static final String KEY_SECONDS = "seconds"; 32 | public static final String KEY_OPEN = "open"; 33 | public static final String KEY_KEY = "key"; 34 | 35 | public String id; 36 | public Key key; 37 | public BigDecimal high_base; 38 | public BigDecimal high_quote; 39 | public BigDecimal low_base; 40 | public BigDecimal low_quote; 41 | public BigDecimal open_base; 42 | public BigDecimal open_quote; 43 | public BigDecimal close_base; 44 | public BigDecimal close_quote; 45 | public BigDecimal base_volume; 46 | public BigDecimal quote_volume; 47 | 48 | public static class Key { 49 | public Asset base; 50 | public Asset quote; 51 | public long seconds; 52 | public Date open; 53 | } 54 | 55 | public static class BucketDeserializer implements JsonDeserializer { 56 | 57 | @Override 58 | public BucketObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 59 | JsonObject jsonBucket = json.getAsJsonObject(); 60 | BucketObject bucket = new BucketObject(); 61 | bucket.high_base = jsonBucket.get(KEY_HIGH_BASE).getAsBigDecimal(); 62 | bucket.high_quote = jsonBucket.get(KEY_HIGH_QUOTE).getAsBigDecimal(); 63 | bucket.low_base = jsonBucket.get(KEY_LOW_BASE).getAsBigDecimal(); 64 | bucket.low_quote = jsonBucket.get(KEY_LOW_QUOTE).getAsBigDecimal(); 65 | bucket.open_base = jsonBucket.get(KEY_OPEN_BASE).getAsBigDecimal(); 66 | bucket.open_quote = jsonBucket.get(KEY_OPEN_QUOTE).getAsBigDecimal(); 67 | bucket.close_base = jsonBucket.get(KEY_CLOSE_BASE).getAsBigDecimal(); 68 | bucket.close_quote = jsonBucket.get(KEY_CLOSE_QUOTE).getAsBigDecimal(); 69 | bucket.base_volume = jsonBucket.get(KEY_BASE_VOLUME).getAsBigDecimal(); 70 | bucket.quote_volume = jsonBucket.get(KEY_QUOTE_VOLUME).getAsBigDecimal(); 71 | bucket.key = new Key(); 72 | String baseId = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_BASE).getAsString(); 73 | String quoteId = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_QUOTE).getAsString(); 74 | bucket.key.base = new Asset(baseId); 75 | bucket.key.quote = new Asset(quoteId); 76 | bucket.key.seconds = jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_SECONDS).getAsLong(); 77 | 78 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 79 | try { 80 | bucket.key.open = dateFormat.parse(jsonBucket.get(KEY_KEY).getAsJsonObject().get(KEY_OPEN).getAsString()); 81 | } catch (ParseException e) { 82 | LOGGER.info("ParseException while deserializing BucketObject. Msg: "+e.getMessage()); 83 | } 84 | return bucket; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/DynamicGlobalProperties.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.google.gson.*; 4 | import com.gxchain.client.graphenej.Util; 5 | import com.gxchain.client.graphenej.objects.GrapheneObject; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.Serializable; 10 | import java.lang.reflect.Type; 11 | import java.text.ParseException; 12 | import java.text.SimpleDateFormat; 13 | import java.util.Date; 14 | import java.util.TimeZone; 15 | 16 | /** 17 | * Class used to deserialize the 'result' field returned by the full node after making a call 18 | * to the 'get_dynamic_global_properties' RPC. 19 | */ 20 | public class DynamicGlobalProperties extends GrapheneObject implements Serializable { 21 | private static final Logger LOGGER = LoggerFactory.getLogger(DynamicGlobalProperties.class); 22 | public static final String KEY_HEAD_BLOCK_NUMBER = "head_block_number"; 23 | public static final String KEY_HEAD_BLOCK_ID ="head_block_id"; 24 | public static final String KEY_TIME = "time"; 25 | public static final String KEY_CURRENT_WITNESS = "current_witness"; 26 | public static final String KEY_NEXT_MAINTENANCE_TIME = "next_maintenance_time"; 27 | public static final String KEY_LAST_BUDGET_TIME = "last_budget_time"; 28 | public static final String KEY_WITNESS_BUDGET = "witness_budget"; 29 | public static final String KEY_ACCOUNTS_REGISTERED_THIS_INTERVAL = "accounts_registered_this_interval"; 30 | public static final String KEY_RECENTLY_MISSED_COUNT = "recently_missed_count"; 31 | public static final String KEY_CURRENT_ASLOT = "current_aslot"; 32 | public static final String KEY_RECENT_SLOTS_FILLED = "recent_slots_filled"; 33 | public static final String KEY_DYNAMIC_FLAGS = "dynamic_flags"; 34 | public static final String KEY_LAST_IRREVERSIBLE_BLOCK_NUM = "last_irreversible_block_num"; 35 | 36 | public long head_block_number; 37 | public String head_block_id; 38 | public Date time; 39 | public String current_witness; 40 | public Date next_maintenance_time; 41 | public String last_budget_time; 42 | public long witness_budget; 43 | public long accounts_registered_this_interval; 44 | public long recently_missed_count; 45 | public long current_aslot; 46 | public String recent_slots_filled; 47 | public int dynamic_flags; 48 | public long last_irreversible_block_num; 49 | 50 | public DynamicGlobalProperties(String id) { 51 | super(id); 52 | } 53 | 54 | /** 55 | * Class that will parse the JSON element containing the dynamic global properties object and 56 | * return an instance of the {@link DynamicGlobalProperties} class. 57 | */ 58 | public static class DynamicGlobalPropertiesDeserializer implements JsonDeserializer { 59 | 60 | @Override 61 | public DynamicGlobalProperties deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 62 | JsonObject jsonObject = jsonElement.getAsJsonObject(); 63 | 64 | // Creating an instance of the DynamicGlobalProperties 65 | DynamicGlobalProperties dynamicGlobal = new DynamicGlobalProperties(jsonElement.getAsJsonObject().get(KEY_ID).getAsString()); 66 | 67 | // Start to fill in the parsed details 68 | dynamicGlobal.head_block_number = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_NUMBER).getAsLong(); 69 | dynamicGlobal.head_block_id = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_ID).getAsString(); 70 | 71 | SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); 72 | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 73 | try { 74 | dynamicGlobal.time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_TIME).getAsString()); 75 | } catch (ParseException e) { 76 | LOGGER.info("ParseException. Msg: "+e.getMessage()); 77 | } 78 | 79 | try { 80 | dynamicGlobal.next_maintenance_time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_NEXT_MAINTENANCE_TIME).getAsString()); 81 | } catch (ParseException e) { 82 | LOGGER.info("ParseException. Msg: "+e.getMessage()); 83 | } 84 | 85 | dynamicGlobal.current_witness = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_WITNESS).getAsString(); 86 | dynamicGlobal.last_budget_time = jsonObject.get(DynamicGlobalProperties.KEY_LAST_BUDGET_TIME).getAsString(); 87 | dynamicGlobal.witness_budget = jsonObject.get(DynamicGlobalProperties.KEY_WITNESS_BUDGET).getAsLong(); 88 | dynamicGlobal.accounts_registered_this_interval = jsonObject.get(DynamicGlobalProperties.KEY_ACCOUNTS_REGISTERED_THIS_INTERVAL).getAsLong(); 89 | dynamicGlobal.recently_missed_count = jsonObject.get(DynamicGlobalProperties.KEY_RECENTLY_MISSED_COUNT).getAsLong(); 90 | dynamicGlobal.current_aslot = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_ASLOT).getAsLong(); 91 | dynamicGlobal.recent_slots_filled = jsonObject.get(DynamicGlobalProperties.KEY_RECENT_SLOTS_FILLED).getAsString(); 92 | dynamicGlobal.dynamic_flags = jsonObject.get(DynamicGlobalProperties.KEY_DYNAMIC_FLAGS).getAsInt(); 93 | dynamicGlobal.last_irreversible_block_num = jsonObject.get(DynamicGlobalProperties.KEY_LAST_IRREVERSIBLE_BLOCK_NUM).getAsLong(); 94 | return dynamicGlobal; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/HistoricalTransfer.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import com.gxchain.client.graphenej.operations.TransferOperation; 4 | 5 | 6 | /** 7 | * This class offers support to deserialization of transfer operations received by the API 8 | * method get_relative_account_history. 9 | * 10 | * More operations types might be listed in the response of that method, but by using this class 11 | * those will be filtered out of the parsed result. 12 | */ 13 | public class HistoricalTransfer { 14 | private String id; 15 | private TransferOperation op; 16 | public Object[] result; 17 | private long block_num; 18 | private long trx_in_block; 19 | private long op_in_trx; 20 | private long virtual_op; 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public void setId(String id) { 27 | this.id = id; 28 | } 29 | 30 | public TransferOperation getOperation() { 31 | return op; 32 | } 33 | 34 | public void setOperation(TransferOperation op) { 35 | this.op = op; 36 | } 37 | 38 | public long getBlockNum() { 39 | return block_num; 40 | } 41 | 42 | public void setBlockNum(long block_num) { 43 | this.block_num = block_num; 44 | } 45 | 46 | public long getTransactionsInBlock() { 47 | return trx_in_block; 48 | } 49 | 50 | public void setTransactionsInBlock(long trx_in_block) { 51 | this.trx_in_block = trx_in_block; 52 | } 53 | 54 | public long getOperationsInTrx() { 55 | return op_in_trx; 56 | } 57 | 58 | public void setOperationsInTrx(long op_in_trx) { 59 | this.op_in_trx = op_in_trx; 60 | } 61 | 62 | public long getVirtualOp() { 63 | return virtual_op; 64 | } 65 | 66 | public void setVirtualOp(long virtual_op) { 67 | this.virtual_op = virtual_op; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/WitnessResponse.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Generic witness response 7 | */ 8 | @Data 9 | public class WitnessResponse extends BaseResponse { 10 | public static final String KEY_ID = "id"; 11 | public static final String KEY_RESULT = "result"; 12 | 13 | public T result; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/backup/LinkedAccount.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.backup; 2 | 3 | /** 4 | * Class used to represent an entry in the "linked_accounts" field of the JSON-formatted backup file. 5 | * Created by nelson on 2/15/17. 6 | */ 7 | public class LinkedAccount { 8 | private String name; 9 | private String chainId; 10 | 11 | public LinkedAccount(String name, String chainId){ 12 | this.name = name; 13 | this.chainId = chainId; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | 24 | public String getChainId() { 25 | return chainId; 26 | } 27 | 28 | public void setChainId(String chainId) { 29 | this.chainId = chainId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/backup/PrivateKeyBackup.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.backup; 2 | 3 | import com.gxchain.client.graphenej.Address; 4 | import com.gxchain.client.graphenej.Util; 5 | import org.bitcoinj.core.ECKey; 6 | 7 | /** 8 | * Class used to represent an entry in the "private_keys" array field in the JSON-formatted 9 | * backup file. 10 | * 11 | * Created by nelson on 2/14/17. 12 | */ 13 | public class PrivateKeyBackup { 14 | public String encrypted_key; 15 | public String pubkey; 16 | public int brainkey_sequence; 17 | public int id; 18 | 19 | public PrivateKeyBackup(byte[] privateKey, int brainkeySequence, int id, byte[] encryptionKey){ 20 | this.encrypted_key = encryptPrivateKey(privateKey, encryptionKey); 21 | this.brainkey_sequence = brainkeySequence; 22 | this.id = id; 23 | deriveAddress(privateKey); 24 | } 25 | 26 | public byte[] decryptPrivateKey(byte[] encryptionKey){ 27 | return Util.decryptAES(Util.hexToBytes(encrypted_key), encryptionKey); 28 | } 29 | 30 | public String encryptPrivateKey(byte[] data, byte[] encryptionKey){ 31 | return Util.bytesToHex(Util.encryptAES(data, encryptionKey)); 32 | } 33 | 34 | private void deriveAddress(byte[] privateKey){ 35 | Address address = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(privateKey).getPubKey())); 36 | this.pubkey = address.toString(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/backup/Wallet.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.backup; 2 | 3 | import com.gxchain.client.graphenej.Address; 4 | import com.gxchain.client.graphenej.Util; 5 | import com.gxchain.client.graphenej.crypto.SecureRandomGenerator; 6 | import org.bitcoinj.core.ECKey; 7 | import org.bitcoinj.core.Sha256Hash; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | import java.security.SecureRandom; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | 14 | /** 15 | * This class holds data deserialized from a wallet in the backup file. 16 | * 17 | * Created by nelson on 2/14/17. 18 | */ 19 | public class Wallet { 20 | private String public_name; 21 | private String password_pubkey; 22 | private String encryption_key; 23 | private String encrypted_brainkey; 24 | private String brainkey_pubkey; 25 | private int brainkey_sequence; 26 | private String brainkey_backup_date; 27 | private String created; 28 | private String last_modified; 29 | private String chain_id; 30 | private String id; 31 | private String backup_date; 32 | 33 | /** 34 | * No args constructor 35 | */ 36 | public Wallet(){} 37 | 38 | public Wallet(String name){ 39 | this.public_name = name; 40 | this.id = name; 41 | } 42 | 43 | /** 44 | * Wallet constructor that takes a few arguments. 45 | * @param name: The name of this wallet. 46 | * @param brainKey: The brain key to be used. 47 | * @param brainkeySequence: The brain key sequence. 48 | * @param chainId: The chain id 49 | * @param password: Password used to encrypt all sensitive data. 50 | */ 51 | public Wallet(String name, String brainKey, int brainkeySequence, String chainId, String password){ 52 | this(name); 53 | SecureRandom secureRandom = SecureRandomGenerator.getSecureRandom(); 54 | byte[] decryptedKey = new byte[Util.KEY_LENGTH]; 55 | secureRandom.nextBytes(decryptedKey); 56 | this.encryption_key = Util.bytesToHex(Util.encryptAES(decryptedKey, password.getBytes())); 57 | this.encrypted_brainkey = Util.bytesToHex(Util.encryptAES(brainKey.getBytes(), decryptedKey)); 58 | this.brainkey_sequence = brainkeySequence; 59 | this.chain_id = chainId; 60 | 61 | try { 62 | byte[] passwordHash = Sha256Hash.hash(password.getBytes("UTF8")); 63 | this.password_pubkey = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(passwordHash).getPubKey())).toString(); 64 | } catch (UnsupportedEncodingException e) { 65 | e.printStackTrace(); 66 | } 67 | try{ 68 | byte[] brainkeyHash = Sha256Hash.hash(brainKey.getBytes("UTF8")); 69 | this.brainkey_pubkey = new Address(ECKey.fromPublicOnly(ECKey.fromPrivate(brainkeyHash).getPubKey())).toString(); 70 | } catch(UnsupportedEncodingException e){ 71 | e.printStackTrace(); 72 | } 73 | 74 | Date now = new Date(); 75 | SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); 76 | this.created = dateFormat.format(now); 77 | this.last_modified = created; 78 | this.backup_date = created; 79 | this.brainkey_backup_date = created; 80 | } 81 | 82 | /** 83 | * Method that will return the decrypted version of the "encrypted_brainkey" field. 84 | * @param password: Password used to encrypt the encryption key. 85 | * @return: The brainkey in its plaintext version. 86 | */ 87 | public String decryptBrainKey(String password){ 88 | byte[] decryptedKey = getEncryptionKey(password); 89 | byte[] encryptedBrainKey = Util.hexToBytes(encrypted_brainkey); 90 | return new String(Util.decryptAES(encryptedBrainKey, decryptedKey)); 91 | } 92 | 93 | /** 94 | * Decrypts the encryption key, which is also provided in an encrypted form. 95 | * @param password: The password used to encrypt the encryption key. 96 | * @return: The encryption key. 97 | */ 98 | public byte[] getEncryptionKey(String password){ 99 | return Util.decryptAES(Util.hexToBytes(encryption_key), password.getBytes()); 100 | } 101 | 102 | public String getPrivateName() { 103 | return public_name; 104 | } 105 | 106 | public void setPrivateName(String privateName) { 107 | this.public_name = privateName; 108 | } 109 | 110 | public String getPasswordPubkey() { 111 | return password_pubkey; 112 | } 113 | 114 | public void setPasswordPubkey(String password_pubkey) { 115 | this.password_pubkey = password_pubkey; 116 | } 117 | 118 | /** 119 | * Gets the cyphertext version of the encryption key. 120 | * @return: Encryption key in its cyphertext version. 121 | */ 122 | public String getEncryptionKey() { 123 | return encryption_key; 124 | } 125 | 126 | public void setEncryptionKey(String encryption_key) { 127 | this.encryption_key = encryption_key; 128 | } 129 | 130 | public String getEncryptedBrainkey() { 131 | return encrypted_brainkey; 132 | } 133 | 134 | public void setEncryptedBrainkey(String encrypted_brainkey) { 135 | this.encrypted_brainkey = encrypted_brainkey; 136 | } 137 | 138 | public String getBrainkeyPubkey() { 139 | return brainkey_pubkey; 140 | } 141 | 142 | public void setBrainkeyPubkey(String brainkey_pubkey) { 143 | this.brainkey_pubkey = brainkey_pubkey; 144 | } 145 | 146 | public int getBrainkeySequence() { 147 | return brainkey_sequence; 148 | } 149 | 150 | public void setBrainkeySequence(int brainkey_sequence) { 151 | this.brainkey_sequence = brainkey_sequence; 152 | } 153 | 154 | public String getBrainkeyBackup_date() { 155 | return brainkey_backup_date; 156 | } 157 | 158 | public void setBrainkeyBackupDate(String brainkey_backup_date) { 159 | this.brainkey_backup_date = brainkey_backup_date; 160 | } 161 | 162 | public String getCreated() { 163 | return created; 164 | } 165 | 166 | public void setCreated(String created) { 167 | this.created = created; 168 | } 169 | 170 | public String getLastModified() { 171 | return last_modified; 172 | } 173 | 174 | public void setLastModified(String last_modified) { 175 | this.last_modified = last_modified; 176 | } 177 | 178 | public String getChainId() { 179 | return chain_id; 180 | } 181 | 182 | public void setChainId(String chain_id) { 183 | this.chain_id = chain_id; 184 | } 185 | 186 | public String getId() { 187 | return id; 188 | } 189 | 190 | public void setId(String id) { 191 | this.id = id; 192 | } 193 | 194 | public String getBackupDate() { 195 | return backup_date; 196 | } 197 | 198 | public void setBackupDate(String backup_date) { 199 | this.backup_date = backup_date; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/backup/WalletBackup.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.backup; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * This class is used to represent the JSON-formatted version of the file backup containing one or more 7 | * wallets and keys. 8 | * 9 | * Created by nelson on 2/14/17. 10 | */ 11 | public class WalletBackup { 12 | private Wallet[] wallet; 13 | private PrivateKeyBackup[] private_keys; 14 | private LinkedAccount[] linked_accounts; 15 | 16 | public WalletBackup(List wallets, List privateKeys, List linkedAccounts){ 17 | this.wallet = wallets.toArray(new Wallet[wallets.size()]); 18 | this.private_keys = privateKeys.toArray(new PrivateKeyBackup[privateKeys.size()]); 19 | this.linked_accounts = linkedAccounts.toArray(new LinkedAccount[linkedAccounts.size()]); 20 | } 21 | 22 | public Wallet[] getWallets(){ 23 | return wallet; 24 | } 25 | 26 | public PrivateKeyBackup[] getPrivateKeys(){ 27 | return private_keys; 28 | } 29 | 30 | public LinkedAccount[] getLinkedAccounts(){ 31 | return linked_accounts; 32 | } 33 | 34 | public Wallet getWallet(int index){ 35 | return wallet[index]; 36 | } 37 | 38 | public PrivateKeyBackup getPrivateKeyBackup(int index){ 39 | return private_keys[index]; 40 | } 41 | 42 | public int getWalletCount(){ 43 | return wallet.length; 44 | } 45 | 46 | public int getKeyCount(){ 47 | return private_keys.length; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/Abi.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author liruobin 11 | * @since 2019/2/15 7:14 PM 12 | */ 13 | @Data 14 | public class Abi { 15 | private String version; 16 | 17 | private JsonArray types; 18 | 19 | private List structs; 20 | 21 | private List actions; 22 | 23 | private List tables; 24 | 25 | @SerializedName("error_messages") 26 | private JsonArray errorMessages; 27 | @SerializedName("abi_extensions") 28 | private JsonArray abiExtensions; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/Action.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liruobin 7 | * @since 2019/2/15 7:17 PM 8 | */ 9 | @Data 10 | public class Action { 11 | 12 | private String name; 13 | 14 | private String type; 15 | 16 | private Boolean payable; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/ContractAccountProperties.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * 8 | */ 9 | @Data 10 | public class ContractAccountProperties { 11 | private String id; 12 | @SerializedName("membership_expiration_date") 13 | private String membershipExpirationDate; 14 | private String registrar; 15 | private String referrer; 16 | @SerializedName("lifetime_referrer") 17 | private String lifetimeReferrer; 18 | @SerializedName("network_fee_percentage") 19 | private long networkFeePercentage; 20 | @SerializedName("lifetime_referrer_fee_percentage") 21 | private long lifetimeReferrerFeePercentage; 22 | @SerializedName("referrer_rewards_percentage") 23 | private long referrerRewardsPercentage; 24 | private String name; 25 | private String statistics; 26 | @SerializedName("whitelisting_accounts") 27 | private String[] whitelistingAccounts; 28 | @SerializedName("blacklisting_accounts") 29 | private String[] blacklistingAccounts; 30 | @SerializedName("whitelisted_accounts") 31 | private String[] whitelistedAccounts; 32 | @SerializedName("blacklisted_accounts") 33 | private String[] blacklistedAccounts; 34 | private Abi abi; 35 | @SerializedName("vm_type") 36 | private String vmType; 37 | @SerializedName("vm_version") 38 | private String vmVersion; 39 | 40 | private String code; 41 | @SerializedName("code_version") 42 | private String codeVersion; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/Field.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liruobin 7 | * @since 2019/2/15 7:15 PM 8 | */ 9 | @Data 10 | public class Field { 11 | private String name; 12 | 13 | private String type; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/Struct.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author liruobin 9 | * @since 2019/2/15 7:15 PM 10 | */ 11 | @Data 12 | public class Struct { 13 | 14 | private String name; 15 | 16 | private String base; 17 | 18 | private List fields; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/models/contract/Table.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.models.contract; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author liruobin 10 | * @since 2019/2/15 7:17 PM 11 | */ 12 | @Data 13 | public class Table { 14 | 15 | private String name; 16 | @SerializedName("index_type") 17 | private String indexType; 18 | 19 | @SerializedName("key_names") 20 | private List keyNames; 21 | @SerializedName("key_types") 22 | private List keyTypes; 23 | 24 | private String type; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/AccountOptions.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.*; 5 | import com.gxchain.client.graphenej.Address; 6 | import com.gxchain.client.graphenej.PublicKey; 7 | import com.gxchain.client.graphenej.Util; 8 | import com.gxchain.client.graphenej.errors.MalformedAddressException; 9 | import com.gxchain.client.graphenej.interfaces.GrapheneSerializable; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.lang.reflect.Type; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by nelson on 12/5/16. 20 | */ 21 | public class AccountOptions implements GrapheneSerializable { 22 | private static final Logger LOGGER = LoggerFactory.getLogger(AccountOptions.class); 23 | public static final String KEY_MEMO_KEY = "memo_key"; 24 | public static final String KEY_NUM_COMMITTEE = "num_committee"; 25 | public static final String KEY_NUM_WITNESS = "num_witness"; 26 | public static final String KEY_VOTES = "votes"; 27 | public static final String KEY_VOTING_ACCOUNT = "voting_account"; 28 | public static final String KEY_EXTENSIONS = Extensions.KEY_EXTENSIONS; 29 | 30 | private PublicKey memo_key; 31 | private UserAccount voting_account; 32 | private int num_witness; 33 | private int num_comittee; 34 | private Vote[] votes; 35 | private Extensions extensions; 36 | 37 | public AccountOptions() { 38 | voting_account = new UserAccount(UserAccount.PROXY_TO_SELF); 39 | this.votes = new Vote[0]; 40 | this.extensions = new Extensions(); 41 | } 42 | 43 | public AccountOptions(PublicKey memoKey) { 44 | this(); 45 | this.memo_key = memoKey; 46 | } 47 | 48 | public AccountOptions(PublicKey memoKey, Vote[] votes) { 49 | this(); 50 | this.memo_key = memoKey; 51 | this.votes = votes; 52 | } 53 | 54 | public PublicKey getMemoKey() { 55 | return memo_key; 56 | } 57 | 58 | public void setMemoKey(PublicKey memo_key) { 59 | this.memo_key = memo_key; 60 | } 61 | 62 | public UserAccount getVotingAccount() { 63 | return voting_account; 64 | } 65 | 66 | public void setVotingAccount(UserAccount voting_account) { 67 | this.voting_account = voting_account; 68 | } 69 | 70 | public int getNumWitness() { 71 | return num_witness; 72 | } 73 | 74 | public void setNumWitness(int num_witness) { 75 | this.num_witness = num_witness; 76 | } 77 | 78 | public int getNumComittee() { 79 | return num_comittee; 80 | } 81 | 82 | public void setNumComittee(int num_comittee) { 83 | this.num_comittee = num_comittee; 84 | } 85 | 86 | public Vote[] getVotes() { 87 | return votes; 88 | } 89 | 90 | public void setVotes(Vote[] votes) { 91 | this.votes = votes; 92 | } 93 | 94 | @Override 95 | public byte[] toBytes() { 96 | List byteArray = new ArrayList(); 97 | 98 | if (memo_key != null) { 99 | // Adding memo key 100 | byteArray.addAll(Bytes.asList(memo_key.toBytes())); 101 | 102 | // Adding voting account 103 | byteArray.addAll(Bytes.asList(voting_account.toBytes())); 104 | 105 | // Adding num_witness 106 | byteArray.addAll(Bytes.asList(Util.revertShort(Short.valueOf((short) num_witness)))); 107 | 108 | // Adding num_committee 109 | byteArray.addAll(Bytes.asList(Util.revertShort(Short.valueOf((short) num_comittee)))); 110 | 111 | // Vote's array length 112 | byteArray.add((byte) votes.length); 113 | 114 | for (Vote vote : votes) { 115 | //TODO: Check this serialization 116 | byteArray.addAll(Bytes.asList(vote.toBytes())); 117 | } 118 | 119 | // Account options's extensions 120 | byteArray.addAll(Bytes.asList(extensions.toBytes())); 121 | } else { 122 | byteArray.add((byte) 0); 123 | } 124 | return Bytes.toArray(byteArray); 125 | } 126 | 127 | @Override 128 | public String toJsonString() { 129 | return toJsonObject().toString(); 130 | } 131 | 132 | @Override 133 | public JsonElement toJsonObject() { 134 | JsonObject options = new JsonObject(); 135 | options.addProperty(KEY_MEMO_KEY, new Address(memo_key.getKey()).toString()); 136 | options.addProperty(KEY_NUM_COMMITTEE, num_comittee); 137 | options.addProperty(KEY_NUM_WITNESS, num_witness); 138 | options.addProperty(KEY_VOTING_ACCOUNT, voting_account.getObjectId()); 139 | JsonArray votesArray = new JsonArray(); 140 | for (Vote vote : votes) { 141 | // Add votes representation 142 | votesArray.add(vote.toString()); 143 | } 144 | options.add(KEY_VOTES, votesArray); 145 | options.add(KEY_EXTENSIONS, extensions.toJsonObject()); 146 | return options; 147 | } 148 | 149 | /** 150 | * Custom deserializer used while parsing the 'get_account_by_name' API call response. 151 | * TODO: Implement all other details besides the key 152 | */ 153 | public static class AccountOptionsDeserializer implements JsonDeserializer { 154 | 155 | @Override 156 | public AccountOptions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 157 | JsonObject baseObject = json.getAsJsonObject(); 158 | AccountOptions options; 159 | try { 160 | Address address = new Address(baseObject.get(KEY_MEMO_KEY).getAsString()); 161 | options = new AccountOptions(address.getPublicKey()); 162 | options.setNumWitness(baseObject.get(KEY_NUM_WITNESS).getAsInt()); 163 | options.setNumComittee(baseObject.get(KEY_NUM_COMMITTEE).getAsInt()); 164 | if (StringUtils.isNotBlank(baseObject.get(KEY_VOTING_ACCOUNT).getAsString())) { 165 | options.setVotingAccount(new UserAccount(baseObject.get(KEY_VOTING_ACCOUNT).getAsString())); 166 | } 167 | JsonArray votes = baseObject.get(KEY_VOTES).getAsJsonArray(); 168 | if (votes.size() > 0) { 169 | List voteList = new ArrayList<>(); 170 | for (JsonElement vote : votes) { 171 | voteList.add(new Vote(vote.getAsString())); 172 | } 173 | options.setVotes(voteList.toArray(new Vote[0])); 174 | } 175 | } catch (MalformedAddressException e) { 176 | LOGGER.info("MalformedAddressException. Msg: " + e.getMessage()); 177 | options = new AccountOptions(); 178 | } 179 | return options; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/Asset.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.common.primitives.UnsignedLong; 4 | import com.google.gson.*; 5 | import com.gxchain.client.graphenej.AssetOptions; 6 | 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * Created by nelson on 11/9/16. 11 | */ 12 | public class Asset extends GrapheneObject { 13 | public final static String TAG = "Asset"; 14 | 15 | public static final String KEY_ID = "id"; 16 | public static final String KEY_SYMBOL = "symbol"; 17 | public static final String KEY_PRECISION = "precision"; 18 | public static final String KEY_ISSUER = "issuer"; 19 | public static final String KEY_OPTIONS = "options"; 20 | public static final String KEY_MAX_SUPPLY = "max_supply"; 21 | public static final String KEY_MARKET_FEE_PERCENT = "market_fee_percent"; 22 | public static final String KEY_MARKET_FEE = "max_market_fee"; 23 | public static final String KEY_ISSUER_PERMISSIONS = "issuer_permissions"; 24 | public static final String KEY_FLAGS = "flags"; 25 | public static final String KEY_CORE_EXCHANGE_RATE = "core_exchange_rate"; 26 | public static final String KEY_DESCRIPTION = "description"; 27 | public static final String KEY_DYNAMIC_ASSET_DATA_ID = "dynamic_asset_data_id"; 28 | public static final String KEY_BITASSET_DATA_ID = "bitasset_data_id"; 29 | 30 | /** 31 | * Enum type used to represent the possible types an asset can be classified into. 32 | */ 33 | public enum AssetType { 34 | CORE_ASSET, 35 | UIA, 36 | SMART_COIN, 37 | PREDICTION_MARKET 38 | } 39 | 40 | private String symbol; 41 | private int precision = -1; 42 | private String issuer; 43 | private String description; 44 | private String dynamic_asset_data_id; 45 | private AssetOptions options; 46 | private String bitasset_data_id; 47 | private AssetType mAssetType; 48 | 49 | /** 50 | * Simple constructor 51 | * @param id 52 | */ 53 | public Asset(String id) { 54 | super(id); 55 | } 56 | 57 | /** 58 | * Constructor 59 | * @param id: The graphene object id. 60 | * @param symbol: The asset symbol. 61 | * @param precision: The asset precision. 62 | */ 63 | public Asset(String id, String symbol, int precision){ 64 | super(id); 65 | this.symbol = symbol; 66 | this.precision = precision; 67 | } 68 | 69 | /** 70 | * Constructor 71 | * @param id: The graphene object id. 72 | * @param symbol: The asset symbol. 73 | * @param precision: The asset precision. 74 | * @param issuer: Graphene object id of the issuer. 75 | */ 76 | public Asset(String id, String symbol, int precision, String issuer){ 77 | super(id); 78 | this.symbol = symbol; 79 | this.precision = precision; 80 | this.issuer = issuer; 81 | } 82 | 83 | public String getSymbol(){ 84 | return this.symbol; 85 | } 86 | 87 | public void setSymbol(String symbol){ 88 | this.symbol = symbol; 89 | } 90 | 91 | public void setPrecision(int precision){ 92 | this.precision = precision; 93 | } 94 | 95 | public int getPrecision(){ 96 | return this.precision; 97 | } 98 | 99 | public void setIssuer(String issuer){ this.issuer = issuer; } 100 | 101 | public String getIssuer() { return this.issuer; } 102 | 103 | public void setDescription(String description) { 104 | this.description = description; 105 | } 106 | 107 | public String getDescription() { 108 | return description; 109 | } 110 | 111 | public void setAssetOptions(AssetOptions options){ 112 | this.options = options; 113 | } 114 | 115 | public AssetOptions getAssetOptions(){ 116 | return this.options; 117 | } 118 | 119 | public String getBitassetId(){ 120 | return this.bitasset_data_id; 121 | } 122 | 123 | public void setBitassetDataId(String id){ 124 | this.bitasset_data_id = id; 125 | } 126 | 127 | public AssetType getAssetType() { 128 | return mAssetType; 129 | } 130 | 131 | public void setAssetType(AssetType mAssetType) { 132 | this.mAssetType = mAssetType; 133 | } 134 | 135 | @Override 136 | public int hashCode() { 137 | return this.getObjectId() == null ? 0 : this.getObjectId().hashCode(); 138 | } 139 | 140 | @Override 141 | public boolean equals(Object other) { 142 | if(other instanceof Asset){ 143 | return this.getObjectId().equals(((Asset)other).getObjectId()); 144 | }else{ 145 | return false; 146 | } 147 | } 148 | 149 | /** 150 | * Custom deserializer used to instantiate a simple version of the Asset class from the response of the 151 | * 'lookup_asset_symbols' API call. 152 | */ 153 | public static class AssetDeserializer implements JsonDeserializer { 154 | 155 | @Override 156 | public Asset deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 157 | JsonObject object = json.getAsJsonObject(); 158 | String id = object.get(KEY_ID).getAsString(); 159 | String symbol = object.get(KEY_SYMBOL).getAsString(); 160 | int precision = object.get(KEY_PRECISION).getAsInt(); 161 | String issuer = object.get(KEY_ISSUER).getAsString(); 162 | JsonObject optionsJson = object.get(KEY_OPTIONS).getAsJsonObject(); 163 | JsonElement bitassetDataId = object.get(KEY_BITASSET_DATA_ID); 164 | 165 | // Deserializing asset options 166 | AssetOptions options = new AssetOptions(); 167 | options.setMaxSupply(UnsignedLong.valueOf(optionsJson.get(KEY_MAX_SUPPLY).getAsString())); 168 | options.setMarketFeePercent(optionsJson.get(KEY_MARKET_FEE_PERCENT).getAsInt()); 169 | options.setMaxMarketFee(UnsignedLong.valueOf(optionsJson.get(KEY_MARKET_FEE).getAsString())); 170 | options.setIssuerPermissions(optionsJson.get(KEY_ISSUER_PERMISSIONS).getAsLong()); 171 | options.setFlags(optionsJson.get(KEY_FLAGS).getAsInt()); 172 | if(optionsJson.has(KEY_DESCRIPTION)) 173 | options.setDescription(optionsJson.get(KEY_DESCRIPTION).getAsString()); 174 | //TODO: Deserialize core_exchange_rate field 175 | 176 | Asset asset = new Asset(id, symbol, precision, issuer); 177 | asset.setAssetOptions(options); 178 | if(bitassetDataId != null){ 179 | asset.setBitassetDataId(bitassetDataId.getAsString()); 180 | } 181 | return asset; 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/AssetTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import static org.junit.Assert.assertNotEquals; 4 | 5 | /** 6 | * Created by nelson on 12/24/16. 7 | */ 8 | public class AssetTest { 9 | 10 | @org.junit.Test 11 | public void equals() throws Exception { 12 | Asset bts = new Asset("1.3.0"); 13 | Asset bitUSD = new Asset("1.3.121"); 14 | assertNotEquals("Different assets should not be equal", bts, bitUSD); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/BlockData.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.gxchain.client.graphenej.Varint; 5 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 6 | 7 | /** 8 | * This class encapsulates all block-related information needed in order to build a valid transaction. 9 | */ 10 | public class BlockData implements ByteSerializable { 11 | private final int REF_BLOCK_NUM_BYTES = 2; 12 | private final int REF_BLOCK_PREFIX_BYTES = 4; 13 | private final int REF_BLOCK_EXPIRATION_BYTES = 4; 14 | 15 | private int refBlockNum; 16 | private long refBlockPrefix; 17 | private long expiration; 18 | 19 | /** 20 | * Block data constructor 21 | * 22 | * @param ref_block_num: Least significant 16 bits from the reference block number. 23 | * If "relative_expiration" is zero, this field must be zero as well. 24 | * @param ref_block_prefix: The first non-block-number 32-bits of the reference block ID. 25 | * Recall that block IDs have 32 bits of block number followed by the 26 | * actual block hash, so this field should be set using the second 32 bits 27 | * in the block_id_type 28 | * @param relative_expiration: Expiration time specified as a POSIX or 29 | * Unix time 30 | */ 31 | public BlockData(int ref_block_num, long ref_block_prefix, long relative_expiration) { 32 | this.refBlockNum = ref_block_num; 33 | this.refBlockPrefix = ref_block_prefix; 34 | this.expiration = relative_expiration; 35 | } 36 | 37 | /** 38 | * Block data constructor that takes in raw blockchain information. 39 | * 40 | * @param head_block_number: The last block number. 41 | * @param head_block_id: The last block apiId. 42 | * @param relative_expiration: The expiration time. 43 | */ 44 | public BlockData(long head_block_number, String head_block_id, long relative_expiration) { 45 | this.setRefBlockNum(head_block_number); 46 | this.setRefBlockPrefix(head_block_id); 47 | this.expiration = relative_expiration; 48 | } 49 | 50 | /** 51 | * Plain setter for the ref_block_num field, assuming its value is exactly the one passed in the argument. 52 | * 53 | * @param refBlockNum: The 'ref_block_num' field. 54 | */ 55 | public void setRefBlockNum(int refBlockNum) { 56 | this.refBlockNum = refBlockNum; 57 | } 58 | 59 | /** 60 | * Setter that receives the block number, and takes the 16 lower bits of it to obtain the 61 | * 'ref_block_num' value. 62 | * 63 | * @param blockNumber: The block number. 64 | */ 65 | public void setRefBlockNum(long blockNumber) { 66 | this.refBlockNum = ((int) blockNumber) & 0xFFFF; 67 | } 68 | 69 | /** 70 | * Plain setter fot the 'ref_block_prefix' field, assumint its value is exactly the one passed in the argument. 71 | * 72 | * @param refBlockPrefix 73 | */ 74 | public void setRefBlockPrefix(long refBlockPrefix) { 75 | this.refBlockPrefix = refBlockPrefix; 76 | } 77 | 78 | /** 79 | * Setter that receives the head block id, and turns it into the little format required for the 80 | * 'ref_block_prefix' field. 81 | * 82 | * @param headBlockId: The head block id as obtained from the network updates. 83 | */ 84 | public void setRefBlockPrefix(String headBlockId) { 85 | String hashData = headBlockId.substring(8, 16); 86 | StringBuilder builder = new StringBuilder(); 87 | for (int i = 0; i < 8; i = i + 2) { 88 | builder.append(hashData.substring(6 - i, 8 - i)); 89 | } 90 | this.refBlockPrefix = Long.parseLong(builder.toString(), 16); 91 | } 92 | 93 | public int getRefBlockNum() { 94 | return refBlockNum; 95 | } 96 | 97 | public long getRefBlockPrefix() { 98 | return refBlockPrefix; 99 | } 100 | 101 | public long getExpiration() { 102 | return expiration; 103 | } 104 | 105 | public void setExpiration(long expiration) { 106 | this.expiration = expiration; 107 | } 108 | 109 | 110 | @Override public byte[] toBytes() { 111 | // Allocating a fixed length byte array, since we will always need 112 | // 2 bytes for the ref_block_num value 113 | // 4 bytes for the ref_block_prefix value 114 | // 4 bytes for the relative_expiration 115 | 116 | // byte[] result = new byte[REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES + REF_BLOCK_EXPIRATION_BYTES]; 117 | // for(int i = 0; i < result.length; i++){ 118 | // if(i < REF_BLOCK_NUM_BYTES){ 119 | // result[i] = (byte) (this.refBlockNum >> 8 * i); 120 | // }else if(i < REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES){ 121 | // result[i] = (byte) (this.refBlockPrefix >> 8 * (i - REF_BLOCK_NUM_BYTES)); 122 | // }else{ 123 | // result[i] = (byte) (this.expiration >> 8 * (i - REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES)); 124 | // } 125 | // } 126 | return Bytes.concat(Varint.writeUnsignedSize(this.refBlockNum), Varint.writeUnsignedSize(this.refBlockPrefix), Varint.writeUnsignedSize(this.expiration)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/BroadcastRequestParams.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.gxchain.client.graphenej.Util; 8 | import com.gxchain.client.graphenej.Varint; 9 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 10 | import com.gxchain.client.graphenej.interfaces.JsonSerializable; 11 | import lombok.Builder; 12 | import lombok.Data; 13 | import org.joda.time.DateTime; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * @Description 19 | * @Author Hanawa 20 | * @Date 2018/3/15 21 | * @Version 1.0 22 | */ 23 | @Data @Builder public class BroadcastRequestParams implements ByteSerializable, JsonSerializable { 24 | private static final long serialVersionUID = 6867501721046700516L; 25 | private UserAccount from; 26 | private UserAccount to; 27 | private UserAccount proxyAccount; 28 | private AssetAmount amount; 29 | private int percentage; 30 | private String memo; 31 | private long expiration; 32 | private List signatures; 33 | 34 | @Override public byte[] toBytes() { 35 | byte[] fromBytes = from.toBytes(); 36 | byte[] toBytes = to.toBytes(); 37 | byte[] proxyAccountBytes = proxyAccount.toBytes(); 38 | byte[] amountBytes = amount.toBytes(); 39 | byte[] percentageBytes = Varint.writeUnsignedSize(percentage); 40 | byte[] memoPrefix = new byte[] {(byte) memo.length()}; 41 | byte[] memoBytes = memo.getBytes(); 42 | byte[] expirationBytes = Varint.writeUnsignedSize(expiration); 43 | byte[] signaturesSizeBytes = new byte[] {0}; 44 | if (null != signatures && signatures.size() != 0) { 45 | signaturesSizeBytes = new byte[] {(byte) signatures.size()}; 46 | StringBuilder s = new StringBuilder(); 47 | signatures.forEach(s::append); 48 | byte[] signatureBytes = Util.hexToBytes(s.toString()); 49 | return Bytes.concat(fromBytes, toBytes, proxyAccountBytes, amountBytes, percentageBytes, memoPrefix, memoBytes, expirationBytes, signaturesSizeBytes, signatureBytes); 50 | } 51 | return Bytes.concat(fromBytes, toBytes, proxyAccountBytes, amountBytes, percentageBytes, memoPrefix, memoBytes, expirationBytes, signaturesSizeBytes); 52 | } 53 | 54 | @Override public String toJsonString() { 55 | return toJsonObject().toString(); 56 | } 57 | 58 | @Override public JsonElement toJsonObject() { 59 | DateTime expirationTime = new DateTime(expiration * 1000); 60 | JsonObject jsonObject = new JsonObject(); 61 | jsonObject.addProperty("from", from.getObjectId()); 62 | jsonObject.addProperty("to", to.getObjectId()); 63 | jsonObject.addProperty("proxy_account", proxyAccount.getObjectId()); 64 | jsonObject.add("amount", amount.toJsonObject()); 65 | jsonObject.addProperty("percentage", percentage); 66 | jsonObject.addProperty("memo", memo); 67 | jsonObject.addProperty("expiration", expirationTime.plusHours(-8).toString().substring(0, 19)); 68 | JsonArray array = new JsonArray(); 69 | signatures.forEach(array::add); 70 | jsonObject.add("signatures", array); 71 | return jsonObject; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/Extensions.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 6 | import com.gxchain.client.graphenej.interfaces.JsonSerializable; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by nelson on 11/9/16. 12 | */ 13 | public class Extensions implements JsonSerializable, ByteSerializable { 14 | public static final String KEY_EXTENSIONS = "extensions"; 15 | 16 | private ArrayList extensions; 17 | 18 | public Extensions(){ 19 | extensions = new ArrayList<>(); 20 | } 21 | 22 | @Override 23 | public String toJsonString() { 24 | return null; 25 | } 26 | 27 | @Override 28 | public JsonElement toJsonObject() { 29 | JsonArray array = new JsonArray(); 30 | for(JsonSerializable o : extensions) 31 | array.add(o.toJsonObject()); 32 | return array; 33 | } 34 | 35 | @Override 36 | public byte[] toBytes() { 37 | return new byte[1]; 38 | } 39 | 40 | public int size(){ 41 | return extensions.size(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/GrapheneObject.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.gxchain.client.graphenej.enums.ObjectType; 5 | 6 | /** 7 | *

8 | * Generic class used to represent a graphene object as defined in 9 | * 10 | *

11 | * Created by nelson on 11/8/16. 12 | */ 13 | public class GrapheneObject { 14 | public static final String KEY_ID = "id"; 15 | 16 | public static final int PROTOCOL_SPACE = 1; 17 | public static final int IMPLEMENTATION_SPACE = 2; 18 | 19 | @Expose 20 | protected String id; 21 | 22 | protected int space; 23 | protected int type; 24 | protected long instance; 25 | 26 | public GrapheneObject(String id){ 27 | this.id = id; 28 | String[] parts = id.split("\\."); 29 | if(parts.length == 3){ 30 | this.space = Integer.parseInt(parts[0]); 31 | this.type = Integer.parseInt(parts[1]); 32 | this.instance = Long.parseLong(parts[2]); 33 | } 34 | } 35 | 36 | /** 37 | * 38 | * @return: A String containing the full object apiId in the form {space}.{type}.{instance} 39 | */ 40 | public String getObjectId(){ 41 | return String.format("%d.%d.%d", space, type, instance); 42 | } 43 | 44 | /** 45 | * Returns the type of this object. 46 | * @return: Instance of the ObjectType enum. 47 | */ 48 | public ObjectType getObjectType(){ 49 | switch(space){ 50 | case PROTOCOL_SPACE: 51 | switch(type){ 52 | case 1: 53 | return ObjectType.BASE_OBJECT; 54 | case 2: 55 | return ObjectType.ACCOUNT_OBJECT; 56 | case 3: 57 | return ObjectType.ASSET_OBJECT; 58 | case 4: 59 | return ObjectType.FORCE_SETTLEMENT_OBJECT; 60 | case 5: 61 | return ObjectType.COMMITTEE_MEMBER_OBJECT; 62 | case 6: 63 | return ObjectType.WITNESS_OBJECT; 64 | case 7: 65 | return ObjectType.LIMIT_ORDER_OBJECT; 66 | case 8: 67 | return ObjectType.CALL_ORDER_OBJECT; 68 | case 9: 69 | return ObjectType.CUSTOM_OBJECT; 70 | case 10: 71 | return ObjectType.PROPOSAL_OBJECT; 72 | case 11: 73 | return ObjectType.OPERATION_HISTORY_OBJECT; 74 | case 12: 75 | return ObjectType.WITHDRAW_PERMISSION_OBJECT; 76 | case 13: 77 | return ObjectType.VESTING_BALANCE_OBJECT; 78 | case 14: 79 | return ObjectType.WORKER_OBJECT; 80 | case 15: 81 | return ObjectType.BALANCE_OBJECT; 82 | } 83 | case IMPLEMENTATION_SPACE: 84 | switch(type){ 85 | case 0: 86 | return ObjectType.GLOBAL_PROPERTY_OBJECT; 87 | case 1: 88 | return ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT; 89 | case 3: 90 | return ObjectType.ASSET_DYNAMIC_DATA; 91 | case 4: 92 | return ObjectType.ASSET_BITASSET_DATA; 93 | case 5: 94 | return ObjectType.ACCOUNT_BALANCE_OBJECT; 95 | case 6: 96 | return ObjectType.ACCOUNT_STATISTICS_OBJECT; 97 | case 7: 98 | return ObjectType.TRANSACTION_OBJECT; 99 | case 8: 100 | return ObjectType.BLOCK_SUMMARY_OBJECT; 101 | case 9: 102 | return ObjectType.ACCOUNT_TRANSACTION_HISTORY_OBJECT; 103 | case 10: 104 | return ObjectType.BLINDED_BALANCE_OBJECT; 105 | case 11: 106 | return ObjectType.CHAIN_PROPERTY_OBJECT; 107 | case 12: 108 | return ObjectType.WITNESS_SCHEDULE_OBJECT; 109 | case 13: 110 | return ObjectType.BUDGET_RECORD_OBJECT; 111 | case 14: 112 | return ObjectType.SPECIAL_AUTHORITY_OBJECT; 113 | } 114 | } 115 | return null; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/LimitOrder.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.gson.*; 4 | import com.gxchain.client.graphenej.Price; 5 | import com.gxchain.client.graphenej.Varint; 6 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.DataOutput; 10 | import java.io.DataOutputStream; 11 | import java.io.IOException; 12 | import java.lang.reflect.Type; 13 | 14 | /** 15 | * 16 | * @author henry 17 | */ 18 | public class LimitOrder extends GrapheneObject implements ByteSerializable { 19 | 20 | public static final String KEY_EXPIRATION = "expiration"; 21 | public static final String KEY_SELLER = "seller"; 22 | public static final String KEY_FOR_SALE = "for_sale"; 23 | public static final String KEY_DEFERRED_FEE = "deferred_fee"; 24 | public static final String KEY_PRICE = "sell_price"; 25 | 26 | private String expiration; 27 | private UserAccount seller; 28 | private long forSale; 29 | private long deferredFee; 30 | private Price sellPrice; 31 | 32 | public LimitOrder(String id) { 33 | super(id); 34 | } 35 | 36 | public String getExpiration() { 37 | return expiration; 38 | } 39 | 40 | public void setExpiration(String expiration) { 41 | this.expiration = expiration; 42 | } 43 | 44 | public UserAccount getSeller() { 45 | return seller; 46 | } 47 | 48 | public void setSeller(UserAccount seller) { 49 | this.seller = seller; 50 | } 51 | 52 | public long getForSale() { 53 | return forSale; 54 | } 55 | 56 | public void setForSale(long forSale) { 57 | this.forSale = forSale; 58 | } 59 | 60 | public long getDeferredFee() { 61 | return deferredFee; 62 | } 63 | 64 | public void setDeferredFee(long deferredFee) { 65 | this.deferredFee = deferredFee; 66 | } 67 | 68 | public Price getSellPrice() { 69 | return sellPrice; 70 | } 71 | 72 | public void setSellPrice(Price sellPrice) { 73 | this.sellPrice = sellPrice; 74 | } 75 | 76 | @Override 77 | public byte[] toBytes() { 78 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 79 | DataOutput out = new DataOutputStream(byteArrayOutputStream); 80 | byte[] serialized = null; 81 | try { 82 | Varint.writeUnsignedVarLong(this.instance, out); 83 | serialized = byteArrayOutputStream.toByteArray(); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | return serialized; 88 | } 89 | 90 | /** 91 | * Custom deserializer for the LimitOrder class, used to deserialize a json-formatted string in 92 | * the following format: 93 | * 94 | * { 95 | * "id": "1.7.2389233", 96 | * "expiration": "2017-04-21T15:40:04", 97 | * "seller": "1.2.114363", 98 | * "forSale": "10564959415", 99 | * "sell_price": { 100 | * "base": { 101 | * "amount": "10565237932", 102 | * "asset_id": "1.3.0" 103 | * }, 104 | * "quote": { 105 | * "amount": 5803878, 106 | * "asset_id": "1.3.121" 107 | * } 108 | * }, 109 | * "deferredFee": 0 110 | * } 111 | */ 112 | public static class LimitOrderDeserializer implements JsonDeserializer { 113 | 114 | @Override 115 | public LimitOrder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 116 | JsonObject object = json.getAsJsonObject(); 117 | String id = object.get(KEY_ID).getAsString(); 118 | String expiration = object.get(KEY_EXPIRATION).getAsString(); 119 | UserAccount seller = context.deserialize(object.get(KEY_SELLER), UserAccount.class); 120 | String forSale = object.get(KEY_FOR_SALE).getAsString(); 121 | Price price = context.deserialize(object.get(KEY_PRICE), Price.class); 122 | long deferredFee = object.get(KEY_DEFERRED_FEE).getAsLong(); 123 | 124 | LimitOrder limitOrder = new LimitOrder(id); 125 | limitOrder.setExpiration(expiration); 126 | limitOrder.setSeller(seller); 127 | limitOrder.setForSale(Long.parseLong(forSale)); 128 | limitOrder.setSellPrice(price); 129 | limitOrder.setDeferredFee(deferredFee); 130 | return limitOrder; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/Optional.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.JsonElement; 5 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 6 | import com.gxchain.client.graphenej.interfaces.GrapheneSerializable; 7 | 8 | /** 9 | * Container template class used whenever we have an optional field. 10 | *

11 | * The idea here is that the binary serialization of this field should be performed 12 | * in a specific way determined by the field implementing the {@link ByteSerializable} 13 | * interface, more specifically using the {@link ByteSerializable#toBytes()} method. 14 | *

15 | * However, if the field is missing, the Optional class should be able to know how 16 | * to serialize it, as this is always done by placing an zero byte. 17 | */ 18 | public class Optional implements GrapheneSerializable { 19 | private T optionalField; 20 | 21 | public Optional(T field) { 22 | optionalField = field; 23 | } 24 | 25 | @Override 26 | public byte[] toBytes() { 27 | if (optionalField == null) 28 | return new byte[]{(byte) 0}; 29 | else 30 | return Bytes.concat(new byte[]{(byte) 1}, optionalField.toBytes()); 31 | } 32 | 33 | public boolean isSet() { 34 | return this.optionalField != null; 35 | } 36 | 37 | @Override 38 | public String toJsonString() { 39 | return optionalField.toJsonString(); 40 | } 41 | 42 | @Override 43 | public JsonElement toJsonObject() { 44 | return optionalField.toJsonObject(); 45 | } 46 | 47 | public T value(){ 48 | return optionalField; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/objects/Vote.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.objects; 2 | 3 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 4 | import lombok.Getter; 5 | 6 | /** 7 | * Created by nelson on 12/5/16. 8 | */ 9 | public class Vote implements ByteSerializable { 10 | @Getter 11 | private int type; 12 | @Getter 13 | private int instance; 14 | 15 | public Vote(String vote) { 16 | String[] parts = vote.split(":"); 17 | assert (parts.length == 2); 18 | this.type = Integer.valueOf(parts[0]); 19 | this.instance = Integer.valueOf(parts[1]); 20 | } 21 | 22 | public Vote(int type, int instance) { 23 | this.type = type; 24 | this.instance = instance; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return String.format("%d:%d", this.type, this.instance); 30 | } 31 | 32 | @Override 33 | public byte[] toBytes() { 34 | return new byte[]{(byte) this.instance, (byte) this.type}; 35 | } 36 | 37 | @Override 38 | public boolean equals(final Object obj) { 39 | if (obj == null) { 40 | return false; 41 | } 42 | final Vote vote = (Vote) obj; 43 | if (this == vote) { 44 | return true; 45 | } else { 46 | return (this.type == vote.type && this.instance == vote.instance); 47 | } 48 | } 49 | 50 | public int hashCode() { 51 | return type * 10000000 + instance; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/AccountUpdateOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.common.primitives.UnsignedLong; 5 | import com.google.gson.Gson; 6 | import com.google.gson.JsonArray; 7 | import com.google.gson.JsonElement; 8 | import com.google.gson.JsonObject; 9 | import com.gxchain.client.graphenej.enums.OperationType; 10 | import com.gxchain.client.graphenej.objects.*; 11 | 12 | /** 13 | * Class used to encapsulate operations related to the ACCOUNT_UPDATE_OPERATION. 14 | */ 15 | public class AccountUpdateOperation extends BaseOperation { 16 | public static final String KEY_ACCOUNT = "account"; 17 | public static final String KEY_OWNER = "owner"; 18 | public static final String KEY_ACTIVE = "active"; 19 | public static final String KEY_FEE = "fee"; 20 | public static final String KEY_NEW_OPTIONS = "new_options"; 21 | public static final String KEY_EXTENSIONS = "extensions"; 22 | 23 | private AssetAmount fee; 24 | private UserAccount account; 25 | private Optional owner; 26 | private Optional active; 27 | private Optional new_options; 28 | 29 | /** 30 | * Account update operation constructor. 31 | * @param account User account to update. Can't be null. 32 | * @param owner Owner authority to set. Can be null. 33 | * @param active Active authority to set. Can be null. 34 | * @param options Active authority to set. Can be null. 35 | * @param fee The fee to pay. Can be null. 36 | */ 37 | public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options, AssetAmount fee){ 38 | super(OperationType.ACCOUNT_UPDATE_OPERATION); 39 | this.fee = fee; 40 | this.account = account; 41 | this.owner = new Optional<>(owner); 42 | this.active = new Optional<>(active); 43 | this.new_options = new Optional<>(options); 44 | extensions = new Extensions(); 45 | } 46 | 47 | public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options){ 48 | this(account, owner, active, options, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0"))); 49 | } 50 | 51 | @Override 52 | public void setFee(AssetAmount fee){ 53 | this.fee = fee; 54 | } 55 | 56 | public void setOwner(Authority owner){ 57 | this.owner = new Optional<>(owner); 58 | } 59 | 60 | public void setActive(Authority active){ 61 | this.active = new Optional<>(active); 62 | } 63 | 64 | public void setAccountOptions(AccountOptions options){ 65 | this.new_options = new Optional<>(options); 66 | } 67 | 68 | @Override 69 | public String toJsonString() { 70 | Gson gson = new Gson(); 71 | return gson.toJson(this); 72 | } 73 | 74 | @Override 75 | public JsonElement toJsonObject() { 76 | JsonArray array = new JsonArray(); 77 | array.add(this.getId()); 78 | 79 | JsonObject accountUpdate = new JsonObject(); 80 | accountUpdate.add(KEY_FEE, fee.toJsonObject()); 81 | accountUpdate.addProperty(KEY_ACCOUNT, account.getObjectId()); 82 | if(owner.isSet()) 83 | accountUpdate.add(KEY_OWNER, owner.toJsonObject()); 84 | if(active.isSet()) 85 | accountUpdate.add(KEY_ACTIVE, active.toJsonObject()); 86 | if(new_options.isSet()) 87 | accountUpdate.add(KEY_NEW_OPTIONS, new_options.toJsonObject()); 88 | accountUpdate.add(KEY_EXTENSIONS, extensions.toJsonObject()); 89 | array.add(accountUpdate); 90 | return array; 91 | } 92 | 93 | @Override 94 | public byte[] toBytes() { 95 | byte[] feeBytes = fee.toBytes(); 96 | byte[] accountBytes = account.toBytes(); 97 | byte[] ownerBytes = owner.toBytes(); 98 | byte[] activeBytes = active.toBytes(); 99 | byte[] newOptionsBytes = new_options.toBytes(); 100 | byte[] extensionBytes = extensions.toBytes(); 101 | return Bytes.concat(feeBytes, accountBytes, ownerBytes, activeBytes, newOptionsBytes, extensionBytes); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/AccountUpdateOperationBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.gxchain.client.graphenej.errors.MalformedOperationException; 4 | import com.gxchain.client.graphenej.objects.AccountOptions; 5 | import com.gxchain.client.graphenej.objects.AssetAmount; 6 | import com.gxchain.client.graphenej.objects.Authority; 7 | import com.gxchain.client.graphenej.objects.UserAccount; 8 | 9 | /** 10 | * Created by nelson on 3/1/17. 11 | */ 12 | public class AccountUpdateOperationBuilder extends BaseOperationBuilder { 13 | private AssetAmount fee; 14 | private UserAccount account; 15 | private Authority owner; 16 | private Authority active; 17 | private AccountOptions new_options; 18 | 19 | public AccountUpdateOperationBuilder setFee(AssetAmount fee) { 20 | this.fee = fee; 21 | return this; 22 | } 23 | 24 | public AccountUpdateOperationBuilder setAccount(UserAccount account) { 25 | this.account = account; 26 | return this; 27 | } 28 | 29 | public AccountUpdateOperationBuilder setOwner(Authority owner) { 30 | this.owner = owner; 31 | return this; 32 | } 33 | 34 | public AccountUpdateOperationBuilder setActive(Authority active) { 35 | this.active = active; 36 | return this; 37 | } 38 | 39 | public AccountUpdateOperationBuilder setOptions(AccountOptions newOptions) { 40 | this.new_options = newOptions; 41 | return this; 42 | } 43 | 44 | @Override 45 | public AccountUpdateOperation build() { 46 | AccountUpdateOperation operation; 47 | if(this.account == null){ 48 | throw new MalformedOperationException("This operation requires an account to be set"); 49 | }else{ 50 | if(owner != null || active != null || new_options != null){ 51 | if(fee == null){ 52 | operation = new AccountUpdateOperation(account, owner, active, new_options); 53 | }else{ 54 | operation = new AccountUpdateOperation(account, owner, active, new_options, fee); 55 | } 56 | }else{ 57 | throw new MalformedOperationException("This operation requires at least either an authority or account options change"); 58 | } 59 | } 60 | return operation; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/BaseOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.gxchain.client.graphenej.enums.OperationType; 6 | import com.gxchain.client.graphenej.interfaces.ByteSerializable; 7 | import com.gxchain.client.graphenej.interfaces.JsonSerializable; 8 | import com.gxchain.client.graphenej.objects.AssetAmount; 9 | import com.gxchain.client.graphenej.objects.Extensions; 10 | 11 | /** 12 | * Created by nelson on 11/5/16. 13 | */ 14 | public abstract class BaseOperation implements ByteSerializable, JsonSerializable { 15 | 16 | public static final String KEY_FEE = "fee"; 17 | public static final String KEY_EXTENSIONS = "extensions"; 18 | 19 | protected OperationType type; 20 | protected Extensions extensions; 21 | 22 | public BaseOperation(OperationType type){ 23 | this.type = type; 24 | this.extensions = new Extensions(); 25 | } 26 | 27 | public byte getId() { 28 | return (byte) this.type.getCode(); 29 | } 30 | 31 | public abstract void setFee(AssetAmount assetAmount); 32 | 33 | public JsonElement toJsonObject(){ 34 | JsonArray array = new JsonArray(); 35 | array.add(this.getId()); 36 | return array; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/BaseOperationBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | /** 4 | * Base template for all operation-specific factory classes. 5 | */ 6 | public abstract class BaseOperationBuilder { 7 | 8 | /** 9 | * Must be implemented and return the specific operation the 10 | * factory is supposed to build. 11 | * 12 | * @return: A usable instance of a given operation. 13 | */ 14 | public abstract BaseOperation build(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/BroadcastOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.gxchain.client.graphenej.enums.OperationType; 8 | import com.gxchain.client.graphenej.objects.AssetAmount; 9 | import com.gxchain.client.graphenej.objects.BroadcastRequestParams; 10 | import com.gxchain.client.graphenej.objects.Extensions; 11 | import lombok.Data; 12 | 13 | /** 14 | * @Description 15 | * @Author Hanawa 16 | * @Date 2018/3/6 17 | * @Version 1.0 18 | */ 19 | @Data public class BroadcastOperation extends BaseOperation { 20 | private static final long serialVersionUID = 4544825479406548507L; 21 | private String proxyMemo; 22 | private AssetAmount fee; 23 | private BroadcastRequestParams requestParams; 24 | private Extensions extensions; 25 | 26 | public BroadcastOperation() { 27 | super(OperationType.BROADCAST_STORE_DATA); 28 | } 29 | 30 | @Override public void setFee(AssetAmount assetAmount) { 31 | this.fee = assetAmount; 32 | } 33 | 34 | @Override public byte[] toBytes() { 35 | byte[] proxyMemoPrefix = new byte[] {(byte) proxyMemo.length()}; 36 | byte[] proxyMemoBytes = proxyMemo.getBytes(); 37 | byte[] feeBytes = fee.toBytes(); 38 | byte[] requestParamsBytes = requestParams.toBytes(); 39 | byte[] extensionsBytes = extensions.toBytes(); 40 | 41 | return Bytes.concat(proxyMemoPrefix, proxyMemoBytes, feeBytes, requestParamsBytes, extensionsBytes); 42 | } 43 | 44 | @Override public String toJsonString() { 45 | return toJsonObject().toString(); 46 | } 47 | 48 | @Override public JsonElement toJsonObject() { 49 | JsonArray array = new JsonArray(); 50 | array.add(this.getId()); 51 | JsonObject jsonObject = new JsonObject(); 52 | jsonObject.addProperty("proxy_memo", proxyMemo); 53 | jsonObject.add("fee", fee.toJsonObject()); 54 | jsonObject.add("request_params",requestParams.toJsonObject()); 55 | jsonObject.add("extensions", new JsonArray()); 56 | array.add(jsonObject); 57 | return array; 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/CallContractOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.gxchain.client.graphenej.enums.OperationType; 7 | import com.gxchain.client.graphenej.objects.AssetAmount; 8 | import com.gxchain.client.graphenej.objects.Optional; 9 | import com.gxchain.client.graphenej.objects.UserAccount; 10 | import lombok.Data; 11 | 12 | /** 13 | * @author liruobin 14 | * @since 2019/2/15 8:59 PM 15 | */ 16 | @Data 17 | public class CallContractOperation extends BaseOperation { 18 | 19 | private AssetAmount fee; 20 | 21 | private UserAccount account; 22 | 23 | private UserAccount contractId; 24 | 25 | private String methodName; 26 | 27 | private Optional amount; 28 | 29 | private String data; 30 | 31 | public CallContractOperation(AssetAmount fee, UserAccount account, UserAccount contractId, String methodName, AssetAmount amount, String data) { 32 | super(OperationType.CALL_CONTRACT); 33 | this.fee = fee; 34 | this.account = account; 35 | this.contractId = contractId; 36 | this.methodName = methodName; 37 | this.amount = new Optional<>(amount); 38 | this.data = data; 39 | } 40 | 41 | @Override 42 | public byte[] toBytes() { 43 | return new byte[0]; 44 | } 45 | 46 | @Override 47 | public String toJsonString() { 48 | return toJsonObject().toString(); 49 | } 50 | 51 | public JsonElement toJsonObject() { 52 | JsonArray array = new JsonArray(); 53 | array.add(this.getId()); 54 | JsonObject jsonObject = new JsonObject(); 55 | if (fee != null) 56 | jsonObject.add("fee", fee.toJsonObject()); 57 | jsonObject.addProperty("account", account.getObjectId()); 58 | jsonObject.addProperty("method_name", methodName); 59 | jsonObject.addProperty("contract_id", contractId.getObjectId()); 60 | jsonObject.addProperty("data", data); 61 | if (amount.isSet() && amount.value().getAmount().longValue() != 0) { 62 | jsonObject.add("amount", amount.toJsonObject()); 63 | } 64 | jsonObject.add(KEY_EXTENSIONS, new JsonArray()); 65 | array.add(jsonObject); 66 | return array; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/DiyOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.gxchain.client.graphenej.Util; 8 | import com.gxchain.client.graphenej.Varint; 9 | import com.gxchain.client.graphenej.enums.OperationType; 10 | import com.gxchain.client.graphenej.objects.AssetAmount; 11 | import com.gxchain.client.graphenej.objects.Extensions; 12 | import com.gxchain.client.graphenej.objects.UserAccount; 13 | import lombok.Data; 14 | 15 | /** 16 | * @Description 17 | * @Author Hanawa 18 | * @Date 2018/8/7 19 | * @Version 1.0 20 | */ 21 | @Data 22 | public class DiyOperation extends BaseOperation { 23 | 24 | private AssetAmount fee; 25 | private UserAccount payer; 26 | private Extensions requiredAuths; 27 | private int d; 28 | private String data; 29 | 30 | public DiyOperation() { 31 | super(OperationType.DIY_OPERATION); 32 | } 33 | 34 | @Override 35 | public byte[] toBytes() { 36 | byte[] feeBytes = fee.toBytes(); 37 | byte[] payerBytes = payer.toBytes(); 38 | byte[] requireAuthsBytes = requiredAuths.toBytes(); 39 | byte[] idBytes = Varint.writeUnsignedSize(d); 40 | byte[] dataPrefix = Varint.writeUnsignedVarInt(data.getBytes().length); 41 | byte[] dataBytes = data.getBytes(); 42 | return Bytes.concat(feeBytes, payerBytes, requireAuthsBytes, idBytes, dataPrefix, dataBytes); 43 | } 44 | 45 | @Override 46 | public String toJsonString() { 47 | return toJsonObject().toString(); 48 | } 49 | 50 | @Override 51 | public JsonElement toJsonObject() { 52 | JsonArray array = new JsonArray(); 53 | array.add(this.getId()); 54 | JsonObject jsonObject = new JsonObject(); 55 | if (fee != null) 56 | jsonObject.add("fee", fee.toJsonObject()); 57 | jsonObject.addProperty("payer", payer.getObjectId()); 58 | jsonObject.add("required_auths", new JsonArray()); 59 | jsonObject.addProperty("id", d); 60 | jsonObject.addProperty("data", Util.bytesToHex(data.getBytes())); 61 | array.add(jsonObject); 62 | return array; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/LimitOrderCancelOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.gxchain.client.graphenej.enums.OperationType; 8 | import com.gxchain.client.graphenej.objects.AssetAmount; 9 | import com.gxchain.client.graphenej.objects.LimitOrder; 10 | import com.gxchain.client.graphenej.objects.UserAccount; 11 | 12 | /** 13 | * Created by nelson on 3/21/17. 14 | */ 15 | public class LimitOrderCancelOperation extends BaseOperation { 16 | 17 | // Constants used in the JSON representation 18 | public static final String KEY_FEE_PAYING_ACCOUNT = "fee_paying_account"; 19 | public static final String KEY_ORDER_ID = "order"; 20 | 21 | 22 | public LimitOrderCancelOperation(LimitOrder order, UserAccount feePayingAccount) { 23 | super(OperationType.LIMIT_ORDER_CANCEL_OPERATION); 24 | this.order = order; 25 | this.feePayingAccount = feePayingAccount; 26 | } 27 | 28 | // Inner fields of a limit order cancel operation 29 | private AssetAmount fee; 30 | private UserAccount feePayingAccount; 31 | private LimitOrder order; 32 | 33 | @Override 34 | public String toJsonString() { 35 | return null; 36 | } 37 | 38 | @Override 39 | public JsonElement toJsonObject() { 40 | JsonArray array = (JsonArray) super.toJsonObject(); 41 | JsonObject jsonObject = new JsonObject(); 42 | if(fee != null) 43 | jsonObject.add(KEY_FEE, fee.toJsonObject()); 44 | jsonObject.addProperty(KEY_FEE_PAYING_ACCOUNT, feePayingAccount.getObjectId()); 45 | jsonObject.addProperty(KEY_ORDER_ID, order.getObjectId()); 46 | jsonObject.add(KEY_EXTENSIONS, new JsonArray()); 47 | array.add(jsonObject); 48 | return array; 49 | } 50 | 51 | @Override 52 | public void setFee(AssetAmount assetAmount) { 53 | this.fee = assetAmount; 54 | } 55 | 56 | @Override 57 | public byte[] toBytes() { 58 | byte[] feeBytes = this.fee.toBytes(); 59 | byte[] feePayingAccountBytes = this.feePayingAccount.toBytes(); 60 | byte[] orderIdBytes = this.order.toBytes(); 61 | byte[] extensions = this.extensions.toBytes(); 62 | return Bytes.concat(feeBytes, feePayingAccountBytes, orderIdBytes, extensions); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/TransferOperation.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.google.common.primitives.Bytes; 4 | import com.google.gson.*; 5 | import com.gxchain.client.graphenej.enums.OperationType; 6 | import com.gxchain.client.graphenej.objects.AssetAmount; 7 | import com.gxchain.client.graphenej.objects.Memo; 8 | import com.gxchain.client.graphenej.objects.UserAccount; 9 | 10 | import java.lang.reflect.Type; 11 | 12 | /** 13 | * Class used to encapsulate the TransferOperation operation related functionalities. 14 | */ 15 | public class TransferOperation extends BaseOperation { 16 | public static final String KEY_AMOUNT = "amount"; 17 | public static final String KEY_FROM = "from"; 18 | public static final String KEY_TO = "to"; 19 | public static final String KEY_MEMO = "memo"; 20 | 21 | private AssetAmount fee; 22 | private AssetAmount amount; 23 | private UserAccount from; 24 | private UserAccount to; 25 | private Memo memo; 26 | 27 | public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount, AssetAmount fee) { 28 | super(OperationType.TRANSFER_OPERATION); 29 | this.from = from; 30 | this.to = to; 31 | this.amount = transferAmount; 32 | this.fee = fee; 33 | this.memo = new Memo(); 34 | } 35 | 36 | public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount) { 37 | super(OperationType.TRANSFER_OPERATION); 38 | this.from = from; 39 | this.to = to; 40 | this.amount = transferAmount; 41 | this.memo = new Memo(); 42 | } 43 | 44 | public TransferOperation(AssetAmount fee, AssetAmount amount, UserAccount from, UserAccount to, Memo memo) { 45 | super(OperationType.TRANSFER_OPERATION); 46 | this.fee = fee; 47 | this.amount = amount; 48 | this.from = from; 49 | this.to = to; 50 | this.memo = memo; 51 | } 52 | 53 | public UserAccount getFrom() { 54 | return this.from; 55 | } 56 | 57 | public UserAccount getTo() { 58 | return this.to; 59 | } 60 | 61 | public AssetAmount getAssetAmount() { 62 | return this.amount; 63 | } 64 | 65 | public AssetAmount getFee() { 66 | return this.fee; 67 | } 68 | 69 | public void setAssetAmount(AssetAmount assetAmount) { 70 | this.amount = assetAmount; 71 | } 72 | 73 | public void setFrom(UserAccount from) { 74 | this.from = from; 75 | } 76 | 77 | public void setTo(UserAccount to) { 78 | this.to = to; 79 | } 80 | 81 | public void setMemo(Memo memo) { 82 | this.memo = memo; 83 | } 84 | 85 | public Memo getMemo() { 86 | return this.memo; 87 | } 88 | 89 | @Override 90 | public void setFee(AssetAmount newFee) { 91 | this.fee = newFee; 92 | } 93 | 94 | @Override 95 | public byte[] toBytes() { 96 | byte[] feeBytes = fee.toBytes(); 97 | byte[] fromBytes = from.toBytes(); 98 | byte[] toBytes = to.toBytes(); 99 | byte[] amountBytes = amount.toBytes(); 100 | byte[] memoBytes = memo.toBytes(); 101 | byte[] extensions = this.extensions.toBytes(); 102 | return Bytes.concat(feeBytes, fromBytes, toBytes, amountBytes, memoBytes, extensions); 103 | } 104 | 105 | @Override 106 | public String toJsonString() { 107 | //TODO: Evaluate using simple Gson class to return a simple string representation and drop the TransferSerializer class 108 | GsonBuilder gsonBuilder = new GsonBuilder(); 109 | gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferSerializer()); 110 | return gsonBuilder.create().toJson(this); 111 | } 112 | 113 | @Override 114 | public JsonElement toJsonObject() { 115 | JsonArray array = new JsonArray(); 116 | array.add(this.getId()); 117 | JsonObject jsonObject = new JsonObject(); 118 | if (fee != null) 119 | jsonObject.add(KEY_FEE, fee.toJsonObject()); 120 | jsonObject.addProperty(KEY_FROM, from.getObjectId()); 121 | jsonObject.addProperty(KEY_TO, to.getObjectId()); 122 | jsonObject.add(KEY_AMOUNT, amount.toJsonObject()); 123 | if (memo != null && memo.toJsonObject() != null) { 124 | jsonObject.add(KEY_MEMO, memo.toJsonObject()); 125 | } 126 | jsonObject.add(KEY_EXTENSIONS, new JsonArray()); 127 | array.add(jsonObject); 128 | return array; 129 | } 130 | 131 | public static class TransferSerializer implements JsonSerializer { 132 | 133 | @Override 134 | public JsonElement serialize(TransferOperation transfer, Type type, JsonSerializationContext jsonSerializationContext) { 135 | // JsonArray arrayRep = new JsonArray(); 136 | // arrayRep.add(transfer.getId()); 137 | // arrayRep.add(transfer.toJsonObject()); 138 | // return arrayRep; 139 | return transfer.toJsonObject(); 140 | } 141 | } 142 | 143 | /** 144 | * This deserializer will work on any transfer operation serialized in the 'array form' used a lot in 145 | * the Graphene Blockchain API. 146 | *

147 | * An example of this serialized form is the following: 148 | *

149 | * [ 150 | * 0, 151 | * { 152 | * "fee": { 153 | * "amount": 264174, 154 | * "asset_id": "1.3.0" 155 | * }, 156 | * "from": "1.2.138632", 157 | * "to": "1.2.129848", 158 | * "amount": { 159 | * "amount": 100, 160 | * "asset_id": "1.3.0" 161 | * }, 162 | * "extensions": [] 163 | * } 164 | * ] 165 | *

166 | * It will convert this data into a nice TransferOperation object. 167 | */ 168 | public static class TransferDeserializer implements JsonDeserializer { 169 | 170 | @Override 171 | public TransferOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 172 | if (json.isJsonArray()) { 173 | // This block is used just to check if we are in the first step of the deserialization 174 | // when we are dealing with an array. 175 | JsonArray serializedTransfer = json.getAsJsonArray(); 176 | if (serializedTransfer.get(0).getAsInt() != OperationType.TRANSFER_OPERATION.getCode()) { 177 | // If the operation type does not correspond to a transfer operation, we return null 178 | return null; 179 | } else { 180 | // Calling itself recursively, this is only done once, so there will be no problems. 181 | return context.deserialize(serializedTransfer.get(1), TransferOperation.class); 182 | } 183 | } else { 184 | // This block is called in the second recursion and takes care of deserializing the 185 | // transfer data itself. 186 | JsonObject jsonObject = json.getAsJsonObject(); 187 | 188 | // Deserializing AssetAmount objects 189 | AssetAmount amount = context.deserialize(jsonObject.get(KEY_AMOUNT), AssetAmount.class); 190 | AssetAmount fee = context.deserialize(jsonObject.get(KEY_FEE), AssetAmount.class); 191 | 192 | // Deserializing UserAccount objects 193 | UserAccount from = new UserAccount(jsonObject.get(KEY_FROM).getAsString()); 194 | UserAccount to = new UserAccount(jsonObject.get(KEY_TO).getAsString()); 195 | TransferOperation transfer = new TransferOperation(from, to, amount, fee); 196 | 197 | // If the transfer had a memo, deserialize it 198 | if (jsonObject.has(KEY_MEMO)) { 199 | Memo memo = context.deserialize(jsonObject.get(KEY_MEMO), Memo.class); 200 | transfer.setMemo(memo); 201 | } 202 | 203 | return transfer; 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/operations/TransferOperationBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.operations; 2 | 3 | import com.gxchain.client.graphenej.errors.MalformedOperationException; 4 | import com.gxchain.client.graphenej.objects.AssetAmount; 5 | import com.gxchain.client.graphenej.objects.Memo; 6 | import com.gxchain.client.graphenej.objects.UserAccount; 7 | 8 | /** 9 | * Factory class used to build a transfer operation 10 | */ 11 | public class TransferOperationBuilder extends BaseOperationBuilder { 12 | private UserAccount from; 13 | private UserAccount to; 14 | private AssetAmount transferAmount; 15 | private AssetAmount fee; 16 | private Memo memo; 17 | 18 | public TransferOperationBuilder setSource(UserAccount from) { 19 | this.from = from; 20 | return this; 21 | } 22 | 23 | public TransferOperationBuilder setDestination(UserAccount to) { 24 | this.to = to; 25 | return this; 26 | } 27 | 28 | public TransferOperationBuilder setTransferAmount(AssetAmount transferAmount) { 29 | this.transferAmount = transferAmount; 30 | return this; 31 | } 32 | 33 | public TransferOperationBuilder setFee(AssetAmount fee) { 34 | this.fee = fee; 35 | return this; 36 | } 37 | 38 | public TransferOperationBuilder setMemo(Memo memo) { 39 | this.memo = memo; 40 | return this; 41 | } 42 | 43 | @Override 44 | public TransferOperation build(){ 45 | TransferOperation transferOperation; 46 | if(from == null ){ 47 | throw new MalformedOperationException("Missing source account information"); 48 | }else if(to == null){ 49 | throw new MalformedOperationException("Missing destination account information"); 50 | }else if(transferAmount == null){ 51 | throw new MalformedOperationException("Missing transfer amount information"); 52 | } 53 | if(fee != null){ 54 | transferOperation = new TransferOperation(from, to, transferAmount, fee); 55 | }else{ 56 | transferOperation = new TransferOperation(from, to, transferAmount); 57 | } 58 | if(memo != null){ 59 | transferOperation.setMemo(this.memo); 60 | } 61 | return transferOperation; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/graphenej/test/NaiveSSLContext.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.graphenej.test; 2 | 3 | /* 4 | * Copyright (C) 2015 Neo Visionaries Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 15 | * either express or implied. See the License for the specific 16 | * language governing permissions and limitations under the 17 | * License. 18 | */ 19 | import javax.net.ssl.SSLContext; 20 | import javax.net.ssl.TrustManager; 21 | import javax.net.ssl.X509TrustManager; 22 | import java.security.KeyManagementException; 23 | import java.security.NoSuchAlgorithmException; 24 | import java.security.NoSuchProviderException; 25 | import java.security.Provider; 26 | import java.security.cert.X509Certificate; 27 | 28 | 29 | /** 30 | * A factory class which creates an {@link SSLContext} that 31 | * naively accepts all certificates without verification. 32 | * 33 | *

 34 |  * // Create an SSL context that naively accepts all certificates.
 35 |  * SSLContext context = NaiveSSLContext.getInstance("TLS");
 36 |  *
 37 |  * // Create a socket factory from the SSL context.
 38 |  * SSLSocketFactory factory = context.getSocketFactory();
 39 |  *
 40 |  * // Create a socket from the socket factory.
 41 |  * SSLSocket socket = factory.createSocket("www.example.com", 443);
 42 |  * 
43 | * 44 | * @author Takahiko Kawasaki 45 | */ 46 | public class NaiveSSLContext 47 | { 48 | private NaiveSSLContext() 49 | { 50 | } 51 | 52 | 53 | /** 54 | * Get an SSLContext that implements the specified secure 55 | * socket protocol and naively accepts all certificates 56 | * without verification. 57 | */ 58 | public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException 59 | { 60 | return init(SSLContext.getInstance(protocol)); 61 | } 62 | 63 | 64 | /** 65 | * Get an SSLContext that implements the specified secure 66 | * socket protocol and naively accepts all certificates 67 | * without verification. 68 | */ 69 | public static SSLContext getInstance(String protocol, Provider provider) throws NoSuchAlgorithmException 70 | { 71 | return init(SSLContext.getInstance(protocol, provider)); 72 | } 73 | 74 | 75 | /** 76 | * Get an SSLContext that implements the specified secure 77 | * socket protocol and naively accepts all certificates 78 | * without verification. 79 | */ 80 | public static SSLContext getInstance(String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException 81 | { 82 | return init(SSLContext.getInstance(protocol, provider)); 83 | } 84 | 85 | 86 | /** 87 | * Set NaiveTrustManager to the given context. 88 | */ 89 | private static SSLContext init(SSLContext context) 90 | { 91 | try 92 | { 93 | // Set NaiveTrustManager. 94 | context.init(null, new TrustManager[] { new NaiveTrustManager() }, null); 95 | } 96 | catch (KeyManagementException e) 97 | { 98 | throw new RuntimeException("Failed to initialize an SSLContext.", e); 99 | } 100 | 101 | return context; 102 | } 103 | 104 | 105 | /** 106 | * A {@link TrustManager} which trusts all certificates naively. 107 | */ 108 | private static class NaiveTrustManager implements X509TrustManager 109 | { 110 | @Override 111 | public X509Certificate[] getAcceptedIssuers() 112 | { 113 | return null; 114 | } 115 | 116 | 117 | public void checkClientTrusted(X509Certificate[] certs, String authType) 118 | { 119 | } 120 | 121 | 122 | public void checkServerTrusted(X509Certificate[] certs, String authType) 123 | { 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/GXChainApiRestClient.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.rpc; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.gxchain.client.domian.params.GetTableRowsParams; 7 | import com.gxchain.client.graphenej.models.AccountProperties; 8 | import com.gxchain.client.graphenej.models.Block; 9 | import com.gxchain.client.graphenej.models.DynamicGlobalProperties; 10 | import com.gxchain.client.graphenej.models.contract.Abi; 11 | import com.gxchain.client.graphenej.models.contract.ContractAccountProperties; 12 | import com.gxchain.client.graphenej.models.contract.Table; 13 | import com.gxchain.client.graphenej.objects.Asset; 14 | import com.gxchain.client.graphenej.objects.AssetAmount; 15 | import com.gxchain.client.graphenej.operations.BaseOperation; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * @author liruobin 21 | * @since 2018/7/5 上午10:39 22 | */ 23 | public interface GXChainApiRestClient { 24 | 25 | ////////////////////// 26 | /// chain api 27 | ///////////////////// 28 | /** 29 | * 链上查询 30 | * 31 | * @param method 方法名 32 | * @param params 参数 33 | * @return JsonElement 34 | */ 35 | JsonElement query(String method, JsonArray params); 36 | 37 | /** 38 | * 查询gxchain chainId 39 | * 40 | * @return 41 | */ 42 | String getChainId(); 43 | 44 | /** 45 | * 查询全局动态参数 46 | * 47 | * @return 48 | */ 49 | DynamicGlobalProperties getDynamicGlobalProperties(); 50 | 51 | /** 52 | * 根据区块高度获取区块信息 53 | * 54 | * @param blockHeight 区块高度 55 | * @return 56 | */ 57 | Block getBlock(long blockHeight); 58 | 59 | /** 60 | * 查询oject 61 | * 1.2.* 账号 62 | * 1.3.* 资产 63 | * 2.1.0 最新区块 64 | * 65 | * @param objectIds 66 | * @return 67 | */ 68 | JsonElement getObjects(List objectIds); 69 | 70 | /////////////////// 71 | ////account api 72 | ////////////////// 73 | 74 | /** 75 | * 查询账户余额 76 | * 77 | * @param accountId 账户id 78 | * @param assetIds 资产id list 79 | * @return 80 | */ 81 | List getAccountBalances(String accountId, List assetIds); 82 | 83 | /** 84 | * 根据名称获取公链账户信息 85 | * 86 | * @param accountName 87 | * @return 88 | */ 89 | AccountProperties getAccountByName(String accountName); 90 | 91 | /** 92 | * 根据名称获取智能合约账户信息 93 | * @param accountName 94 | * @return 95 | */ 96 | ContractAccountProperties getContractAccountByName(String accountName); 97 | 98 | /** 99 | * 根据公钥查询账户id 100 | * @param publicKey 公钥 101 | * @return 账户id列表 102 | */ 103 | List getAccountByPublicKey(String publicKey); 104 | 105 | /** 106 | * 根据accountId查询公链账户信息 107 | * 108 | * @param accountIds 109 | * @return 110 | */ 111 | List getAccounts(List accountIds); 112 | 113 | /** 114 | * 获取交易费率 115 | * 116 | * @param operations 交易操作 117 | * @param feeAsset 费用资产 118 | */ 119 | List getRequiredFees(List operations, Asset feeAsset); 120 | 121 | /** 122 | * 根据资产标识获取资产信息 123 | * @param symbols [GXC] 124 | * @return 125 | */ 126 | List getAssets(List symbols); 127 | 128 | /** 129 | * get contract abi by contract_name 130 | * @param contractName 131 | * @return 132 | */ 133 | Abi getContractABI(String contractName); 134 | 135 | /** 136 | * get contract table by contract_name 137 | * @param contractName 138 | * @return 139 | */ 140 | List
getContractTable(String contractName); 141 | 142 | /** 143 | * 查询智能合约表数据 144 | * @param contractName 合约名称 145 | * @param tableName 表名 146 | * @param lowerBound 查询时指定的key最小值, 默认为0 147 | * @param upperBound 查询时指定的key最大值,默认为-1,即最大的无符号整形 148 | * @return 149 | */ 150 | JsonElement getTableRows(String contractName, String tableName, Number lowerBound, Number upperBound); 151 | 152 | /** 153 | * 查询智能合约表数据(扩展) 154 | * @param contractName 合约名称 155 | * @param tableName 表名 156 | * @param getTableRowsParams 查询扩展字段 157 | * @return 158 | */ 159 | JsonElement getTableRowsEx(String contractName, String tableName, GetTableRowsParams getTableRowsParams); 160 | 161 | /** 162 | * 广播交易 163 | * 广播成功后立即返回,耗时短 164 | * @param transaction 165 | * @return 166 | */ 167 | JsonElement broadcast(JsonObject transaction); 168 | 169 | /** 170 | * 广播交易 171 | * 打包成功后返回,不过被打包的也有可能被回滚,耗时长 172 | * @param transaction 173 | * @return 174 | */ 175 | JsonElement broadcastSynchronous(JsonObject transaction); 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/GXChainClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.rpc; 2 | 3 | 4 | import com.gxchain.client.rpc.impl.GXChainApiRestClientImpl; 5 | 6 | /** 7 | * gxchain client 工厂类 8 | * 9 | * @author liruobin 10 | * @since 2018/7/3 上午10:15 11 | */ 12 | public class GXChainClientFactory { 13 | private static GXChainClientFactory clientFactory = new GXChainClientFactory(); 14 | 15 | public static GXChainClientFactory getInstance() { 16 | return clientFactory; 17 | } 18 | /** 19 | * 创建http client 20 | * @param url 21 | * @return 22 | */ 23 | public GXChainApiRestClient newRestClient(String url) { 24 | return new GXChainApiRestClientImpl(url); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/api/GXChainApiService.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.rpc.api; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.gxchain.client.graphenej.models.ApiCall; 5 | import com.gxchain.client.graphenej.models.WitnessResponse; 6 | import retrofit2.Call; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.POST; 9 | 10 | /** 11 | * @author liruobin 12 | * @since 2018/7/5 上午10:33 13 | */ 14 | public interface GXChainApiService { 15 | @POST("/rpc") 16 | Call> call(@Body ApiCall apiCall); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/api/GxbApiFactory.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.rpc.api; 2 | 3 | 4 | import com.gxchain.client.exception.HttpAccessFailException; 5 | import okhttp3.HttpUrl; 6 | import okhttp3.Interceptor; 7 | import okhttp3.OkHttpClient; 8 | import okhttp3.Response; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import retrofit2.Retrofit; 12 | 13 | import java.io.IOException; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | 18 | public class GxbApiFactory { 19 | private static final Logger logger = LoggerFactory.getLogger(GxbApiFactory.class); 20 | private ConcurrentHashMap, Object> typeCache = new ConcurrentHashMap<>(); 21 | 22 | private Retrofit retrofit; 23 | 24 | private GxbApiFactory(final String baseUrl, final Long timeout) { 25 | OkHttpClient httpClient = new OkHttpClient().newBuilder().readTimeout(timeout, TimeUnit.MILLISECONDS).addInterceptor(new Interceptor() { 26 | 27 | /* 28 | * 记录访问日志,统一接口异常处理 29 | * 30 | * @see okhttp3.Interceptor#intercept(okhttp3.Interceptor.Chain) 31 | */ 32 | @Override 33 | public Response intercept(Chain chain) throws IOException { 34 | logger.info("gxb api request:" + chain.request().toString()); 35 | long l1 = System.currentTimeMillis(); 36 | Response response = chain.proceed(chain.request()); 37 | logger.info("gxb response," + (System.currentTimeMillis() - l1) + "ms," + response.toString()); 38 | if (!response.isSuccessful()) { 39 | throw new HttpAccessFailException(response.body().string()); 40 | } else { 41 | return response; 42 | } 43 | } 44 | 45 | }).writeTimeout(timeout, TimeUnit.MILLISECONDS).build(); 46 | httpClient.dispatcher().setMaxRequestsPerHost(100); 47 | httpClient.dispatcher().setMaxRequests(150); 48 | 49 | retrofit = new Retrofit.Builder().baseUrl(HttpUrl.parse(baseUrl)).addConverterFactory(GxbGsonConverterFactory.create()).callFactory(httpClient).build(); 50 | } 51 | 52 | 53 | public T newApi(Class clz) { 54 | Object object = typeCache.get(clz); 55 | if (object != null) { 56 | return (T) object; 57 | } else { 58 | if (clz.isInterface()) { 59 | T result = retrofit.create(clz); 60 | typeCache.putIfAbsent(clz, result); 61 | return result; 62 | } else { 63 | throw new IllegalArgumentException("interface class required"); 64 | } 65 | } 66 | } 67 | 68 | public static Builder builder() { 69 | return new Builder(); 70 | } 71 | 72 | public static class Builder { 73 | private String baseUrl; 74 | private Long timeout = 15000L; // default 15s; 75 | 76 | public Builder baseUrl(String url) { 77 | this.baseUrl = url; 78 | return this; 79 | } 80 | 81 | public Builder timeout(Long ms) { 82 | this.timeout = ms; 83 | return this; 84 | } 85 | 86 | public GxbApiFactory build() { 87 | if (baseUrl == null || baseUrl.isEmpty()) { 88 | throw new IllegalArgumentException("baseUrl invalid"); 89 | } 90 | return new GxbApiFactory(this.baseUrl, timeout); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/api/GxbGsonConverterFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The 3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any 4 | * incorporation of the same into any other material in any media or format of any kind is strictly 5 | * prohibited. All rights are reserved. 6 | * 7 | * Copyright (c) gxb.io Inc. 2017 8 | */ 9 | package com.gxchain.client.rpc.api; 10 | 11 | import com.google.gson.Gson; 12 | import com.google.gson.TypeAdapter; 13 | import com.google.gson.reflect.TypeToken; 14 | import com.gxchain.client.util.GXGsonUtil; 15 | import okhttp3.RequestBody; 16 | import okhttp3.ResponseBody; 17 | import retrofit2.Converter; 18 | import retrofit2.Retrofit; 19 | 20 | import java.lang.annotation.Annotation; 21 | import java.lang.reflect.Type; 22 | 23 | public class GxbGsonConverterFactory extends Converter.Factory { 24 | 25 | private final Gson gson; 26 | 27 | private GxbGsonConverterFactory(Gson gson) { 28 | if (gson == null) throw new NullPointerException("gson == null"); 29 | this.gson = gson; 30 | } 31 | 32 | public static GxbGsonConverterFactory create() { 33 | return create(GXGsonUtil.getGson()); 34 | } 35 | 36 | public static GxbGsonConverterFactory create(Gson gson) { 37 | return new GxbGsonConverterFactory(gson); 38 | } 39 | 40 | @Override 41 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { 42 | TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); 43 | return new GxbGsonResponseBodyConverter(gson, adapter); 44 | } 45 | 46 | @Override 47 | public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, 48 | Retrofit retrofit) { 49 | TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); 50 | return new GxbGsonRequestBodyConverter(gson, adapter); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/api/GxbGsonRequestBodyConverter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The 3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any 4 | * incorporation of the same into any other material in any media or format of any kind is strictly 5 | * prohibited. All rights are reserved. 6 | * 7 | * Copyright (c) gxb.io Inc. 2017 8 | */ 9 | package com.gxchain.client.rpc.api; 10 | 11 | import com.google.gson.Gson; 12 | import com.google.gson.TypeAdapter; 13 | import com.google.gson.stream.JsonWriter; 14 | import okhttp3.MediaType; 15 | import okhttp3.RequestBody; 16 | import okio.Buffer; 17 | import okio.ByteString; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import retrofit2.Converter; 21 | 22 | import java.io.IOException; 23 | import java.io.OutputStreamWriter; 24 | import java.io.Writer; 25 | import java.nio.charset.Charset; 26 | 27 | public class GxbGsonRequestBodyConverter implements Converter { 28 | private static final Logger logger = LoggerFactory.getLogger(GxbGsonRequestBodyConverter.class); 29 | 30 | private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); 31 | private static final Charset UTF_8 = Charset.forName("UTF-8"); 32 | 33 | private final Gson gson; 34 | private final TypeAdapter adapter; 35 | 36 | GxbGsonRequestBodyConverter(Gson gson, TypeAdapter adapter) { 37 | this.gson = gson; 38 | this.adapter = adapter; 39 | } 40 | 41 | @Override 42 | public RequestBody convert(T value) throws IOException { 43 | Buffer buffer = new Buffer(); 44 | Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); 45 | JsonWriter jsonWriter = gson.newJsonWriter(writer); 46 | adapter.write(jsonWriter, value); 47 | jsonWriter.close(); 48 | ByteString requestBody = buffer.readByteString(); 49 | logger.debug("get GxbGsonRequestBody: {}", requestBody.string(UTF_8)); 50 | 51 | return RequestBody.create(MEDIA_TYPE, requestBody); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/rpc/api/GxbGsonResponseBodyConverter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This document and its contents are protected by copyright 2017 and owned by gxb.io Inc. The 3 | * copying and reproduction of this document and/or its content (whether wholly or partly) or any 4 | * incorporation of the same into any other material in any media or format of any kind is strictly 5 | * prohibited. All rights are reserved. 6 | * 7 | * Copyright (c) gxb.io Inc. 2017 8 | */ 9 | package com.gxchain.client.rpc.api; 10 | 11 | import com.google.gson.Gson; 12 | import com.google.gson.TypeAdapter; 13 | import com.google.gson.stream.JsonReader; 14 | import okhttp3.MediaType; 15 | import okhttp3.ResponseBody; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import retrofit2.Converter; 19 | 20 | import java.io.*; 21 | import java.nio.charset.Charset; 22 | 23 | 24 | public class GxbGsonResponseBodyConverter implements Converter { 25 | private static final Logger logger = LoggerFactory.getLogger(GxbGsonResponseBodyConverter.class); 26 | private final Gson gson; 27 | private final TypeAdapter adapter; 28 | 29 | GxbGsonResponseBodyConverter(Gson gson, TypeAdapter adapter) { 30 | this.gson = gson; 31 | this.adapter = adapter; 32 | } 33 | 34 | @Override 35 | public T convert(ResponseBody value) throws IOException { 36 | String response = value.string(); 37 | logger.debug("get GxbGsonResponseBody: {}", response); 38 | 39 | MediaType contentType = value.contentType(); 40 | Charset charset = contentType != null ? contentType.charset(Charset.forName("UTF-8")) : Charset.forName("UTF-8"); 41 | InputStream inputStream = new ByteArrayInputStream(response.getBytes()); 42 | Reader reader = new InputStreamReader(inputStream, charset); 43 | JsonReader jsonReader = gson.newJsonReader(reader); 44 | try { 45 | return adapter.read(jsonReader); 46 | } finally { 47 | value.close(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/util/BeanUtils.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.util; 2 | 3 | import net.sf.cglib.beans.BeanCopier; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | public final class BeanUtils { 9 | private BeanUtils() { 10 | 11 | } 12 | 13 | private static final Map beanCopierMap = new ConcurrentHashMap<>(); 14 | 15 | /** 16 | * 基于CGLIB的bean properties 的拷贝,性能要远优于{@code org.springframework.beans.BeanUtils.copyProperties} 17 | * 18 | * @param source 19 | * @param target 20 | */ 21 | public static void copyProperties(Object source, Object target) { 22 | if (source == null || target == null) { 23 | target = null; 24 | return; 25 | } 26 | 27 | String key = String.format("%s:%s", source.getClass().getName(), target.getClass().getName()); 28 | if (!beanCopierMap.containsKey(key)) { 29 | BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false); 30 | beanCopierMap.putIfAbsent(key, beanCopier); 31 | } 32 | BeanCopier beanCopier = beanCopierMap.get(key); 33 | beanCopier.copy(source, target, null); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/util/GXGsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.gxchain.client.graphenej.models.ApiCall; 6 | import com.gxchain.client.graphenej.models.DynamicGlobalProperties; 7 | import com.gxchain.client.graphenej.objects.*; 8 | import com.gxchain.client.graphenej.operations.AccountCreateOperation; 9 | import com.gxchain.client.graphenej.operations.TransferOperation; 10 | 11 | import java.lang.reflect.Type; 12 | 13 | /** 14 | * @author liruobin 15 | * @since 2018/7/3 下午4:48 16 | */ 17 | public class GXGsonUtil { 18 | private static GsonBuilder builder = new GsonBuilder(); 19 | 20 | static { 21 | builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()); 22 | builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()); 23 | builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); 24 | builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer()); 25 | builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); 26 | builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); 27 | builder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer()); 28 | builder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer()); 29 | builder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer()); 30 | builder.registerTypeAdapter(ApiCall.class, new ApiCall.ApiCallSerializer()); 31 | builder.registerTypeAdapter(AccountCreateOperation.class, new AccountCreateOperation.AccountCreateDeserializer()); 32 | } 33 | 34 | public static Gson getGson() { 35 | return builder.create(); 36 | } 37 | 38 | public static T fromJson(String json, Type typeOfT) { 39 | return getGson().fromJson(json, typeOfT); 40 | } 41 | 42 | public static T fromJson(String json, Class className) { 43 | return getGson().fromJson(json, className); 44 | } 45 | 46 | public static String toJson(Object src) { 47 | return getGson().toJson(src); 48 | } 49 | 50 | public static T mapJson(Object src, Class className) { 51 | return fromJson(toJson(src), className); 52 | } 53 | 54 | public static T mapJson(Object src, Type typeOfT) { 55 | return fromJson(toJson(src), typeOfT); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/gxchain/client/util/TxSerializerUtil.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.util; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.gxchain.client.exception.GXChainApiException; 6 | import com.gxchain.common.signature.utils.Util; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import javax.script.ScriptEngine; 10 | import javax.script.ScriptEngineManager; 11 | import java.io.*; 12 | 13 | /** 14 | * @author liruobin 15 | * @since 2019/2/14 5:27 PM 16 | */ 17 | @Slf4j 18 | public class TxSerializerUtil { 19 | private static ScriptEngine engine; 20 | 21 | static { 22 | log.info("init script engine"); 23 | ScriptEngineManager manager = new ScriptEngineManager(); 24 | engine = manager.getEngineByName("nashorn"); 25 | try { 26 | InputStream inputStream = TxSerializerUtil.class.getClassLoader().getResourceAsStream("js/tx_serializer.min.js"); 27 | StringBuilder script = new StringBuilder(); 28 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); 29 | String line = ""; 30 | while ((line = br.readLine()) != null) { 31 | script.append(line); 32 | } 33 | engine.eval(script.toString()); 34 | } catch (Exception e) { 35 | log.error(e.getMessage(), e); 36 | throw new GXChainApiException(e.getMessage()); 37 | } 38 | log.info("init script engine finish"); 39 | } 40 | 41 | public static byte[] serializeTransaction(JsonObject transaction) { 42 | try { 43 | log.debug("start serialize"); 44 | String result = (String) engine.eval("serializer.serializeTransaction(" + transaction.toString() + ").toString('hex')"); 45 | log.debug("serialize finished:{}", result); 46 | return Util.hexToBytes(result); 47 | } catch (Exception e) { 48 | log.error(e.getMessage(), e); 49 | throw new GXChainApiException(e.getMessage()); 50 | } 51 | } 52 | 53 | public static String serializeCallData(String methodName, JsonElement param, JsonElement abi) { 54 | try { 55 | String script = String.format("serializer.serializeCallData(\"%s\",%s,%s).toString('hex')", methodName, param == null ? new JsonObject().toString() : param.toString(), abi.toString()); 56 | return (String) engine.eval(script); 57 | } catch (Exception e) { 58 | log.error(e.getMessage(), e); 59 | throw new GXChainApiException(e.getMessage()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/gxchain/client/util/GXGsonUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.gxchain.client.util; 2 | 3 | import com.gxchain.client.graphenej.objects.Transaction; 4 | import org.junit.Test; 5 | 6 | /** 7 | * @author liruobin 8 | * @since 2019/6/4 9:31 PM 9 | */ 10 | public class GXGsonUtilTest { 11 | @Test 12 | public void transferDeserializerTest() { 13 | String hex = "{\"ref_block_num\": 33185,\n" + 14 | " \"ref_block_prefix\": 1487534235,\n" + 15 | " \"expiration\": \"2019-05-23T11:34:15\",\n" + 16 | " \"operations\": [\n" + 17 | " [\n" + 18 | " 0,\n" + 19 | " {\n" + 20 | " \"fee\": {\n" + 21 | " \"amount\": 1000,\n" + 22 | " \"asset_id\": \"1.3.1\"\n" + 23 | " },\n" + 24 | " \"from\": \"1.2.521\",\n" + 25 | " \"to\": \"1.2.2136\",\n" + 26 | " \"amount\": {\n" + 27 | " \"amount\": 110000,\n" + 28 | " \"asset_id\": \"1.3.1\"\n" + 29 | " },\n" + 30 | " \"extensions\": []\n" + 31 | " }\n" + 32 | " ]\n" + 33 | " ],\n" + 34 | " \"extensions\": [],\n" + 35 | " \"signatures\": [\n" + 36 | " \"1f56095fd3ed01eea62d609c01d8f00160b42e5d12de7d0b9538bce63b80b02a53504cd67b152cca1348f8678afdbaeee32461d6946d1bb4ef83d2cf4a03e1b539\"\n" + 37 | " ]}"; 38 | Transaction transaction = GXGsonUtil.fromJson(hex, Transaction.class); 39 | transaction.setChainId("4f7d07969c446f8342033acb3ab2ae5044cbe0fde93db02de75bd17fa8fd84b8"); 40 | 41 | System.out.println(transaction.toJsonObjectNoSign()); 42 | } 43 | 44 | @Test 45 | public void createAccountDeserializerTest() { 46 | String hex = "{\"ref_block_num\": 25100,\n" + 47 | " \"ref_block_prefix\": 2370734044,\n" + 48 | " \"expiration\": \"2019-06-05T02:55:12\",\n" + 49 | " \"operations\": [\n" + 50 | " [\n" + 51 | " 5,\n" + 52 | " {\n" + 53 | " \"fee\": {\n" + 54 | " \"amount\": 102,\n" + 55 | " \"asset_id\": \"1.3.1\"\n" + 56 | " },\n" + 57 | " \"registrar\": \"1.2.26\",\n" + 58 | " \"referrer\": \"1.2.26\",\n" + 59 | " \"referrer_percent\": 0,\n" + 60 | " \"name\": \"w13920644712\",\n" + 61 | " \"owner\": {\n" + 62 | " \"weight_threshold\": 1,\n" + 63 | " \"account_auths\": [],\n" + 64 | " \"key_auths\": [\n" + 65 | " [\n" + 66 | " \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" + 67 | " 1\n" + 68 | " ]\n" + 69 | " ],\n" + 70 | " \"address_auths\": []\n" + 71 | " },\n" + 72 | " \"active\": {\n" + 73 | " \"weight_threshold\": 1,\n" + 74 | " \"account_auths\": [],\n" + 75 | " \"key_auths\": [\n" + 76 | " [\n" + 77 | " \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" + 78 | " 1\n" + 79 | " ]\n" + 80 | " ],\n" + 81 | " \"address_auths\": []\n" + 82 | " },\n" + 83 | " \"options\": {\n" + 84 | " \"memo_key\": \"GXC6uQFDBDUYvR4mx3K4qTNSNJLckqozkXFKLAnYjJmhprJaC3PHL\",\n" + 85 | " \"voting_account\": \"1.2.5\",\n" + 86 | " \"num_witness\": 0,\n" + 87 | " \"num_committee\": 0,\n" + 88 | " \"votes\": [],\n" + 89 | " \"extensions\": []\n" + 90 | " },\n" + 91 | " \"extensions\": {}\n" + 92 | " }\n" + 93 | " ]\n" + 94 | " ],\n" + 95 | " \"extensions\": [],\n" + 96 | " \"signatures\": [\n" + 97 | " \"2029cab05cc3d032ee402092c4b6ccc7b20f6380aab2dca4763448d50e69e3581b7494ed1f92a9da8e7a0053766e7cb112f8aff324d7e6e86f9be78d374319c377\"\n" + 98 | " ]}"; 99 | Transaction transaction = GXGsonUtil.fromJson(hex, Transaction.class); 100 | transaction.setChainId("4f7d07969c446f8342033acb3ab2ae5044cbe0fde93db02de75bd17fa8fd84b8"); 101 | transaction.setPrivateKey("5J8CYVMcMz2f7vDc9fYcySbVUx7f3JnCJJVBeE6eWJmwZToTSqz"); 102 | System.out.println(transaction.toJsonObject()); 103 | } 104 | } --------------------------------------------------------------------------------