mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-23 04:36:50 +00:00
Account lastReference cache, now with Block support.
As this changes how lastReferences are checked and updated, this is not suitable for rolling into current chain without a "feature trigger", or chain restart! Added unit tests.
This commit is contained in:
@@ -1,76 +1,297 @@
|
||||
package org.qortal.test;
|
||||
|
||||
public class AccountRefCacheTests {
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
// Test no cache in play (existing account):
|
||||
// fetch 1st ref
|
||||
// generate 2nd ref and call Account.setLastReference
|
||||
// fetch 3rd ref
|
||||
// 3rd ref should match 2st ref
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
// Test no cache in play (no account):
|
||||
// fetch 1st ref
|
||||
// generate 2nd ref and call Account.setLastReference
|
||||
// fetch 3rd ref
|
||||
// 3rd ref should match 2st ref
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.AccountRefCache;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.data.transaction.PaymentTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TestAccount;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.test.common.transaction.TestTransaction;
|
||||
|
||||
// Test cache in play (existing account, no commit):
|
||||
// fetch 1st ref
|
||||
// begin caching
|
||||
// fetch 2nd ref
|
||||
// 2nd ref should match 1st ref
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
// fetch 4th ref
|
||||
// 4th ref should match 1st ref
|
||||
// discard cache
|
||||
// fetch 5th ref
|
||||
// 5th ref should match 1st ref
|
||||
public class AccountRefCacheTests extends Common {
|
||||
|
||||
// Test cache in play (existing account, with commit):
|
||||
// fetch 1st ref
|
||||
// begin caching
|
||||
// fetch 2nd ref
|
||||
// 2nd ref should match 1st ref
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
// fetch 4th ref
|
||||
// 4th ref should match 1st ref
|
||||
// commit cache
|
||||
// fetch 5th ref
|
||||
// 5th ref should match 3rd ref
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
// Test cache in play (new account, no commit):
|
||||
// fetch 1st ref (null)
|
||||
// begin caching
|
||||
// fetch 2nd ref
|
||||
// 2nd ref should match 1st ref
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
// fetch 4th ref
|
||||
// 4th ref should match 1st ref
|
||||
// discard cache
|
||||
// fetch 5th ref
|
||||
// 5th ref should match 1st ref
|
||||
@Before
|
||||
public void before() throws DataException {
|
||||
Common.useDefaultSettings();
|
||||
}
|
||||
|
||||
// Test cache in play (new account, with commit):
|
||||
// fetch 1st ref (null)
|
||||
// begin caching
|
||||
// fetch 2nd ref
|
||||
// 2nd ref should match 1st ref
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
// fetch 4th ref
|
||||
// 4th ref should match 1st ref
|
||||
// commit cache
|
||||
// fetch 5th ref
|
||||
// 5th ref should match 3rd ref
|
||||
// Test no cache in play (existing account)
|
||||
@Test
|
||||
public void testNoCacheExistingAccount() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount account = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
|
||||
// generate 2nd ref and call Account.setLastReference
|
||||
byte[] lastRef2 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef2);
|
||||
account.setLastReference(lastRef2);
|
||||
|
||||
// fetch 3rd ref
|
||||
byte[] lastRef3 = account.getLastReference();
|
||||
|
||||
// 3rd ref should match 2st ref
|
||||
assertTrue("getLastReference() should return latest value", Arrays.equals(lastRef2, lastRef3));
|
||||
|
||||
// 3rd ref should not match 1st ref
|
||||
assertFalse("setLastReference() failed?", Arrays.equals(lastRef1, lastRef3));
|
||||
}
|
||||
}
|
||||
|
||||
// Test no cache in play (new account)
|
||||
@Test
|
||||
public void testNoCacheNewAccount() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = createRandomAccount(repository);
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
assertNull("new account's initial lastReference should be null", lastRef1);
|
||||
|
||||
// generate 2nd ref and call Account.setLastReference
|
||||
byte[] lastRef2 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef2);
|
||||
account.setLastReference(lastRef2);
|
||||
|
||||
// fetch 3rd ref
|
||||
byte[] lastRef3 = account.getLastReference();
|
||||
|
||||
// 3rd ref should match 2st ref
|
||||
assertTrue("getLastReference() should return latest value", Arrays.equals(lastRef2, lastRef3));
|
||||
|
||||
// 3rd ref should not match 1st ref
|
||||
assertFalse("setLastReference() failed?", Arrays.equals(lastRef1, lastRef3));
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache in play (existing account, no commit)
|
||||
@Test
|
||||
public void testWithCacheExistingAccountNoCommit() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount account = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
|
||||
// begin caching
|
||||
try (final AccountRefCache accountRefCache = new AccountRefCache(repository)) {
|
||||
// fetch 2nd ref
|
||||
byte[] lastRef2 = account.getLastReference();
|
||||
|
||||
// 2nd ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef2));
|
||||
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
byte[] lastRef3 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef3);
|
||||
account.setLastReference(lastRef3);
|
||||
|
||||
// fetch 4th ref
|
||||
byte[] lastRef4 = account.getLastReference();
|
||||
|
||||
// 4th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef4));
|
||||
}
|
||||
// cache discarded
|
||||
|
||||
// fetch 5th ref
|
||||
byte[] lastRef5 = account.getLastReference();
|
||||
|
||||
// 5th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef5));
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache in play (existing account, with commit)
|
||||
@Test
|
||||
public void testWithCacheExistingAccountWithCommit() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount account = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
|
||||
// begin caching
|
||||
byte[] committedRef;
|
||||
try (final AccountRefCache accountRefCache = new AccountRefCache(repository)) {
|
||||
// fetch 2nd ref
|
||||
byte[] lastRef2 = account.getLastReference();
|
||||
|
||||
// 2nd ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef2));
|
||||
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
byte[] lastRef3 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef3);
|
||||
account.setLastReference(lastRef3);
|
||||
committedRef = lastRef3;
|
||||
|
||||
// fetch 4th ref
|
||||
byte[] lastRef4 = account.getLastReference();
|
||||
|
||||
// 4th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef4));
|
||||
|
||||
// Commit cache
|
||||
accountRefCache.commit();
|
||||
}
|
||||
|
||||
// fetch 5th ref
|
||||
byte[] lastRef5 = account.getLastReference();
|
||||
|
||||
// 5th ref should match committed ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(committedRef, lastRef5));
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache in play (new account, no commit)
|
||||
@Test
|
||||
public void testWithCacheNewAccountNoCommit() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = createRandomAccount(repository);
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
assertNull("new account's initial lastReference should be null", lastRef1);
|
||||
|
||||
// begin caching
|
||||
try (final AccountRefCache accountRefCache = new AccountRefCache(repository)) {
|
||||
// fetch 2nd ref
|
||||
byte[] lastRef2 = account.getLastReference();
|
||||
|
||||
// 2nd ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef2));
|
||||
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
byte[] lastRef3 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef3);
|
||||
account.setLastReference(lastRef3);
|
||||
|
||||
// fetch 4th ref
|
||||
byte[] lastRef4 = account.getLastReference();
|
||||
|
||||
// 4th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef4));
|
||||
}
|
||||
// cache discarded
|
||||
|
||||
// fetch 5th ref
|
||||
byte[] lastRef5 = account.getLastReference();
|
||||
|
||||
// 5th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef5));
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache in play (new account, with commit)
|
||||
@Test
|
||||
public void testWithCacheNewAccountWithCommit() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Account account = createRandomAccount(repository);
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = account.getLastReference();
|
||||
assertNull("new account's initial lastReference should be null", lastRef1);
|
||||
|
||||
// begin caching
|
||||
byte[] committedRef;
|
||||
try (final AccountRefCache accountRefCache = new AccountRefCache(repository)) {
|
||||
// fetch 2nd ref
|
||||
byte[] lastRef2 = account.getLastReference();
|
||||
|
||||
// 2nd ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef2));
|
||||
|
||||
// generate 3rd ref and call Account.setLastReference
|
||||
byte[] lastRef3 = new byte[32];
|
||||
RANDOM.nextBytes(lastRef3);
|
||||
account.setLastReference(lastRef3);
|
||||
committedRef = lastRef3;
|
||||
|
||||
// fetch 4th ref
|
||||
byte[] lastRef4 = account.getLastReference();
|
||||
|
||||
// 4th ref should match 1st ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(lastRef1, lastRef4));
|
||||
|
||||
// Commit cache
|
||||
accountRefCache.commit();
|
||||
}
|
||||
|
||||
// fetch 5th ref
|
||||
byte[] lastRef5 = account.getLastReference();
|
||||
|
||||
// 5th ref should match committed ref
|
||||
assertTrue("getLastReference() should return pre-cache value", Arrays.equals(committedRef, lastRef5));
|
||||
}
|
||||
}
|
||||
|
||||
// Test Block support
|
||||
// fetch 1st ref for Alice
|
||||
// generate new payment from Alice to new account Ellen
|
||||
// generate another payment from Alice to new account Ellen
|
||||
// mint block containing payments
|
||||
// confirm Ellen's ref is 1st payment's sig
|
||||
// confirm Alice's ref if 2nd payment's sig
|
||||
// orphan block
|
||||
// confirm Ellen's ref is null
|
||||
// confirm Alice's ref matches 1st ref
|
||||
@Test
|
||||
public void testBlockSupport() throws DataException {
|
||||
final BigDecimal amount = BigDecimal.valueOf(12345670000L, 8);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
Account newbie = createRandomAccount(repository);
|
||||
|
||||
// fetch 1st ref
|
||||
byte[] lastRef1 = alice.getLastReference();
|
||||
|
||||
// generate new payment from Alice to new account
|
||||
TransactionData paymentData1 = new PaymentTransactionData(TestTransaction.generateBase(alice), newbie.getAddress(), amount);
|
||||
TransactionUtils.signAsUnconfirmed(repository, paymentData1, alice); // updates paymentData1's signature
|
||||
|
||||
// generate another payment from Alice to new account
|
||||
TransactionData paymentData2 = new PaymentTransactionData(TestTransaction.generateBase(alice), newbie.getAddress(), amount);
|
||||
TransactionUtils.signAsUnconfirmed(repository, paymentData2, alice); // updates paymentData2's signature
|
||||
|
||||
// mint block containing payments (uses cache)
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// confirm new account's ref is last payment's sig
|
||||
byte[] newAccountRef = newbie.getLastReference();
|
||||
assertTrue("new account's lastReference should match last payment's sig", Arrays.equals(paymentData2.getSignature(), newAccountRef));
|
||||
|
||||
// confirm Alice's ref is last payment's sig
|
||||
byte[] lastRef2 = alice.getLastReference();
|
||||
assertTrue("Alice's lastReference should match last payment's sig", Arrays.equals(paymentData2.getSignature(), lastRef2));
|
||||
|
||||
// orphan block
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// confirm new account's ref reverted back to null
|
||||
newAccountRef = newbie.getLastReference();
|
||||
assertNull("new account's lastReference should have reverted back to null", newAccountRef);
|
||||
|
||||
// confirm Alice's ref matches 1st ref
|
||||
byte[] lastRef3 = alice.getLastReference();
|
||||
assertTrue("Alice's lastReference should match initial lastReference", Arrays.equals(lastRef1, lastRef3));
|
||||
}
|
||||
}
|
||||
|
||||
private static Account createRandomAccount(Repository repository) {
|
||||
byte[] randomPublicKey = new byte[32];
|
||||
RANDOM.nextBytes(randomPublicKey);
|
||||
return new PublicKeyAccount(repository, randomPublicKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user