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);