Browse Source

Added more blockchain-related FunctionCode tests & improved TestAPI accordingly.

master
catbref 5 years ago
parent
commit
e0fe988b71
  1. 592
      Java/src/test/java/BlockchainFunctionCodeTests.java
  2. 130
      Java/src/test/java/common/TestAPI.java

592
Java/src/test/java/BlockchainFunctionCodeTests.java

@ -1,15 +1,19 @@
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Random;
import org.ciyam.at.API;
import org.ciyam.at.ExecutionException;
import org.ciyam.at.FunctionCode;
import org.ciyam.at.MachineState;
import org.ciyam.at.OpCode;
import org.ciyam.at.Timestamp;
import org.junit.Test;
import common.ExecutableTest;
import common.TestAPI;
import common.TestAPI.TestAccount;
import common.TestAPI.TestBlock;
import common.TestAPI.TestTransaction;
@ -131,13 +135,20 @@ public class BlockchainFunctionCodeTests extends ExecutableTest {
dataByteBuffer.putLong(initialTimestamp);
// Generate some blocks containing transactions (but none to AT)
api.generateBlockWithNonAtTransactions();
api.generateBlockWithNonAtTransactions();
TestBlock newBlock = api.generateBlockWithNonAtTransactions();
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
newBlock = api.generateBlockWithNonAtTransactions();
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
// Generate a block containing transaction to AT
api.generateBlockWithAtTransaction();
newBlock = api.generateBlockWithAtTransaction();
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
int currentBlockHeight = api.blockchain.size();
api.setCurrentBlockHeight(currentBlockHeight);
int currentBlockHeight = api.getCurrentBlockHeight();
// Fetch transaction signature/hash after timestamp stored in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
@ -162,9 +173,12 @@ public class BlockchainFunctionCodeTests extends ExecutableTest {
dataByteBuffer.putLong(initialTimestamp);
// Generate a block containing transaction to AT
api.generateBlockWithAtTransaction();
TestBlock newBlock = api.generateBlockWithAtTransaction();
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
api.generateBlockWithAtTransaction();
newBlock = api.generateBlockWithAtTransaction();
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
long expectedTransactionsCount = 0;
@ -199,6 +213,100 @@ public class BlockchainFunctionCodeTests extends ExecutableTest {
assertFalse(state.getHadFatalError());
}
@Test
public void testGetTypeFromTxInA() throws ExecutionException {
int initialBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT;
long initialTimestamp = Timestamp.toLong(initialBlockHeight, 0);
dataByteBuffer.putLong(initialTimestamp);
// Generate new block containing 2 transactions to AT, one PAYMENT, one MESSAGE
TestBlock newBlock = api.generateEmptyBlock();
String sender = "Bystander";
String recipient = TestAPI.AT_ADDRESS;
TestTransaction paymentTx = api.generateTransaction(sender, recipient, API.ATTransactionType.PAYMENT);
newBlock.transactions.add(paymentTx);
TestTransaction messageTx = api.generateTransaction(sender, recipient, API.ATTransactionType.MESSAGE);
newBlock.transactions.add(messageTx);
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction type into address 1
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TYPE_FROM_TX_IN_A.value).putInt(1);
// Update latest timestamp in address 0
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(0);
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction type into address 2
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TYPE_FROM_TX_IN_A.value).putInt(2);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
long paymentTxType = getData(1);
assertEquals("Payment tx type mismatch", paymentTx.txType.value, paymentTxType);
long messageTxType = getData(2);
assertEquals("Message tx type mismatch", messageTx.txType.value, messageTxType);
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testGetAmountFromTxInA() throws ExecutionException {
int initialBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT;
long initialTimestamp = Timestamp.toLong(initialBlockHeight, 0);
dataByteBuffer.putLong(initialTimestamp);
// Generate new block containing 2 transactions to AT, one PAYMENT, one MESSAGE
TestBlock newBlock = api.generateEmptyBlock();
String sender = "Bystander";
String recipient = TestAPI.AT_ADDRESS;
TestTransaction paymentTx = api.generateTransaction(sender, recipient, API.ATTransactionType.PAYMENT);
newBlock.transactions.add(paymentTx);
TestTransaction messageTx = api.generateTransaction(sender, recipient, API.ATTransactionType.MESSAGE);
newBlock.transactions.add(messageTx);
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction's amount into address 1
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_AMOUNT_FROM_TX_IN_A.value).putInt(1);
// Update latest timestamp in address 0
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(0);
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction's amount into address 2
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_AMOUNT_FROM_TX_IN_A.value).putInt(2);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
long paymentTxAmount = getData(1);
assertEquals("Payment tx amount mismatch", paymentTx.amount, paymentTxAmount);
long messageTxAmount = getData(2);
assertEquals("Message tx amount mismatch", messageTx.amount, messageTxAmount);
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testRandom() throws ExecutionException {
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(Timestamp.toLong(api.getCurrentBlockHeight(), 0));
@ -216,4 +324,474 @@ public class BlockchainFunctionCodeTests extends ExecutableTest {
assertFalse(state.getHadFatalError());
}
@Test
public void testPutMessageFromTxInAIntoB() throws ExecutionException {
int initialBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT;
long initialTimestamp = Timestamp.toLong(initialBlockHeight, 0);
dataByteBuffer.putLong(initialTimestamp);
// Where to save message (in B) from payment tx
dataByteBuffer.putLong(4L);
// Where to save message (in B) from message tx
dataByteBuffer.putLong(8L);
// Generate new block containing 2 transactions to AT, one PAYMENT, one MESSAGE
TestBlock newBlock = api.generateEmptyBlock();
String sender = "Bystander";
String recipient = TestAPI.AT_ADDRESS;
TestTransaction paymentTx = api.generateTransaction(sender, recipient, API.ATTransactionType.PAYMENT);
newBlock.transactions.add(paymentTx);
TestTransaction messageTx = api.generateTransaction(sender, recipient, API.ATTransactionType.MESSAGE);
newBlock.transactions.add(messageTx);
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction's message into addresses 4 to 7
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B.value);
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.GET_B_IND.value).putInt(1);
// Update latest timestamp in address 0
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_TIMESTAMP_FROM_TX_IN_A.value).putInt(0);
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction's message into addresses 4 to 7
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_MESSAGE_FROM_TX_IN_A_INTO_B.value);
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.GET_B_IND.value).putInt(2);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
byte[] actualMessage = new byte[32];
byte[] expectedMessage = new byte[32]; // Blank for non-message transactions
getDataBytes(4, actualMessage);
assertTrue("Payment tx message mismatch", Arrays.equals(expectedMessage, actualMessage));
expectedMessage = messageTx.message;
getDataBytes(8, actualMessage);
assertTrue("Message tx message mismatch", Arrays.equals(expectedMessage, actualMessage));
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testPutAddressFromTxInAIntoB() throws ExecutionException {
int initialBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT;
long initialTimestamp = Timestamp.toLong(initialBlockHeight, 0);
dataByteBuffer.putLong(initialTimestamp);
// Where to save address (in B) from tx
dataByteBuffer.putLong(4L);
// Generate new block containing a transaction to AT
TestBlock newBlock = api.generateEmptyBlock();
String sender = "Bystander";
String recipient = TestAPI.AT_ADDRESS;
TestTransaction messageTx = api.generateTransaction(sender, recipient, API.ATTransactionType.MESSAGE);
newBlock.transactions.add(messageTx);
api.addBlockToChain(newBlock);
api.bumpCurrentBlockHeight();
// Get transaction after timestamp held in address 0
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PUT_TX_AFTER_TIMESTAMP_INTO_A.value).putInt(0);
// Save transaction's sender into addresses 4 to 7
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_ADDRESS_FROM_TX_IN_A_INTO_B.value);
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.GET_B_IND.value).putInt(1);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
byte[] expectedSenderBytes = TestAPI.encodeAddress(sender);
byte[] actualSenderBytes = new byte[32];
getDataBytes(4, actualSenderBytes);
assertTrue("Sender address bytes mismatch", Arrays.equals(expectedSenderBytes, actualSenderBytes));
String expectedSender = sender;
String actualSender = TestAPI.decodeAddress(actualSenderBytes);
assertEquals("Sender address string mismatch", expectedSender, actualSender);
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testPutCreatorIntoB() throws ExecutionException {
// Where to save creator address (in B)
dataByteBuffer.putLong(4L);
// Save creator's address into data addresses 4 to 7
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_CREATOR_INTO_B.value);
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.GET_B_IND.value).putInt(0);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
byte[] expectedAtCreatorBytes = TestAPI.encodeAddress(TestAPI.AT_CREATOR_ADDRESS);
byte[] actualAtCreatorBytes = new byte[32];
getDataBytes(4, actualAtCreatorBytes);
assertTrue("AT creator address bytes mismatch", Arrays.equals(expectedAtCreatorBytes, actualAtCreatorBytes));
String expectedAtCreator = TestAPI.AT_CREATOR_ADDRESS;
String actualAtCreator = TestAPI.decodeAddress(actualAtCreatorBytes);
assertEquals("AT creator address string mismatch", expectedAtCreator, actualAtCreator);
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testGetCurrentBalance() throws ExecutionException {
// Number of bits to shift right
dataByteBuffer.putLong(1L);
// Save current balance into address 1
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_CURRENT_BALANCE.value).putInt(1);
// Copy balance from address 1 into address 2
codeByteBuffer.put(OpCode.SET_DAT.value).putInt(2).putInt(1);
// Halve balance in address 2
codeByteBuffer.put(OpCode.SHR_DAT.value).putInt(2).putInt(0);
// Pay amount in address 2 to creator (via B)
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_CREATOR_INTO_B.value);
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PAY_TO_ADDRESS_IN_B.value).putInt(2);
// Save new current balance into address 3
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_CURRENT_BALANCE.value).putInt(3);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
final long initialBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
execute(true);
long expectedBalance = initialBalance - TestAPI.STEPS_PER_FUNCTION_CALL /* GET_CURRENT_BALANCE */;
assertEquals("Initial 'current balance' mismatch", expectedBalance, getData(1));
final long amount = expectedBalance >>> 1;
expectedBalance -= 1 /* SET_DAT */
+ 1 /* SHR_DAT */
+ TestAPI.STEPS_PER_FUNCTION_CALL /* PUT_CREATOR_INTO_B */
+ TestAPI.STEPS_PER_FUNCTION_CALL /* PAY_TO_ADDRESS_IN_B */
+ TestAPI.STEPS_PER_FUNCTION_CALL /* GET_CURRENT_BALANCE */
+ amount;
assertEquals("Final 'current balance' mismatch", expectedBalance, getData(3));
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testGetPreviousBalance() throws ExecutionException {
// Save previous balance into address 1
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.GET_PREVIOUS_BALANCE.value).putInt(1);
// Done
codeByteBuffer.put(OpCode.STP_IMD.value);
final long initialBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
execute(true);
long expectedBalance = initialBalance;
assertEquals("Initial 'previous balance' mismatch", expectedBalance, getData(1));
execute(true);
expectedBalance -= 1 /* STP_IMD */ + TestAPI.STEPS_PER_FUNCTION_CALL /* GET_CURRENT_BALANCE */;
assertEquals("Final 'previous balance' mismatch", expectedBalance, getData(1));
assertFalse(state.getHadFatalError());
}
@Test
public void testPayToAddressInB() throws ExecutionException {
final long amount = 123L;
TestAccount recipient = api.accounts.get("Bystander");
// Where recipient address is stored
final long addressPosition = 2L;
dataByteBuffer.putLong(addressPosition);
// Amount to send to recipient
dataByteBuffer.putLong(amount);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(0);
// Pay amount in address 1 to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PAY_TO_ADDRESS_IN_B.value).putInt(1);
// STOP, not finish, so we retain balance instead of sending leftover to AT creator
codeByteBuffer.put(OpCode.STP_IMD.value);
final long initialAtBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
final long initialRecipientBalance = recipient.balance;
execute(true);
long expectedBalance = initialAtBalance - amount;
long actualBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
assertTrue("Final AT balance mismatch", actualBalance <= expectedBalance && actualBalance > 0);
expectedBalance = initialRecipientBalance + amount;
actualBalance = recipient.balance;
assertEquals("Final recipient balance mismatch", expectedBalance, actualBalance);
assertFalse(state.getHadFatalError());
}
@Test
public void testPayToAddressInBexcessive() throws ExecutionException {
final long amount = 999999999L; // More than AT's balance
TestAccount recipient = api.accounts.get("Bystander");
// Where recipient address is stored
final long addressPosition = 2L;
dataByteBuffer.putLong(addressPosition);
// Amount to send to recipient
dataByteBuffer.putLong(amount);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(0);
// Pay amount in address 1 to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.PAY_TO_ADDRESS_IN_B.value).putInt(1);
// STOP, not finish, so we retain balance instead of sending leftover to AT creator
codeByteBuffer.put(OpCode.STP_IMD.value);
final long initialAtBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
final long initialRecipientBalance = recipient.balance;
execute(true);
long expectedAmount = initialAtBalance
- TestAPI.STEPS_PER_FUNCTION_CALL /* SET_B_IND */
- TestAPI.STEPS_PER_FUNCTION_CALL /* PAY_TO_ADDRESS_IN_B */;
long expectedBalance = 0L;
long actualBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
assertEquals("Final AT balance mismatch", expectedBalance, actualBalance);
expectedBalance = initialRecipientBalance + expectedAmount;
actualBalance = recipient.balance;
assertEquals("Final recipient balance mismatch", expectedBalance, actualBalance);
assertFalse(state.getHadFatalError());
}
@Test
public void testPayAllToAddressInB() throws ExecutionException {
TestAccount recipient = api.accounts.get("Bystander");
// Where recipient address is stored
final long addressPosition = 1L;
dataByteBuffer.putLong(addressPosition);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(0);
// Pay all amount to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_ALL_TO_ADDRESS_IN_B.value);
// STOP, not finish, so we retain balance instead of sending leftover to AT creator
codeByteBuffer.put(OpCode.STP_IMD.value);
final long initialAtBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
final long initialRecipientBalance = recipient.balance;
execute(true);
long expectedAmount = initialAtBalance
- TestAPI.STEPS_PER_FUNCTION_CALL /* SET_B_IND */
- TestAPI.STEPS_PER_FUNCTION_CALL /* PAY_ALL_TO_ADDRESS_IN_B */;
long expectedBalance = 0L;
long actualBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
assertEquals("Final AT balance mismatch", expectedBalance, actualBalance);
expectedBalance = initialRecipientBalance + expectedAmount;
actualBalance = recipient.balance;
assertEquals("Final recipient balance mismatch", expectedBalance, actualBalance);
assertFalse(state.getHadFatalError());
}
@Test
public void testPayPreviousToAddressInB() throws ExecutionException {
TestAccount recipient = api.accounts.get("Bystander");
// Where recipient address is stored
final long addressPosition = 1L;
dataByteBuffer.putLong(addressPosition);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(0);
// Pay previous balance to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_PREVIOUS_TO_ADDRESS_IN_B.value);
// STOP, not finish, so we retain balance instead of sending leftover to AT creator
codeByteBuffer.put(OpCode.STP_IMD.value);
final long initialAtBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
final long initialRecipientBalance = recipient.balance;
execute(true);
long expectedAmount = initialAtBalance
- TestAPI.STEPS_PER_FUNCTION_CALL /* SET_B_IND */
- TestAPI.STEPS_PER_FUNCTION_CALL /* PAY_PREVIOUS_TO_ADDRESS_IN_B */;
long expectedBalance = 0L;
long actualBalance = api.accounts.get(TestAPI.AT_ADDRESS).balance;
assertEquals("Final AT balance mismatch", expectedBalance, actualBalance);
expectedBalance = initialRecipientBalance + expectedAmount;
actualBalance = recipient.balance;
assertEquals("Final recipient balance mismatch", expectedBalance, actualBalance);
assertFalse(state.getHadFatalError());
}
@Test
public void testPayPreviousToAddressInBextra() throws ExecutionException {
TestAccount recipient = api.accounts.get("Bystander");
// Where recipient address is stored
final long addressPosition = 1L;
dataByteBuffer.putLong(addressPosition);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Sleep until next block
codeByteBuffer.put(OpCode.SLP_IMD.value);
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(0);
// Pay previous balance to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PAY_PREVIOUS_TO_ADDRESS_IN_B.value);
// STOP, not finish, so we retain balance instead of sending leftover to AT creator
codeByteBuffer.put(OpCode.STP_IMD.value);
execute(true);
final TestAccount atAccount = api.accounts.get(TestAPI.AT_ADDRESS);
final long previousAtBalance = atAccount.balance;
final long initialRecipientBalance = recipient.balance;
// Simulate AT receiving a payment (in excess of cost of running AT for one round)
final long incomingAtPayment = 25000L;
atAccount.balance += incomingAtPayment;
execute(true);
long expectedBalance = incomingAtPayment
- TestAPI.STEPS_PER_FUNCTION_CALL /* SET_B_IND */
- TestAPI.STEPS_PER_FUNCTION_CALL /* PAY_PREVIOUS_TO_ADDRESS_IN_B */
- 1 /* STP_IMD */;
long actualBalance = atAccount.balance;
assertEquals("Final AT balance mismatch", expectedBalance, actualBalance);
expectedBalance = initialRecipientBalance + previousAtBalance;
actualBalance = recipient.balance;
assertEquals("Final recipient balance mismatch", expectedBalance, actualBalance);
assertFalse(state.getHadFatalError());
}
@Test
public void testMessageAToAddressInB() throws ExecutionException {
Random random = new Random();
byte[] message = new byte[32];
random.nextBytes(message);
TestAccount recipient = api.accounts.get("Bystander");
// Where message is stored
final long messagePosition = 2L;
dataByteBuffer.putLong(messagePosition);
// Where recipient address is stored
final long addressPosition = 6L;
dataByteBuffer.putLong(addressPosition);
// Message
assertEquals(messagePosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(message);
// Recipient address
assertEquals(addressPosition * MachineState.VALUE_SIZE, dataByteBuffer.position());
dataByteBuffer.put(TestAPI.encodeAddress(recipient.address));
// Copy message into A
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_A_IND.value).putInt(0);
// Copy recipient address into B
codeByteBuffer.put(OpCode.EXT_FUN_DAT.value).putShort(FunctionCode.SET_B_IND.value).putInt(1);
// Send message (in A) to recipient (via B)
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.MESSAGE_A_TO_ADDRESS_IN_B.value);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
assertEquals("Recipient message count incorrect", 1, recipient.messages.size());
byte[] actualMessage = recipient.messages.get(0);
assertTrue("Recipient message incorrect", Arrays.equals(message, actualMessage));
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
@Test
public void testAddMinutesToTimestamp() throws ExecutionException {
int initialBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT;
long initialTimestamp = Timestamp.toLong(initialBlockHeight, 0);
dataByteBuffer.putLong(initialTimestamp);
long minutes = 34L;
dataByteBuffer.putLong(minutes);
// Add minutes (from address 1) to timestamp (in address 0) and store the result in address 2
codeByteBuffer.put(OpCode.EXT_FUN_RET_DAT_2.value).putShort(FunctionCode.ADD_MINUTES_TO_TIMESTAMP.value).putInt(2).putInt(0).putInt(1);
// Done
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
int expectedBlockHeight = initialBlockHeight + ((int) minutes * 60 / TestAPI.BLOCK_PERIOD);
long expectedTimestamp = Timestamp.toLong(expectedBlockHeight, 0);
long actualTimestamp = getData(2);
assertEquals("Expected timestamp incorrect", expectedTimestamp, actualTimestamp);
assertTrue(state.getIsFinished());
assertFalse(state.getHadFatalError());
}
}

130
Java/src/test/java/common/TestAPI.java

@ -61,21 +61,21 @@ public class TestAPI extends API {
public long amount;
public byte[] message;
private TestTransaction(byte[] txHash, API.ATTransactionType txType, String creator, String recipient) {
private TestTransaction(byte[] txHash, API.ATTransactionType txType, String sender, String recipient) {
this.txHash = txHash;
this.txType = txType;
this.sender = creator;
this.sender = sender;
this.recipient = recipient;
}
public TestTransaction(byte[] txHash, String creator, String recipient, long amount) {
this(txHash, API.ATTransactionType.PAYMENT, creator, recipient);
public TestTransaction(byte[] txHash, String sender, String recipient, long amount) {
this(txHash, API.ATTransactionType.PAYMENT, sender, recipient);
this.amount = amount;
}
public TestTransaction(byte[] txHash, String creator, String recipient, byte[] message) {
this(txHash, API.ATTransactionType.MESSAGE, creator, recipient);
public TestTransaction(byte[] txHash, String sender, String recipient, byte[] message) {
this(txHash, API.ATTransactionType.MESSAGE, sender, recipient);
this.message = new byte[32];
System.arraycopy(message, 0, this.message, 0, message.length);
@ -127,6 +127,21 @@ public class TestAPI extends API {
transactions = new HashMap<>();
}
public static byte[] encodeAddress(String address) {
byte[] encodedAddress = new byte[32];
System.arraycopy(address.getBytes(), 0, encodedAddress, 0, address.length());
return encodedAddress;
}
public static String decodeAddress(byte[] encodedAddress) {
String address = new String(encodedAddress, StandardCharsets.ISO_8859_1);
return address.replace("\0", "");
}
public static String stringifyHash(byte[] hash) {
return new String(hash, StandardCharsets.ISO_8859_1);
}
public void bumpCurrentBlockHeight() {
++this.currentBlockHeight;
}
@ -138,12 +153,28 @@ public class TestAPI extends API {
this.currentBlockHeight = blockHeight;
}
private void generateBlock(boolean withTransactions, boolean includeTransactionToAt) {
TestBlock newBlock = new TestBlock();
public TestBlock addBlockToChain(TestBlock newBlock) {
blockchain.add(newBlock);
final int blockHeight = blockchain.size();
for (int seq = 0; seq < newBlock.transactions.size(); ++seq) {
TestTransaction transaction = newBlock.transactions.get(seq);
// Set transaction timestamp
transaction.timestamp = Timestamp.toLong(blockHeight, seq);
// Add to transactions map
transactions.put(stringifyHash(transaction.txHash), transaction);
}
return newBlock;
}
private TestBlock generateBlock(boolean withTransactions, boolean includeTransactionToAt) {
TestBlock newBlock = new TestBlock();
if (!withTransactions)
return;
return newBlock;
TestAccount atAccount = accounts.get(AT_ADDRESS);
List<TestAccount> senderAccounts = new ArrayList<>(accounts.values());
@ -160,39 +191,46 @@ public class TestAPI extends API {
TestAccount recipient = recipientAccounts.get(RANDOM.nextInt(recipientAccounts.size()));
// Pick random transaction type
API.ATTransactionType txType = API.ATTransactionType.valueOf(RANDOM.nextInt(2));
// Generate tx hash
byte[] txHash = new byte[32];
RANDOM.nextBytes(txHash);
TestTransaction transaction;
if (txType == API.ATTransactionType.PAYMENT) {
long amount = RANDOM.nextInt(100); // small amounts
transaction = new TestTransaction(txHash, sender.address, recipient.address, amount);
} else {
byte[] message = new byte[32];
RANDOM.nextBytes(message);
transaction = new TestTransaction(txHash, sender.address, recipient.address, message);
}
transaction.timestamp = Timestamp.toLong(blockchain.size(), newBlock.transactions.size());
transactions.put(new String(txHash, StandardCharsets.ISO_8859_1), transaction);
TestTransaction transaction = generateTransaction(sender.address, recipient.address, txType);
newBlock.transactions.add(transaction);
if (recipient.address.equals(AT_ADDRESS))
includesAtTransaction = true;
}
return newBlock;
}
public void generateEmptyBlock() {
generateBlock(false, false);
public TestBlock generateEmptyBlock() {
return generateBlock(false, false);
}
public void generateBlockWithNonAtTransactions() {
generateBlock(true, false);
public TestBlock generateBlockWithNonAtTransactions() {
return generateBlock(true, false);
}
public void generateBlockWithAtTransaction() {
generateBlock(true, true);
public TestBlock generateBlockWithAtTransaction() {
return generateBlock(true, true);
}
public TestTransaction generateTransaction(String sender, String recipient, API.ATTransactionType txType) {
TestTransaction transaction;
// Generate tx hash
byte[] txHash = new byte[32];
RANDOM.nextBytes(txHash);
if (txType == API.ATTransactionType.PAYMENT) {
long amount = RANDOM.nextInt(100); // small amounts
transaction = new TestTransaction(txHash, sender, recipient, amount);
} else {
byte[] message = new byte[32];
RANDOM.nextBytes(message);
transaction = new TestTransaction(txHash, sender, recipient, message);
}
return transaction;
}
@Override
@ -239,7 +277,7 @@ public class TestAPI extends API {
List<TestTransaction> transactions = block.transactions;
if (transactionSequence > transactions.size() - 1) {
if (transactionSequence >= transactions.size()) {
// No more transactions at this height
++blockHeight;
transactionSequence = 0;
@ -261,12 +299,13 @@ public class TestAPI extends API {
}
// Nothing found
System.out.println("No more transactions found at height " + this.currentBlockHeight);
this.setA(state, new byte[32]);
}
public TestTransaction getTransactionFromA(MachineState state) {
byte[] aBytes = state.getA();
String txHashString = new String(aBytes, StandardCharsets.ISO_8859_1); // ISO_8859_1 for simplistic 8-bit encoding
String txHashString = stringifyHash(aBytes);
return transactions.get(txHashString);
}
@ -323,15 +362,13 @@ public class TestAPI extends API {
@Override
public void putAddressFromTransactionInAIntoB(MachineState state) {
TestTransaction transaction = getTransactionFromA(state);
byte[] bBytes = new byte[32];
System.arraycopy(transaction.sender.getBytes(), 0, bBytes, 0, transaction.sender.length());
byte[] bBytes = encodeAddress(transaction.sender);
this.setB(state, bBytes);
}
@Override
public void putCreatorAddressIntoB(MachineState state) {
byte[] bBytes = new byte[32];
System.arraycopy(AT_CREATOR_ADDRESS.getBytes(), 0, bBytes, 0, AT_CREATOR_ADDRESS.length());
byte[] bBytes = encodeAddress(AT_CREATOR_ADDRESS);
this.setB(state, bBytes);
}
@ -348,30 +385,25 @@ public class TestAPI extends API {
@Override
public void payAmountToB(long amount, MachineState state) {
if (amount <= 0)
return;
byte[] bBytes = state.getB();
String address = new String(bBytes, StandardCharsets.ISO_8859_1);
address = address.replace("\0", "");
String address = decodeAddress(bBytes);
TestAccount recipient = accounts.get(address);
if (recipient == null)
throw new IllegalStateException("Refusing to pay to unknown account: " + address);
System.out.println(String.format("Paid %s to %s, their balance now: %s", prettyAmount(amount), recipient.address, recipient.balance));
recipient.balance += amount;
System.out.println(String.format("Paid %s to '%s', their balance now: %s", prettyAmount(amount), recipient.address, prettyAmount(recipient.balance)));
TestAccount atAccount = accounts.get(AT_ADDRESS);
atAccount.balance -= amount;
System.out.println(String.format("AT balance now: %s", atAccount.balance));
final long previousBalance = state.getCurrentBalance();
final long newBalance = previousBalance - amount;
System.out.println(String.format("AT balance was %s, now: %s", prettyAmount(previousBalance), prettyAmount(newBalance)));
}
@Override
public void messageAToB(MachineState state) {
byte[] bBytes = state.getB();
String address = new String(bBytes, StandardCharsets.ISO_8859_1);
address = address.replace("\0", "");
String address = decodeAddress(bBytes);
TestAccount recipient = accounts.get(address);
if (recipient == null)
@ -382,7 +414,7 @@ public class TestAPI extends API {
@Override
public long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state) {
timestamp.blockHeight = ((int) minutes * 60) / BLOCK_PERIOD;
timestamp.blockHeight += ((int) minutes * 60) / BLOCK_PERIOD;
return timestamp.longValue();
}
@ -392,7 +424,7 @@ public class TestAPI extends API {
TestAccount atCreatorAccount = accounts.get(AT_CREATOR_ADDRESS);
atCreatorAccount.balance += amount;
System.out.println(String.format("Paid %s to creator %s, their balance now: %s", prettyAmount(amount), atCreatorAccount.address, atCreatorAccount.balance));
System.out.println(String.format("Paid %s to AT creator '%s', their balance now: %s", prettyAmount(amount), atCreatorAccount.address, prettyAmount(atCreatorAccount.balance)));
accounts.get(AT_ADDRESS).balance -= amount;
}

Loading…
Cancel
Save