mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-14 11:15:49 +00:00
Conversion of Payment Transactions
+ some fixes spotted en route
This commit is contained in:
parent
a0824b21d4
commit
16a92305d2
43
src/data/transaction/PaymentTransactionData.java
Normal file
43
src/data/transaction/PaymentTransactionData.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package data.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
|
public class PaymentTransactionData extends TransactionData {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private byte[] senderPublicKey;
|
||||||
|
private String recipient;
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference,
|
||||||
|
byte[] signature) {
|
||||||
|
super(TransactionType.PAYMENT, fee, senderPublicKey, timestamp, reference, signature);
|
||||||
|
|
||||||
|
this.senderPublicKey = senderPublicKey;
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
|
||||||
|
this(senderPublicKey, recipient, amount, fee, timestamp, reference, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters/Setters
|
||||||
|
|
||||||
|
public byte[] getSenderPublicKey() {
|
||||||
|
return this.senderPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRecipient() {
|
||||||
|
return this.recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getAmount() {
|
||||||
|
return this.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,239 +1,103 @@
|
|||||||
package qora.transaction;
|
package qora.transaction;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
import data.transaction.PaymentTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
import com.google.common.hash.HashCode;
|
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
import com.google.common.primitives.Longs;
|
|
||||||
|
|
||||||
import database.DB;
|
|
||||||
import database.NoDataFoundException;
|
|
||||||
import qora.account.Account;
|
import qora.account.Account;
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
import qora.assets.Asset;
|
import qora.assets.Asset;
|
||||||
import qora.crypto.Crypto;
|
import qora.crypto.Crypto;
|
||||||
import repository.hsqldb.HSQLDBSaver;
|
import repository.DataException;
|
||||||
import transform.TransformationException;
|
import repository.Repository;
|
||||||
import utils.Base58;
|
|
||||||
import utils.Serialization;
|
|
||||||
|
|
||||||
public class PaymentTransaction extends TransactionHandler {
|
public class PaymentTransaction extends Transaction {
|
||||||
|
|
||||||
// Properties
|
|
||||||
private PublicKeyAccount sender;
|
|
||||||
private Account recipient;
|
|
||||||
private BigDecimal amount;
|
|
||||||
|
|
||||||
// Property lengths
|
|
||||||
private static final int SENDER_LENGTH = 32;
|
|
||||||
private static final int AMOUNT_LENGTH = 8;
|
|
||||||
private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH;
|
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
public PaymentTransaction(PublicKeyAccount sender, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference,
|
public PaymentTransaction(Repository repository, TransactionData transactionData) {
|
||||||
byte[] signature) {
|
super(repository, transactionData);
|
||||||
super(TransactionType.PAYMENT, fee, sender, timestamp, reference, signature);
|
|
||||||
|
|
||||||
this.sender = sender;
|
|
||||||
this.recipient = new Account(recipient);
|
|
||||||
this.amount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PaymentTransaction(PublicKeyAccount sender, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference) {
|
|
||||||
this(sender, recipient, amount, fee, timestamp, reference, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters/Setters
|
|
||||||
|
|
||||||
public PublicKeyAccount getSender() {
|
|
||||||
return this.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account getRecipient() {
|
|
||||||
return this.recipient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
|
||||||
return this.amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// More information
|
|
||||||
|
|
||||||
public int getDataLength() {
|
|
||||||
return TYPE_LENGTH + TYPELESS_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load/Save
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct PaymentTransaction from DB using signature.
|
|
||||||
*
|
|
||||||
* @param signature
|
|
||||||
* @throws NoDataFoundException
|
|
||||||
* if no matching row found
|
|
||||||
* @throws SQLException
|
|
||||||
*/
|
|
||||||
protected PaymentTransaction(byte[] signature) throws SQLException {
|
|
||||||
super(TransactionType.PAYMENT, signature);
|
|
||||||
|
|
||||||
ResultSet rs = DB.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
|
|
||||||
if (rs == null)
|
|
||||||
throw new NoDataFoundException();
|
|
||||||
|
|
||||||
this.sender = new PublicKeyAccount(DB.getResultSetBytes(rs.getBinaryStream(1), CREATOR_LENGTH));
|
|
||||||
this.recipient = new Account(rs.getString(2));
|
|
||||||
this.amount = rs.getBigDecimal(3).setScale(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load PaymentTransaction from DB using signature.
|
|
||||||
*
|
|
||||||
* @param signature
|
|
||||||
* @return PaymentTransaction, or null if not found
|
|
||||||
* @throws SQLException
|
|
||||||
*/
|
|
||||||
public static PaymentTransaction fromSignature(byte[] signature) throws SQLException {
|
|
||||||
try {
|
|
||||||
return new PaymentTransaction(signature);
|
|
||||||
} catch (NoDataFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save() throws SQLException {
|
|
||||||
super.save();
|
|
||||||
|
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("PaymentTransactions");
|
|
||||||
saveHelper.bind("signature", this.signature).bind("sender", this.sender.getPublicKey()).bind("recipient", this.recipient.getAddress()).bind("amount",
|
|
||||||
this.amount);
|
|
||||||
saveHelper.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converters
|
|
||||||
|
|
||||||
protected static TransactionHandler parse(ByteBuffer byteBuffer) throws TransformationException {
|
|
||||||
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
|
||||||
throw new TransformationException("Byte data too short for PaymentTransaction");
|
|
||||||
|
|
||||||
long timestamp = byteBuffer.getLong();
|
|
||||||
|
|
||||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
|
||||||
byteBuffer.get(reference);
|
|
||||||
|
|
||||||
PublicKeyAccount sender = Serialization.deserializePublicKey(byteBuffer);
|
|
||||||
String recipient = Serialization.deserializeRecipient(byteBuffer);
|
|
||||||
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
|
||||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
|
||||||
|
|
||||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
|
||||||
byteBuffer.get(signature);
|
|
||||||
|
|
||||||
return new PaymentTransaction(sender, recipient, amount, fee, timestamp, reference, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public JSONObject toJSON() throws SQLException {
|
|
||||||
JSONObject json = getBaseJSON();
|
|
||||||
|
|
||||||
json.put("sender", this.sender.getAddress());
|
|
||||||
json.put("senderPublicKey", HashCode.fromBytes(this.sender.getPublicKey()).toString());
|
|
||||||
json.put("recipient", this.recipient.getAddress());
|
|
||||||
json.put("amount", this.amount.toPlainString());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toBytes() {
|
|
||||||
try {
|
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(getDataLength());
|
|
||||||
bytes.write(Ints.toByteArray(this.type.value));
|
|
||||||
bytes.write(Longs.toByteArray(this.timestamp));
|
|
||||||
bytes.write(this.reference);
|
|
||||||
bytes.write(this.sender.getPublicKey());
|
|
||||||
bytes.write(Base58.decode(this.recipient.getAddress()));
|
|
||||||
bytes.write(Serialization.serializeBigDecimal(this.amount));
|
|
||||||
bytes.write(Serialization.serializeBigDecimal(this.fee));
|
|
||||||
bytes.write(this.signature);
|
|
||||||
return bytes.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing
|
// Processing
|
||||||
|
|
||||||
public ValidationResult isValid() throws SQLException {
|
public ValidationResult isValid() throws DataException {
|
||||||
// Lowest cost checks first
|
// Lowest cost checks first
|
||||||
|
|
||||||
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) this.transactionData;
|
||||||
|
|
||||||
// Check recipient is a valid address
|
// Check recipient is a valid address
|
||||||
if (!Crypto.isValidAddress(this.recipient.getAddress()))
|
if (!Crypto.isValidAddress(paymentTransactionData.getRecipient()))
|
||||||
return ValidationResult.INVALID_ADDRESS;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check amount is positive
|
// Check amount is positive
|
||||||
if (this.amount.compareTo(BigDecimal.ZERO) <= 0)
|
if (paymentTransactionData.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
return ValidationResult.NEGATIVE_AMOUNT;
|
return ValidationResult.NEGATIVE_AMOUNT;
|
||||||
|
|
||||||
// Check fee is positive
|
// Check fee is positive
|
||||||
if (this.fee.compareTo(BigDecimal.ZERO) <= 0)
|
if (paymentTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
// Check reference is correct
|
// Check reference is correct
|
||||||
if (!Arrays.equals(this.sender.getLastReference(), this.reference))
|
Account sender = new PublicKeyAccount(repository, paymentTransactionData.getSenderPublicKey());
|
||||||
|
if (!Arrays.equals(sender.getLastReference(), paymentTransactionData.getReference()))
|
||||||
return ValidationResult.INVALID_REFERENCE;
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
// Check sender has enough funds
|
// Check sender has enough funds
|
||||||
if (this.sender.getConfirmedBalance(Asset.QORA).compareTo(this.amount.add(this.fee)) == -1)
|
if (sender.getConfirmedBalance(Asset.QORA).compareTo(paymentTransactionData.getAmount().add(paymentTransactionData.getFee())) == -1)
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process() throws SQLException {
|
public void process() throws DataException {
|
||||||
this.save();
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) this.transactionData;
|
||||||
|
|
||||||
|
// Save this transaction itself
|
||||||
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Update sender's balance
|
// Update sender's balance
|
||||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).subtract(this.amount).subtract(this.fee));
|
Account sender = new PublicKeyAccount(repository, paymentTransactionData.getSenderPublicKey());
|
||||||
|
sender.setConfirmedBalance(Asset.QORA,
|
||||||
|
sender.getConfirmedBalance(Asset.QORA).subtract(paymentTransactionData.getAmount()).subtract(paymentTransactionData.getFee()));
|
||||||
|
|
||||||
// Update recipient's balance
|
// Update recipient's balance
|
||||||
this.recipient.setConfirmedBalance(Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).add(this.amount));
|
Account recipient = new Account(repository, paymentTransactionData.getRecipient());
|
||||||
|
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).add(paymentTransactionData.getAmount()));
|
||||||
|
|
||||||
// Update sender's reference
|
// Update sender's reference
|
||||||
this.sender.setLastReference(this.signature);
|
sender.setLastReference(paymentTransactionData.getSignature());
|
||||||
|
|
||||||
// If recipient has no reference yet, then this is their starting reference
|
// If recipient has no reference yet, then this is their starting reference
|
||||||
if (this.recipient.getLastReference() == null)
|
if (recipient.getLastReference() == null)
|
||||||
this.recipient.setLastReference(this.signature);
|
recipient.setLastReference(paymentTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan() throws SQLException {
|
public void orphan() throws DataException {
|
||||||
this.delete();
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) this.transactionData;
|
||||||
|
|
||||||
|
// Delete this transaction
|
||||||
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Update sender's balance
|
// Update sender's balance
|
||||||
this.sender.setConfirmedBalance(Asset.QORA, this.sender.getConfirmedBalance(Asset.QORA).add(this.amount).add(this.fee));
|
Account sender = new PublicKeyAccount(repository, paymentTransactionData.getSenderPublicKey());
|
||||||
|
sender.setConfirmedBalance(Asset.QORA,
|
||||||
|
sender.getConfirmedBalance(Asset.QORA).add(paymentTransactionData.getAmount()).add(paymentTransactionData.getFee()));
|
||||||
|
|
||||||
// Update recipient's balance
|
// Update recipient's balance
|
||||||
this.recipient.setConfirmedBalance(Asset.QORA, this.recipient.getConfirmedBalance(Asset.QORA).subtract(this.amount));
|
Account recipient = new Account(repository, paymentTransactionData.getRecipient());
|
||||||
|
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).subtract(paymentTransactionData.getAmount()));
|
||||||
|
|
||||||
// Update sender's reference
|
// Update sender's reference
|
||||||
this.sender.setLastReference(this.reference);
|
sender.setLastReference(paymentTransactionData.getReference());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which would have changed
|
* If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own (which would have changed
|
||||||
* their last reference) thus this is their first reference so remove it.
|
* their last reference) thus this is their first reference so remove it.
|
||||||
*/
|
*/
|
||||||
if (Arrays.equals(this.recipient.getLastReference(), this.signature))
|
if (Arrays.equals(recipient.getLastReference(), paymentTransactionData.getSignature()))
|
||||||
this.recipient.setLastReference(null);
|
recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,15 @@ public abstract class Transaction {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return new GenesisTransaction(repository, transactionData);
|
return new GenesisTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return new PaymentTransaction(repository, transactionData);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return new IssueAssetTransaction(repository, transactionData);
|
return new IssueAssetTransaction(repository, transactionData);
|
||||||
|
|
||||||
|
case CREATE_ASSET_ORDER:
|
||||||
|
return new CreateOrderTransaction(repository, transactionData);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -224,7 +230,7 @@ public abstract class Transaction {
|
|||||||
return Arrays.copyOf(bytes, bytes.length - Transformer.SIGNATURE_LENGTH);
|
return Arrays.copyOf(bytes, bytes.length - Transformer.SIGNATURE_LENGTH);
|
||||||
} catch (TransformationException e) {
|
} catch (TransformationException e) {
|
||||||
// XXX this isn't good
|
// XXX this isn't good
|
||||||
return null;
|
throw new RuntimeException("Unable to transform transaction to signature-less byte array", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package repository.hsqldb.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import data.transaction.PaymentTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.hsqldb.HSQLDBRepository;
|
||||||
|
import repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
|
public class HSQLDBPaymentTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
|
public HSQLDBPaymentTransactionRepository(HSQLDBRepository repository) {
|
||||||
|
super(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||||
|
try {
|
||||||
|
ResultSet rs = this.repository.checkedExecute("SELECT sender, recipient, amount FROM PaymentTransactions WHERE signature = ?", signature);
|
||||||
|
if (rs == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
byte[] senderPublicKey = this.repository.getResultSetBytes(rs.getBinaryStream(1));
|
||||||
|
String recipient = rs.getString(2);
|
||||||
|
BigDecimal amount = rs.getBigDecimal(3);
|
||||||
|
|
||||||
|
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch payment transaction from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransactionData transactionData) throws DataException {
|
||||||
|
super.save(transactionData);
|
||||||
|
|
||||||
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||||
|
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("PaymentTransactions");
|
||||||
|
|
||||||
|
saveHelper.bind("signature", paymentTransactionData.getSignature()).bind("sender", paymentTransactionData.getSenderPublicKey())
|
||||||
|
.bind("recipient", paymentTransactionData.getRecipient()).bind("amount", paymentTransactionData.getAmount());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save payment transaction into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,12 +17,14 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
|
|
||||||
protected HSQLDBRepository repository;
|
protected HSQLDBRepository repository;
|
||||||
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
|
||||||
|
private HSQLDBPaymentTransactionRepository paymentTransactionRepository;
|
||||||
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
private HSQLDBIssueAssetTransactionRepository issueAssetTransactionRepository;
|
||||||
private HSQLDBCreateOrderTransactionRepository createOrderTransactionRepository;
|
private HSQLDBCreateOrderTransactionRepository createOrderTransactionRepository;
|
||||||
|
|
||||||
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
|
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
|
this.genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
|
||||||
|
this.paymentTransactionRepository = new HSQLDBPaymentTransactionRepository(repository);
|
||||||
this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository);
|
this.issueAssetTransactionRepository = new HSQLDBIssueAssetTransactionRepository(repository);
|
||||||
this.createOrderTransactionRepository = new HSQLDBCreateOrderTransactionRepository(repository);
|
this.createOrderTransactionRepository = new HSQLDBCreateOrderTransactionRepository(repository);
|
||||||
}
|
}
|
||||||
@ -69,6 +71,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return this.genesisTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.genesisTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return this.paymentTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return this.issueAssetTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.issueAssetTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ public class CreateOrderTransactionTransformer extends TransactionTransformer {
|
|||||||
Serialization.serializeBigDecimal(createOrderTransactionData.getAmount(), AMOUNT_LENGTH);
|
Serialization.serializeBigDecimal(createOrderTransactionData.getAmount(), AMOUNT_LENGTH);
|
||||||
Serialization.serializeBigDecimal(createOrderTransactionData.getPrice(), AMOUNT_LENGTH);
|
Serialization.serializeBigDecimal(createOrderTransactionData.getPrice(), AMOUNT_LENGTH);
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(createOrderTransactionData.getFee());
|
||||||
bytes.write(createOrderTransactionData.getSignature());
|
bytes.write(createOrderTransactionData.getSignature());
|
||||||
|
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
|
@ -90,6 +90,7 @@ public class IssueAssetTransactionTransformer extends TransactionTransformer {
|
|||||||
bytes.write(Longs.toByteArray(issueAssetTransactionData.getQuantity()));
|
bytes.write(Longs.toByteArray(issueAssetTransactionData.getQuantity()));
|
||||||
bytes.write((byte) (issueAssetTransactionData.getIsDivisible() ? 1 : 0));
|
bytes.write((byte) (issueAssetTransactionData.getIsDivisible() ? 1 : 0));
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(issueAssetTransactionData.getFee());
|
||||||
bytes.write(issueAssetTransactionData.getSignature());
|
bytes.write(issueAssetTransactionData.getSignature());
|
||||||
|
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
|
99
src/transform/transaction/PaymentTransactionTransformer.java
Normal file
99
src/transform/transaction/PaymentTransactionTransformer.java
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
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.hash.HashCode;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
// Property lengths
|
||||||
|
private static final int SENDER_LENGTH = PUBLIC_KEY_LENGTH;
|
||||||
|
private static final int RECIPIENT_LENGTH = ADDRESS_LENGTH;
|
||||||
|
private static final int AMOUNT_LENGTH = 8;
|
||||||
|
|
||||||
|
private static final int TYPELESS_LENGTH = BASE_TYPELESS_LENGTH + SENDER_LENGTH + RECIPIENT_LENGTH + AMOUNT_LENGTH;
|
||||||
|
|
||||||
|
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||||
|
if (byteBuffer.remaining() < TYPELESS_LENGTH)
|
||||||
|
throw new TransformationException("Byte data too short for PaymentTransaction");
|
||||||
|
|
||||||
|
long timestamp = byteBuffer.getLong();
|
||||||
|
|
||||||
|
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||||
|
byteBuffer.get(reference);
|
||||||
|
|
||||||
|
byte[] senderPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||||
|
String recipient = Serialization.deserializeRecipient(byteBuffer);
|
||||||
|
BigDecimal amount = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
|
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
|
byteBuffer.get(signature);
|
||||||
|
|
||||||
|
return new PaymentTransactionData(senderPublicKey, recipient, amount, fee, timestamp, reference, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
|
return TYPE_LENGTH + TYPELESS_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||||
|
try {
|
||||||
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
bytes.write(Ints.toByteArray(paymentTransactionData.getType().value));
|
||||||
|
bytes.write(Longs.toByteArray(paymentTransactionData.getTimestamp()));
|
||||||
|
bytes.write(paymentTransactionData.getReference());
|
||||||
|
|
||||||
|
bytes.write(paymentTransactionData.getSenderPublicKey());
|
||||||
|
bytes.write(Base58.decode(paymentTransactionData.getRecipient()));
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(paymentTransactionData.getAmount());
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(paymentTransactionData.getFee());
|
||||||
|
bytes.write(paymentTransactionData.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 {
|
||||||
|
PaymentTransactionData paymentTransactionData = (PaymentTransactionData) transactionData;
|
||||||
|
|
||||||
|
byte[] senderPublicKey = paymentTransactionData.getSenderPublicKey();
|
||||||
|
|
||||||
|
json.put("sender", PublicKeyAccount.getAddress(senderPublicKey));
|
||||||
|
json.put("senderPublicKey", HashCode.fromBytes(senderPublicKey).toString());
|
||||||
|
json.put("recipient", paymentTransactionData.getRecipient());
|
||||||
|
json.put("amount", paymentTransactionData.getAmount().toPlainString());
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return GenesisTransactionTransformer.fromByteBuffer(byteBuffer);
|
return GenesisTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return PaymentTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return IssueAssetTransactionTransformer.fromByteBuffer(byteBuffer);
|
return IssueAssetTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
@ -49,6 +52,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return GenesisTransactionTransformer.getDataLength(transactionData);
|
return GenesisTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return PaymentTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return IssueAssetTransactionTransformer.getDataLength(transactionData);
|
return IssueAssetTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
@ -65,6 +71,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return GenesisTransactionTransformer.toBytes(transactionData);
|
return GenesisTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return PaymentTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return IssueAssetTransactionTransformer.toBytes(transactionData);
|
return IssueAssetTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
@ -81,6 +90,9 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case GENESIS:
|
case GENESIS:
|
||||||
return GenesisTransactionTransformer.toJSON(transaction);
|
return GenesisTransactionTransformer.toJSON(transaction);
|
||||||
|
|
||||||
|
case PAYMENT:
|
||||||
|
return PaymentTransactionTransformer.toJSON(transaction);
|
||||||
|
|
||||||
case ISSUE_ASSET:
|
case ISSUE_ASSET:
|
||||||
return IssueAssetTransactionTransformer.toJSON(transaction);
|
return IssueAssetTransactionTransformer.toJSON(transaction);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user