package qora; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import ntp.NTP; import controller.Controller; import qora.account.Account; import qora.account.PrivateKeyAccount; import qora.account.PublicKeyAccount; import qora.assets.Asset; import qora.assets.Order; import qora.block.Block; import qora.naming.Name; import qora.naming.NameSale; import qora.payment.Payment; import qora.transaction.ArbitraryTransaction; import qora.transaction.BuyNameTransaction; import qora.transaction.CancelOrderTransaction; import qora.transaction.CancelSellNameTransaction; import qora.transaction.CreateOrderTransaction; import qora.transaction.CreatePollTransaction; import qora.transaction.IssueAssetTransaction; import qora.transaction.MultiPaymentTransaction; import qora.transaction.PaymentTransaction; import qora.transaction.RegisterNameTransaction; import qora.transaction.SellNameTransaction; import qora.transaction.Transaction; import qora.transaction.TransferAssetTransaction; import qora.transaction.UpdateNameTransaction; import qora.transaction.VoteOnPollTransaction; import qora.voting.Poll; import utils.Pair; import utils.TransactionTimestampComparator; import database.DBSet; public class TransactionCreator { private DBSet fork; private Block lastBlock; private void checkUpdate() { //CHECK IF WE ALREADY HAVE A FORK if(this.lastBlock == null) { updateFork(); } else { //CHECK IF WE NEED A NEW FORK if(!Arrays.equals(this.lastBlock.getSignature(), Controller.getInstance().getLastBlock().getSignature())) { updateFork(); } } } private void updateFork() { //CREATE NEW FORK this.fork = DBSet.getInstance().fork(); //UPDATE LAST BLOCK this.lastBlock = Controller.getInstance().getLastBlock(); //SCAN UNCONFIRMED TRANSACTIONS FOR TRANSACTIONS WHERE ACCOUNT IS CREATOR OF List<Transaction> transactions = DBSet.getInstance().getTransactionMap().getTransactions(); List<Transaction> accountTransactions = new ArrayList<Transaction>(); for(Transaction transaction: transactions) { if(Controller.getInstance().getAccounts().contains(transaction.getCreator())) { accountTransactions.add(transaction); } } //SORT THEM BY TIMESTAMP Collections.sort(accountTransactions, new TransactionTimestampComparator()); //VALIDATE AND PROCESS THOSE TRANSACTIONS IN FORK for(Transaction transaction: accountTransactions) { if(transaction.isValid(this.fork) == Transaction.VALIDATE_OKE && transaction.isSignatureValid()) { transaction.process(this.fork); } else { //THE TRANSACTION BECAME INVALID LET DBSet.getInstance().getTransactionMap().delete(transaction); } } } public Pair<Transaction, Integer> createPayment(PrivateKeyAccount sender, Account recipient, BigDecimal amount, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = PaymentTransaction.generateSignature(this.fork, sender, recipient, amount, fee, time); //CREATE PAYMENT PaymentTransaction payment = new PaymentTransaction(new PublicKeyAccount(sender.getPublicKey()), recipient, amount, fee, time, sender.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(payment); } public Pair<Transaction, Integer> createNameRegistration(PrivateKeyAccount registrant, Name name, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = RegisterNameTransaction.generateSignature(this.fork, registrant, name, fee, time); //CREATE NAME REGISTRATION RegisterNameTransaction nameRegistration = new RegisterNameTransaction(registrant, name, fee, time, registrant.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(nameRegistration); } public Pair<Transaction, Integer> createNameUpdate(PrivateKeyAccount owner, Name name, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = UpdateNameTransaction.generateSignature(this.fork, owner, name, fee, time); //CREATE NAME UPDATE UpdateNameTransaction nameUpdate = new UpdateNameTransaction(owner, name, fee, time, owner.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(nameUpdate); } public Pair<Transaction, Integer> createNameSale(PrivateKeyAccount owner, NameSale nameSale, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = SellNameTransaction.generateSignature(this.fork, owner, nameSale, fee, time); //CREATE NAME SALE SellNameTransaction nameSaleTransaction = new SellNameTransaction(owner, nameSale, fee, time, owner.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(nameSaleTransaction); } public Pair<Transaction, Integer> createCancelNameSale(PrivateKeyAccount owner, NameSale nameSale, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = CancelSellNameTransaction.generateSignature(this.fork, owner, nameSale.getKey(), fee, time); //CREATE CANCEL NAME SALE CancelSellNameTransaction cancelNameSaleTransaction = new CancelSellNameTransaction(owner, nameSale.getKey(), fee, time, owner.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(cancelNameSaleTransaction); } public Pair<Transaction, Integer> createNamePurchase(PrivateKeyAccount buyer, NameSale nameSale, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = BuyNameTransaction.generateSignature(this.fork, buyer, nameSale, nameSale.getName().getOwner(), fee, time); //CREATE NAME PURCHASE BuyNameTransaction namePurchase = new BuyNameTransaction(buyer, nameSale, nameSale.getName().getOwner(), fee, time, buyer.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(namePurchase); } public Pair<Transaction, Integer> createPollCreation(PrivateKeyAccount creator, Poll poll, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = CreatePollTransaction.generateSignature(this.fork, creator, poll, fee, time); //CREATE POLL CREATION CreatePollTransaction pollCreation = new CreatePollTransaction(creator, poll, fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(pollCreation); } public Pair<Transaction, Integer> createPollVote(PrivateKeyAccount creator, String poll, int optionIndex, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = VoteOnPollTransaction.generateSignature(this.fork, creator, poll, optionIndex, fee, time); //CREATE POLL VOTE VoteOnPollTransaction pollVote = new VoteOnPollTransaction(creator, poll, optionIndex, fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(pollVote); } public Pair<Transaction, Integer> createArbitraryTransaction(PrivateKeyAccount creator, int service, byte[] data, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = ArbitraryTransaction.generateSignature(this.fork, creator, service, data, fee, time); //CREATE ARBITRARY TRANSACTION ArbitraryTransaction arbitraryTransaction = new ArbitraryTransaction(creator, service, data, fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(arbitraryTransaction); } public Pair<Transaction, Integer> createIssueAssetTransaction(PrivateKeyAccount creator, String name, String description, long quantity, boolean divisible, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE Asset asset = new Asset(creator, name, description, quantity, divisible, new byte[64]); byte[] signature = IssueAssetTransaction.generateSignature(this.fork, creator, asset, fee, time); //CREATE ISSUE ASSET TRANSACTION asset = new Asset(creator, name, description, quantity, divisible, signature); IssueAssetTransaction issueAssetTransaction = new IssueAssetTransaction(creator, asset, fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(issueAssetTransaction); } public Pair<Transaction, Integer> createOrderTransaction(PrivateKeyAccount creator, Asset have, Asset want, BigDecimal amount, BigDecimal price, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = CreateOrderTransaction.generateSignature(this.fork, creator, have.getKey(), want.getKey(), amount, price, fee, time); //CREATE PRDER TRANSACTION CreateOrderTransaction createOrderTransaction = new CreateOrderTransaction(creator, have.getKey(), want.getKey(), amount, price, fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(createOrderTransaction); } public Pair<Transaction, Integer> createCancelOrderTransaction(PrivateKeyAccount creator, Order order, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = CancelOrderTransaction.generateSignature(this.fork, creator, order.getId(), fee, time); //CREATE PRDER TRANSACTION CancelOrderTransaction cancelOrderTransaction = new CancelOrderTransaction(creator, order.getId(), fee, time, creator.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(cancelOrderTransaction); } public Pair<Transaction, Integer> createAssetTransfer(PrivateKeyAccount sender, Account recipient, Asset asset, BigDecimal amount, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = TransferAssetTransaction.generateSignature(this.fork, sender, recipient, asset.getKey(), amount, fee, time); //CREATE ASSET TRANSFER TransferAssetTransaction assetTransfer = new TransferAssetTransaction(sender, recipient, asset.getKey(), amount, fee, time, sender.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(assetTransfer); } public Pair<Transaction, Integer> sendMultiPayment(PrivateKeyAccount sender, List<Payment> payments, BigDecimal fee) { //CHECK FOR UPDATES this.checkUpdate(); //TIME long time = NTP.getTime(); //CREATE SIGNATURE byte[] signature = MultiPaymentTransaction.generateSignature(this.fork, sender, payments, fee, time); //CREATE MULTI PAYMENTS MultiPaymentTransaction multiPayment = new MultiPaymentTransaction(sender, payments, fee, time, sender.getLastReference(this.fork), signature); //VALIDATE AND PROCESS return this.afterCreate(multiPayment); } private Pair<Transaction, Integer> afterCreate(Transaction transaction) { //CHECK IF PAYMENT VALID int valid = transaction.isValid(this.fork); if(valid == Transaction.VALIDATE_OKE) { //PROCESS IN FORK transaction.process(this.fork); //CONTROLLER ONTRANSACTION Controller.getInstance().onTransactionCreate(transaction); } //RETURN return new Pair<Transaction, Integer>(transaction, valid); } }