2018-06-13 15:48:28 +00:00
|
|
|
package test;
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
import org.junit.jupiter.api.AfterEach;
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-07-03 10:40:24 +00:00
|
|
|
import java.io.UnsupportedEncodingException;
|
2018-06-15 16:16:44 +00:00
|
|
|
import java.math.BigDecimal;
|
2018-10-04 13:38:59 +00:00
|
|
|
import java.math.RoundingMode;
|
2018-06-21 11:38:45 +00:00
|
|
|
import java.util.ArrayList;
|
2018-06-29 09:29:18 +00:00
|
|
|
import java.util.Arrays;
|
2018-06-21 11:38:45 +00:00
|
|
|
import java.util.List;
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-07-04 11:49:56 +00:00
|
|
|
import org.json.simple.JSONObject;
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-15 16:16:44 +00:00
|
|
|
import com.google.common.hash.HashCode;
|
|
|
|
|
2018-07-04 12:53:20 +00:00
|
|
|
import data.PaymentData;
|
2018-06-15 16:16:44 +00:00
|
|
|
import data.account.AccountBalanceData;
|
|
|
|
import data.account.AccountData;
|
2018-07-04 11:49:56 +00:00
|
|
|
import data.assets.AssetData;
|
2018-07-05 15:24:05 +00:00
|
|
|
import data.assets.OrderData;
|
|
|
|
import data.assets.TradeData;
|
2018-06-13 15:48:28 +00:00
|
|
|
import data.block.BlockData;
|
2018-07-01 15:35:45 +00:00
|
|
|
import data.naming.NameData;
|
2018-07-03 10:40:24 +00:00
|
|
|
import data.transaction.BuyNameTransactionData;
|
2018-07-05 15:24:05 +00:00
|
|
|
import data.transaction.CancelOrderTransactionData;
|
2018-07-02 17:59:11 +00:00
|
|
|
import data.transaction.CancelSellNameTransactionData;
|
2018-07-05 15:24:05 +00:00
|
|
|
import data.transaction.CreateOrderTransactionData;
|
2018-06-21 11:38:45 +00:00
|
|
|
import data.transaction.CreatePollTransactionData;
|
2018-07-04 11:49:56 +00:00
|
|
|
import data.transaction.IssueAssetTransactionData;
|
2018-07-03 10:40:24 +00:00
|
|
|
import data.transaction.MessageTransactionData;
|
2018-07-04 12:53:20 +00:00
|
|
|
import data.transaction.MultiPaymentTransactionData;
|
2018-06-15 16:16:44 +00:00
|
|
|
import data.transaction.PaymentTransactionData;
|
2018-07-01 15:35:45 +00:00
|
|
|
import data.transaction.RegisterNameTransactionData;
|
2018-07-02 17:09:36 +00:00
|
|
|
import data.transaction.SellNameTransactionData;
|
2018-07-04 11:49:56 +00:00
|
|
|
import data.transaction.TransferAssetTransactionData;
|
2018-07-01 15:35:45 +00:00
|
|
|
import data.transaction.UpdateNameTransactionData;
|
2018-06-29 09:29:18 +00:00
|
|
|
import data.transaction.VoteOnPollTransactionData;
|
2018-06-21 11:38:45 +00:00
|
|
|
import data.voting.PollData;
|
|
|
|
import data.voting.PollOptionData;
|
2018-06-29 10:05:15 +00:00
|
|
|
import data.voting.VoteOnPollData;
|
2018-06-15 16:16:44 +00:00
|
|
|
import qora.account.Account;
|
|
|
|
import qora.account.PrivateKeyAccount;
|
|
|
|
import qora.account.PublicKeyAccount;
|
|
|
|
import qora.assets.Asset;
|
2018-06-13 15:48:28 +00:00
|
|
|
import qora.block.Block;
|
2018-06-15 16:16:44 +00:00
|
|
|
import qora.block.BlockChain;
|
2018-07-03 10:40:24 +00:00
|
|
|
import qora.transaction.BuyNameTransaction;
|
2018-07-05 15:24:05 +00:00
|
|
|
import qora.transaction.CancelOrderTransaction;
|
2018-07-02 17:59:11 +00:00
|
|
|
import qora.transaction.CancelSellNameTransaction;
|
2018-07-05 15:24:05 +00:00
|
|
|
import qora.transaction.CreateOrderTransaction;
|
2018-06-21 11:38:45 +00:00
|
|
|
import qora.transaction.CreatePollTransaction;
|
2018-07-04 11:49:56 +00:00
|
|
|
import qora.transaction.IssueAssetTransaction;
|
2018-07-03 10:40:24 +00:00
|
|
|
import qora.transaction.MessageTransaction;
|
2018-07-04 12:53:20 +00:00
|
|
|
import qora.transaction.MultiPaymentTransaction;
|
2018-06-15 16:16:44 +00:00
|
|
|
import qora.transaction.PaymentTransaction;
|
2018-07-01 15:35:45 +00:00
|
|
|
import qora.transaction.RegisterNameTransaction;
|
2018-07-02 17:09:36 +00:00
|
|
|
import qora.transaction.SellNameTransaction;
|
2018-06-13 15:48:28 +00:00
|
|
|
import qora.transaction.Transaction;
|
2018-06-15 16:16:44 +00:00
|
|
|
import qora.transaction.Transaction.ValidationResult;
|
2018-07-04 11:49:56 +00:00
|
|
|
import qora.transaction.TransferAssetTransaction;
|
2018-07-01 15:35:45 +00:00
|
|
|
import qora.transaction.UpdateNameTransaction;
|
2018-06-29 09:29:18 +00:00
|
|
|
import qora.transaction.VoteOnPollTransaction;
|
2018-06-15 16:16:44 +00:00
|
|
|
import repository.AccountRepository;
|
2018-07-04 11:49:56 +00:00
|
|
|
import repository.AssetRepository;
|
2018-06-13 15:48:28 +00:00
|
|
|
import repository.DataException;
|
|
|
|
import repository.Repository;
|
2018-06-15 16:16:44 +00:00
|
|
|
import repository.RepositoryFactory;
|
2018-06-13 15:48:28 +00:00
|
|
|
import repository.RepositoryManager;
|
2018-06-15 16:16:44 +00:00
|
|
|
import repository.hsqldb.HSQLDBRepositoryFactory;
|
2018-06-29 09:29:18 +00:00
|
|
|
import settings.Settings;
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-15 16:16:44 +00:00
|
|
|
// Don't extend Common as we want to use an in-memory database
|
|
|
|
public class TransactionTests {
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-15 16:16:44 +00:00
|
|
|
private static final String connectionUrl = "jdbc:hsqldb:mem:db/test;create=true;close_result=true;sql.strict_exec=true;sql.enforce_names=true;sql.syntax_mys=true";
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-15 16:16:44 +00:00
|
|
|
private static final byte[] generatorSeed = HashCode.fromString("0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210").asBytes();
|
|
|
|
private static final byte[] senderSeed = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").asBytes();
|
|
|
|
private static final byte[] recipientSeed = HashCode.fromString("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210").asBytes();
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
private static final BigDecimal initialGeneratorBalance = BigDecimal.valueOf(1_000_000_000L).setScale(8);
|
|
|
|
private static final BigDecimal initialSenderBalance = BigDecimal.valueOf(1_000_000L).setScale(8);
|
|
|
|
private static final BigDecimal genericPaymentAmount = BigDecimal.valueOf(1_000L).setScale(8);
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
private Repository repository;
|
|
|
|
private AccountRepository accountRepository;
|
2018-07-01 15:35:45 +00:00
|
|
|
private BlockData parentBlockData;
|
2018-06-21 11:38:45 +00:00
|
|
|
private PrivateKeyAccount sender;
|
|
|
|
private PrivateKeyAccount generator;
|
|
|
|
private byte[] reference;
|
|
|
|
|
2018-07-04 11:49:56 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2018-06-29 09:29:18 +00:00
|
|
|
public void createTestAccounts(Long genesisTimestamp) throws DataException {
|
2018-06-15 16:16:44 +00:00
|
|
|
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
|
|
|
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-13 16:46:51 +00:00
|
|
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(0, repository.getBlockRepository().getBlockchainHeight(), "Blockchain should be empty for this test");
|
2018-06-15 16:16:44 +00:00
|
|
|
}
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-29 09:29:18 +00:00
|
|
|
// [Un]set genesis timestamp as required by test
|
2018-07-04 11:49:56 +00:00
|
|
|
JSONObject settingsJSON = new JSONObject();
|
2018-06-29 09:29:18 +00:00
|
|
|
if (genesisTimestamp != null)
|
2018-07-04 11:49:56 +00:00
|
|
|
settingsJSON.put("testnetstamp", genesisTimestamp);
|
|
|
|
|
|
|
|
Settings.test(settingsJSON);
|
2018-06-29 09:29:18 +00:00
|
|
|
|
2018-06-15 16:16:44 +00:00
|
|
|
// This needs to be called outside of acquiring our own repository or it will deadlock
|
|
|
|
BlockChain.validate();
|
2018-06-13 15:48:28 +00:00
|
|
|
|
2018-06-21 11:38:45 +00:00
|
|
|
// Grab repository for further use, including during test itself
|
|
|
|
repository = RepositoryManager.getRepository();
|
|
|
|
|
|
|
|
// Grab genesis block
|
2018-07-01 15:35:45 +00:00
|
|
|
parentBlockData = repository.getBlockRepository().fromHeight(1);
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
accountRepository = repository.getAccountRepository();
|
|
|
|
|
|
|
|
// Create test generator account
|
|
|
|
generator = new PrivateKeyAccount(repository, generatorSeed);
|
2018-12-10 13:27:41 +00:00
|
|
|
accountRepository.save(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey()));
|
2018-07-04 11:49:56 +00:00
|
|
|
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance));
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Create test sender account
|
|
|
|
sender = new PrivateKeyAccount(repository, senderSeed);
|
|
|
|
|
|
|
|
// Mock account
|
|
|
|
reference = senderSeed;
|
2018-12-10 13:27:41 +00:00
|
|
|
accountRepository.save(new AccountData(sender.getAddress(), reference, sender.getPublicKey()));
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Mock balance
|
2018-07-04 11:49:56 +00:00
|
|
|
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance));
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
repository.saveChanges();
|
|
|
|
}
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
@AfterEach
|
2018-06-21 11:38:45 +00:00
|
|
|
public void closeRepository() throws DataException {
|
|
|
|
RepositoryManager.closeRepositoryFactory();
|
|
|
|
}
|
|
|
|
|
2018-07-03 10:40:24 +00:00
|
|
|
private Transaction createPayment(PrivateKeyAccount sender, String recipient) throws DataException {
|
|
|
|
// Make a new payment transaction
|
2018-07-05 15:24:05 +00:00
|
|
|
BigDecimal amount = genericPaymentAmount;
|
2018-07-03 10:40:24 +00:00
|
|
|
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);
|
2018-08-02 09:02:33 +00:00
|
|
|
paymentTransaction.sign(sender);
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
return paymentTransaction;
|
|
|
|
}
|
|
|
|
|
2018-06-21 11:38:45 +00:00
|
|
|
@Test
|
|
|
|
public void testPaymentTransaction() throws DataException {
|
2018-06-29 09:29:18 +00:00
|
|
|
createTestAccounts(null);
|
|
|
|
|
2018-06-21 11:38:45 +00:00
|
|
|
// Make a new payment transaction
|
|
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
2018-07-01 15:35:45 +00:00
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-06-21 11:38:45 +00:00
|
|
|
PaymentTransactionData paymentTransactionData = new PaymentTransactionData(sender.getPublicKey(), recipient.getAddress(), amount, fee, timestamp,
|
|
|
|
reference);
|
|
|
|
|
|
|
|
Transaction paymentTransaction = new PaymentTransaction(repository, paymentTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
paymentTransaction.sign(sender);
|
2018-06-21 11:38:45 +00:00
|
|
|
assertTrue(paymentTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-06-21 11:38:45 +00:00
|
|
|
block.addTransaction(paymentTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
2018-06-21 11:38:45 +00:00
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
2018-06-21 11:38:45 +00:00
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Amount should be in recipient's balance
|
|
|
|
expectedBalance = amount;
|
|
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check recipient's reference
|
|
|
|
byte[] recipientsReference = recipient.getLastReference();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(Arrays.equals(paymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check generator's balance
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-06-21 11:38:45 +00:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:35:45 +00:00
|
|
|
@Test
|
|
|
|
public void testRegisterNameTransaction() throws DataException {
|
|
|
|
createTestAccounts(null);
|
|
|
|
|
|
|
|
// Make a new register name transaction
|
|
|
|
String name = "test name";
|
|
|
|
String data = "{\"key\":\"value\"}";
|
|
|
|
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
RegisterNameTransactionData registerNameTransactionData = new RegisterNameTransactionData(sender.getPublicKey(), sender.getAddress(), name, data, fee,
|
|
|
|
timestamp, reference);
|
|
|
|
|
|
|
|
Transaction registerNameTransaction = new RegisterNameTransaction(repository, registerNameTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
registerNameTransaction.sign(sender);
|
2018-07-01 15:35:45 +00:00
|
|
|
assertTrue(registerNameTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, registerNameTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-01 15:35:45 +00:00
|
|
|
block.addTransaction(registerNameTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-01 15:35:45 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
2018-07-01 15:35:45 +00:00
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-07-01 15:35:45 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
2018-07-01 15:35:45 +00:00
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-01 15:35:45 +00:00
|
|
|
|
|
|
|
// Check name was registered
|
|
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertNotNull(actualNameData);
|
|
|
|
|
|
|
|
// Check sender's reference
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(Arrays.equals(registerNameTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
2018-07-01 15:35:45 +00:00
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2018-07-02 17:09:36 +00:00
|
|
|
public void testUpdateNameTransaction() throws DataException {
|
2018-07-01 15:35:45 +00:00
|
|
|
// Register name using another test
|
|
|
|
testRegisterNameTransaction();
|
|
|
|
|
|
|
|
String name = "test name";
|
|
|
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
|
|
|
|
// Update name's owner and data
|
|
|
|
Account newOwner = new PublicKeyAccount(repository, recipientSeed);
|
|
|
|
String newData = "{\"newKey\":\"newValue\"}";
|
|
|
|
byte[] nameReference = reference;
|
|
|
|
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
2018-07-02 17:59:11 +00:00
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-07-01 15:35:45 +00:00
|
|
|
UpdateNameTransactionData updateNameTransactionData = new UpdateNameTransactionData(sender.getPublicKey(), newOwner.getAddress(), name, newData,
|
|
|
|
nameReference, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction updateNameTransaction = new UpdateNameTransaction(repository, updateNameTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
updateNameTransaction.sign(sender);
|
2018-07-01 15:35:45 +00:00
|
|
|
assertTrue(updateNameTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, updateNameTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-01 15:35:45 +00:00
|
|
|
block.addTransaction(updateNameTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-01 15:35:45 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check name was updated
|
|
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertEquals(newOwner.getAddress(), actualNameData.getOwner());
|
|
|
|
assertEquals(newData, actualNameData.getData());
|
|
|
|
|
|
|
|
// Now orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check name has been reverted correctly
|
|
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertEquals(originalNameData.getOwner(), actualNameData.getOwner());
|
|
|
|
assertEquals(originalNameData.getData(), actualNameData.getData());
|
|
|
|
}
|
|
|
|
|
2018-07-02 17:09:36 +00:00
|
|
|
@Test
|
|
|
|
public void testSellNameTransaction() throws DataException {
|
|
|
|
// Register name using another test
|
|
|
|
testRegisterNameTransaction();
|
|
|
|
|
|
|
|
String name = "test name";
|
|
|
|
|
|
|
|
// Sale price
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(1234L).setScale(8);
|
|
|
|
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
2018-07-02 17:59:11 +00:00
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-07-02 17:09:36 +00:00
|
|
|
SellNameTransactionData sellNameTransactionData = new SellNameTransactionData(sender.getPublicKey(), name, amount, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction sellNameTransaction = new SellNameTransaction(repository, sellNameTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
sellNameTransaction.sign(sender);
|
2018-07-02 17:09:36 +00:00
|
|
|
assertTrue(sellNameTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, sellNameTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-02 17:09:36 +00:00
|
|
|
block.addTransaction(sellNameTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-02 17:09:36 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check name was updated
|
|
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertTrue(actualNameData.getIsForSale());
|
|
|
|
assertEquals(amount, actualNameData.getSalePrice());
|
|
|
|
|
|
|
|
// Now orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check name has been reverted correctly
|
|
|
|
actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertFalse(actualNameData.getIsForSale());
|
|
|
|
assertNull(actualNameData.getSalePrice());
|
2018-07-02 17:59:11 +00:00
|
|
|
|
|
|
|
// Re-process block for use by other tests
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testCancelSellNameTransaction() throws DataException {
|
|
|
|
// Register and sell name using another test
|
|
|
|
testSellNameTransaction();
|
|
|
|
|
|
|
|
String name = "test name";
|
|
|
|
NameData originalNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
CancelSellNameTransactionData cancelSellNameTransactionData = new CancelSellNameTransactionData(sender.getPublicKey(), name, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction cancelSellNameTransaction = new CancelSellNameTransaction(repository, cancelSellNameTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
cancelSellNameTransaction.sign(sender);
|
2018-07-02 17:59:11 +00:00
|
|
|
assertTrue(cancelSellNameTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, cancelSellNameTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-02 17:59:11 +00:00
|
|
|
block.addTransaction(cancelSellNameTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-02 17:59:11 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check name was updated
|
|
|
|
NameData actualNameData = this.repository.getNameRepository().fromName(name);
|
|
|
|
assertFalse(actualNameData.getIsForSale());
|
|
|
|
assertEquals(originalNameData.getSalePrice(), actualNameData.getSalePrice());
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
2018-07-02 17:09:36 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 10:40:24 +00:00
|
|
|
@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
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-03 10:40:24 +00:00
|
|
|
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);
|
2018-08-02 09:02:33 +00:00
|
|
|
buyNameTransaction.sign(buyer);
|
2018-07-03 10:40:24 +00:00
|
|
|
assertTrue(buyNameTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, buyNameTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
block = new Block(repository, parentBlockData, generator);
|
2018-07-03 10:40:24 +00:00
|
|
|
block.addTransaction(buyNameTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2018-06-21 11:38:45 +00:00
|
|
|
@Test
|
|
|
|
public void testCreatePollTransaction() throws DataException {
|
2018-06-29 09:29:18 +00:00
|
|
|
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
|
2018-07-04 11:49:56 +00:00
|
|
|
createTestAccounts(BlockChain.getVotingReleaseTimestamp() + 1_000L);
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Make a new create poll transaction
|
|
|
|
String pollName = "test poll";
|
|
|
|
String description = "test poll description";
|
|
|
|
|
|
|
|
List<PollOptionData> pollOptions = new ArrayList<PollOptionData>();
|
|
|
|
pollOptions.add(new PollOptionData("abort"));
|
|
|
|
pollOptions.add(new PollOptionData("retry"));
|
|
|
|
pollOptions.add(new PollOptionData("fail"));
|
|
|
|
|
|
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
2018-07-01 15:35:45 +00:00
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-06-21 11:38:45 +00:00
|
|
|
CreatePollTransactionData createPollTransactionData = new CreatePollTransactionData(sender.getPublicKey(), recipient.getAddress(), pollName,
|
|
|
|
description, pollOptions, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction createPollTransaction = new CreatePollTransaction(repository, createPollTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
createPollTransaction.sign(sender);
|
2018-06-21 11:38:45 +00:00
|
|
|
assertTrue(createPollTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
|
|
|
|
|
2018-06-29 09:29:18 +00:00
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-06-21 11:38:45 +00:00
|
|
|
block.addTransaction(createPollTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
2018-06-21 11:38:45 +00:00
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
2018-06-21 11:38:45 +00:00
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-06-21 11:38:45 +00:00
|
|
|
|
|
|
|
// Check poll was created
|
|
|
|
PollData actualPollData = this.repository.getVotingRepository().fromPollName(pollName);
|
|
|
|
assertNotNull(actualPollData);
|
2018-06-29 09:29:18 +00:00
|
|
|
|
|
|
|
// Check sender's reference
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(Arrays.equals(createPollTransactionData.getSignature(), sender.getLastReference()), "Sender's new reference incorrect");
|
2018-06-29 09:29:18 +00:00
|
|
|
|
2018-07-01 15:35:45 +00:00
|
|
|
// Update variables for use by other tests
|
2018-06-29 09:29:18 +00:00
|
|
|
reference = sender.getLastReference();
|
2018-07-01 15:35:45 +00:00
|
|
|
parentBlockData = block.getBlockData();
|
2018-06-29 09:29:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testVoteOnPollTransaction() throws DataException {
|
|
|
|
// Create poll using another test
|
|
|
|
testCreatePollTransaction();
|
|
|
|
|
|
|
|
// Try all options, plus invalid optionIndex (note use of <= for this)
|
|
|
|
String pollName = "test poll";
|
|
|
|
int pollOptionsSize = 3;
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
2018-07-01 15:35:45 +00:00
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-06-29 09:29:18 +00:00
|
|
|
|
|
|
|
for (int optionIndex = 0; optionIndex <= pollOptionsSize; ++optionIndex) {
|
|
|
|
// Make a vote-on-poll transaction
|
|
|
|
VoteOnPollTransactionData voteOnPollTransactionData = new VoteOnPollTransactionData(sender.getPublicKey(), pollName, optionIndex, fee, timestamp,
|
|
|
|
reference);
|
|
|
|
|
|
|
|
Transaction voteOnPollTransaction = new VoteOnPollTransaction(repository, voteOnPollTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
voteOnPollTransaction.sign(sender);
|
2018-06-29 09:29:18 +00:00
|
|
|
assertTrue(voteOnPollTransaction.isSignatureValid());
|
|
|
|
|
|
|
|
if (optionIndex == pollOptionsSize) {
|
|
|
|
assertEquals(ValidationResult.POLL_OPTION_DOES_NOT_EXIST, voteOnPollTransaction.isValid());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assertEquals(ValidationResult.OK, voteOnPollTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-06-29 09:29:18 +00:00
|
|
|
block.addTransaction(voteOnPollTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-06-29 09:29:18 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
2018-06-29 10:05:15 +00:00
|
|
|
// Check vote was registered properly
|
|
|
|
VoteOnPollData actualVoteOnPollData = repository.getVotingRepository().getVote(pollName, sender.getPublicKey());
|
|
|
|
assertNotNull(actualVoteOnPollData);
|
|
|
|
assertEquals(optionIndex, actualVoteOnPollData.getOptionIndex());
|
|
|
|
|
2018-06-29 09:29:18 +00:00
|
|
|
// update variables for next round
|
2018-07-01 15:35:45 +00:00
|
|
|
parentBlockData = block.getBlockData();
|
2018-06-29 09:29:18 +00:00
|
|
|
timestamp += 1_000;
|
|
|
|
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
|
|
|
}
|
2018-06-29 10:05:15 +00:00
|
|
|
|
|
|
|
// Check poll's votes
|
|
|
|
List<VoteOnPollData> votes = repository.getVotingRepository().getVotes(pollName);
|
|
|
|
assertNotNull(votes);
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(1, votes.size(), "Only one vote expected");
|
2018-06-29 10:05:15 +00:00
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(pollOptionsSize - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
|
|
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
2018-06-29 10:05:15 +00:00
|
|
|
|
|
|
|
// Orphan last block
|
|
|
|
BlockData lastBlockData = repository.getBlockRepository().getLastBlock();
|
|
|
|
Block lastBlock = new Block(repository, lastBlockData);
|
|
|
|
lastBlock.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Re-check poll's votes
|
2018-06-29 10:05:15 +00:00
|
|
|
votes = repository.getVotingRepository().getVotes(pollName);
|
|
|
|
assertNotNull(votes);
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(1, votes.size(), "Only one vote expected");
|
2018-06-29 10:05:15 +00:00
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(pollOptionsSize - 1 - 1, votes.get(0).getOptionIndex(), "Wrong vote option index");
|
|
|
|
assertTrue(Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()), "Wrong voter public key");
|
2018-06-13 15:48:28 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 10:40:24 +00:00
|
|
|
@Test
|
|
|
|
public void testIssueAssetTransaction() throws DataException {
|
2018-07-04 11:49:56 +00:00
|
|
|
createTestAccounts(null);
|
|
|
|
|
|
|
|
// Create new asset
|
|
|
|
String assetName = "test asset";
|
|
|
|
String description = "test asset description";
|
|
|
|
long quantity = 1_000_000L;
|
2018-10-04 13:38:59 +00:00
|
|
|
boolean isDivisible = true;
|
2018-07-04 11:49:56 +00:00
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
|
|
|
|
IssueAssetTransactionData issueAssetTransactionData = new IssueAssetTransactionData(sender.getPublicKey(), sender.getAddress(), assetName, description,
|
|
|
|
quantity, isDivisible, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction issueAssetTransaction = new IssueAssetTransaction(repository, issueAssetTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
issueAssetTransaction.sign(sender);
|
2018-07-04 11:49:56 +00:00
|
|
|
assertTrue(issueAssetTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, issueAssetTransaction.isValid());
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-04 11:49:56 +00:00
|
|
|
block.addTransaction(issueAssetTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
|
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
|
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check we now have an assetId
|
|
|
|
Long assetId = issueAssetTransactionData.getAssetId();
|
|
|
|
assertNotNull(assetId);
|
|
|
|
// Should NOT collide with Asset.QORA
|
|
|
|
assertFalse(assetId == Asset.QORA);
|
|
|
|
|
|
|
|
// Check asset now exists
|
|
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
|
|
assertTrue(assetRepo.assetExists(assetId));
|
|
|
|
assertTrue(assetRepo.assetExists(assetName));
|
|
|
|
// Check asset data
|
|
|
|
AssetData assetData = assetRepo.fromAssetId(assetId);
|
|
|
|
assertNotNull(assetData);
|
|
|
|
assertEquals(assetName, assetData.getName());
|
|
|
|
assertEquals(description, assetData.getDescription());
|
|
|
|
|
|
|
|
// Orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check generator's balance
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check asset no longer exists
|
|
|
|
assertFalse(assetRepo.assetExists(assetId));
|
|
|
|
assertFalse(assetRepo.assetExists(assetName));
|
|
|
|
assetData = assetRepo.fromAssetId(assetId);
|
|
|
|
assertNull(assetData);
|
|
|
|
|
|
|
|
// Re-process block for use by other tests
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testTransferAssetTransaction() throws DataException {
|
2018-07-04 11:49:56 +00:00
|
|
|
// Issue asset using another test
|
|
|
|
testIssueAssetTransaction();
|
|
|
|
|
|
|
|
String assetName = "test asset";
|
|
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
BigDecimal originalSenderBalance = sender.getConfirmedBalance(Asset.QORA);
|
|
|
|
BigDecimal originalGeneratorBalance = generator.getConfirmedBalance(Asset.QORA);
|
|
|
|
|
|
|
|
// Transfer asset to new recipient
|
|
|
|
Account recipient = new PublicKeyAccount(repository, recipientSeed);
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L).setScale(8);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
|
|
|
|
TransferAssetTransactionData transferAssetTransactionData = new TransferAssetTransactionData(sender.getPublicKey(), recipient.getAddress(), amount,
|
|
|
|
assetId, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction transferAssetTransaction = new TransferAssetTransaction(repository, transferAssetTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
transferAssetTransaction.sign(sender);
|
2018-07-04 11:49:56 +00:00
|
|
|
assertTrue(transferAssetTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, transferAssetTransaction.isValid());
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-04 11:49:56 +00:00
|
|
|
block.addTransaction(transferAssetTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
BigDecimal expectedBalance = originalSenderBalance.subtract(fee);
|
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
|
|
|
expectedBalance = originalGeneratorBalance.add(fee);
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check asset balances
|
|
|
|
BigDecimal actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
|
|
|
assertNotNull(actualSenderAssetBalance);
|
|
|
|
BigDecimal expectedSenderAssetBalance = BigDecimal.valueOf(originalAssetData.getQuantity()).setScale(8).subtract(amount);
|
|
|
|
assertEquals(expectedSenderAssetBalance, actualSenderAssetBalance);
|
|
|
|
|
|
|
|
BigDecimal actualRecipientAssetBalance = recipient.getConfirmedBalance(assetId);
|
|
|
|
assertNotNull(actualRecipientAssetBalance);
|
|
|
|
assertEquals(amount, actualRecipientAssetBalance);
|
|
|
|
|
|
|
|
// Orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(originalSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check generator's balance
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(originalGeneratorBalance.compareTo(actualBalance) == 0, "Generator's reverted balance incorrect");
|
2018-07-04 11:49:56 +00:00
|
|
|
|
|
|
|
// Check asset balances
|
|
|
|
actualSenderAssetBalance = sender.getConfirmedBalance(assetId);
|
|
|
|
assertNotNull(actualSenderAssetBalance);
|
|
|
|
expectedSenderAssetBalance = BigDecimal.valueOf(originalAssetData.getQuantity()).setScale(8);
|
|
|
|
assertEquals(expectedSenderAssetBalance, actualSenderAssetBalance);
|
|
|
|
|
|
|
|
actualRecipientAssetBalance = recipient.getConfirmedBalance(assetId);
|
|
|
|
if (actualRecipientAssetBalance != null)
|
|
|
|
assertEquals(BigDecimal.ZERO.setScale(8), actualRecipientAssetBalance);
|
|
|
|
|
|
|
|
// Re-process block for use by other tests
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testCreateAssetOrderTransaction() throws DataException {
|
2018-07-05 15:24:05 +00:00
|
|
|
// Issue asset using another test
|
|
|
|
testIssueAssetTransaction();
|
|
|
|
|
|
|
|
// Asset info
|
|
|
|
String assetName = "test asset";
|
|
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
|
|
|
|
// Buyer
|
|
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
|
|
|
|
// 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
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-05 15:24:05 +00:00
|
|
|
block.addTransaction(somePaymentTransaction.getTransactionData());
|
|
|
|
block.sign();
|
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
parentBlockData = block.getBlockData();
|
|
|
|
|
|
|
|
// Order: buyer has 10 QORA and wants to buy "test asset" at a price of 50 "test asset" per QORA.
|
|
|
|
long haveAssetId = Asset.QORA;
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(10).setScale(8);
|
|
|
|
long wantAssetId = assetId;
|
|
|
|
BigDecimal price = BigDecimal.valueOf(50).setScale(8);
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
|
|
|
|
CreateOrderTransactionData createOrderTransactionData = new CreateOrderTransactionData(buyer.getPublicKey(), haveAssetId, wantAssetId, amount, price,
|
|
|
|
fee, timestamp, buyersReference);
|
|
|
|
Transaction createOrderTransaction = new CreateOrderTransaction(this.repository, createOrderTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
createOrderTransaction.sign(buyer);
|
2018-07-05 15:24:05 +00:00
|
|
|
assertTrue(createOrderTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
block = new Block(repository, parentBlockData, generator);
|
2018-07-05 15:24:05 +00:00
|
|
|
block.addTransaction(createOrderTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-05 15:24:05 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order was created
|
|
|
|
byte[] orderId = createOrderTransactionData.getSignature();
|
|
|
|
OrderData orderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNotNull(orderData);
|
|
|
|
|
|
|
|
// Check buyer's balance reduced
|
|
|
|
BigDecimal expectedBalance = genericPaymentAmount.subtract(amount).subtract(fee);
|
|
|
|
BigDecimal actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
|
|
|
// Orphan transaction
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order no longer exists
|
|
|
|
orderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNull(orderData);
|
|
|
|
|
|
|
|
// Check buyer's balance restored
|
|
|
|
expectedBalance = genericPaymentAmount;
|
|
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
|
|
|
// Re-process to allow use by other tests
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Update variables for use by other tests
|
|
|
|
reference = sender.getLastReference();
|
|
|
|
parentBlockData = block.getBlockData();
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testCancelAssetOrderTransaction() throws DataException {
|
2018-07-05 15:24:05 +00:00
|
|
|
// Issue asset and create order using another test
|
|
|
|
testCreateAssetOrderTransaction();
|
|
|
|
|
|
|
|
// Asset info
|
|
|
|
String assetName = "test asset";
|
|
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
|
|
|
|
// Buyer
|
|
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
|
|
|
|
// Fetch orders
|
|
|
|
long haveAssetId = Asset.QORA;
|
|
|
|
long wantAssetId = assetId;
|
|
|
|
List<OrderData> orders = assetRepo.getOpenOrders(haveAssetId, wantAssetId);
|
|
|
|
|
|
|
|
assertNotNull(orders);
|
|
|
|
assertEquals(1, orders.size());
|
|
|
|
|
|
|
|
OrderData originalOrderData = orders.get(0);
|
|
|
|
assertNotNull(originalOrderData);
|
|
|
|
assertFalse(originalOrderData.getIsClosed());
|
|
|
|
|
|
|
|
// Create cancel order transaction
|
|
|
|
byte[] orderId = originalOrderData.getOrderId();
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
byte[] buyersReference = buyer.getLastReference();
|
|
|
|
CancelOrderTransactionData cancelOrderTransactionData = new CancelOrderTransactionData(buyer.getPublicKey(), orderId, fee, timestamp, buyersReference);
|
|
|
|
|
|
|
|
Transaction cancelOrderTransaction = new CancelOrderTransaction(this.repository, cancelOrderTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
cancelOrderTransaction.sign(buyer);
|
2018-07-05 15:24:05 +00:00
|
|
|
assertTrue(cancelOrderTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, cancelOrderTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-05 15:24:05 +00:00
|
|
|
block.addTransaction(cancelOrderTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-05 15:24:05 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order is marked as cancelled
|
|
|
|
OrderData cancelledOrderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNotNull(cancelledOrderData);
|
|
|
|
assertTrue(cancelledOrderData.getIsClosed());
|
|
|
|
|
|
|
|
// Orphan
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order is no longer marked as cancelled
|
|
|
|
OrderData uncancelledOrderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNotNull(uncancelledOrderData);
|
|
|
|
assertFalse(uncancelledOrderData.getIsClosed());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testMatchingCreateAssetOrderTransaction() throws DataException {
|
|
|
|
// Issue asset and create order using another test
|
|
|
|
testCreateAssetOrderTransaction();
|
|
|
|
|
|
|
|
// Asset info
|
|
|
|
String assetName = "test asset";
|
|
|
|
AssetRepository assetRepo = this.repository.getAssetRepository();
|
|
|
|
AssetData originalAssetData = assetRepo.fromAssetName(assetName);
|
|
|
|
long assetId = originalAssetData.getAssetId();
|
|
|
|
|
|
|
|
// Buyer
|
|
|
|
PrivateKeyAccount buyer = new PrivateKeyAccount(repository, recipientSeed);
|
|
|
|
|
|
|
|
// Fetch orders
|
|
|
|
long originalHaveAssetId = Asset.QORA;
|
|
|
|
long originalWantAssetId = assetId;
|
|
|
|
List<OrderData> orders = assetRepo.getOpenOrders(originalHaveAssetId, originalWantAssetId);
|
|
|
|
|
|
|
|
assertNotNull(orders);
|
|
|
|
assertEquals(1, orders.size());
|
|
|
|
|
|
|
|
OrderData originalOrderData = orders.get(0);
|
|
|
|
assertNotNull(originalOrderData);
|
|
|
|
assertFalse(originalOrderData.getIsClosed());
|
|
|
|
|
2018-10-04 13:38:59 +00:00
|
|
|
// Unfulfilled order: "buyer" has 10 QORA and wants to buy "test asset" at a price of 50 "test asset" per QORA.
|
|
|
|
// buyer's order: have=QORA, amount=10, want=test-asset, price=50 (test-asset per QORA, so max return is 500 test-asset)
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Original asset owner (sender) will sell asset to "buyer"
|
|
|
|
|
|
|
|
// Order: seller has 40 "test asset" and wants to buy QORA at a price of 1/60 QORA per "test asset".
|
|
|
|
// This order should be a partial match for original order, and at a better price than asked
|
2018-10-04 13:38:59 +00:00
|
|
|
long haveAssetId = assetId;
|
2018-07-05 15:24:05 +00:00
|
|
|
BigDecimal amount = BigDecimal.valueOf(40).setScale(8);
|
2018-10-04 13:38:59 +00:00
|
|
|
long wantAssetId = Asset.QORA;
|
|
|
|
BigDecimal price = BigDecimal.ONE.setScale(8).divide(BigDecimal.valueOf(60).setScale(8), RoundingMode.DOWN);
|
2018-07-05 15:24:05 +00:00
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
2018-10-04 13:38:59 +00:00
|
|
|
BigDecimal senderPreTradeWantBalance = sender.getConfirmedBalance(wantAssetId);
|
2018-07-05 15:24:05 +00:00
|
|
|
|
|
|
|
CreateOrderTransactionData createOrderTransactionData = new CreateOrderTransactionData(sender.getPublicKey(), haveAssetId, wantAssetId, amount, price,
|
|
|
|
fee, timestamp, reference);
|
|
|
|
Transaction createOrderTransaction = new CreateOrderTransaction(this.repository, createOrderTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
createOrderTransaction.sign(sender);
|
2018-07-05 15:24:05 +00:00
|
|
|
assertTrue(createOrderTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-05 15:24:05 +00:00
|
|
|
block.addTransaction(createOrderTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-05 15:24:05 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order was created
|
|
|
|
byte[] orderId = createOrderTransactionData.getSignature();
|
|
|
|
OrderData orderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNotNull(orderData);
|
|
|
|
|
|
|
|
// Check order has trades
|
|
|
|
List<TradeData> trades = assetRepo.getOrdersTrades(orderId);
|
|
|
|
assertNotNull(trades);
|
2018-10-04 20:58:04 +00:00
|
|
|
assertEquals(1, trades.size(), "Trade didn't happen");
|
2018-07-05 15:24:05 +00:00
|
|
|
TradeData tradeData = trades.get(0);
|
|
|
|
|
|
|
|
// Check trade has correct values
|
2018-10-04 13:38:59 +00:00
|
|
|
BigDecimal expectedAmount = amount.divide(originalOrderData.getPrice()).setScale(8);
|
2018-07-05 15:24:05 +00:00
|
|
|
BigDecimal actualAmount = tradeData.getAmount();
|
|
|
|
assertTrue(expectedAmount.compareTo(actualAmount) == 0);
|
|
|
|
|
2018-10-04 13:38:59 +00:00
|
|
|
BigDecimal expectedPrice = amount;
|
2018-07-05 15:24:05 +00:00
|
|
|
BigDecimal actualPrice = tradeData.getPrice();
|
|
|
|
assertTrue(expectedPrice.compareTo(actualPrice) == 0);
|
|
|
|
|
|
|
|
// Check seller's "test asset" balance
|
|
|
|
BigDecimal expectedBalance = BigDecimal.valueOf(1_000_000L).setScale(8).subtract(amount);
|
|
|
|
BigDecimal actualBalance = sender.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
|
|
|
// Check buyer's "test asset" balance
|
|
|
|
expectedBalance = amount;
|
|
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
|
|
|
// Check seller's QORA balance
|
2018-10-04 13:38:59 +00:00
|
|
|
expectedBalance = senderPreTradeWantBalance.subtract(BigDecimal.ONE).add(expectedAmount);
|
2018-07-05 15:24:05 +00:00
|
|
|
actualBalance = sender.getConfirmedBalance(wantAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
2018-10-04 13:38:59 +00:00
|
|
|
// Check seller's order is correctly fulfilled
|
|
|
|
assertTrue(orderData.getIsFulfilled());
|
|
|
|
|
|
|
|
// Check buyer's order is still not fulfilled
|
|
|
|
OrderData buyersOrderData = assetRepo.fromOrderId(originalOrderData.getOrderId());
|
|
|
|
assertFalse(buyersOrderData.getIsFulfilled());
|
|
|
|
|
2018-07-05 15:24:05 +00:00
|
|
|
// Orphan transaction
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check order no longer exists
|
|
|
|
orderData = assetRepo.fromOrderId(orderId);
|
|
|
|
assertNull(orderData);
|
|
|
|
|
|
|
|
// Check trades no longer exist
|
|
|
|
trades = assetRepo.getOrdersTrades(orderId);
|
|
|
|
assertNotNull(trades);
|
|
|
|
assertEquals(0, trades.size());
|
|
|
|
|
|
|
|
// Check seller's "test asset" balance restored
|
|
|
|
expectedBalance = BigDecimal.valueOf(1_000_000L).setScale(8);
|
|
|
|
actualBalance = sender.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
|
|
|
|
|
|
|
// Check buyer's "test asset" balance restored
|
|
|
|
expectedBalance = BigDecimal.ZERO.setScale(8);
|
|
|
|
actualBalance = buyer.getConfirmedBalance(haveAssetId);
|
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0);
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testMultiPaymentTransaction() throws DataException {
|
2018-07-04 12:53:20 +00:00
|
|
|
createTestAccounts(null);
|
|
|
|
|
|
|
|
// Make a new multi-payment transaction
|
|
|
|
BigDecimal fee = BigDecimal.ONE;
|
|
|
|
long timestamp = parentBlockData.getTimestamp() + 1_000;
|
|
|
|
|
|
|
|
// Payments
|
|
|
|
BigDecimal expectedSenderBalance = initialSenderBalance.subtract(fee);
|
|
|
|
List<PaymentData> payments = new ArrayList<PaymentData>();
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
|
|
byte[] seed = recipientSeed.clone();
|
|
|
|
seed[0] += i;
|
|
|
|
Account recipient = new PublicKeyAccount(repository, seed);
|
|
|
|
long assetId = Asset.QORA;
|
|
|
|
|
|
|
|
BigDecimal amount = BigDecimal.valueOf(1_000L + i).setScale(8);
|
|
|
|
expectedSenderBalance = expectedSenderBalance.subtract(amount);
|
|
|
|
|
|
|
|
PaymentData paymentData = new PaymentData(recipient.getAddress(), assetId, amount);
|
|
|
|
|
|
|
|
payments.add(paymentData);
|
|
|
|
}
|
|
|
|
|
|
|
|
MultiPaymentTransactionData multiPaymentTransactionData = new MultiPaymentTransactionData(sender.getPublicKey(), payments, fee, timestamp, reference);
|
|
|
|
|
|
|
|
Transaction multiPaymentTransaction = new MultiPaymentTransaction(repository, multiPaymentTransactionData);
|
2018-08-02 09:02:33 +00:00
|
|
|
multiPaymentTransaction.sign(sender);
|
2018-07-04 12:53:20 +00:00
|
|
|
assertTrue(multiPaymentTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, multiPaymentTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with payment transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-04 12:53:20 +00:00
|
|
|
block.addTransaction(multiPaymentTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedSenderBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
|
|
|
BigDecimal expectedBalance = initialGeneratorBalance.add(fee);
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
// Check recipients
|
|
|
|
for (int i = 0; i < payments.size(); ++i) {
|
|
|
|
PaymentData paymentData = payments.get(i);
|
|
|
|
Account recipient = new Account(this.repository, paymentData.getRecipient());
|
|
|
|
|
|
|
|
byte[] recipientsReference = recipient.getLastReference();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(Arrays.equals(multiPaymentTransaction.getTransactionData().getSignature(), recipientsReference), "Recipient's new reference incorrect");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
// Amount should be in recipient's balance
|
|
|
|
expectedBalance = paymentData.getAmount();
|
|
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Orphan block
|
|
|
|
block.orphan();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
|
|
|
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialSenderBalance.compareTo(actualBalance) == 0, "Sender's reverted balance incorrect");
|
2018-07-04 12:53:20 +00:00
|
|
|
|
|
|
|
// Check generator's balance
|
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(initialGeneratorBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testMessageTransaction() throws DataException, UnsupportedEncodingException {
|
2018-07-04 11:49:56 +00:00
|
|
|
createTestAccounts(1431861220336L); // timestamp taken from main blockchain block 99000
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
// 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);
|
2018-08-02 09:02:33 +00:00
|
|
|
messageTransaction.sign(sender);
|
2018-07-03 10:40:24 +00:00
|
|
|
assertTrue(messageTransaction.isSignatureValid());
|
|
|
|
assertEquals(ValidationResult.OK, messageTransaction.isValid());
|
|
|
|
|
|
|
|
// Forge new block with message transaction
|
Finally syncs with qora1 chain!
ATData no longer needs deploySignature as a link back to DeployATTransaction,
but does need creator and creation [timestamp].
"creation" is critical for ordering ATs when creating/validating blocks.
Similar changes to ATStateData, adding creation, stateHash (for quicker
comparison with blocks received over the network), and fees incurred
by running AT on that block.
Also added more explicit constructors for different scenarios.
BlockData upgraded from simplistic "atBytes" to use ATStateData (above)
which has details on ATs run for that block, fees incurred, and a hash
of the AT's state. atCount added to keep track of how many ATs ran.
ATTransactions essentially reuse the GenesisAccount's publickey as
creator/sender as they're brought into existence by the Qora code
rather than an end user. ATTransactionData updated to reflect this
and the AT's address used as a "sender" field.
Account tidied up with respect to CIYAM ATs and setConfirmedBalance
ensures there is a corresponding record in Accounts (DB table).
Account, and subclasses, don't need "throws DataException" on
constructor any more.
Fixed bug in Asset Order matching where the matching engine
would give up after first potential match instead of trying others.
Lots more work on CIYAM AT, albeit mainly blind importing of old
v1 ATs from static JSON file as they're all dead and new
v2 implementation is not backwards compatible.
More work on Blocks, mostly AT stuff, but also fork-based corruption
prevention using fix from Qora v1.
Payment-related transactions (multipayment, etc.) always expect/use
non-null (albeit maybe empty) list of PaymentData when validating,
processing or orphaning.
Mainly a change in HSQLDBTransactionRepository.getPayments()
Payment.isValid(byte[], PaymentData, BigDecimal, boolean isZeroAmountValid)
didn't pass on isZeroAmountValid to called method - whoops!
Lots of work on ATTransactions themselves.
MessageTransactions incorrectly assumed the optional payment was always
in Qora. Now fixed to use the transaction's provided assetId.
Mass of fixes/additions to HSQLDBATRepository, especially fixing
incorrect reference to Assets DB table!
In HSQLDBDatabaseUpdates, bump QoraAmount type from DECIMAL(19,8)
to DECIMAL(27,8) to allow for huge asset quantities.
You WILL have to rebuild your database!
2018-10-26 16:47:47 +00:00
|
|
|
Block block = new Block(repository, parentBlockData, generator);
|
2018-07-03 10:40:24 +00:00
|
|
|
block.addTransaction(messageTransactionData);
|
|
|
|
block.sign();
|
|
|
|
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(block.isSignatureValid(), "Block signatures invalid");
|
|
|
|
assertEquals(Block.ValidationResult.OK, block.isValid(), "Block is invalid");
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
block.process();
|
|
|
|
repository.saveChanges();
|
|
|
|
|
|
|
|
// Check sender's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
|
2018-07-03 10:40:24 +00:00
|
|
|
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Sender's new balance incorrect");
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
// Fee should be in generator's balance
|
2018-07-04 11:49:56 +00:00
|
|
|
expectedBalance = initialGeneratorBalance.add(fee);
|
2018-07-03 10:40:24 +00:00
|
|
|
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Generator's new balance incorrect");
|
2018-07-03 10:40:24 +00:00
|
|
|
|
|
|
|
// Amount should be in recipient's balance
|
|
|
|
expectedBalance = amount;
|
|
|
|
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
|
2018-10-04 20:58:04 +00:00
|
|
|
assertTrue(expectedBalance.compareTo(actualBalance) == 0, "Recipient's new balance incorrect");
|
2018-07-03 10:40:24 +00:00
|
|
|
}
|
|
|
|
|
2018-06-13 15:48:28 +00:00
|
|
|
}
|