From 2bfb0227aefa0ddadc14ad66069e637c0e75674e Mon Sep 17 00:00:00 2001 From: catbref Date: Tue, 3 Jul 2018 11:40:24 +0100 Subject: [PATCH] Added BuyNameTransaction support Fixed IssueAssetTransactions not being constructed with signature. Fixed incorrect MessageTransactionData constructors. Refactored various transactions to remove duplicate code. e.g. in CancelOrderTransaction.process() use getCreator() instead of explicit repository call. Added name_reference to BuyNameTransactions HSQLDB table. Fixed incorrect SQL in HSQLDBMultiPaymentTransactionRepository. More unit tests! Fixed wrong data length in CancelOrderTransactionTransformer. Fixed wrong data length in CreateOrderTransactionTransformer. Fixed missing payment bytes in MultiPaymentTransactionTransformer.toBytes(); --- .../transaction/BuyNameTransactionData.java | 68 ++++++++ .../CancelOrderTransactionData.java | 4 + .../IssueAssetTransactionData.java | 2 +- .../transaction/MessageTransactionData.java | 10 +- .../UpdateNameTransactionData.java | 5 + src/qora/naming/Name.java | 85 +++++++--- src/qora/transaction/BuyNameTransaction.java | 155 +++++++++++++++++ .../transaction/CancelOrderTransaction.java | 12 +- .../CancelSellNameTransaction.java | 6 +- .../transaction/CreateOrderTransaction.java | 10 +- .../transaction/CreatePollTransaction.java | 6 +- .../transaction/IssueAssetTransaction.java | 10 +- src/qora/transaction/MessageTransaction.java | 2 +- .../transaction/MultiPaymentTransaction.java | 2 +- src/qora/transaction/PaymentTransaction.java | 2 +- .../transaction/RegisterNameTransaction.java | 6 +- src/qora/transaction/SellNameTransaction.java | 7 +- src/qora/transaction/Transaction.java | 16 +- .../transaction/TransferAssetTransaction.java | 2 +- .../transaction/UpdateNameTransaction.java | 6 +- .../transaction/VoteOnPollTransaction.java | 22 ++- .../hsqldb/HSQLDBDatabaseUpdates.java | 2 +- .../HSQLDBBuyNameTransactionRepository.java | 54 ++++++ .../HSQLDBMessageTransactionRepository.java | 2 +- ...QLDBMultiPaymentTransactionRepository.java | 2 +- .../HSQLDBTransactionRepository.java | 9 + src/test/SerializationTests.java | 42 ++++- src/test/TransactionTests.java | 156 ++++++++++++++++++ src/transform/PaymentTransformer.java | 5 +- .../BuyNameTransactionTransformer.java | 118 +++++++++++++ .../CancelOrderTransactionTransformer.java | 2 +- .../CreateOrderTransactionTransformer.java | 3 +- .../CreatePollTransactionTransformer.java | 5 +- .../GenesisTransactionTransformer.java | 5 +- .../IssueAssetTransactionTransformer.java | 5 +- .../MessageTransactionTransformer.java | 7 +- .../MultiPaymentTransactionTransformer.java | 6 +- .../PaymentTransactionTransformer.java | 5 +- .../RegisterNameTransactionTransformer.java | 5 +- .../transaction/TransactionTransformer.java | 12 ++ .../TransferAssetTransactionTransformer.java | 5 +- .../UpdateNameTransactionTransformer.java | 7 +- src/utils/Serialization.java | 6 +- 43 files changed, 790 insertions(+), 111 deletions(-) create mode 100644 src/data/transaction/BuyNameTransactionData.java create mode 100644 src/qora/transaction/BuyNameTransaction.java create mode 100644 src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java create mode 100644 src/transform/transaction/BuyNameTransactionTransformer.java diff --git a/src/data/transaction/BuyNameTransactionData.java b/src/data/transaction/BuyNameTransactionData.java new file mode 100644 index 00000000..635e6a24 --- /dev/null +++ b/src/data/transaction/BuyNameTransactionData.java @@ -0,0 +1,68 @@ +package data.transaction; + +import java.math.BigDecimal; + +import qora.transaction.Transaction.TransactionType; + +public class BuyNameTransactionData extends TransactionData { + + // Properties + private byte[] buyerPublicKey; + private String name; + private BigDecimal amount; + private String seller; + private byte[] nameReference; + + // Constructors + + public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp, + byte[] reference, byte[] signature) { + super(TransactionType.BUY_NAME, fee, buyerPublicKey, timestamp, reference, signature); + + this.buyerPublicKey = buyerPublicKey; + this.name = name; + this.amount = amount; + this.seller = seller; + } + + public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference, + byte[] signature) { + this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, signature); + } + + public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, byte[] nameReference, BigDecimal fee, long timestamp, + byte[] reference) { + this(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, null); + } + + public BuyNameTransactionData(byte[] buyerPublicKey, String name, BigDecimal amount, String seller, BigDecimal fee, long timestamp, byte[] reference) { + this(buyerPublicKey, name, amount, seller, null, fee, timestamp, reference, null); + } + + // Getters / setters + + public byte[] getBuyerPublicKey() { + return this.buyerPublicKey; + } + + public String getName() { + return this.name; + } + + public BigDecimal getAmount() { + return this.amount; + } + + public String getSeller() { + return this.seller; + } + + public byte[] getNameReference() { + return this.nameReference; + } + + public void setNameReference(byte[] nameReference) { + this.nameReference = nameReference; + } + +} diff --git a/src/data/transaction/CancelOrderTransactionData.java b/src/data/transaction/CancelOrderTransactionData.java index 1c025940..b4dff9bc 100644 --- a/src/data/transaction/CancelOrderTransactionData.java +++ b/src/data/transaction/CancelOrderTransactionData.java @@ -19,6 +19,10 @@ public class CancelOrderTransactionData extends TransactionData { this.orderId = orderId; } + public CancelOrderTransactionData(byte[] creatorPublicKey, byte[] orderId, BigDecimal fee, long timestamp, byte[] reference) { + this(creatorPublicKey, orderId, fee, timestamp, reference, null); + } + // Getters/Setters public byte[] getCreatorPublicKey() { diff --git a/src/data/transaction/IssueAssetTransactionData.java b/src/data/transaction/IssueAssetTransactionData.java index a08d5a9c..a5f3f0a2 100644 --- a/src/data/transaction/IssueAssetTransactionData.java +++ b/src/data/transaction/IssueAssetTransactionData.java @@ -20,7 +20,7 @@ public class IssueAssetTransactionData extends TransactionData { public IssueAssetTransactionData(Long assetId, byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) { - super(TransactionType.ISSUE_ASSET, fee, issuerPublicKey, timestamp, reference); + super(TransactionType.ISSUE_ASSET, fee, issuerPublicKey, timestamp, reference, signature); this.assetId = assetId; this.issuerPublicKey = issuerPublicKey; diff --git a/src/data/transaction/MessageTransactionData.java b/src/data/transaction/MessageTransactionData.java index e20060ed..e9da1e67 100644 --- a/src/data/transaction/MessageTransactionData.java +++ b/src/data/transaction/MessageTransactionData.java @@ -18,8 +18,9 @@ public class MessageTransactionData extends TransactionData { protected boolean isEncrypted; // Constructors - public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, BigDecimal fee, byte[] data, - boolean isText, boolean isEncrypted, long timestamp, byte[] reference, byte[] signature) { + + public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText, + boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) { super(TransactionType.MESSAGE, fee, senderPublicKey, timestamp, reference, signature); this.version = version; @@ -37,6 +38,11 @@ public class MessageTransactionData extends TransactionData { this.isEncrypted = isEncrypted; } + public MessageTransactionData(int version, byte[] senderPublicKey, String recipient, Long assetId, BigDecimal amount, byte[] data, boolean isText, + boolean isEncrypted, BigDecimal fee, long timestamp, byte[] reference) { + this(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, null); + } + // Getters/Setters public int getVersion() { diff --git a/src/data/transaction/UpdateNameTransactionData.java b/src/data/transaction/UpdateNameTransactionData.java index 519087f0..9183eb00 100644 --- a/src/data/transaction/UpdateNameTransactionData.java +++ b/src/data/transaction/UpdateNameTransactionData.java @@ -26,6 +26,11 @@ public class UpdateNameTransactionData extends TransactionData { this.nameReference = nameReference; } + public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, BigDecimal fee, long timestamp, byte[] reference, + byte[] signature) { + this(ownerPublicKey, newOwner, name, newData, null, fee, timestamp, reference, signature); + } + public UpdateNameTransactionData(byte[] ownerPublicKey, String newOwner, String name, String newData, byte[] nameReference, BigDecimal fee, long timestamp, byte[] reference) { this(ownerPublicKey, newOwner, name, newData, nameReference, fee, timestamp, reference, null); diff --git a/src/qora/naming/Name.java b/src/qora/naming/Name.java index ece09e2e..917e9479 100644 --- a/src/qora/naming/Name.java +++ b/src/qora/naming/Name.java @@ -1,11 +1,14 @@ package qora.naming; import data.naming.NameData; +import data.transaction.BuyNameTransactionData; import data.transaction.CancelSellNameTransactionData; import data.transaction.RegisterNameTransactionData; import data.transaction.SellNameTransactionData; import data.transaction.TransactionData; import data.transaction.UpdateNameTransactionData; +import qora.account.Account; +import qora.account.PublicKeyAccount; import repository.DataException; import repository.Repository; @@ -56,6 +59,35 @@ public class Name { this.repository.getNameRepository().delete(this.nameData.getName()); } + private void revert() throws DataException { + TransactionData previousTransactionData = this.repository.getTransactionRepository().fromSignature(this.nameData.getReference()); + if (previousTransactionData == null) + throw new DataException("Unable to revert name transaction as referenced transaction not found in repository"); + + switch (previousTransactionData.getType()) { + case REGISTER_NAME: + RegisterNameTransactionData previousRegisterNameTransactionData = (RegisterNameTransactionData) previousTransactionData; + this.nameData.setOwner(previousRegisterNameTransactionData.getOwner()); + this.nameData.setData(previousRegisterNameTransactionData.getData()); + break; + + case UPDATE_NAME: + UpdateNameTransactionData previousUpdateNameTransactionData = (UpdateNameTransactionData) previousTransactionData; + this.nameData.setData(previousUpdateNameTransactionData.getNewData()); + this.nameData.setOwner(previousUpdateNameTransactionData.getNewOwner()); + break; + + case BUY_NAME: + BuyNameTransactionData previousBuyNameTransactionData = (BuyNameTransactionData) previousTransactionData; + Account buyer = new PublicKeyAccount(this.repository, previousBuyNameTransactionData.getBuyerPublicKey()); + this.nameData.setOwner(buyer.getAddress()); + break; + + default: + throw new IllegalStateException("Unable to revert name transaction due to unsupported referenced transaction"); + } + } + public void update(UpdateNameTransactionData updateNameTransactionData) throws DataException { // Update reference in transaction data updateNameTransactionData.setNameReference(this.nameData.getReference()); @@ -76,26 +108,7 @@ public class Name { this.nameData.setReference(updateNameTransactionData.getNameReference()); // Previous Name's owner and/or data taken from referenced transaction - TransactionData previousTransactionData = this.repository.getTransactionRepository().fromSignature(this.nameData.getReference()); - if (previousTransactionData == null) - throw new DataException("Unable to un-update name as referenced transaction not found in repository"); - - switch (previousTransactionData.getType()) { - case REGISTER_NAME: - RegisterNameTransactionData previousRegisterNameTransactionData = (RegisterNameTransactionData) previousTransactionData; - this.nameData.setOwner(previousRegisterNameTransactionData.getOwner()); - this.nameData.setData(previousRegisterNameTransactionData.getData()); - break; - - case UPDATE_NAME: - UpdateNameTransactionData previousUpdateNameTransactionData = (UpdateNameTransactionData) previousTransactionData; - this.nameData.setData(previousUpdateNameTransactionData.getNewData()); - this.nameData.setOwner(previousUpdateNameTransactionData.getNewOwner()); - break; - - default: - throw new IllegalStateException("Unable to revert update name transaction due to unsupported referenced transaction"); - } + this.revert(); // Save reverted name data this.repository.getNameRepository().save(this.nameData); @@ -135,4 +148,36 @@ public class Name { this.repository.getNameRepository().save(this.nameData); } + public void buy(BuyNameTransactionData buyNameTransactionData) throws DataException { + // Mark not for-sale but leave price in case we want to orphan + this.nameData.setIsForSale(false); + + // Set new owner + Account buyer = new PublicKeyAccount(this.repository, buyNameTransactionData.getBuyerPublicKey()); + this.nameData.setOwner(buyer.getAddress()); + + // Update reference in transaction data + buyNameTransactionData.setNameReference(this.nameData.getReference()); + + // New name reference is this transaction's signature + this.nameData.setReference(buyNameTransactionData.getSignature()); + + // Save updated name data + this.repository.getNameRepository().save(this.nameData); + } + + public void unbuy(BuyNameTransactionData buyNameTransactionData) throws DataException { + // Mark as for-sale using existing price + this.nameData.setIsForSale(true); + + // Previous name reference is taken from this transaction's cached copy + this.nameData.setReference(buyNameTransactionData.getNameReference()); + + // Previous Name's owner and/or data taken from referenced transaction + this.revert(); + + // Save reverted name data + this.repository.getNameRepository().save(this.nameData); + } + } diff --git a/src/qora/transaction/BuyNameTransaction.java b/src/qora/transaction/BuyNameTransaction.java new file mode 100644 index 00000000..a42545a6 --- /dev/null +++ b/src/qora/transaction/BuyNameTransaction.java @@ -0,0 +1,155 @@ +package qora.transaction; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.google.common.base.Utf8; + +import data.naming.NameData; +import data.transaction.BuyNameTransactionData; +import data.transaction.TransactionData; +import qora.account.Account; +import qora.account.PublicKeyAccount; +import qora.assets.Asset; +import qora.naming.Name; +import repository.DataException; +import repository.Repository; + +public class BuyNameTransaction extends Transaction { + + // Properties + private BuyNameTransactionData buyNameTransactionData; + + // Constructors + + public BuyNameTransaction(Repository repository, TransactionData transactionData) { + super(repository, transactionData); + + this.buyNameTransactionData = (BuyNameTransactionData) this.transactionData; + } + + // More information + + @Override + public List getRecipientAccounts() throws DataException { + return Collections.singletonList(new Account(this.repository, this.buyNameTransactionData.getSeller())); + } + + @Override + public boolean isInvolved(Account account) throws DataException { + String address = account.getAddress(); + + if (address.equals(this.getBuyer().getAddress())) + return true; + + if (address.equals(this.buyNameTransactionData.getSeller())) + return true; + + return false; + } + + @Override + public BigDecimal getAmount(Account account) throws DataException { + String address = account.getAddress(); + BigDecimal amount = BigDecimal.ZERO.setScale(8); + + if (address.equals(this.getBuyer().getAddress())) + amount = amount.subtract(this.transactionData.getFee()); + + return amount; + } + + // Navigation + + public Account getBuyer() throws DataException { + return new PublicKeyAccount(this.repository, this.buyNameTransactionData.getBuyerPublicKey()); + } + + // Processing + + @Override + public ValidationResult isValid() throws DataException { + // Check name size bounds + int nameLength = Utf8.encodedLength(buyNameTransactionData.getName()); + if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) + return ValidationResult.INVALID_NAME_LENGTH; + + // Check name is lowercase + if (!buyNameTransactionData.getName().equals(buyNameTransactionData.getName().toLowerCase())) + return ValidationResult.NAME_NOT_LOWER_CASE; + + NameData nameData = this.repository.getNameRepository().fromName(buyNameTransactionData.getName()); + + // Check name exists + if (nameData == null) + return ValidationResult.NAME_DOES_NOT_EXIST; + + // Check name is currently for sale + if (!nameData.getIsForSale()) + return ValidationResult.NAME_NOT_FOR_SALE; + + // Check buyer isn't trying to buy own name + Account buyer = getBuyer(); + if (buyer.getAddress().equals(nameData.getOwner())) + return ValidationResult.BUYER_ALREADY_OWNER; + + // Check expected seller currently owns name + if (!buyNameTransactionData.getSeller().equals(nameData.getOwner())) + return ValidationResult.INVALID_SELLER; + + // Check amounts agree + if (buyNameTransactionData.getAmount().compareTo(nameData.getSalePrice()) != 0) + return ValidationResult.INVALID_AMOUNT; + + // Check fee is positive + if (buyNameTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) + return ValidationResult.NEGATIVE_FEE; + + // Check reference is correct + if (!Arrays.equals(buyer.getLastReference(), buyNameTransactionData.getReference())) + return ValidationResult.INVALID_REFERENCE; + + // Check issuer has enough funds + if (buyer.getConfirmedBalance(Asset.QORA).compareTo(buyNameTransactionData.getFee()) == -1) + return ValidationResult.NO_BALANCE; + + return ValidationResult.OK; + } + + @Override + public void process() throws DataException { + // Update Name + Name name = new Name(this.repository, buyNameTransactionData.getName()); + name.buy(buyNameTransactionData); + + // Save this transaction, now with updated "name reference" to previous transaction that updated name + this.repository.getTransactionRepository().save(buyNameTransactionData); + + // Update buyer's balance + Account buyer = getBuyer(); + buyer.setConfirmedBalance(Asset.QORA, buyer.getConfirmedBalance(Asset.QORA).subtract(buyNameTransactionData.getFee())); + + // Update buyer's reference + buyer.setLastReference(buyNameTransactionData.getSignature()); + } + + @Override + public void orphan() throws DataException { + // Revert name + Name name = new Name(this.repository, buyNameTransactionData.getName()); + name.unbuy(buyNameTransactionData); + + // Delete this transaction itself + this.repository.getTransactionRepository().delete(buyNameTransactionData); + + // Update buyer's balance + Account buyer = getBuyer(); + buyer.setConfirmedBalance(Asset.QORA, buyer.getConfirmedBalance(Asset.QORA).add(buyNameTransactionData.getFee())); + + // Update buyer's reference + buyer.setLastReference(buyNameTransactionData.getReference()); + } + +} diff --git a/src/qora/transaction/CancelOrderTransaction.java b/src/qora/transaction/CancelOrderTransaction.java index 0b6137fc..15bbf456 100644 --- a/src/qora/transaction/CancelOrderTransaction.java +++ b/src/qora/transaction/CancelOrderTransaction.java @@ -49,6 +49,12 @@ public class CancelOrderTransaction extends Transaction { return amount; } + // Navigation + + public Account getCreator() throws DataException { + return new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey()); + } + // Processing @Override @@ -65,7 +71,7 @@ public class CancelOrderTransaction extends Transaction { if (orderData == null) return ValidationResult.ORDER_DOES_NOT_EXIST; - Account creator = new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Check creator's public key results in valid address if (!Crypto.isValidAddress(creator.getAddress())) @@ -89,7 +95,7 @@ public class CancelOrderTransaction extends Transaction { @Override public void process() throws DataException { - Account creator = new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Save this transaction itself this.repository.getTransactionRepository().save(this.transactionData); @@ -111,7 +117,7 @@ public class CancelOrderTransaction extends Transaction { @Override public void orphan() throws DataException { - Account creator = new PublicKeyAccount(this.repository, cancelOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Save this transaction itself this.repository.getTransactionRepository().delete(this.transactionData); diff --git a/src/qora/transaction/CancelSellNameTransaction.java b/src/qora/transaction/CancelSellNameTransaction.java index b2e36572..be4b6bd8 100644 --- a/src/qora/transaction/CancelSellNameTransaction.java +++ b/src/qora/transaction/CancelSellNameTransaction.java @@ -88,7 +88,7 @@ public class CancelSellNameTransaction extends Transaction { return ValidationResult.NAME_NOT_FOR_SALE; // Check transaction's public key matches name's current owner - Account owner = new PublicKeyAccount(this.repository, cancelSellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); if (!owner.getAddress().equals(nameData.getOwner())) return ValidationResult.INVALID_NAME_OWNER; @@ -118,7 +118,7 @@ public class CancelSellNameTransaction extends Transaction { this.repository.getTransactionRepository().save(cancelSellNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, cancelSellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(cancelSellNameTransactionData.getFee())); // Update owner's reference @@ -135,7 +135,7 @@ public class CancelSellNameTransaction extends Transaction { this.repository.getTransactionRepository().delete(cancelSellNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, cancelSellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(cancelSellNameTransactionData.getFee())); // Update owner's reference diff --git a/src/qora/transaction/CreateOrderTransaction.java b/src/qora/transaction/CreateOrderTransaction.java index 253afb8c..1b4680a2 100644 --- a/src/qora/transaction/CreateOrderTransaction.java +++ b/src/qora/transaction/CreateOrderTransaction.java @@ -52,6 +52,10 @@ public class CreateOrderTransaction extends Transaction { // Navigation + public Account getCreator() throws DataException { + return new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey()); + } + public Order getOrder() throws DataException { // orderId is the transaction signature OrderData orderData = this.repository.getAssetRepository().fromOrderId(this.createOrderTransactionData.getSignature()); @@ -92,7 +96,7 @@ public class CreateOrderTransaction extends Transaction { if (wantAssetData == null) return ValidationResult.ASSET_DOES_NOT_EXIST; - Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Check reference is correct if (!Arrays.equals(creator.getLastReference(), createOrderTransactionData.getReference())) @@ -129,7 +133,7 @@ public class CreateOrderTransaction extends Transaction { } public void process() throws DataException { - Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Update creator's balance due to fee creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createOrderTransactionData.getFee())); @@ -152,7 +156,7 @@ public class CreateOrderTransaction extends Transaction { } public void orphan() throws DataException { - Account creator = new PublicKeyAccount(this.repository, createOrderTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); // Update creator's balance due to fee creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createOrderTransactionData.getFee())); diff --git a/src/qora/transaction/CreatePollTransaction.java b/src/qora/transaction/CreatePollTransaction.java index 7a64875d..78c1c9b9 100644 --- a/src/qora/transaction/CreatePollTransaction.java +++ b/src/qora/transaction/CreatePollTransaction.java @@ -131,7 +131,7 @@ public class CreatePollTransaction extends Transaction { return ValidationResult.NEGATIVE_FEE; // Check reference is correct - PublicKeyAccount creator = new PublicKeyAccount(this.repository, createPollTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); if (!Arrays.equals(creator.getLastReference(), createPollTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; @@ -153,7 +153,7 @@ public class CreatePollTransaction extends Transaction { this.repository.getTransactionRepository().save(createPollTransactionData); // Update creator's balance - Account creator = new PublicKeyAccount(this.repository, createPollTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(createPollTransactionData.getFee())); // Update creator's reference @@ -170,7 +170,7 @@ public class CreatePollTransaction extends Transaction { this.repository.getTransactionRepository().delete(createPollTransactionData); // Update creator's balance - Account creator = new PublicKeyAccount(this.repository, createPollTransactionData.getCreatorPublicKey()); + Account creator = getCreator(); creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(createPollTransactionData.getFee())); // Update creator's reference diff --git a/src/qora/transaction/IssueAssetTransaction.java b/src/qora/transaction/IssueAssetTransaction.java index fde4e712..d518d385 100644 --- a/src/qora/transaction/IssueAssetTransaction.java +++ b/src/qora/transaction/IssueAssetTransaction.java @@ -106,7 +106,7 @@ public class IssueAssetTransaction extends Transaction { return ValidationResult.NEGATIVE_FEE; // Check reference is correct - PublicKeyAccount issuer = new PublicKeyAccount(this.repository, issueAssetTransactionData.getIssuerPublicKey()); + Account issuer = getIssuer(); if (!Arrays.equals(issuer.getLastReference(), issueAssetTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; @@ -134,20 +134,20 @@ public class IssueAssetTransaction extends Transaction { this.repository.getTransactionRepository().save(issueAssetTransactionData); // Update issuer's balance - Account issuer = new PublicKeyAccount(this.repository, issueAssetTransactionData.getIssuerPublicKey()); + Account issuer = getIssuer(); issuer.setConfirmedBalance(Asset.QORA, issuer.getConfirmedBalance(Asset.QORA).subtract(issueAssetTransactionData.getFee())); // Update issuer's reference issuer.setLastReference(issueAssetTransactionData.getSignature()); // Add asset to owner - Account owner = new Account(this.repository, issueAssetTransactionData.getOwner()); + Account owner = getOwner(); owner.setConfirmedBalance(issueAssetTransactionData.getAssetId(), BigDecimal.valueOf(issueAssetTransactionData.getQuantity()).setScale(8)); } public void orphan() throws DataException { // Remove asset from owner - Account owner = new Account(this.repository, issueAssetTransactionData.getOwner()); + Account owner = getOwner(); owner.deleteBalance(issueAssetTransactionData.getAssetId()); // Issue asset @@ -158,7 +158,7 @@ public class IssueAssetTransaction extends Transaction { this.repository.getTransactionRepository().delete(issueAssetTransactionData); // Update issuer's balance - Account issuer = new PublicKeyAccount(this.repository, issueAssetTransactionData.getIssuerPublicKey()); + Account issuer = getIssuer(); issuer.setConfirmedBalance(Asset.QORA, issuer.getConfirmedBalance(Asset.QORA).add(issueAssetTransactionData.getFee())); // Update issuer's reference diff --git a/src/qora/transaction/MessageTransaction.java b/src/qora/transaction/MessageTransaction.java index 43322566..f30b96da 100644 --- a/src/qora/transaction/MessageTransaction.java +++ b/src/qora/transaction/MessageTransaction.java @@ -98,7 +98,7 @@ public class MessageTransaction extends Transaction { return ValidationResult.INVALID_DATA_LENGTH; // Check reference is correct - Account sender = new PublicKeyAccount(this.repository, messageTransactionData.getSenderPublicKey()); + Account sender = getSender(); if (!Arrays.equals(sender.getLastReference(), messageTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; diff --git a/src/qora/transaction/MultiPaymentTransaction.java b/src/qora/transaction/MultiPaymentTransaction.java index a8def611..bf3d3fb9 100644 --- a/src/qora/transaction/MultiPaymentTransaction.java +++ b/src/qora/transaction/MultiPaymentTransaction.java @@ -98,7 +98,7 @@ public class MultiPaymentTransaction extends Transaction { return ValidationResult.INVALID_PAYMENTS_COUNT; // Check reference is correct - PublicKeyAccount sender = new PublicKeyAccount(this.repository, multiPaymentTransactionData.getSenderPublicKey()); + Account sender = getSender(); if (!Arrays.equals(sender.getLastReference(), multiPaymentTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; diff --git a/src/qora/transaction/PaymentTransaction.java b/src/qora/transaction/PaymentTransaction.java index 31d5cf21..4ce901e2 100644 --- a/src/qora/transaction/PaymentTransaction.java +++ b/src/qora/transaction/PaymentTransaction.java @@ -74,7 +74,7 @@ public class PaymentTransaction extends Transaction { public ValidationResult isValid() throws DataException { // Check reference is correct - Account sender = new PublicKeyAccount(repository, paymentTransactionData.getSenderPublicKey()); + Account sender = getSender(); if (!Arrays.equals(sender.getLastReference(), paymentTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; diff --git a/src/qora/transaction/RegisterNameTransaction.java b/src/qora/transaction/RegisterNameTransaction.java index 6051de31..7cb9cf1f 100644 --- a/src/qora/transaction/RegisterNameTransaction.java +++ b/src/qora/transaction/RegisterNameTransaction.java @@ -102,7 +102,7 @@ public class RegisterNameTransaction extends Transaction { return ValidationResult.NEGATIVE_FEE; // Check reference is correct - PublicKeyAccount registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey()); + Account registrant = getRegistrant(); if (!Arrays.equals(registrant.getLastReference(), registerNameTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; @@ -124,7 +124,7 @@ public class RegisterNameTransaction extends Transaction { this.repository.getTransactionRepository().save(registerNameTransactionData); // Update registrant's balance - Account registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey()); + Account registrant = getRegistrant(); registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).subtract(registerNameTransactionData.getFee())); // Update registrant's reference @@ -141,7 +141,7 @@ public class RegisterNameTransaction extends Transaction { this.repository.getTransactionRepository().delete(registerNameTransactionData); // Update registrant's balance - Account registrant = new PublicKeyAccount(this.repository, registerNameTransactionData.getRegistrantPublicKey()); + Account registrant = getRegistrant(); registrant.setConfirmedBalance(Asset.QORA, registrant.getConfirmedBalance(Asset.QORA).add(registerNameTransactionData.getFee())); // Update registrant's reference diff --git a/src/qora/transaction/SellNameTransaction.java b/src/qora/transaction/SellNameTransaction.java index a718efee..2e6e1b05 100644 --- a/src/qora/transaction/SellNameTransaction.java +++ b/src/qora/transaction/SellNameTransaction.java @@ -89,7 +89,7 @@ public class SellNameTransaction extends Transaction { return ValidationResult.NAME_ALREADY_FOR_SALE; // Check transaction's public key matches name's current owner - Account owner = new PublicKeyAccount(this.repository, sellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); if (!owner.getAddress().equals(nameData.getOwner())) return ValidationResult.INVALID_NAME_OWNER; @@ -114,7 +114,6 @@ public class SellNameTransaction extends Transaction { return ValidationResult.NO_BALANCE; return ValidationResult.OK; - } @Override @@ -127,7 +126,7 @@ public class SellNameTransaction extends Transaction { this.repository.getTransactionRepository().save(sellNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, sellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(sellNameTransactionData.getFee())); // Update owner's reference @@ -144,7 +143,7 @@ public class SellNameTransaction extends Transaction { this.repository.getTransactionRepository().delete(sellNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, sellNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(sellNameTransactionData.getFee())); // Update owner's reference diff --git a/src/qora/transaction/Transaction.java b/src/qora/transaction/Transaction.java index 9df0e5f5..2c4156b2 100644 --- a/src/qora/transaction/Transaction.java +++ b/src/qora/transaction/Transaction.java @@ -44,12 +44,13 @@ public abstract class Transaction { // Validation results public enum ValidationResult { OK(1), INVALID_ADDRESS(2), NEGATIVE_AMOUNT(3), NEGATIVE_FEE(4), NO_BALANCE(5), INVALID_REFERENCE(6), INVALID_NAME_LENGTH(7), INVALID_VALUE_LENGTH( - 8), NAME_ALREADY_REGISTERED(9), NAME_DOES_NOT_EXIST(10), INVALID_NAME_OWNER(11), NAME_ALREADY_FOR_SALE(12), NAME_NOT_FOR_SALE(13), INVALID_AMOUNT( - 15), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH(18), INVALID_OPTIONS_COUNT(19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION( - 21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST(24), POLL_OPTION_DOES_NOT_EXIST(25), ALREADY_VOTED_FOR_THAT_OPTION( - 26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY(28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(30), HAVE_EQUALS_WANT( - 31), ORDER_DOES_NOT_EXIST(32), INVALID_ORDER_CREATOR( - 33), INVALID_PAYMENTS_COUNT(34), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000); + 8), NAME_ALREADY_REGISTERED(9), NAME_DOES_NOT_EXIST(10), INVALID_NAME_OWNER(11), NAME_ALREADY_FOR_SALE(12), NAME_NOT_FOR_SALE( + 13), BUYER_ALREADY_OWNER(14), INVALID_AMOUNT(15), INVALID_SELLER(16), NAME_NOT_LOWER_CASE(17), INVALID_DESCRIPTION_LENGTH( + 18), INVALID_OPTIONS_COUNT(19), INVALID_OPTION_LENGTH(20), DUPLICATE_OPTION(21), POLL_ALREADY_EXISTS(22), POLL_DOES_NOT_EXIST( + 24), POLL_OPTION_DOES_NOT_EXIST(25), ALREADY_VOTED_FOR_THAT_OPTION(26), INVALID_DATA_LENGTH(27), INVALID_QUANTITY( + 28), ASSET_DOES_NOT_EXIST(29), INVALID_RETURN(30), HAVE_EQUALS_WANT(31), ORDER_DOES_NOT_EXIST( + 32), INVALID_ORDER_CREATOR(33), INVALID_PAYMENTS_COUNT( + 34), NEGATIVE_PRICE(35), ASSET_ALREADY_EXISTS(43), NOT_YET_RELEASED(1000); public final int value; @@ -117,6 +118,9 @@ public abstract class Transaction { case CANCEL_SELL_NAME: return new CancelSellNameTransaction(repository, transactionData); + case BUY_NAME: + return new BuyNameTransaction(repository, transactionData); + case CREATE_POLL: return new CreatePollTransaction(repository, transactionData); diff --git a/src/qora/transaction/TransferAssetTransaction.java b/src/qora/transaction/TransferAssetTransaction.java index e6d861a8..6b410dc3 100644 --- a/src/qora/transaction/TransferAssetTransaction.java +++ b/src/qora/transaction/TransferAssetTransaction.java @@ -89,7 +89,7 @@ public class TransferAssetTransaction extends Transaction { return ValidationResult.NOT_YET_RELEASED; // Check reference is correct - PublicKeyAccount sender = new PublicKeyAccount(this.repository, transferAssetTransactionData.getSenderPublicKey()); + Account sender = getSender(); if (!Arrays.equals(sender.getLastReference(), transferAssetTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; diff --git a/src/qora/transaction/UpdateNameTransaction.java b/src/qora/transaction/UpdateNameTransaction.java index 6e3cd3c6..713bc858 100644 --- a/src/qora/transaction/UpdateNameTransaction.java +++ b/src/qora/transaction/UpdateNameTransaction.java @@ -105,7 +105,7 @@ public class UpdateNameTransaction extends Transaction { return ValidationResult.NAME_ALREADY_FOR_SALE; // Check transaction's public key matches name's current owner - Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); if (!owner.getAddress().equals(nameData.getOwner())) return ValidationResult.INVALID_NAME_OWNER; @@ -134,7 +134,7 @@ public class UpdateNameTransaction extends Transaction { this.repository.getTransactionRepository().save(updateNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).subtract(updateNameTransactionData.getFee())); // Update owner's reference @@ -151,7 +151,7 @@ public class UpdateNameTransaction extends Transaction { this.repository.getTransactionRepository().delete(updateNameTransactionData); // Update owner's balance - Account owner = new PublicKeyAccount(this.repository, updateNameTransactionData.getOwnerPublicKey()); + Account owner = getOwner(); owner.setConfirmedBalance(Asset.QORA, owner.getConfirmedBalance(Asset.QORA).add(updateNameTransactionData.getFee())); // Update owner's reference diff --git a/src/qora/transaction/VoteOnPollTransaction.java b/src/qora/transaction/VoteOnPollTransaction.java index 794daef9..ac7deb8d 100644 --- a/src/qora/transaction/VoteOnPollTransaction.java +++ b/src/qora/transaction/VoteOnPollTransaction.java @@ -56,6 +56,12 @@ public class VoteOnPollTransaction extends Transaction { return amount; } + // Navigation + + public Account getVoter() throws DataException { + return new PublicKeyAccount(this.repository, voteOnPollTransactionData.getVoterPublicKey()); + } + // Processing @Override @@ -98,13 +104,13 @@ public class VoteOnPollTransaction extends Transaction { return ValidationResult.NEGATIVE_FEE; // Check reference is correct - PublicKeyAccount creator = new PublicKeyAccount(this.repository, voteOnPollTransactionData.getCreatorPublicKey()); + Account voter = getVoter(); - if (!Arrays.equals(creator.getLastReference(), voteOnPollTransactionData.getReference())) + if (!Arrays.equals(voter.getLastReference(), voteOnPollTransactionData.getReference())) return ValidationResult.INVALID_REFERENCE; - // Check issuer has enough funds - if (creator.getConfirmedBalance(Asset.QORA).compareTo(voteOnPollTransactionData.getFee()) == -1) + // Check voter has enough funds + if (voter.getConfirmedBalance(Asset.QORA).compareTo(voteOnPollTransactionData.getFee()) == -1) return ValidationResult.NO_BALANCE; return ValidationResult.OK; @@ -113,7 +119,7 @@ public class VoteOnPollTransaction extends Transaction { @Override public void process() throws DataException { // Update voter's balance - Account voter = new PublicKeyAccount(this.repository, voteOnPollTransactionData.getVoterPublicKey()); + Account voter = getVoter(); voter.setConfirmedBalance(Asset.QORA, voter.getConfirmedBalance(Asset.QORA).subtract(voteOnPollTransactionData.getFee())); // Update vote's reference @@ -138,11 +144,11 @@ public class VoteOnPollTransaction extends Transaction { @Override public void orphan() throws DataException { - // Update issuer's balance - Account voter = new PublicKeyAccount(this.repository, voteOnPollTransactionData.getVoterPublicKey()); + // Update voter's balance + Account voter = getVoter(); voter.setConfirmedBalance(Asset.QORA, voter.getConfirmedBalance(Asset.QORA).add(voteOnPollTransactionData.getFee())); - // Update issuer's reference + // Update voter's reference voter.setLastReference(voteOnPollTransactionData.getReference()); // Does this transaction have previous vote info? diff --git a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java index 3fb9d368..0610a9bb 100644 --- a/src/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -190,7 +190,7 @@ public class HSQLDBDatabaseUpdates { case 9: // Buy Name Transactions stmt.execute("CREATE TABLE BuyNameTransactions (signature Signature, buyer QoraPublicKey NOT NULL, name RegisteredName NOT NULL, " - + "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, " + + "seller QoraAddress NOT NULL, amount QoraAmount NOT NULL, name_reference Signature NOT NULL, " + "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)"); break; diff --git a/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java new file mode 100644 index 00000000..1daef638 --- /dev/null +++ b/src/repository/hsqldb/transaction/HSQLDBBuyNameTransactionRepository.java @@ -0,0 +1,54 @@ +package repository.hsqldb.transaction; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; + +import data.transaction.BuyNameTransactionData; +import data.transaction.TransactionData; +import repository.DataException; +import repository.hsqldb.HSQLDBRepository; +import repository.hsqldb.HSQLDBSaver; + +public class HSQLDBBuyNameTransactionRepository extends HSQLDBTransactionRepository { + + public HSQLDBBuyNameTransactionRepository(HSQLDBRepository repository) { + this.repository = repository; + } + + TransactionData fromBase(byte[] signature, byte[] reference, byte[] buyerPublicKey, long timestamp, BigDecimal fee) throws DataException { + try { + ResultSet rs = this.repository.checkedExecute("SELECT name, amount, seller, name_reference FROM BuyNameTransactions WHERE signature = ?", + signature); + if (rs == null) + return null; + + String name = rs.getString(1); + BigDecimal amount = rs.getBigDecimal(2); + String seller = rs.getString(3); + byte[] nameReference = rs.getBytes(4); + + return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, nameReference, fee, timestamp, reference, signature); + } catch (SQLException e) { + throw new DataException("Unable to fetch buy name transaction from repository", e); + } + } + + @Override + public void save(TransactionData transactionData) throws DataException { + BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; + + HSQLDBSaver saveHelper = new HSQLDBSaver("BuyNameTransactions"); + + saveHelper.bind("signature", buyNameTransactionData.getSignature()).bind("buyer", buyNameTransactionData.getBuyerPublicKey()) + .bind("name", buyNameTransactionData.getName()).bind("amount", buyNameTransactionData.getAmount()) + .bind("seller", buyNameTransactionData.getSeller()).bind("name_reference", buyNameTransactionData.getNameReference()); + + try { + saveHelper.execute(this.repository); + } catch (SQLException e) { + throw new DataException("Unable to save buy name transaction into repository", e); + } + } + +} diff --git a/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java index cce2766a..aa9ee082 100644 --- a/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java +++ b/src/repository/hsqldb/transaction/HSQLDBMessageTransactionRepository.java @@ -32,7 +32,7 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit Long assetId = rs.getLong(7); byte[] data = rs.getBytes(8); - return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, fee, data, isText, isEncrypted, timestamp, reference, + return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, signature); } catch (SQLException e) { throw new DataException("Unable to fetch message transaction from repository", e); diff --git a/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java index e08e5ebd..539cda3c 100644 --- a/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java +++ b/src/repository/hsqldb/transaction/HSQLDBMultiPaymentTransactionRepository.java @@ -20,7 +20,7 @@ public class HSQLDBMultiPaymentTransactionRepository extends HSQLDBTransactionRe TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException { try { - ResultSet rs = this.repository.checkedExecute("SELECT sender MultiPaymentTransactions WHERE signature = ?", signature); + ResultSet rs = this.repository.checkedExecute("SELECT sender from MultiPaymentTransactions WHERE signature = ?", signature); if (rs == null) return null; diff --git a/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java b/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java index 41acff47..2182ff7c 100644 --- a/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java +++ b/src/repository/hsqldb/transaction/HSQLDBTransactionRepository.java @@ -25,6 +25,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { private HSQLDBUpdateNameTransactionRepository updateNameTransactionRepository; private HSQLDBSellNameTransactionRepository sellNameTransactionRepository; private HSQLDBCancelSellNameTransactionRepository cancelSellNameTransactionRepository; + private HSQLDBBuyNameTransactionRepository buyNameTransactionRepository; private HSQLDBCreatePollTransactionRepository createPollTransactionRepository; private HSQLDBVoteOnPollTransactionRepository voteOnPollTransactionRepository; private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository; @@ -42,6 +43,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository { this.updateNameTransactionRepository = new HSQLDBUpdateNameTransactionRepository(repository); this.sellNameTransactionRepository = new HSQLDBSellNameTransactionRepository(repository); this.cancelSellNameTransactionRepository = new HSQLDBCancelSellNameTransactionRepository(repository); + this.buyNameTransactionRepository = new HSQLDBBuyNameTransactionRepository(repository); this.createPollTransactionRepository = new HSQLDBCreatePollTransactionRepository(repository); this.voteOnPollTransactionRepository = new HSQLDBVoteOnPollTransactionRepository(repository); this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository); @@ -112,6 +114,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository { case CANCEL_SELL_NAME: return this.cancelSellNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee); + case BUY_NAME: + return this.buyNameTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee); + case CREATE_POLL: return this.createPollTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee); @@ -256,6 +261,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository { this.cancelSellNameTransactionRepository.save(transactionData); break; + case BUY_NAME: + this.buyNameTransactionRepository.save(transactionData); + break; + case CREATE_POLL: this.createPollTransactionRepository.save(transactionData); break; diff --git a/src/test/SerializationTests.java b/src/test/SerializationTests.java index 980a4af6..ede95240 100644 --- a/src/test/SerializationTests.java +++ b/src/test/SerializationTests.java @@ -61,9 +61,9 @@ public class SerializationTests extends Common { TransactionData parsedTransactionData = TransactionTransformer.fromBytes(bytes); - assertTrue(Arrays.equals(transactionData.getSignature(), parsedTransactionData.getSignature())); + assertTrue("Transaction signature mismatch", Arrays.equals(transactionData.getSignature(), parsedTransactionData.getSignature())); - assertEquals(TransactionTransformer.getDataLength(transactionData), bytes.length); + assertEquals("Data length mismatch", TransactionTransformer.getDataLength(transactionData), bytes.length); } private void testSpecificBlockTransactions(int height, TransactionType type) throws DataException, TransformationException { @@ -84,13 +84,11 @@ public class SerializationTests extends Common { @Test public void testPaymentSerialization() throws TransformationException, DataException { - // Blocks 390 & 754 have only payment transactions testSpecificBlockTransactions(754, TransactionType.PAYMENT); } @Test public void testRegisterNameSerialization() throws TransformationException, DataException { - // Block 120 has only name registration transactions testSpecificBlockTransactions(120, TransactionType.REGISTER_NAME); } @@ -109,12 +107,46 @@ public class SerializationTests extends Common { testSpecificBlockTransactions(741, TransactionType.CANCEL_SELL_NAME); } + @Test + public void testBuyNameSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(973, TransactionType.BUY_NAME); + } + @Test public void testCreatePollSerialization() throws TransformationException, DataException { - // Block 10537 has only create poll transactions testSpecificBlockTransactions(10537, TransactionType.CREATE_POLL); } + @Test + public void testVoteOnPollSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(10540, TransactionType.CREATE_POLL); + } + + @Test + public void testIssueAssetSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(33661, TransactionType.ISSUE_ASSET); + } + + @Test + public void testTransferAssetSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(39039, TransactionType.TRANSFER_ASSET); + } + + @Test + public void testCreateAssetOrderSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(35611, TransactionType.CREATE_ASSET_ORDER); + } + + @Test + public void testCancelAssetOrderSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(36176, TransactionType.CANCEL_ASSET_ORDER); + } + + @Test + public void testMultiPaymentSerialization() throws TransformationException, DataException { + testSpecificBlockTransactions(34500, TransactionType.MULTIPAYMENT); + } + @Test public void testMessageSerialization() throws TransformationException, DataException { // Message transactions went live block 99000 diff --git a/src/test/TransactionTests.java b/src/test/TransactionTests.java index c3348d25..5aad9355 100644 --- a/src/test/TransactionTests.java +++ b/src/test/TransactionTests.java @@ -2,6 +2,7 @@ package test; import static org.junit.Assert.*; +import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; @@ -16,8 +17,10 @@ import data.account.AccountBalanceData; import data.account.AccountData; import data.block.BlockData; import data.naming.NameData; +import data.transaction.BuyNameTransactionData; import data.transaction.CancelSellNameTransactionData; import data.transaction.CreatePollTransactionData; +import data.transaction.MessageTransactionData; import data.transaction.PaymentTransactionData; import data.transaction.RegisterNameTransactionData; import data.transaction.SellNameTransactionData; @@ -32,8 +35,10 @@ import qora.account.PublicKeyAccount; import qora.assets.Asset; import qora.block.Block; import qora.block.BlockChain; +import qora.transaction.BuyNameTransaction; import qora.transaction.CancelSellNameTransaction; import qora.transaction.CreatePollTransaction; +import qora.transaction.MessageTransaction; import qora.transaction.PaymentTransaction; import qora.transaction.RegisterNameTransaction; import qora.transaction.SellNameTransaction; @@ -116,6 +121,19 @@ public class TransactionTests { RepositoryManager.closeRepositoryFactory(); } + private Transaction createPayment(PrivateKeyAccount sender, String recipient) throws DataException { + // Make a new payment transaction + BigDecimal amount = BigDecimal.valueOf(1_000L); + BigDecimal fee = BigDecimal.ONE; + long timestamp = parentBlockData.getTimestamp() + 1_000; + PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient, amount, fee, timestamp, reference); + + Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData); + paymentTransaction.calcSignature(sender); + + return paymentTransaction; + } + @Test public void testPaymentTransaction() throws DataException { createTestAccounts(null); @@ -360,6 +378,70 @@ public class TransactionTests { parentBlockData = block.getBlockData(); } + @Test + public void testBuyNameTransaction() throws DataException { + // Register and sell name using another test + testSellNameTransaction(); + + String name = "test name"; + NameData originalNameData = this.repository.getNameRepository().fromName(name); + String seller = originalNameData.getOwner(); + + // Buyer + PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed); + byte[] nameReference = reference; + + // Send buyer some funds so they have a reference + Transaction somePaymentTransaction = createPayment(sender, buyer.getAddress()); + byte[] buyersReference = somePaymentTransaction.getTransactionData().getSignature(); + + // Forge new block with transaction + Block block = new Block(repository, parentBlockData, generator, null, null); + block.addTransaction(somePaymentTransaction.getTransactionData()); + block.sign(); + + block.process(); + repository.saveChanges(); + parentBlockData = block.getBlockData(); + + BigDecimal fee = BigDecimal.ONE; + long timestamp = parentBlockData.getTimestamp() + 1_000; + BuyNameTransactionData buyNameTransactionData = new BuyNameTransactionData(buyer.getPublicKey(), name, originalNameData.getSalePrice(), seller, + nameReference, fee, timestamp, buyersReference); + + Transaction buyNameTransaction = new BuyNameTransaction(repository, buyNameTransactionData); + buyNameTransaction.calcSignature(buyer); + assertTrue(buyNameTransaction.isSignatureValid()); + assertEquals(ValidationResult.OK, buyNameTransaction.isValid()); + + // Forge new block with transaction + block = new Block(repository, parentBlockData, generator, null, null); + block.addTransaction(buyNameTransactionData); + block.sign(); + + assertTrue("Block signatures invalid", block.isSignatureValid()); + assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid()); + + block.process(); + repository.saveChanges(); + + // Check name was updated + NameData actualNameData = this.repository.getNameRepository().fromName(name); + assertFalse(actualNameData.getIsForSale()); + assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice()); + assertEquals(buyer.getAddress(), actualNameData.getOwner()); + + // Now orphan block + block.orphan(); + repository.saveChanges(); + + // Check name has been reverted correctly + actualNameData = this.repository.getNameRepository().fromName(name); + assertTrue(actualNameData.getIsForSale()); + assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice()); + assertEquals(originalNameData.getOwner(), actualNameData.getOwner()); + } + @Test public void testCreatePollTransaction() throws DataException { // This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP @@ -491,4 +573,78 @@ public class TransactionTests { assertTrue("Wrong voter public key", Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey())); } + @Test + public void testIssueAssetTransaction() throws DataException { + // TODO + } + + @Test + public void testTransferAssetTransaction() throws DataException { + // TODO + } + + @Test + public void testCreateAssetOrderTransaction() throws DataException { + // TODO + } + + @Test + public void testCancelAssetOrderTransaction() throws DataException { + // TODO + } + + @Test + public void testMultiPaymentTransaction() throws DataException { + // TODO + } + + @Test + public void testMessageTransaction() throws DataException, UnsupportedEncodingException { + createTestAccounts(null); + + // Make a new message transaction + Account recipient = new PublicKeyAccount(repository, recipientSeed); + BigDecimal amount = BigDecimal.valueOf(1_000L); + BigDecimal fee = BigDecimal.ONE; + long timestamp = parentBlockData.getTimestamp() + 1_000; + int version = Transaction.getVersionByTimestamp(timestamp); + byte[] data = "test".getBytes("UTF-8"); + boolean isText = true; + boolean isEncrypted = false; + + MessageTransactionData messageTransactionData = new MessageTransactionData(version, sender.getPublicKey(), recipient.getAddress(), Asset.QORA, amount, + data, isText, isEncrypted, fee, timestamp, reference); + + Transaction messageTransaction = new MessageTransaction(repository, messageTransactionData); + messageTransaction.calcSignature(sender); + assertTrue(messageTransaction.isSignatureValid()); + assertEquals(ValidationResult.OK, messageTransaction.isValid()); + + // Forge new block with message transaction + Block block = new Block(repository, parentBlockData, generator, null, null); + block.addTransaction(messageTransactionData); + block.sign(); + + assertTrue("Block signatures invalid", block.isSignatureValid()); + assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid()); + + block.process(); + repository.saveChanges(); + + // Check sender's balance + BigDecimal expectedBalance = senderBalance.subtract(amount).subtract(fee); + BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance(); + assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0); + + // Fee should be in generator's balance + expectedBalance = generatorBalance.add(fee); + actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance(); + assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0); + + // Amount should be in recipient's balance + expectedBalance = amount; + actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance(); + assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0); + } + } \ No newline at end of file diff --git a/src/transform/PaymentTransformer.java b/src/transform/PaymentTransformer.java index 6c197e55..b664cd43 100644 --- a/src/transform/PaymentTransformer.java +++ b/src/transform/PaymentTransformer.java @@ -11,7 +11,6 @@ import com.google.common.primitives.Longs; import data.PaymentData; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class PaymentTransformer extends Transformer { @@ -27,7 +26,7 @@ public class PaymentTransformer extends Transformer { if (byteBuffer.remaining() < TOTAL_LENGTH) throw new TransformationException("Byte data too short for payment information"); - String recipient = Serialization.deserializeRecipient(byteBuffer); + String recipient = Serialization.deserializeAddress(byteBuffer); long assetId = byteBuffer.getLong(); BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); @@ -42,7 +41,7 @@ public class PaymentTransformer extends Transformer { try { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - bytes.write(Base58.decode(paymentData.getRecipient())); + Serialization.serializeAddress(bytes, paymentData.getRecipient()); bytes.write(Longs.toByteArray(paymentData.getAssetId())); Serialization.serializeBigDecimal(bytes, paymentData.getAmount()); diff --git a/src/transform/transaction/BuyNameTransactionTransformer.java b/src/transform/transaction/BuyNameTransactionTransformer.java new file mode 100644 index 00000000..a99d52b0 --- /dev/null +++ b/src/transform/transaction/BuyNameTransactionTransformer.java @@ -0,0 +1,118 @@ +package transform.transaction; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.ByteBuffer; + +import org.json.simple.JSONObject; + +import com.google.common.base.Utf8; +import com.google.common.hash.HashCode; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; + +import data.transaction.BuyNameTransactionData; +import data.transaction.TransactionData; +import qora.account.PublicKeyAccount; +import qora.naming.Name; +import transform.TransformationException; +import utils.Serialization; + +public class BuyNameTransactionTransformer extends TransactionTransformer { + + // Property lengths + private static final int BUYER_LENGTH = PUBLIC_KEY_LENGTH; + private static final int NAME_SIZE_LENGTH = INT_LENGTH; + private static final int AMOUNT_LENGTH = BIG_DECIMAL_LENGTH; + private static final int SELLER_LENGTH = ADDRESS_LENGTH; + + private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + BUYER_LENGTH + NAME_SIZE_LENGTH + AMOUNT_LENGTH + SELLER_LENGTH; + + static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { + if (byteBuffer.remaining() < TYPELESS_DATALESS_LENGTH) + throw new TransformationException("Byte data too short for BuyNameTransaction"); + + long timestamp = byteBuffer.getLong(); + + byte[] reference = new byte[REFERENCE_LENGTH]; + byteBuffer.get(reference); + + byte[] buyerPublicKey = Serialization.deserializePublicKey(byteBuffer); + + String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); + + // Still need to make sure there are enough bytes left for remaining fields + if (byteBuffer.remaining() < AMOUNT_LENGTH + SELLER_LENGTH + FEE_LENGTH + SIGNATURE_LENGTH) + throw new TransformationException("Byte data too short for BuyNameTransaction"); + + BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); + + String seller = Serialization.deserializeAddress(byteBuffer); + + BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); + + byte[] signature = new byte[SIGNATURE_LENGTH]; + byteBuffer.get(signature); + + return new BuyNameTransactionData(buyerPublicKey, name, amount, seller, fee, timestamp, reference, signature); + } + + public static int getDataLength(TransactionData transactionData) throws TransformationException { + BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; + + int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(buyNameTransactionData.getName()); + + return dataLength; + } + + public static byte[] toBytes(TransactionData transactionData) throws TransformationException { + try { + BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + bytes.write(Ints.toByteArray(buyNameTransactionData.getType().value)); + bytes.write(Longs.toByteArray(buyNameTransactionData.getTimestamp())); + bytes.write(buyNameTransactionData.getReference()); + + bytes.write(buyNameTransactionData.getBuyerPublicKey()); + Serialization.serializeSizedString(bytes, buyNameTransactionData.getName()); + Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getAmount()); + Serialization.serializeAddress(bytes, buyNameTransactionData.getSeller()); + + Serialization.serializeBigDecimal(bytes, buyNameTransactionData.getFee()); + + if (buyNameTransactionData.getSignature() != null) + bytes.write(buyNameTransactionData.getSignature()); + + return bytes.toByteArray(); + } catch (IOException | ClassCastException e) { + throw new TransformationException(e); + } + } + + @SuppressWarnings("unchecked") + public static JSONObject toJSON(TransactionData transactionData) throws TransformationException { + JSONObject json = TransactionTransformer.getBaseJSON(transactionData); + + try { + BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData; + + byte[] buyerPublicKey = buyNameTransactionData.getBuyerPublicKey(); + + json.put("buyer", PublicKeyAccount.getAddress(buyerPublicKey)); + json.put("buyerPublicKey", HashCode.fromBytes(buyerPublicKey).toString()); + + json.put("name", buyNameTransactionData.getName()); + json.put("amount", buyNameTransactionData.getAmount().toPlainString()); + + json.put("seller", buyNameTransactionData.getSeller()); + } catch (ClassCastException e) { + throw new TransformationException(e); + } + + return json; + } + +} diff --git a/src/transform/transaction/CancelOrderTransactionTransformer.java b/src/transform/transaction/CancelOrderTransactionTransformer.java index 1c08d38a..6f02945d 100644 --- a/src/transform/transaction/CancelOrderTransactionTransformer.java +++ b/src/transform/transaction/CancelOrderTransactionTransformer.java @@ -21,7 +21,7 @@ import utils.Serialization; public class CancelOrderTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int CREATOR_LENGTH = LONG_LENGTH; + private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int ORDER_ID_LENGTH = SIGNATURE_LENGTH; private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + ORDER_ID_LENGTH; diff --git a/src/transform/transaction/CreateOrderTransactionTransformer.java b/src/transform/transaction/CreateOrderTransactionTransformer.java index 6bdd0295..1ef5a7d4 100644 --- a/src/transform/transaction/CreateOrderTransactionTransformer.java +++ b/src/transform/transaction/CreateOrderTransactionTransformer.java @@ -20,10 +20,11 @@ import utils.Serialization; public class CreateOrderTransactionTransformer extends TransactionTransformer { // Property lengths + private static final int CREATOR_LENGTH = PUBLIC_KEY_LENGTH; private static final int ASSET_ID_LENGTH = LONG_LENGTH; private static final int AMOUNT_LENGTH = 12; // Not standard BIG_DECIMAL_LENGTH - private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + (ASSET_ID_LENGTH + AMOUNT_LENGTH) * 2; + private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + CREATOR_LENGTH + (ASSET_ID_LENGTH + AMOUNT_LENGTH) * 2; static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { if (byteBuffer.remaining() < TYPELESS_LENGTH) diff --git a/src/transform/transaction/CreatePollTransactionTransformer.java b/src/transform/transaction/CreatePollTransactionTransformer.java index 971c0ac4..3338a018 100644 --- a/src/transform/transaction/CreatePollTransactionTransformer.java +++ b/src/transform/transaction/CreatePollTransactionTransformer.java @@ -21,7 +21,6 @@ import data.voting.PollOptionData; import qora.account.PublicKeyAccount; import qora.voting.Poll; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class CreatePollTransactionTransformer extends TransactionTransformer { @@ -47,7 +46,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { byte[] creatorPublicKey = Serialization.deserializePublicKey(byteBuffer); - String owner = Serialization.deserializeRecipient(byteBuffer); + String owner = Serialization.deserializeAddress(byteBuffer); String pollName = Serialization.deserializeSizedString(byteBuffer, Poll.MAX_NAME_SIZE); String description = Serialization.deserializeSizedString(byteBuffer, Poll.MAX_DESCRIPTION_SIZE); @@ -103,7 +102,7 @@ public class CreatePollTransactionTransformer extends TransactionTransformer { bytes.write(createPollTransactionData.getReference()); bytes.write(createPollTransactionData.getCreatorPublicKey()); - bytes.write(Base58.decode(createPollTransactionData.getOwner())); + Serialization.serializeAddress(bytes, createPollTransactionData.getOwner()); Serialization.serializeSizedString(bytes, createPollTransactionData.getPollName()); Serialization.serializeSizedString(bytes, createPollTransactionData.getDescription()); diff --git a/src/transform/transaction/GenesisTransactionTransformer.java b/src/transform/transaction/GenesisTransactionTransformer.java index 1d1e8ec2..2cf1132d 100644 --- a/src/transform/transaction/GenesisTransactionTransformer.java +++ b/src/transform/transaction/GenesisTransactionTransformer.java @@ -13,7 +13,6 @@ import com.google.common.primitives.Longs; import data.transaction.TransactionData; import data.transaction.GenesisTransactionData; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class GenesisTransactionTransformer extends TransactionTransformer { @@ -30,7 +29,7 @@ public class GenesisTransactionTransformer extends TransactionTransformer { throw new TransformationException("Byte data too short for GenesisTransaction"); long timestamp = byteBuffer.getLong(); - String recipient = Serialization.deserializeRecipient(byteBuffer); + String recipient = Serialization.deserializeAddress(byteBuffer); BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); return new GenesisTransactionData(recipient, amount, timestamp); @@ -49,7 +48,7 @@ public class GenesisTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(genesisTransactionData.getType().value)); bytes.write(Longs.toByteArray(genesisTransactionData.getTimestamp())); - bytes.write(Base58.decode(genesisTransactionData.getRecipient())); + Serialization.serializeAddress(bytes, genesisTransactionData.getRecipient()); Serialization.serializeBigDecimal(bytes, genesisTransactionData.getAmount()); return bytes.toByteArray(); diff --git a/src/transform/transaction/IssueAssetTransactionTransformer.java b/src/transform/transaction/IssueAssetTransactionTransformer.java index 9319a5b2..b8e29b95 100644 --- a/src/transform/transaction/IssueAssetTransactionTransformer.java +++ b/src/transform/transaction/IssueAssetTransactionTransformer.java @@ -17,7 +17,6 @@ import qora.account.PublicKeyAccount; import qora.transaction.IssueAssetTransaction; import data.transaction.IssueAssetTransactionData; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class IssueAssetTransactionTransformer extends TransactionTransformer { @@ -43,7 +42,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { byteBuffer.get(reference); byte[] issuerPublicKey = Serialization.deserializePublicKey(byteBuffer); - String owner = Serialization.deserializeRecipient(byteBuffer); + String owner = Serialization.deserializeAddress(byteBuffer); String assetName = Serialization.deserializeSizedString(byteBuffer, IssueAssetTransaction.MAX_NAME_SIZE); String description = Serialization.deserializeSizedString(byteBuffer, IssueAssetTransaction.MAX_DESCRIPTION_SIZE); @@ -81,7 +80,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer { bytes.write(issueAssetTransactionData.getReference()); bytes.write(issueAssetTransactionData.getIssuerPublicKey()); - bytes.write(Base58.decode(issueAssetTransactionData.getOwner())); + Serialization.serializeAddress(bytes, issueAssetTransactionData.getOwner()); Serialization.serializeSizedString(bytes, issueAssetTransactionData.getAssetName()); Serialization.serializeSizedString(bytes, issueAssetTransactionData.getDescription()); bytes.write(Longs.toByteArray(issueAssetTransactionData.getQuantity())); diff --git a/src/transform/transaction/MessageTransactionTransformer.java b/src/transform/transaction/MessageTransactionTransformer.java index 5a3f432c..c3f60801 100644 --- a/src/transform/transaction/MessageTransactionTransformer.java +++ b/src/transform/transaction/MessageTransactionTransformer.java @@ -18,7 +18,6 @@ import qora.assets.Asset; import qora.transaction.MessageTransaction; import data.transaction.MessageTransactionData; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class MessageTransactionTransformer extends TransactionTransformer { @@ -54,7 +53,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { byteBuffer.get(reference); byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer); - String recipient = Serialization.deserializeRecipient(byteBuffer); + String recipient = Serialization.deserializeAddress(byteBuffer); long assetId; if (version == 1) @@ -84,7 +83,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, fee, data, isText, isEncrypted, timestamp, reference, + return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference, signature); } @@ -108,7 +107,7 @@ public class MessageTransactionTransformer extends TransactionTransformer { bytes.write(messageTransactionData.getReference()); bytes.write(messageTransactionData.getSenderPublicKey()); - bytes.write(Base58.decode(messageTransactionData.getRecipient())); + Serialization.serializeAddress(bytes, messageTransactionData.getRecipient()); if (messageTransactionData.getVersion() != 1) bytes.write(Longs.toByteArray(messageTransactionData.getAssetId())); diff --git a/src/transform/transaction/MultiPaymentTransactionTransformer.java b/src/transform/transaction/MultiPaymentTransactionTransformer.java index 93878247..59d853c8 100644 --- a/src/transform/transaction/MultiPaymentTransactionTransformer.java +++ b/src/transform/transaction/MultiPaymentTransactionTransformer.java @@ -32,7 +32,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException { if (byteBuffer.remaining() < TYPELESS_LENGTH) - throw new TransformationException("Byte data too short for PaymentTransaction"); + throw new TransformationException("Byte data too short for MultiPaymentTransaction"); long timestamp = byteBuffer.getLong(); @@ -45,7 +45,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { // Check remaining buffer size int minRemaining = paymentsCount * PaymentTransformer.getDataLength() + FEE_LENGTH + SIGNATURE_LENGTH; if (byteBuffer.remaining() < minRemaining) - throw new TransformationException("Byte data too short for PaymentTransaction"); + throw new TransformationException("Byte data too short for MultiPaymentTransaction"); List payments = new ArrayList(); for (int i = 0; i < paymentsCount; ++i) @@ -81,7 +81,7 @@ public class MultiPaymentTransactionTransformer extends TransactionTransformer { bytes.write(Ints.toByteArray(payments.size())); for (PaymentData paymentData : payments) - PaymentTransformer.toBytes(paymentData); + bytes.write(PaymentTransformer.toBytes(paymentData)); Serialization.serializeBigDecimal(bytes, multiPaymentTransactionData.getFee()); diff --git a/src/transform/transaction/PaymentTransactionTransformer.java b/src/transform/transaction/PaymentTransactionTransformer.java index 019d67ac..7c39c791 100644 --- a/src/transform/transaction/PaymentTransactionTransformer.java +++ b/src/transform/transaction/PaymentTransactionTransformer.java @@ -15,7 +15,6 @@ import data.transaction.TransactionData; import qora.account.PublicKeyAccount; import data.transaction.PaymentTransactionData; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class PaymentTransactionTransformer extends TransactionTransformer { @@ -37,7 +36,7 @@ public class PaymentTransactionTransformer extends TransactionTransformer { byteBuffer.get(reference); byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer); - String recipient = Serialization.deserializeRecipient(byteBuffer); + String recipient = Serialization.deserializeAddress(byteBuffer); BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer); BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); @@ -63,7 +62,7 @@ public class PaymentTransactionTransformer extends TransactionTransformer { bytes.write(paymentTransactionData.getReference()); bytes.write(paymentTransactionData.getSenderPublicKey()); - bytes.write(Base58.decode(paymentTransactionData.getRecipient())); + Serialization.serializeAddress(bytes, paymentTransactionData.getRecipient()); Serialization.serializeBigDecimal(bytes, paymentTransactionData.getAmount()); Serialization.serializeBigDecimal(bytes, paymentTransactionData.getFee()); diff --git a/src/transform/transaction/RegisterNameTransactionTransformer.java b/src/transform/transaction/RegisterNameTransactionTransformer.java index 22904335..4f136f44 100644 --- a/src/transform/transaction/RegisterNameTransactionTransformer.java +++ b/src/transform/transaction/RegisterNameTransactionTransformer.java @@ -17,7 +17,6 @@ import data.transaction.TransactionData; import qora.account.PublicKeyAccount; import qora.naming.Name; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class RegisterNameTransactionTransformer extends TransactionTransformer { @@ -41,7 +40,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { byte[] registrantPublicKey = Serialization.deserializePublicKey(byteBuffer); - String owner = Serialization.deserializeRecipient(byteBuffer); + String owner = Serialization.deserializeAddress(byteBuffer); String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); String data = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); @@ -78,7 +77,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { bytes.write(registerNameTransactionData.getReference()); bytes.write(registerNameTransactionData.getRegistrantPublicKey()); - bytes.write(Base58.decode(registerNameTransactionData.getOwner())); + Serialization.serializeAddress(bytes, registerNameTransactionData.getOwner()); Serialization.serializeSizedString(bytes, registerNameTransactionData.getName()); Serialization.serializeSizedString(bytes, registerNameTransactionData.getData()); diff --git a/src/transform/transaction/TransactionTransformer.java b/src/transform/transaction/TransactionTransformer.java index 9606b8bb..85c28f17 100644 --- a/src/transform/transaction/TransactionTransformer.java +++ b/src/transform/transaction/TransactionTransformer.java @@ -49,6 +49,9 @@ public class TransactionTransformer extends Transformer { case CANCEL_SELL_NAME: return CancelSellNameTransactionTransformer.fromByteBuffer(byteBuffer); + case BUY_NAME: + return BuyNameTransactionTransformer.fromByteBuffer(byteBuffer); + case CREATE_POLL: return CreatePollTransactionTransformer.fromByteBuffer(byteBuffer); @@ -98,6 +101,9 @@ public class TransactionTransformer extends Transformer { case CANCEL_SELL_NAME: return CancelSellNameTransactionTransformer.getDataLength(transactionData); + case BUY_NAME: + return BuyNameTransactionTransformer.getDataLength(transactionData); + case CREATE_POLL: return CreatePollTransactionTransformer.getDataLength(transactionData); @@ -147,6 +153,9 @@ public class TransactionTransformer extends Transformer { case CANCEL_SELL_NAME: return CancelSellNameTransactionTransformer.toBytes(transactionData); + case BUY_NAME: + return BuyNameTransactionTransformer.toBytes(transactionData); + case CREATE_POLL: return CreatePollTransactionTransformer.toBytes(transactionData); @@ -196,6 +205,9 @@ public class TransactionTransformer extends Transformer { case CANCEL_SELL_NAME: return CancelSellNameTransactionTransformer.toJSON(transactionData); + case BUY_NAME: + return BuyNameTransactionTransformer.toJSON(transactionData); + case CREATE_POLL: return CreatePollTransactionTransformer.toJSON(transactionData); diff --git a/src/transform/transaction/TransferAssetTransactionTransformer.java b/src/transform/transaction/TransferAssetTransactionTransformer.java index bcded125..8e9afb5c 100644 --- a/src/transform/transaction/TransferAssetTransactionTransformer.java +++ b/src/transform/transaction/TransferAssetTransactionTransformer.java @@ -15,7 +15,6 @@ import data.transaction.TransactionData; import data.transaction.TransferAssetTransactionData; import qora.account.PublicKeyAccount; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class TransferAssetTransactionTransformer extends TransactionTransformer { @@ -38,7 +37,7 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer byteBuffer.get(reference); byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer); - String recipient = Serialization.deserializeRecipient(byteBuffer); + String recipient = Serialization.deserializeAddress(byteBuffer); long assetId = byteBuffer.getLong(); BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer, AMOUNT_LENGTH); @@ -65,7 +64,7 @@ public class TransferAssetTransactionTransformer extends TransactionTransformer bytes.write(transferAssetTransactionData.getReference()); bytes.write(transferAssetTransactionData.getSenderPublicKey()); - bytes.write(Base58.decode(transferAssetTransactionData.getRecipient())); + Serialization.serializeAddress(bytes, transferAssetTransactionData.getRecipient()); bytes.write(Longs.toByteArray(transferAssetTransactionData.getAssetId())); Serialization.serializeBigDecimal(bytes, transferAssetTransactionData.getAmount(), AMOUNT_LENGTH); diff --git a/src/transform/transaction/UpdateNameTransactionTransformer.java b/src/transform/transaction/UpdateNameTransactionTransformer.java index d612c1fc..5f3bab84 100644 --- a/src/transform/transaction/UpdateNameTransactionTransformer.java +++ b/src/transform/transaction/UpdateNameTransactionTransformer.java @@ -17,7 +17,6 @@ import data.transaction.TransactionData; import qora.account.PublicKeyAccount; import qora.naming.Name; import transform.TransformationException; -import utils.Base58; import utils.Serialization; public class UpdateNameTransactionTransformer extends TransactionTransformer { @@ -41,7 +40,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { byte[] ownerPublicKey = Serialization.deserializePublicKey(byteBuffer); - String newOwner = Serialization.deserializeRecipient(byteBuffer); + String newOwner = Serialization.deserializeAddress(byteBuffer); String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); String newData = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); @@ -55,7 +54,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, null, fee, timestamp, reference, signature); + return new UpdateNameTransactionData(ownerPublicKey, newOwner, name, newData, fee, timestamp, reference, signature); } public static int getDataLength(TransactionData transactionData) throws TransformationException { @@ -78,7 +77,7 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { bytes.write(updateNameTransactionData.getReference()); bytes.write(updateNameTransactionData.getOwnerPublicKey()); - bytes.write(Base58.decode(updateNameTransactionData.getNewOwner())); + Serialization.serializeAddress(bytes, updateNameTransactionData.getNewOwner()); Serialization.serializeSizedString(bytes, updateNameTransactionData.getName()); Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewData()); diff --git a/src/utils/Serialization.java b/src/utils/Serialization.java index 38be78ff..959c42af 100644 --- a/src/utils/Serialization.java +++ b/src/utils/Serialization.java @@ -50,7 +50,11 @@ public class Serialization { return Serialization.deserializeBigDecimal(byteBuffer, 8); } - public static String deserializeRecipient(ByteBuffer byteBuffer) { + public static void serializeAddress(ByteArrayOutputStream bytes, String address) throws IOException { + bytes.write(Base58.decode(address)); + } + + public static String deserializeAddress(ByteBuffer byteBuffer) { byte[] bytes = new byte[Transformer.ADDRESS_LENGTH]; byteBuffer.get(bytes); return Base58.encode(bytes);