More tests and fixes resulting from such

Settings class reworked to allow easier testing

Fix to Payment.orphan() where fee was being incorrectly subtracted instead of added

Added AssetRepository.fromAssetName(String): AssetData

Fixed deleting assets from HSQLDB repository due to broken column name in SQL.

Fixed saving IssueAssetTransactions in HSQLDB repository due to missing column binding.

More TransactionTests!
This commit is contained in:
catbref 2018-07-04 12:49:56 +01:00
parent 2bfb0227ae
commit 7536ab37fa
17 changed files with 387 additions and 57 deletions

View File

@ -36,6 +36,11 @@ public class IssueAssetTransactionData extends TransactionData {
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, signature);
}
public IssueAssetTransactionData(byte[] issuerPublicKey, String owner, String assetName, String description, long quantity, boolean isDivisible,
BigDecimal fee, long timestamp, byte[] reference) {
this(null, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference, null);
}
// Getters/Setters
public Long getAssetId() {

View File

@ -155,9 +155,9 @@ public class Block {
* @return 1, 2 or 3
*/
public int getNextBlockVersion() {
if (this.blockData.getHeight() < BlockChain.AT_BLOCK_HEIGHT_RELEASE)
if (this.blockData.getHeight() < BlockChain.getATReleaseHeight())
return 1;
else if (this.blockData.getTimestamp() < BlockChain.POWFIX_RELEASE_TIMESTAMP)
else if (this.blockData.getTimestamp() < BlockChain.getPowFixReleaseTimestamp())
return 2;
else
return 3;

View File

@ -10,6 +10,7 @@ import repository.BlockRepository;
import repository.DataException;
import repository.Repository;
import repository.RepositoryManager;
import settings.Settings;
/**
* Class representing the blockchain as a whole.
@ -31,11 +32,11 @@ public class BlockChain {
public static final long BLOCK_TIMESTAMP_MARGIN = 500L;
// Various release timestamps / block heights
public static final int MESSAGE_RELEASE_HEIGHT = 99000;
public static final int AT_BLOCK_HEIGHT_RELEASE = 99000;
public static final long POWFIX_RELEASE_TIMESTAMP = 1456426800000L; // Block Version 3 // 2016-02-25T19:00:00+00:00
public static final long ASSETS_RELEASE_TIMESTAMP = 0L; // From Qora epoch
public static final long VOTING_RELEASE_TIMESTAMP = 1403715600000L; // 2014-06-25T17:00:00+00:00
private static final int MESSAGE_RELEASE_HEIGHT = 99000;
private static final int AT_RELEASE_HEIGHT = 99000;
private static final long POWFIX_RELEASE_TIMESTAMP = 1456426800000L; // Block Version 3 // 2016-02-25T19:00:00+00:00
private static final long ASSETS_RELEASE_TIMESTAMP = 0L; // From Qora epoch
private static final long VOTING_RELEASE_TIMESTAMP = 1403715600000L; // 2014-06-25T17:00:00+00:00
/**
* Some sort start-up/initialization/checking method.
@ -96,4 +97,39 @@ public class BlockChain {
return balance;
}
public static int getMessageReleaseHeight() {
if (Settings.getInstance().isTestNet())
return 0;
return MESSAGE_RELEASE_HEIGHT;
}
public static int getATReleaseHeight() {
if (Settings.getInstance().isTestNet())
return 0;
return AT_RELEASE_HEIGHT;
}
public static long getPowFixReleaseTimestamp() {
if (Settings.getInstance().isTestNet())
return 0;
return POWFIX_RELEASE_TIMESTAMP;
}
public static long getAssetsReleaseTimestamp() {
if (Settings.getInstance().isTestNet())
return 0;
return ASSETS_RELEASE_TIMESTAMP;
}
public static long getVotingReleaseTimestamp() {
if (Settings.getInstance().isTestNet())
return 0;
return VOTING_RELEASE_TIMESTAMP;
}
}

View File

@ -128,7 +128,7 @@ public class Payment {
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
// Update sender's balance due to fee
sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).subtract(fee));
sender.setConfirmedBalance(Asset.QORA, sender.getConfirmedBalance(Asset.QORA).add(fee));
// Update sender's reference
sender.setLastReference(reference);

View File

@ -115,7 +115,7 @@ public class CreateOrderTransaction extends Transaction {
// Check creator has enough funds for fee in QORA
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
if (createOrderTransactionData.getTimestamp() >= BlockChain.POWFIX_RELEASE_TIMESTAMP
if (createOrderTransactionData.getTimestamp() >= BlockChain.getPowFixReleaseTimestamp()
&& creator.getConfirmedBalance(Asset.QORA).compareTo(createOrderTransactionData.getFee()) == -1)
return ValidationResult.NO_BALANCE;
}

View File

@ -77,7 +77,7 @@ public class CreatePollTransaction extends Transaction {
public ValidationResult isValid() throws DataException {
// Are CreatePollTransactions even allowed at this point?
// XXX In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used?
if (this.createPollTransactionData.getTimestamp() < BlockChain.VOTING_RELEASE_TIMESTAMP)
if (this.createPollTransactionData.getTimestamp() < BlockChain.getVotingReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check owner address is valid

View File

@ -79,7 +79,7 @@ public class IssueAssetTransaction extends Transaction {
public ValidationResult isValid() throws DataException {
// Are IssueAssetTransactions even allowed at this point?
// XXX In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used?
if (this.issueAssetTransactionData.getTimestamp() < BlockChain.ASSETS_RELEASE_TIMESTAMP)
if (this.issueAssetTransactionData.getTimestamp() < BlockChain.getAssetsReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check owner address is valid

View File

@ -90,7 +90,7 @@ public class MessageTransaction extends Transaction {
if (messageTransactionData.getVersion() != MessageTransaction.getVersionByTimestamp(messageTransactionData.getTimestamp()))
return ValidationResult.NOT_YET_RELEASED;
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.MESSAGE_RELEASE_HEIGHT)
if (this.repository.getBlockRepository().getBlockchainHeight() < BlockChain.getMessageReleaseHeight())
return ValidationResult.NOT_YET_RELEASED;
// Check data length

View File

@ -90,7 +90,7 @@ public class MultiPaymentTransaction extends Transaction {
List<PaymentData> payments = multiPaymentTransactionData.getPayments();
// Are MultiPaymentTransactions even allowed at this point?
if (NTP.getTime() < BlockChain.ASSETS_RELEASE_TIMESTAMP)
if (NTP.getTime() < BlockChain.getAssetsReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check number of payments
@ -105,7 +105,7 @@ public class MultiPaymentTransaction extends Transaction {
// Check sender has enough funds for fee
// NOTE: in Gen1 pre-POWFIX-RELEASE transactions didn't have this check
if (multiPaymentTransactionData.getTimestamp() >= BlockChain.POWFIX_RELEASE_TIMESTAMP
if (multiPaymentTransactionData.getTimestamp() >= BlockChain.getPowFixReleaseTimestamp()
&& sender.getConfirmedBalance(Asset.QORA).compareTo(multiPaymentTransactionData.getFee()) == -1)
return ValidationResult.NO_BALANCE;

View File

@ -206,7 +206,7 @@ public abstract class Transaction {
* @return transaction version number, likely 1 or 3
*/
public static int getVersionByTimestamp(long timestamp) {
if (timestamp < BlockChain.POWFIX_RELEASE_TIMESTAMP) {
if (timestamp < BlockChain.getPowFixReleaseTimestamp()) {
return 1;
} else {
return 3;

View File

@ -85,7 +85,7 @@ public class TransferAssetTransaction extends Transaction {
TransferAssetTransactionData transferAssetTransactionData = (TransferAssetTransactionData) this.transactionData;
// Are IssueAssetTransactions even allowed at this point?
if (NTP.getTime() < BlockChain.ASSETS_RELEASE_TIMESTAMP)
if (NTP.getTime() < BlockChain.getAssetsReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check reference is correct

View File

@ -68,7 +68,7 @@ public class VoteOnPollTransaction extends Transaction {
public ValidationResult isValid() throws DataException {
// Are VoteOnPollTransactions even allowed at this point?
// XXX In gen1 this used NTP.getTime() but surely the transaction's timestamp should be used?
if (this.voteOnPollTransactionData.getTimestamp() < BlockChain.VOTING_RELEASE_TIMESTAMP)
if (this.voteOnPollTransactionData.getTimestamp() < BlockChain.getVotingReleaseTimestamp())
return ValidationResult.NOT_YET_RELEASED;
// Check name size bounds

View File

@ -9,6 +9,8 @@ public interface AssetRepository {
public AssetData fromAssetId(long assetId) throws DataException;
public AssetData fromAssetName(String assetName) throws DataException;
public boolean assetExists(long assetId) throws DataException;
public boolean assetExists(String assetName) throws DataException;

View File

@ -39,6 +39,26 @@ public class HSQLDBAssetRepository implements AssetRepository {
}
}
public AssetData fromAssetName(String assetName) throws DataException {
try {
ResultSet resultSet = this.repository
.checkedExecute("SELECT owner, asset_id, description, quantity, is_divisible, reference FROM Assets WHERE asset_name = ?", assetName);
if (resultSet == null)
return null;
String owner = resultSet.getString(1);
long assetId = resultSet.getLong(2);
String description = resultSet.getString(3);
long quantity = resultSet.getLong(4);
boolean isDivisible = resultSet.getBoolean(5);
byte[] reference = resultSet.getBytes(6);
return new AssetData(assetId, owner, assetName, description, quantity, isDivisible, reference);
} catch (SQLException e) {
throw new DataException("Unable to fetch asset from repository", e);
}
}
public boolean assetExists(long assetId) throws DataException {
try {
return this.repository.exists("Assets", "asset_id = ?", assetId);
@ -73,7 +93,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
public void delete(long assetId) throws DataException {
try {
this.repository.delete("Assets", "assetId = ?", assetId);
this.repository.delete("Assets", "asset_id = ?", assetId);
} catch (SQLException e) {
throw new DataException("Unable to delete asset from repository", e);
}

View File

@ -46,9 +46,9 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
HSQLDBSaver saveHelper = new HSQLDBSaver("IssueAssetTransactions");
saveHelper.bind("signature", issueAssetTransactionData.getSignature()).bind("issuer", issueAssetTransactionData.getIssuerPublicKey())
.bind("asset_name", issueAssetTransactionData.getAssetName()).bind("description", issueAssetTransactionData.getDescription())
.bind("quantity", issueAssetTransactionData.getQuantity()).bind("is_divisible", issueAssetTransactionData.getIsDivisible())
.bind("asset_id", issueAssetTransactionData.getAssetId());
.bind("owner", issueAssetTransactionData.getOwner()).bind("asset_name", issueAssetTransactionData.getAssetName())
.bind("description", issueAssetTransactionData.getDescription()).bind("quantity", issueAssetTransactionData.getQuantity())
.bind("is_divisible", issueAssetTransactionData.getIsDivisible()).bind("asset_id", issueAssetTransactionData.getAssetId());
try {
saveHelper.execute(this.repository);

View File

@ -1,38 +1,118 @@
package settings;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import qora.block.GenesisBlock;
public class Settings {
private static Settings instance;
// Properties
private long genesisTimestamp = -1;
private static Settings instance;
private long genesisTimestamp = GenesisBlock.GENESIS_TIMESTAMP;
private int maxBytePerFee = 1024;
// Constants
private static final String SETTINGS_FILENAME = "settings.json";
// Constructors
private Settings() {
}
private Settings(String filename) {
// Read from file
String path = "";
try {
do {
File file = new File(path + filename);
if (!file.exists()) {
// log lack of settings file
break;
}
List<String> lines = Files.readLines(file, Charsets.UTF_8);
// Concatenate lines for JSON parsing
String jsonString = "";
for (String line : lines) {
// Escape single backslashes in "userpath" entries, typically Windows-style paths
if (line.contains("userpath"))
line.replace("\\", "\\\\");
jsonString += line;
}
JSONObject settingsJSON = (JSONObject) JSONValue.parse(jsonString);
String userpath = (String) settingsJSON.get("userpath");
if (userpath != null) {
path = userpath;
// Add trailing directory separator if needed
if (!path.endsWith(File.separator))
path += File.separator;
continue;
}
process(settingsJSON);
break;
} while (true);
} catch (IOException | ClassCastException e) {
}
}
// Other methods
public static Settings getInstance() {
if (instance == null)
instance = new Settings();
instance = new Settings(SETTINGS_FILENAME);
return instance;
}
public static void test(JSONObject settingsJSON) {
// Discard previous settings
if (instance != null)
instance = null;
instance = new Settings();
getInstance().process(settingsJSON);
}
private void process(JSONObject json) {
if (json.containsKey("testnetstamp")) {
if (json.get("testnetstamp").toString().equals("now") || ((Long) json.get("testnetstamp")).longValue() == 0) {
this.genesisTimestamp = System.currentTimeMillis();
} else {
this.genesisTimestamp = ((Long) json.get("testnetstamp")).longValue();
}
}
}
public boolean isTestNet() {
return this.genesisTimestamp != GenesisBlock.GENESIS_TIMESTAMP;
}
// Getters / setters
public int getMaxBytePerFee() {
return 1024;
return this.maxBytePerFee;
}
public long getGenesisTimestamp() {
if (this.genesisTimestamp != -1)
return this.genesisTimestamp;
return GenesisBlock.GENESIS_TIMESTAMP;
}
public void setGenesisTimestamp(long timestamp) {
this.genesisTimestamp = timestamp;
}
public void unsetGenesisTimestamp() {
this.genesisTimestamp = -1;
return this.genesisTimestamp;
}
}

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.json.simple.JSONObject;
import org.junit.After;
import org.junit.Test;
@ -15,15 +16,18 @@ import com.google.common.hash.HashCode;
import data.account.AccountBalanceData;
import data.account.AccountData;
import data.assets.AssetData;
import data.block.BlockData;
import data.naming.NameData;
import data.transaction.BuyNameTransactionData;
import data.transaction.CancelSellNameTransactionData;
import data.transaction.CreatePollTransactionData;
import data.transaction.IssueAssetTransactionData;
import data.transaction.MessageTransactionData;
import data.transaction.PaymentTransactionData;
import data.transaction.RegisterNameTransactionData;
import data.transaction.SellNameTransactionData;
import data.transaction.TransferAssetTransactionData;
import data.transaction.UpdateNameTransactionData;
import data.transaction.VoteOnPollTransactionData;
import data.voting.PollData;
@ -38,15 +42,18 @@ import qora.block.BlockChain;
import qora.transaction.BuyNameTransaction;
import qora.transaction.CancelSellNameTransaction;
import qora.transaction.CreatePollTransaction;
import qora.transaction.IssueAssetTransaction;
import qora.transaction.MessageTransaction;
import qora.transaction.PaymentTransaction;
import qora.transaction.RegisterNameTransaction;
import qora.transaction.SellNameTransaction;
import qora.transaction.Transaction;
import qora.transaction.Transaction.ValidationResult;
import qora.transaction.TransferAssetTransaction;
import qora.transaction.UpdateNameTransaction;
import qora.transaction.VoteOnPollTransaction;
import repository.AccountRepository;
import repository.AssetRepository;
import repository.DataException;
import repository.Repository;
import repository.RepositoryFactory;
@ -63,8 +70,8 @@ public class TransactionTests {
private static final byte[] senderSeed = HashCode.fromString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").asBytes();
private static final byte[] recipientSeed = HashCode.fromString("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210").asBytes();
private static final BigDecimal generatorBalance = BigDecimal.valueOf(1_000_000_000L);
private static final BigDecimal senderBalance = BigDecimal.valueOf(1_000_000L);
private static final BigDecimal initialGeneratorBalance = BigDecimal.valueOf(1_000_000_000L);
private static final BigDecimal initialSenderBalance = BigDecimal.valueOf(1_000_000L);
private Repository repository;
private AccountRepository accountRepository;
@ -73,6 +80,7 @@ public class TransactionTests {
private PrivateKeyAccount generator;
private byte[] reference;
@SuppressWarnings("unchecked")
public void createTestAccounts(Long genesisTimestamp) throws DataException {
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
RepositoryManager.setRepositoryFactory(repositoryFactory);
@ -82,10 +90,11 @@ public class TransactionTests {
}
// [Un]set genesis timestamp as required by test
JSONObject settingsJSON = new JSONObject();
if (genesisTimestamp != null)
Settings.getInstance().setGenesisTimestamp(genesisTimestamp);
else
Settings.getInstance().unsetGenesisTimestamp();
settingsJSON.put("testnetstamp", genesisTimestamp);
Settings.test(settingsJSON);
// This needs to be called outside of acquiring our own repository or it will deadlock
BlockChain.validate();
@ -101,7 +110,7 @@ public class TransactionTests {
// Create test generator account
generator = new PrivateKeyAccount(repository, generatorSeed);
accountRepository.save(new AccountData(generator.getAddress(), generatorSeed));
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, generatorBalance));
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance));
// Create test sender account
sender = new PrivateKeyAccount(repository, senderSeed);
@ -111,7 +120,7 @@ public class TransactionTests {
accountRepository.save(new AccountData(sender.getAddress(), reference));
// Mock balance
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, senderBalance));
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance));
repository.saveChanges();
}
@ -163,12 +172,12 @@ public class TransactionTests {
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(amount).subtract(fee);
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
expectedBalance = initialGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
@ -176,6 +185,22 @@ public class TransactionTests {
expectedBalance = amount;
actualBalance = accountRepository.getBalance(recipient.getAddress(), Asset.QORA).getBalance();
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Check recipient's reference
byte[] recipientsReference = recipient.getLastReference();
assertTrue("Recipient's new reference incorrect", Arrays.equals(paymentTransaction.getTransactionData().getSignature(), recipientsReference));
// Orphan block
block.orphan();
repository.saveChanges();
// Check sender's balance
actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's reverted balance incorrect", initialSenderBalance.compareTo(actualBalance) == 0);
// Check generator's balance
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", initialGeneratorBalance.compareTo(actualBalance) == 0);
}
@Test
@ -208,12 +233,12 @@ public class TransactionTests {
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(fee);
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
expectedBalance = initialGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
@ -445,7 +470,7 @@ public class TransactionTests {
@Test
public void testCreatePollTransaction() throws DataException {
// This test requires GenesisBlock's timestamp is set to something after BlockChain.VOTING_RELEASE_TIMESTAMP
createTestAccounts(BlockChain.VOTING_RELEASE_TIMESTAMP + 1_000L);
createTestAccounts(BlockChain.getVotingReleaseTimestamp() + 1_000L);
// Make a new create poll transaction
String pollName = "test poll";
@ -479,12 +504,12 @@ public class TransactionTests {
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(fee);
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
expectedBalance = initialGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
@ -575,12 +600,174 @@ public class TransactionTests {
@Test
public void testIssueAssetTransaction() throws DataException {
// TODO
createTestAccounts(null);
// Create new asset
String assetName = "test asset";
String description = "test asset description";
long quantity = 1_000_000L;
boolean isDivisible = false;
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);
issueAssetTransaction.calcSignature(sender);
assertTrue(issueAssetTransaction.isSignatureValid());
assertEquals(ValidationResult.OK, issueAssetTransaction.isValid());
// Forge new block with payment transaction
Block block = new Block(repository, parentBlockData, generator, null, null);
block.addTransaction(issueAssetTransactionData);
block.sign();
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
block.process();
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = initialSenderBalance.subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = initialGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// 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();
assertTrue("Sender's reverted balance incorrect", initialSenderBalance.compareTo(actualBalance) == 0);
// Check generator's balance
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's reverted balance incorrect", initialGeneratorBalance.compareTo(actualBalance) == 0);
// 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();
}
@Test
public void testTransferAssetTransaction() throws DataException {
// TODO
// 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);
transferAssetTransaction.calcSignature(sender);
assertTrue(transferAssetTransaction.isSignatureValid());
assertEquals(ValidationResult.OK, transferAssetTransaction.isValid());
// Forge new block with payment transaction
Block block = new Block(repository, parentBlockData, generator, null, null);
block.addTransaction(transferAssetTransactionData);
block.sign();
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
block.process();
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = originalSenderBalance.subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = originalGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// 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();
assertTrue("Sender's reverted balance incorrect", originalSenderBalance.compareTo(actualBalance) == 0);
// Check generator's balance
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's reverted balance incorrect", originalGeneratorBalance.compareTo(actualBalance) == 0);
// 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();
}
@Test
@ -600,7 +787,7 @@ public class TransactionTests {
@Test
public void testMessageTransaction() throws DataException, UnsupportedEncodingException {
createTestAccounts(null);
createTestAccounts(1431861220336L); // timestamp taken from main blockchain block 99000
// Make a new message transaction
Account recipient = new PublicKeyAccount(repository, recipientSeed);
@ -632,12 +819,12 @@ public class TransactionTests {
repository.saveChanges();
// Check sender's balance
BigDecimal expectedBalance = senderBalance.subtract(amount).subtract(fee);
BigDecimal expectedBalance = initialSenderBalance.subtract(amount).subtract(fee);
BigDecimal actualBalance = accountRepository.getBalance(sender.getAddress(), Asset.QORA).getBalance();
assertTrue("Sender's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
// Fee should be in generator's balance
expectedBalance = generatorBalance.add(fee);
expectedBalance = initialGeneratorBalance.add(fee);
actualBalance = accountRepository.getBalance(generator.getAddress(), Asset.QORA).getBalance();
assertTrue("Generator's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);