diff --git a/.gitignore b/.gitignore index 7b5bc56..18f0443 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -.gitignore -.idea/* -wavesj.iml +.idea +target +*.iml \ No newline at end of file diff --git a/examples/src/main/java/GRPCTest.java b/examples/src/main/java/GRPCTest.java new file mode 100644 index 0000000..b54e81d --- /dev/null +++ b/examples/src/main/java/GRPCTest.java @@ -0,0 +1,26 @@ +import com.wavesplatform.api.grpc.TransactionsApiOuterClass.*; +import com.wavesplatform.api.grpc.TransactionsApiGrpc; +import com.wavesplatform.protobuf.transaction.RecipientOuterClass; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; + +import java.util.Iterator; + +public class GRPCTest { + public static void main(String[] args) { + final ManagedChannel channel = ManagedChannelBuilder.forAddress("mainnet-aws-fr-3.wavesnodes.com", 6870) + .usePlaintext() + .build(); + + final TransactionsApiGrpc.TransactionsApiBlockingStub blockingStub = TransactionsApiGrpc.newBlockingStub(channel); + final TransactionsRequest transactionsRequest = TransactionsRequest.newBuilder() + .setRecipient(RecipientOuterClass.Recipient.newBuilder().setAlias("test_recipient").build()) + .build(); + + final Iterator transactions = blockingStub.getTransactions(transactionsRequest); + for (int i = 0; i < 1000 && transactions.hasNext(); i++) { + final TransactionResponse txResponse = transactions.next(); + System.out.printf("Transaction = %s, height = %d\n", txResponse.getTransaction(), txResponse.getHeight()); + } + } +} diff --git a/pom.xml b/pom.xml index f7e369e..d9fc1ff 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,55 @@ + + org.apache.maven.plugins + maven-toolchains-plugin + 1.0 + + + validate + + toolchain + + + + + + + [2.4,2.5) + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + true + + + + compile + test-compile + compile-custom + + + + + com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier} + + + + + + kr.motd.maven + os-maven-plugin + 1.6.0 + + @@ -177,5 +225,20 @@ 4.12 test + + io.grpc + grpc-netty-shaded + 1.20.0 + + + io.grpc + grpc-protobuf + 1.20.0 + + + io.grpc + grpc-stub + 1.20.0 + diff --git a/src/main/java/com/wavesplatform/wavesj/ByteUtils.java b/src/main/java/com/wavesplatform/wavesj/ByteUtils.java index f585866..f5a67c3 100644 --- a/src/main/java/com/wavesplatform/wavesj/ByteUtils.java +++ b/src/main/java/com/wavesplatform/wavesj/ByteUtils.java @@ -88,7 +88,7 @@ public static String putRecipient(ByteBuffer buffer, byte chainId, String recipi } public static String hash(byte[] bytes) { - return Base58.encode(Hash.hash(bytes, 0, bytes.length, Hash.BLAKE2B256)); + return Base58.encode(Hash.blake2b(bytes, 0, bytes.length)); } public static String sign(PrivateKeyAccount account, ByteBuffer buffer) { diff --git a/src/main/java/com/wavesplatform/wavesj/Hash.java b/src/main/java/com/wavesplatform/wavesj/Hash.java index e05efa4..2d58b30 100644 --- a/src/main/java/com/wavesplatform/wavesj/Hash.java +++ b/src/main/java/com/wavesplatform/wavesj/Hash.java @@ -6,35 +6,43 @@ import org.bouncycastle.crypto.digests.SHA256Digest; public class Hash { - public static final ThreadLocal BLAKE2B256 = new ThreadLocal(); - public static final ThreadLocal KECCAK256 = new ThreadLocal(); - public static final ThreadLocal SHA256 = new ThreadLocal(); + private static final ThreadLocal BLAKE2B256 = new ThreadLocal(); + private static final ThreadLocal KECCAK256 = new ThreadLocal(); + private static final ThreadLocal SHA256 = new ThreadLocal(); + + public static byte[] secureHash(byte[] message, int ofs, int len) { + final byte[] blake2b = hash(message, ofs, len, Hash.BLAKE2B256); + return hash(blake2b, 0, blake2b.length, Hash.KECCAK256); + } + + public static byte[] blake2b(byte[] message, int ofs, int len) { + return hash(message, ofs, len, Hash.BLAKE2B256); + } + + public static byte[] sha256(byte[] message, int ofs, int len) { + return hash(message, ofs, len, Hash.SHA256); + } private static Digest digest(ThreadLocal cache) { - Digest d = cache.get(); - if (d == null) { + Digest digest = cache.get(); + if (digest == null) { if (cache == BLAKE2B256) { - d = new Blake2bDigest(256); + digest = new Blake2bDigest(256); } else if (cache == KECCAK256) { - d = new KeccakDigest(256); + digest = new KeccakDigest(256); } else if (cache == SHA256) { - d = new SHA256Digest(); + digest = new SHA256Digest(); } - cache.set(d); + cache.set(digest); } - return d; - } - - protected static byte[] hash(byte[] message, int ofs, int len, ThreadLocal alg) { - Digest d = digest(alg); - byte[] res = new byte[d.getDigestSize()]; - d.update(message, ofs, len); - d.doFinal(res, 0); - return res; + return digest; } - public static byte[] secureHash(byte[] message, int ofs, int len) { - byte[] blake2b = hash(message, ofs, len, Hash.BLAKE2B256); - return hash(blake2b, 0, blake2b.length, Hash.KECCAK256); + private static byte[] hash(byte[] message, int ofs, int len, ThreadLocal alg) { + final Digest digest = digest(alg); + final byte[] result = new byte[digest.getDigestSize()]; + digest.update(message, ofs, len); + digest.doFinal(result, 0); + return result; } } diff --git a/src/main/java/com/wavesplatform/wavesj/PrivateKeyAccount.java b/src/main/java/com/wavesplatform/wavesj/PrivateKeyAccount.java index 3c46460..7585fc7 100644 --- a/src/main/java/com/wavesplatform/wavesj/PrivateKeyAccount.java +++ b/src/main/java/com/wavesplatform/wavesj/PrivateKeyAccount.java @@ -8,8 +8,6 @@ import java.security.SecureRandom; import java.util.Arrays; -import static com.wavesplatform.wavesj.Hash.*; - public class PrivateKeyAccount extends PublicKeyAccount { private static final Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST); @@ -250,7 +248,7 @@ public String sign(byte[] bytes) { public static String generateSeed() { byte[] bytes = new byte[21]; new SecureRandom().nextBytes(bytes); - byte[] rhash = hash(bytes, 0, 20, SHA256); + byte[] rhash = Hash.sha256(bytes, 0, 20); bytes[20] = rhash[0]; BigInteger rand = new BigInteger(bytes); BigInteger mask = new BigInteger(new byte[]{0, 0, 7, -1}); // 11 lower bits @@ -267,10 +265,10 @@ private static byte[] privateKey(String seed, int nonce) { // account seed from seed & nonce ByteBuffer buf = ByteBuffer.allocate(seed.getBytes().length + 4); buf.putInt(nonce).put(seed.getBytes()); - byte[] accountSeed = secureHash(buf.array(), 0, buf.array().length); + byte[] accountSeed = Hash.secureHash(buf.array(), 0, buf.array().length); // private key from account seed & chainId - byte[] hashedSeed = hash(accountSeed, 0, accountSeed.length, SHA256); + byte[] hashedSeed = Hash.sha256(accountSeed, 0, accountSeed.length); byte[] privateKey = Arrays.copyOf(hashedSeed, 32); privateKey[0] &= 248; privateKey[31] &= 127; diff --git a/src/main/java/com/wavesplatform/wavesj/PublicKeyAccount.java b/src/main/java/com/wavesplatform/wavesj/PublicKeyAccount.java index 68a65e3..624da53 100644 --- a/src/main/java/com/wavesplatform/wavesj/PublicKeyAccount.java +++ b/src/main/java/com/wavesplatform/wavesj/PublicKeyAccount.java @@ -3,8 +3,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import static com.wavesplatform.wavesj.Hash.secureHash; - public class PublicKeyAccount implements Account { private final byte chainId; @@ -35,9 +33,9 @@ public final byte getChainId() { private static byte[] address(byte[] publicKey, byte chainId) { ByteBuffer buf = ByteBuffer.allocate(26); - byte[] hash = secureHash(publicKey, 0, publicKey.length); + byte[] hash = Hash.secureHash(publicKey, 0, publicKey.length); buf.put((byte) 1).put((byte) chainId).put(hash, 0, 20); - byte[] checksum = secureHash(buf.array(), 0, 22); + byte[] checksum = Hash.secureHash(buf.array(), 0, 22); buf.put(checksum, 0, 4); return buf.array(); } diff --git a/src/main/java/com/wavesplatform/wavesj/protobuf/PBTransactions.java b/src/main/java/com/wavesplatform/wavesj/protobuf/PBTransactions.java new file mode 100644 index 0000000..3b96d4a --- /dev/null +++ b/src/main/java/com/wavesplatform/wavesj/protobuf/PBTransactions.java @@ -0,0 +1,418 @@ +package com.wavesplatform.wavesj.protobuf; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Empty; +import com.wavesplatform.protobuf.transaction.RecipientOuterClass; +import com.wavesplatform.protobuf.transaction.ScriptOuterClass; +import com.wavesplatform.protobuf.transaction.TransactionOuterClass; +import com.wavesplatform.wavesj.*; +import com.wavesplatform.wavesj.matcher.Order; +import com.wavesplatform.wavesj.matcher.OrderV1; +import com.wavesplatform.wavesj.matcher.OrderV2; +import com.wavesplatform.wavesj.transactions.*; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings({"unused", "WeakerAccess"}) +public class PBTransactions { + public static Transaction toVanilla(final TransactionOuterClass.SignedTransaction signedTransaction) { + final TransactionOuterClass.Transaction tx = signedTransaction.getTransaction(); + if (tx.getVersion() != 1 && tx.getVersion() != 2) + throw new IllegalArgumentException("TX version not supported: " + tx); + + final PublicKeyAccount senderPublicKey = new PublicKeyAccount(tx.getSenderPublicKey().toByteArray(), (byte) tx.getChainId()); + final long feeAmount = tx.getFee().getAmount(); + final long timestamp = tx.getTimestamp(); + final com.wavesplatform.wavesj.ByteString signature = toSignature(signedTransaction.getProofsList()); + final List proofs = toVanillaProofs(signedTransaction.getProofsList()); + + if (tx.hasBurn()) { + final TransactionOuterClass.BurnTransactionData burn = tx.getBurn(); + switch (tx.getVersion()) { + case Transaction.V1: + return new BurnTransactionV1(senderPublicKey, toVanillaAssetId(burn.getAssetAmount().getAssetId()), burn.getAssetAmount().getAmount(), feeAmount, timestamp, signature); + + case Transaction.V2: + return new BurnTransactionV2(senderPublicKey, (byte) tx.getChainId(), toVanillaAssetId(burn.getAssetAmount().getAssetId()), burn.getAssetAmount().getAmount(), feeAmount, timestamp, proofs); + } + } else if (tx.hasCreateAlias()) { + final TransactionOuterClass.CreateAliasTransactionData createAlias = tx.getCreateAlias(); + switch (tx.getVersion()) { + case Transaction.V1: + return new AliasTransactionV1(senderPublicKey, new Alias(createAlias.getAlias(), (byte) tx.getChainId()), feeAmount, timestamp, signature); + + case Transaction.V2: + return new AliasTransactionV2(senderPublicKey, new Alias(createAlias.getAlias(), (byte) tx.getChainId()), feeAmount, timestamp, proofs); + } + } else if (tx.hasDataTransaction()) { + final TransactionOuterClass.DataTransactionData data = tx.getDataTransaction(); + return new DataTransaction(senderPublicKey, toVanillaDataEntryList(data.getDataList()), feeAmount, timestamp, proofs); + } else if (tx.hasExchange()) { + final TransactionOuterClass.ExchangeTransactionData exchange = tx.getExchange(); + switch (tx.getVersion()) { + case Transaction.V1: + return new ExchangeTransactionV1(toVanillaOrder(exchange.getOrders(0)), toVanillaOrder(exchange.getOrders(1)), senderPublicKey, exchange.getAmount(), exchange.getPrice(), exchange.getBuyMatcherFee(), exchange.getSellMatcherFee(), feeAmount, timestamp, signature); + + case Transaction.V2: + return new ExchangeTransactionV2(senderPublicKey, toVanillaOrder(exchange.getOrders(0)), toVanillaOrder(exchange.getOrders(1)), exchange.getAmount(), exchange.getPrice(), exchange.getBuyMatcherFee(), exchange.getSellMatcherFee(), feeAmount, timestamp, proofs); + } + + } else if (tx.hasInvokeScript()) { + final TransactionOuterClass.InvokeScriptTransactionData data = tx.getInvokeScript(); + final List payments = new ArrayList(data.getPaymentsCount()); + for (TransactionOuterClass.Amount payment : data.getPaymentsList()) payments.add(new InvokeScriptTransaction.Payment(payment.getAmount(), toVanillaAssetId(payment.getAssetId().getIssuedAsset()))); + return new InvokeScriptTransaction((byte)tx.getChainId(), senderPublicKey, Base58.encode(data.getDappAddress().toByteArray()), InvokeScriptTransaction.FunctionCall.fromBytes(data.getFunctionCall().asReadOnlyByteBuffer()), Collections.unmodifiableList(payments), feeAmount, toVanillaAssetId(tx.getFee().getAssetId().getIssuedAsset()), timestamp, proofs); + } else if (tx.hasIssue()) { + final TransactionOuterClass.IssueTransactionData issue = tx.getIssue(); + switch (tx.getVersion()) { + case Transaction.V1: + return new IssueTransactionV1(senderPublicKey, new String(issue.getName().toByteArray()), new String(issue.getDescription().toByteArray()), issue.getAmount(), (byte) issue.getDecimals(), issue.getReissuable(), feeAmount, timestamp, signature); + + case Transaction.V2: + return new IssueTransactionV2(senderPublicKey, (byte) tx.getChainId(), new String(issue.getName().toByteArray()), new String(issue.getDescription().toByteArray()), issue.getAmount(), (byte) issue.getDecimals(), issue.getReissuable(), Base58.encode(issue.getScript().getBytes().toByteArray()), feeAmount, timestamp, proofs); + } + } else if (tx.hasReissue()) { + final TransactionOuterClass.ReissueTransactionData reissue = tx.getReissue(); + switch (tx.getVersion()) { + case Transaction.V1: + return new ReissueTransactionV1(senderPublicKey, toVanillaAssetId(reissue.getAssetAmount().getAssetId()), reissue.getAssetAmount().getAmount(), reissue.getReissuable(), feeAmount, timestamp, signature); + + case Transaction.V2: + return new ReissueTransactionV2(senderPublicKey, (byte) tx.getChainId(), toVanillaAssetId(reissue.getAssetAmount().getAssetId()), reissue.getAssetAmount().getAmount(), reissue.getReissuable(), feeAmount, timestamp, proofs); + } + } else if (tx.hasSetAssetScript()) { + final TransactionOuterClass.SetAssetScriptTransactionData sas = tx.getSetAssetScript(); + return new SetAssetScriptTransaction(senderPublicKey, (byte) tx.getChainId(), toVanillaAssetId(sas.getAssetId()), Base58.encode(sas.getScript().getBytes().toByteArray()), feeAmount, timestamp, proofs); + } else if (tx.hasSetScript()) { + final TransactionOuterClass.SetScriptTransactionData setScript = tx.getSetScript(); + return new SetScriptTransaction(senderPublicKey, Base58.encode(setScript.getScript().getBytes().toByteArray()), (byte) tx.getChainId(), feeAmount, timestamp, proofs); + } else if (tx.hasTransfer()) { + final TransactionOuterClass.TransferTransactionData transfer = tx.getTransfer(); + switch (tx.getVersion()) { + case Transaction.V1: + return new TransferTransactionV1(senderPublicKey, toRecipientString(transfer.getRecipient(), (byte) tx.getChainId()), transfer.getAmount().getAmount(), toVanillaAssetId(transfer.getAmount().getAssetId().getIssuedAsset()), feeAmount, toVanillaAssetId(tx.getFee().getAssetId().getIssuedAsset()), toVanillaByteString(transfer.getAttachment()), timestamp, signature); + + case Transaction.V2: + return new TransferTransactionV2(senderPublicKey, toRecipientString(transfer.getRecipient(), (byte) tx.getChainId()), transfer.getAmount().getAmount(), toVanillaAssetId(transfer.getAmount().getAssetId().getIssuedAsset()), feeAmount, toVanillaAssetId(tx.getFee().getAssetId().getIssuedAsset()), toVanillaByteString(transfer.getAttachment()), timestamp, proofs); + } + } else if (tx.hasLease()) { + final TransactionOuterClass.LeaseTransactionData lease = tx.getLease(); + switch (tx.getVersion()) { + case Transaction.V1: + return new LeaseTransactionV1(senderPublicKey, toRecipientString(lease.getRecipient(), (byte) tx.getChainId()), lease.getAmount(), feeAmount, timestamp, signature); + + case Transaction.V2: + return new LeaseTransactionV2(senderPublicKey, toRecipientString(lease.getRecipient(), (byte) tx.getChainId()), lease.getAmount(), feeAmount, timestamp, proofs); + } + } else if (tx.hasLeaseCancel()) { + final TransactionOuterClass.LeaseCancelTransactionData leaseCancel = tx.getLeaseCancel(); + switch (tx.getVersion()) { + case Transaction.V1: + return new LeaseCancelTransactionV1(senderPublicKey, Base58.encode(leaseCancel.getLeaseId().toByteArray()), feeAmount, timestamp, signature); + + case Transaction.V2: + return new LeaseCancelTransactionV2(senderPublicKey, (byte) tx.getChainId(), Base58.encode(leaseCancel.getLeaseId().toByteArray()), feeAmount, timestamp, proofs); + } + } else if (tx.hasMassTransfer()) { + final TransactionOuterClass.MassTransferTransactionData massTransfer = tx.getMassTransfer(); + final List transfers = new ArrayList(massTransfer.getTransfersList().size()); + for (TransactionOuterClass.MassTransferTransactionData.Transfer transfer : massTransfer.getTransfersList()) { + final Transfer transfer1 = new Transfer(toRecipientString(transfer.getAddress(), (byte) tx.getChainId()), transfer.getAmount()); + transfers.add(transfer1); + } + return new MassTransferTransaction(senderPublicKey, toVanillaAssetId(massTransfer.getAssetId().getIssuedAsset()), Collections.unmodifiableList(transfers), feeAmount, toVanillaByteString(massTransfer.getAttachment()), timestamp, proofs); + } else if (tx.hasSponsorFee()) { + final TransactionOuterClass.SponsorFeeTransactionData sponsorFee = tx.getSponsorFee(); + return new SponsorTransaction(senderPublicKey, toVanillaAssetId(sponsorFee.getMinFee().getAssetId()), sponsorFee.getMinFee().getAmount(), feeAmount, timestamp, proofs); + } + + throw new IllegalArgumentException("Invalid TX: " + tx); + } + + public static TransactionOuterClass.SignedTransaction toPB(final Transaction tx) { + TransactionOuterClass.Transaction.Builder base = TransactionOuterClass.Transaction.newBuilder() + .setFee(toPBAmount(Asset.WAVES, tx.getFee())) + .setTimestamp(tx.getTimestamp()) + .setVersion(tx.getVersion()); + + if (tx instanceof IssueTransaction) { + final IssueTransaction issue = (IssueTransaction) tx; + + ByteString script = ByteString.EMPTY; + if (issue instanceof IssueTransactionV2) + script = ByteString.copyFrom(Base58.decode(((IssueTransactionV2) issue).getScript())); + + final TransactionOuterClass.IssueTransactionData data = TransactionOuterClass.IssueTransactionData.newBuilder() + .setAmount(issue.getQuantity()) + .setDecimals(issue.getDecimals()) + .setName(ByteString.copyFrom(issue.getName().getBytes())) + .setDescription(ByteString.copyFrom(issue.getDescription().getBytes())) + .setReissuable(issue.isReissuable()) + .setScript(ScriptOuterClass.Script.newBuilder().setBytes(script)) + .build(); + base.setIssue(data); + } else if (tx instanceof ReissueTransaction) { + final ReissueTransaction reissue = (ReissueTransaction) tx; + final TransactionOuterClass.ReissueTransactionData data = TransactionOuterClass.ReissueTransactionData.newBuilder() + .setAssetAmount(TransactionOuterClass.AssetAmount.newBuilder().setAssetId(assetIdToBytes(reissue.getAssetId())).setAmount(reissue.getQuantity()).build()) + .setReissuable(reissue.isReissuable()) + .build(); + base.setReissue(data); + } else if (tx instanceof BurnTransaction) { + final BurnTransaction burn = (BurnTransaction) tx; + final TransactionOuterClass.BurnTransactionData data = TransactionOuterClass.BurnTransactionData.newBuilder() + .setAssetAmount(TransactionOuterClass.AssetAmount.newBuilder().setAssetId(assetIdToBytes(burn.getAssetId())).setAmount(burn.getAmount()).build()) + .build(); + base.setBurn(data); + } else if (tx instanceof SetScriptTransaction) { + final SetScriptTransaction setScript = (SetScriptTransaction) tx; + final TransactionOuterClass.SetScriptTransactionData data = TransactionOuterClass.SetScriptTransactionData.newBuilder() + .setScript(ScriptOuterClass.Script.newBuilder().setBytes(ByteString.copyFrom(Base58.decode(setScript.getScript()))).build()) + .build(); + base.setSetScript(data); + } else if (tx instanceof SetAssetScriptTransaction) { + final SetAssetScriptTransaction sas = (SetAssetScriptTransaction) tx; + final TransactionOuterClass.SetAssetScriptTransactionData data = TransactionOuterClass.SetAssetScriptTransactionData + .newBuilder() + .setAssetId(assetIdToBytes(sas.getAssetId())) + .setScript(ScriptOuterClass.Script.newBuilder().setBytes(ByteString.copyFrom(Base58.decode(sas.getScript()))).build()) + .build(); + base.setSetAssetScript(data); + } else if (tx instanceof DataTransaction) { + final DataTransaction dataTransaction = (DataTransaction) tx; + + final List dataEntries = new ArrayList(dataTransaction.getData().size()); + for (DataEntry dataEntry : dataTransaction.getData()) dataEntries.add(toPBDataEntry(dataEntry)); + + final TransactionOuterClass.DataTransactionData data = TransactionOuterClass.DataTransactionData.newBuilder() + .addAllData(dataEntries) + .build(); + + base.setDataTransaction(data); + } else if (tx instanceof MassTransferTransaction) { + final MassTransferTransaction mtt = (MassTransferTransaction) tx; + final ByteString assetId = assetIdToBytes(mtt.getAssetId()); + final List transfers = new ArrayList(mtt.getTransfers().size()); + for (Transfer transfer : mtt.getTransfers()) { + TransactionOuterClass.MassTransferTransactionData.Transfer transfer1 = TransactionOuterClass.MassTransferTransactionData.Transfer.newBuilder() + .setAddress(toPBRecipient(transfer.getRecipient())) + .setAmount(transfer.getAmount()).build(); + transfers.add(transfer1); + } + + final TransactionOuterClass.MassTransferTransactionData data = TransactionOuterClass.MassTransferTransactionData.newBuilder() + .setAssetId(createPBAssetId(assetId)) + .setAttachment(toPBByteString(mtt.getAttachment())) + .addAllTransfers(transfers) + .build(); + base.setMassTransfer(data); + } else if (tx instanceof TransferTransaction) { + final TransferTransaction transfer = (TransferTransaction) tx; + final TransactionOuterClass.TransferTransactionData data = TransactionOuterClass.TransferTransactionData.newBuilder() + .setRecipient(toPBRecipient(transfer.getRecipient())) + .setAmount(toPBAmount(transfer.getAssetId(), transfer.getAmount())) + .setAttachment(toPBByteString(transfer.getAttachment())) + .build(); + base.setTransfer(data); + + if (tx instanceof TransferTransactionV2) + base.setFee(toPBAmount(((TransferTransactionV2) tx).getFeeAssetId(), tx.getFee())); + } else if (tx instanceof SponsorTransaction) { + final SponsorTransaction sponsor = (SponsorTransaction) tx; + final TransactionOuterClass.SponsorFeeTransactionData data = TransactionOuterClass.SponsorFeeTransactionData.newBuilder() + .setMinFee(TransactionOuterClass.AssetAmount.newBuilder().setAssetId(assetIdToBytes(sponsor.getAssetId())).setAmount(sponsor.getMinSponsoredAssetFee()).build()) + .build(); + base.setSponsorFee(data); + } else if (tx instanceof ExchangeTransaction) { + final ExchangeTransaction exchange = (ExchangeTransaction) tx; + final TransactionOuterClass.ExchangeTransactionData data = TransactionOuterClass.ExchangeTransactionData.newBuilder() + .setPrice(exchange.getPrice()) + .setAmount(exchange.getAmount()) + .setBuyMatcherFee(exchange.getBuyMatcherFee()) + .setSellMatcherFee(exchange.getSellMatcherFee()) + .setOrders(0, toPBOrder(exchange.getOrder1())) + .setOrders(1, toPBOrder(exchange.getOrder2())) + .build(); + base.setExchange(data); + } else if (tx instanceof InvokeScriptTransaction) { + final InvokeScriptTransaction ist = (InvokeScriptTransaction) tx; + final List payments = new ArrayList(ist.getPayments().size()); + for (InvokeScriptTransaction.Payment payment : ist.getPayments()) + payments.add(toPBAmount(payment.getAssetId(), payment.getAmount())); + + final TransactionOuterClass.InvokeScriptTransactionData data = TransactionOuterClass.InvokeScriptTransactionData.newBuilder() + .setDappAddress(ByteString.copyFrom(Base58.decode(ist.getdApp()))) + .setFunctionCall(ByteString.copyFrom(ist.getCall().toBytes())) + .addAllPayments(payments) + .build(); + base.setInvokeScript(data); + } + + List proofs = new ArrayList(); + if (tx instanceof TransactionWithProofs) + //noinspection unchecked + for (com.wavesplatform.wavesj.ByteString proof : ((List) (((TransactionWithProofs) tx).getProofs()))) + proofs.add(toPBByteString(proof)); + else if (tx instanceof TransactionWithSignature) + proofs.add(toPBByteString(((TransactionWithSignature) tx).getSignature())); + + return TransactionOuterClass.SignedTransaction.newBuilder() + .addAllProofs(proofs) + .setTransaction(base) + .build(); + } + + public static String toVanillaAssetId(final ByteString assetId) { + if (assetId.isEmpty()) return Asset.WAVES; + else return Asset.normalize(Base58.encode(assetId.toByteArray())); + } + + public static ByteString assetIdToBytes(final String assetId) { + if (Asset.isWaves(assetId)) return ByteString.EMPTY; + else return ByteString.copyFrom(Base58.decode(assetId)); + } + + public static TransactionOuterClass.Amount toPBAmount(final String assetId, final long amount) { + return TransactionOuterClass.Amount.newBuilder().setAssetId(createPBAssetId(assetIdToBytes(assetId))).build(); + } + + public static DataEntry toVanillaDataEntry(final TransactionOuterClass.DataTransactionData.DataEntry dataEntry) { + DataEntry result; + switch (dataEntry.getValueCase()) { + case STRING_VALUE: + result = new DataEntry.StringEntry(dataEntry.getKey(), dataEntry.getStringValue()); + break; + case BOOL_VALUE: + result = new DataEntry.BooleanEntry(dataEntry.getKey(), dataEntry.getBoolValue()); + break; + case INT_VALUE: + result = new DataEntry.LongEntry(dataEntry.getKey(), dataEntry.getIntValue()); + break; + case BINARY_VALUE: + result = new DataEntry.BinaryEntry(dataEntry.getKey(), toVanillaByteString(dataEntry.getBinaryValue())); + break; + default: + throw new IllegalArgumentException("Not supported: " + dataEntry); + } + return result; + } + + public static TransactionOuterClass.DataTransactionData.DataEntry toPBDataEntry(final DataEntry dataEntry) { + TransactionOuterClass.DataTransactionData.DataEntry.Builder builder = TransactionOuterClass.DataTransactionData.DataEntry.newBuilder(); + + if (dataEntry.getType().equals("integer")) builder.setIntValue((Long) dataEntry.getValue()); + else if (dataEntry.getType().equals("string")) builder.setStringValue((String) dataEntry.getValue()); + else if (dataEntry.getType().equals("boolean")) builder.setBoolValue((Boolean) dataEntry.getValue()); + else if (dataEntry.getType().equals("binary")) + builder.setBinaryValue(toPBByteString((com.wavesplatform.wavesj.ByteString) dataEntry.getValue())); + + return builder.setKey(dataEntry.getKey()).build(); + } + + public static Order toVanillaOrder(final TransactionOuterClass.ExchangeTransactionData.Order order) { + final Order.Type orderType = order.getOrderSide() == TransactionOuterClass.ExchangeTransactionData.Order.Side.BUY ? Order.Type.BUY : Order.Type.SELL; + final AssetPair assetPair = new AssetPair(toVanillaAssetId(order.getAssetPair().getAmountAssetId()), toVanillaAssetId(order.getAssetPair().getPriceAssetId())); + final PublicKeyAccount senderPublicKey = new PublicKeyAccount(order.getSenderPublicKey().toByteArray(), (byte) order.getChainId()); + final PublicKeyAccount matcherPk = new PublicKeyAccount(order.getMatcherPublicKey().toByteArray(), (byte) order.getChainId()); + + switch (order.getVersion()) { + case Order.V1: + return new OrderV1(senderPublicKey, matcherPk, orderType, assetPair, order.getAmount(), order.getPrice(), order.getTimestamp(), order.getExpiration(), order.getMatcherFee().getAmount(), toSignature(order.getProofsList())); + case Order.V2: + return new OrderV2(senderPublicKey, matcherPk, orderType, assetPair, order.getAmount(), order.getPrice(), order.getTimestamp(), order.getExpiration(), order.getMatcherFee().getAmount(), (byte) order.getVersion(), toVanillaProofs(order.getProofsList())); + default: + throw new IllegalArgumentException("Order not supported: " + order); + } + } + + public static TransactionOuterClass.ExchangeTransactionData.Order toPBOrder(final Order order) { + final TransactionOuterClass.ExchangeTransactionData.Order.Side orderType = order.getOrderType() == Order.Type.BUY ? TransactionOuterClass.ExchangeTransactionData.Order.Side.BUY : TransactionOuterClass.ExchangeTransactionData.Order.Side.SELL; + + final List proofs = new ArrayList(order.getProofs().size()); + for (com.wavesplatform.wavesj.ByteString proof : order.getProofs()) proofs.add(toPBByteString(proof)); + + return TransactionOuterClass.ExchangeTransactionData.Order.newBuilder() + .setAmount(order.getAmount()) + .setPrice(order.getPrice()) + .setAssetPair(TransactionOuterClass.ExchangeTransactionData.Order.AssetPair.newBuilder() + .setAmountAssetId(assetIdToBytes(order.getAssetPair().getAmountAsset())) + .setPriceAssetId(assetIdToBytes(order.getAssetPair().getPriceAsset())).build()) + .setExpiration(order.getExpiration()) + .setMatcherFee(TransactionOuterClass.Amount.newBuilder().setAmount(order.getMatcherFee()).build()) + .setMatcherPublicKey(ByteString.copyFrom(order.getMatcherPublicKey().getPublicKey())) + .setOrderSide(orderType) + .setSenderPublicKey(ByteString.copyFrom(order.getSenderPublicKey().getPublicKey())) + .setVersion(1) + .addAllProofs(proofs) + .build(); + } + + public static String toRecipientString(final RecipientOuterClass.Recipient recipient, byte chainId) { + switch (recipient.getRecipientCase()) { + case ALIAS: + return recipient.getAlias(); + case ADDRESS: + final ByteBuffer withoutChecksum = ByteBuffer.allocate(2 + recipient.getAddress().size()) + .put((byte) 1) + .put(recipient.getAddress().toByteArray()); + withoutChecksum.flip(); + final byte[] checksum = Hash.blake2b(withoutChecksum.array(), 0, withoutChecksum.capacity()); + + final ByteBuffer addrBytes = ByteBuffer.allocate(withoutChecksum.capacity() + 4) + .put(withoutChecksum) + .put(checksum, 0, 4); + addrBytes.flip(); + return Base58.encode(addrBytes.array()); + + default: + throw new IllegalArgumentException("Recipient not supported: " + recipient); + } + } + + public static RecipientOuterClass.Recipient toPBRecipient(final String recipient) { + try { + final byte[] sourceAddr = Base58.decode(recipient); + assert sourceAddr.length == 20; + + final byte[] addr = Arrays.copyOfRange(sourceAddr, 2, sourceAddr.length - 4); + return RecipientOuterClass.Recipient.newBuilder().setAddress(ByteString.copyFrom(addr)).build(); + } catch (Throwable e) { + return RecipientOuterClass.Recipient.newBuilder().setAlias(recipient).build(); + } + } + + public static TransactionOuterClass.AssetId createPBAssetId(final ByteString assetId) { + return assetId.isEmpty() ? TransactionOuterClass.AssetId.newBuilder().setWaves(Empty.newBuilder().build()).build() : TransactionOuterClass.AssetId.newBuilder().setIssuedAsset(assetId).build(); + } + + private static com.wavesplatform.wavesj.ByteString toSignature(final List proofs) { + if (proofs.isEmpty()) return com.wavesplatform.wavesj.ByteString.EMPTY; + else return toVanillaByteString(proofs.get(0)); + } + + public static com.wavesplatform.wavesj.ByteString toVanillaByteString(final ByteString bs) { + return new com.wavesplatform.wavesj.ByteString(bs.toByteArray()); + } + + + public static ByteString toPBByteString(final com.wavesplatform.wavesj.ByteString bs) { + return ByteString.copyFrom(bs.getBytes()); + } + + private static List toVanillaProofs(final List proofs) { + final List result = new ArrayList(proofs.size()); + for (ByteString proof : proofs) result.add(toVanillaByteString(proof)); + return Collections.unmodifiableList(result); + } + + private static List> toVanillaDataEntryList(final List dataEntries) { + final List> result = new ArrayList>(dataEntries.size()); + for (TransactionOuterClass.DataTransactionData.DataEntry dataEntry : dataEntries) + result.add(toVanillaDataEntry(dataEntry)); + return Collections.unmodifiableList(result); + } +} diff --git a/src/main/java/com/wavesplatform/wavesj/transactions/InvokeScriptTransaction.java b/src/main/java/com/wavesplatform/wavesj/transactions/InvokeScriptTransaction.java index 71d8695..7bd69f9 100644 --- a/src/main/java/com/wavesplatform/wavesj/transactions/InvokeScriptTransaction.java +++ b/src/main/java/com/wavesplatform/wavesj/transactions/InvokeScriptTransaction.java @@ -5,6 +5,7 @@ import com.wavesplatform.wavesj.*; import java.io.Serializable; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -312,6 +313,50 @@ public void write(ByteBuffer buf) { } } + public ByteBuffer toBytes() { + final ByteBuffer buf; + try { + final int nameLength = name.getBytes("UTF-8").length; + int argsLength = 4; + for (FunctionalArg arg : args) argsLength += arg.bytesSize(); + buf = ByteBuffer.allocate(2 + 4 + nameLength + 4 + argsLength); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + // special bytes to indicate function call. Used in Serde serializer + buf.put(E_FUNCALL); + buf.put(FH_USER); + + // write function name + ByteUtils.putString(buf, name, ByteUtils.BytesFormatter.LENGTH_AS_INT); + + // write function's arguments + buf.putInt(args.size()); + for (FunctionalArg arg : args) { + arg.write(buf); + } + buf.flip(); + return buf.compact().asReadOnlyBuffer(); + } + + public static FunctionCall fromBytes(final ByteBuffer buf) { + buf.position(buf.position() + 2); + final byte[] nameBytes = new byte[buf.getInt()]; + buf.get(nameBytes); + + final int argsSize = buf.getInt(); + assert argsSize >= 0 && argsSize <= 22; + final LinkedList> args = new LinkedList>(); + for (int i = 0; i < argsSize; i++) args.add(FunctionalArg.fromBytes(buf)); + + try { + return new FunctionCall(new String(nameBytes, "UTF-8"), args); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + public String getName() { return name; } @@ -364,6 +409,38 @@ public abstract static class FunctionalArg implements Se public abstract void write(ByteBuffer buf); + public static FunctionalArg fromBytes(ByteBuffer buf) { + switch (buf.get()) { + case StringArg.E_STRING: + final byte[] string = new byte[buf.getInt()]; + buf.get(string); + try { + return new StringArg(new String(string, "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + case BinaryArg.E_BYTES: + final byte[] bytes = new byte[buf.getInt()]; + buf.get(bytes); + return new BinaryArg(new ByteString(bytes)); + + case LongArg.E_LONG: + return new LongArg(buf.getLong()); + + case BooleanArg.E_TRUE: + return new BooleanArg(true); + + case BooleanArg.E_FALSE: + return new BooleanArg(false); + + default: + throw new IllegalArgumentException("Data type not supported"); + } + } + + public abstract int bytesSize(); + public T getValue() { return value; } @@ -408,6 +485,11 @@ public void write(ByteBuffer buf) { buf.put(E_LONG); buf.putLong(value); } + + @Override + public int bytesSize() { + return 1 + 8; + } } public static class BinaryArg extends FunctionalArg { @@ -430,6 +512,11 @@ public void write(ByteBuffer buf) { buf.put(E_BYTES); ByteUtils.putBytes(buf, binary, ByteUtils.BytesFormatter.LENGTH_AS_INT); } + + @Override + public int bytesSize() { + return 1 + 4 + value.getBytes().length; + } } public static class StringArg extends FunctionalArg { @@ -451,6 +538,15 @@ public void write(ByteBuffer buf) { buf.put(E_STRING); ByteUtils.putString(buf, value, ByteUtils.BytesFormatter.LENGTH_AS_INT); } + + @Override + public int bytesSize() { + try { + return 1 + 4 + value.getBytes("UTF-8").length; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } } public static class BooleanArg extends FunctionalArg { @@ -476,6 +572,11 @@ public void write(ByteBuffer buf) { buf.put(E_FALSE); } } + + @Override + public int bytesSize() { + return 1; + } } public static class Payment implements Serializable { diff --git a/src/main/proto/accounts_api.proto b/src/main/proto/accounts_api.proto new file mode 100644 index 0000000..306e052 --- /dev/null +++ b/src/main/proto/accounts_api.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; +package com.wavesplatform.api.grpc; + +import "transactions_api.proto"; +import "transaction.proto"; + +service AccountsApi { + rpc GetBalances (BalancesRequest) returns (stream BalanceResponse); + rpc GetScript (AccountRequest) returns (ScriptData); + rpc GetActiveLeases (AccountRequest) returns (stream TransactionResponse); + rpc GetDataEntries (DataRequest) returns (stream DataEntryResponse); +} + +message AccountRequest { + bytes address = 1; +} + +message DataRequest { + bytes address = 1; + string key = 2; +} + +message BalancesRequest { + bytes address = 1; + repeated AssetId assets = 4; +} + +message BalanceResponse { + message WavesBalances { + int64 regular = 1; + int64 generating = 2; + int64 available = 3; + int64 effective = 4; + int64 lease_in = 5; + int64 lease_out = 6; + } + + bytes address = 1; // TODO: ??? + oneof balance { + WavesBalances waves = 2; + AssetAmount asset = 3; + } +} + +message DataEntryResponse { + bytes address = 1; + DataTransactionData.DataEntry entry = 2; +} + +message ScriptData { + bytes script_bytes = 1; + string script_text = 2; + int64 complexity = 3; +} diff --git a/src/main/proto/assets_api.proto b/src/main/proto/assets_api.proto new file mode 100644 index 0000000..8747cbb --- /dev/null +++ b/src/main/proto/assets_api.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package com.wavesplatform.api.grpc; + +import "transaction.proto"; +import "accounts_api.proto"; + +service AssetsApi { + rpc GetInfo (AssetRequest) returns (AssetInfoResponse); +} + +message AssetRequest { + bytes asset_id = 1; +} + +message AssetInfoResponse { + bytes issuer = 1; + bytes name = 2; + bytes description = 3; + int32 decimals = 4; + bool reissuable = 5; + int64 total_volume = 6; + ScriptData script = 7; + int64 sponsorship = 8; + SignedTransaction issue_transaction = 11; + int64 sponsor_balance = 10; +} diff --git a/src/main/proto/block.proto b/src/main/proto/block.proto new file mode 100644 index 0000000..b1ec3ae --- /dev/null +++ b/src/main/proto/block.proto @@ -0,0 +1,21 @@ +// Transactions +syntax = "proto3"; +option java_package = "com.wavesplatform.protobuf.block"; +import "transaction.proto"; + +message Block { + message Header { + int32 chain_id = 1; + bytes reference = 2; + int64 base_target = 3; + bytes generation_signature = 4; + repeated uint32 feature_votes = 5; + int64 timestamp = 6; + int32 version = 7; + bytes generator = 8; + } + + Header header = 1; + bytes signature = 2; + repeated SignedTransaction transactions = 3; +} diff --git a/src/main/proto/blockchain_api.proto b/src/main/proto/blockchain_api.proto new file mode 100644 index 0000000..86ce4b0 --- /dev/null +++ b/src/main/proto/blockchain_api.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; +package com.wavesplatform.api.grpc; + +import "google/protobuf/empty.proto"; + +service BlockchainApi { + rpc GetActivationStatus (ActivationStatusRequest) returns (ActivationStatusResponse); + rpc GetBaseTarget (google.protobuf.Empty) returns (BaseTargetResponse); + rpc GetCumulativeScore (google.protobuf.Empty) returns (ScoreResponse); +} + +message ActivationStatusRequest { + int32 height = 1; +} + +message ActivationStatusResponse { + int32 height = 1; + int32 voting_interval = 2; + int32 voting_threshold = 3; + int32 next_check = 4; + repeated FeatureActivationStatus features = 5; +} + +message FeatureActivationStatus { + enum BlockchainFeatureStatus { + UNDEFINED = 0; + APPROVED = 1; + ACTIVATED = 2; + }; + + enum NodeFeatureStatus { + NOT_IMPLEMENTED = 0; + IMPLEMENTED = 1; + VOTED = 2; + } + + int32 id = 1; + string description = 2; + BlockchainFeatureStatus blockchain_status = 3; + NodeFeatureStatus node_status = 4; + int32 activation_height = 5; + int32 supporting_blocks = 6; +} + +message BaseTargetResponse { + int64 base_target = 1; +} + +message ScoreResponse { + bytes score = 1; // BigInt +} diff --git a/src/main/proto/blocks_api.proto b/src/main/proto/blocks_api.proto new file mode 100644 index 0000000..c7dadbe --- /dev/null +++ b/src/main/proto/blocks_api.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package com.wavesplatform.api.grpc; + +import "block.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +service BlocksApi { + rpc GetBlock (BlockRequest) returns (BlockWithHeight); + rpc GetBlockRange (BlockRangeRequest) returns (stream BlockWithHeight); + rpc GetCurrentHeight (google.protobuf.Empty) returns (google.protobuf.UInt32Value); +} + +message BlockRequest { + oneof request { + bytes block_id = 1; + int32 height = 2; + bytes reference = 3; + } + + bool include_transactions = 100; +} + +message BlockRangeRequest { + uint32 from_height = 1; + uint32 to_height = 2; + oneof filter { + bytes generator = 3; + } + + bool include_transactions = 100; +} + +message BlockWithHeight { + Block block = 1; + uint32 height = 2; +} diff --git a/src/main/proto/recipient.proto b/src/main/proto/recipient.proto new file mode 100644 index 0000000..0d7eaf5 --- /dev/null +++ b/src/main/proto/recipient.proto @@ -0,0 +1,10 @@ +// Transactions +syntax = "proto3"; +option java_package = "com.wavesplatform.protobuf.transaction"; + +message Recipient { + oneof recipient { + bytes address = 1; + string alias = 2; + }; +}; diff --git a/src/main/proto/script.proto b/src/main/proto/script.proto new file mode 100644 index 0000000..e0d3e39 --- /dev/null +++ b/src/main/proto/script.proto @@ -0,0 +1,7 @@ +// Transactions +syntax = "proto3"; +option java_package = "com.wavesplatform.protobuf.transaction"; + +message Script { + bytes bytes = 1; +} diff --git a/src/main/proto/transaction.proto b/src/main/proto/transaction.proto new file mode 100644 index 0000000..39ea374 --- /dev/null +++ b/src/main/proto/transaction.proto @@ -0,0 +1,201 @@ +// Transactions +syntax = "proto3"; +option java_package = "com.wavesplatform.protobuf.transaction"; +import "script.proto"; +import "recipient.proto"; +import "google/protobuf/empty.proto"; + +message AssetId { + oneof asset { + google.protobuf.Empty waves = 1; + bytes issued_asset = 2; + } +} + +message AssetAmount { + bytes asset_id = 1; // Shouldn't be empty + int64 amount = 2; +} + +message Amount { + AssetId asset_id = 1; + int64 amount = 2; +} + +message SignedTransaction { + Transaction transaction = 1; + repeated bytes proofs = 2; +} + +message Transaction { + int32 chain_id = 1; + bytes sender_public_key = 2; + Amount fee = 3; + int64 timestamp = 4; + int32 version = 5; + + oneof data { + GenesisTransactionData genesis = 101; + PaymentTransactionData payment = 102; + IssueTransactionData issue = 103; + TransferTransactionData transfer = 104; + ReissueTransactionData reissue = 105; + BurnTransactionData burn = 106; + ExchangeTransactionData exchange = 107; + LeaseTransactionData lease = 108; + LeaseCancelTransactionData lease_cancel = 109; + CreateAliasTransactionData create_alias = 110; + MassTransferTransactionData mass_transfer = 111; + DataTransactionData data_transaction = 112; + SetScriptTransactionData set_script = 113; + SponsorFeeTransactionData sponsor_fee = 114; + SetAssetScriptTransactionData set_asset_script = 115; + InvokeScriptTransactionData invoke_script = 116; + }; +}; + +message GenesisTransactionData { + bytes recipient_address = 1; + int64 amount = 2; +}; + +message PaymentTransactionData { + bytes recipient_address = 1; + int64 amount = 2; +}; + +message TransferTransactionData { + Recipient recipient = 1; + Amount amount = 2; + bytes attachment = 3; +}; + +message CreateAliasTransactionData { + string alias = 1; +}; + +message DataTransactionData { + message DataEntry { + string key = 1; + oneof value { + int64 int_value = 10; + bool bool_value = 11; + bytes binary_value = 12; + string string_value = 13; + }; + }; + + repeated DataEntry data = 1; +}; + +message MassTransferTransactionData { + message Transfer { + Recipient address = 1; + int64 amount = 2; + }; + + AssetId asset_id = 1; + repeated Transfer transfers = 2; + bytes attachment = 3; +}; + +message LeaseTransactionData { + Recipient recipient = 1; + int64 amount = 2; +}; + +message LeaseCancelTransactionData { + bytes lease_id = 1; +}; + +message BurnTransactionData { + AssetAmount asset_amount = 1; +}; + +message IssueTransactionData { + bytes name = 1; + bytes description = 2; + int64 amount = 3; + int32 decimals = 4; + bool reissuable = 5; + Script script = 6; +}; + + +message ReissueTransactionData { + AssetAmount asset_amount = 1; + bool reissuable = 2; +}; + +message SetAssetScriptTransactionData { + bytes asset_id = 1; + Script script = 2; +}; + +message SetScriptTransactionData { + Script script = 2; +}; + +message ExchangeTransactionData { + message BuySellOrders { + Order buy_order = 1; + Order sell_order = 2; + } + + message MakerTakerOrders { + Order maker_order = 1; + Order taker_order = 2; + } + + message Order { + enum Side { + BUY = 0; + SELL = 1; + }; + + message AssetPair { + bytes amount_asset_id = 1; + bytes price_asset_id = 2; + }; + + int32 chain_id = 1; + bytes sender_public_key = 2; + bytes matcher_public_key = 3; + AssetPair asset_pair = 4; + Side order_side = 5; + int64 amount = 6; + int64 price = 7; + int64 timestamp = 8; + int64 expiration = 9; + Amount matcher_fee = 10; + int32 version = 11; + repeated bytes proofs = 12; + }; + + int64 amount = 1; + int64 price = 2; + int64 buy_matcher_fee = 3; + int64 sell_matcher_fee = 4; + repeated Order orders = 5; + int32 taker = 6; +}; + +message SponsorFeeTransactionData { + AssetAmount min_fee = 1; +}; + +message InvokeScriptTransactionData { + bytes dapp_address = 1; + bytes function_call = 2; + repeated Amount payments = 3; +} + +message InvokeScriptResult { + message Payment { + bytes address = 1; + Amount amount = 2; + } + + repeated DataTransactionData.DataEntry data = 1; + repeated Payment transfers = 2; +} diff --git a/src/main/proto/transactions_api.proto b/src/main/proto/transactions_api.proto new file mode 100644 index 0000000..b829624 --- /dev/null +++ b/src/main/proto/transactions_api.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; +package com.wavesplatform.api.grpc; +import "recipient.proto"; +import "transaction.proto"; +import "google/protobuf/empty.proto"; + +service TransactionsApi { + rpc GetTransactions (TransactionsRequest) returns (stream TransactionResponse); + rpc GetStateChanges (TransactionsRequest) returns (stream InvokeScriptResult); + rpc GetStatuses (TransactionsByIdRequest) returns (stream TransactionStatus); + rpc GetUnconfirmed (TransactionsRequest) returns (stream TransactionResponse); + + rpc Sign (SignRequest) returns (SignedTransaction); + rpc Broadcast (SignedTransaction) returns (SignedTransaction); +} + +message TransactionStatus { + enum Status { + NOT_EXISTS = 0; + UNCONFIRMED = 1; + CONFIRMED = 2; + } + + bytes id = 1; + Status status = 2; + int64 height = 3; +} + +message TransactionResponse { + bytes id = 1; + int64 height = 2; + SignedTransaction transaction = 3; +} + +message TransactionsRequest { + bytes sender = 1; + Recipient recipient = 2; + repeated bytes transaction_ids = 3; +} + +message TransactionsByIdRequest { + repeated bytes transaction_ids = 3; +} + +message CalculateFeeResponse { + bytes asset_id = 1; + uint64 amount = 2; +} + +message SignRequest { + Transaction transaction = 1; + bytes signer_public_key = 2; +} diff --git a/src/test/java/com/wavesplatform/wavesj/NodeTest.java b/src/test/java/com/wavesplatform/wavesj/NodeTest.java index 95bf82a..d6db255 100644 --- a/src/test/java/com/wavesplatform/wavesj/NodeTest.java +++ b/src/test/java/com/wavesplatform/wavesj/NodeTest.java @@ -4,8 +4,6 @@ import com.wavesplatform.wavesj.transactions.MassTransferTransaction; import com.wavesplatform.wavesj.transactions.TransferTransactionV1; import com.wavesplatform.wavesj.transactions.TransferTransactionV2; -import org.hamcrest.CoreMatchers; -import org.hamcrest.Matcher; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -20,10 +18,9 @@ import java.util.List; import static com.wavesplatform.wavesj.DataEntry.*; -import static com.wavesplatform.wavesj.Hash.*; +import static com.wavesplatform.wavesj.Hash.secureHash; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; @Ignore public class NodeTest { @@ -307,8 +304,7 @@ public void testHash() { String accountSeedBase58 = Base58.encode(accountSeed); assertEquals(expectedAccountSeed, accountSeedBase58); - byte[] blake2b = hash(buf.array(), 0, buf.array().length, Hash.BLAKE2B256); - byte[] keccak = hash(blake2b, 0, blake2b.length, Hash.KECCAK256); + final byte[] keccak = secureHash(buf.array(), 0, buf.array().length); assertEquals(expectedAccountSeed, Base58.encode(keccak)); assertThat(Base58.decode(expectedAccountSeed), equalTo(keccak));