diff --git a/Java/pom.xml b/Java/pom.xml
index f49c4cd..25fd5d7 100644
--- a/Java/pom.xml
+++ b/Java/pom.xml
@@ -6,16 +6,20 @@
AT
1.4.0
jar
+
UTF-8
false
3.8.1
+ 3.2.0
3.3.1
3.0.0-M4
+ 3.2.0
1.64
+
src/main/java
src/test/java
@@ -37,6 +41,19 @@
${skipTests}
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven-source-plugin.version}
+
+
+ attach-sources
+
+ jar
+
+
+
+
org.apache.maven.plugins
maven-javadoc-plugin
@@ -50,9 +67,47 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
+
+
+ test-jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven-source-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
org.bouncycastle
bcprov-jdk15on
diff --git a/Java/src/main/java/org/ciyam/at/AtLoggerFactory.java b/Java/src/main/java/org/ciyam/at/AtLoggerFactory.java
index 2a07fa2..b327e8d 100644
--- a/Java/src/main/java/org/ciyam/at/AtLoggerFactory.java
+++ b/Java/src/main/java/org/ciyam/at/AtLoggerFactory.java
@@ -1,5 +1,6 @@
package org.ciyam.at;
+@FunctionalInterface
public interface AtLoggerFactory {
AtLogger create(final Class> loggerName);
diff --git a/Java/src/main/java/org/ciyam/at/OpCode.java b/Java/src/main/java/org/ciyam/at/OpCode.java
index ae13ece..a6ffb88 100644
--- a/Java/src/main/java/org/ciyam/at/OpCode.java
+++ b/Java/src/main/java/org/ciyam/at/OpCode.java
@@ -561,7 +561,7 @@ public enum OpCode {
/**
* SLeeP until DATa
* 0x25 addr
- * sleep until $addr, then carry on from current PC
+ * Sleep until $addr
, then carry on from current PC
* Note: The value from $addr
is considered to be a block height.
*/
SLP_DAT(0x25, OpCodeParam.BLOCK_HEIGHT) {
@@ -634,7 +634,7 @@ public enum OpCode {
/**
* SLeeP IMmeDiately
* 0x2a
- * sleep until next block, then carry on from current PC
+ * Sleep until next block, then carry on from current PC
*/
SLP_IMD(0x2a) {
@Override
@@ -659,8 +659,7 @@ public enum OpCode {
/**
* SLeeP for VALue blocks
* 0x2c value
- * sleep until $addr, then carry on from current PC
- * Note: The value from $addr
is considered to be a block height.
+ * Sleep for value
blocks, then carry on from current PC
*/
SLP_VAL(0x2c, OpCodeParam.VALUE) {
@Override
@@ -1070,7 +1069,11 @@ public enum OpCode {
public byte[] compile(Object... args) throws CompilationException {
if (args.length != this.params.length)
- throw new IllegalArgumentException(String.format("%s requires %d args, only %d passed", this.name(), this.params.length, args.length));
+ throw new IllegalArgumentException(String.format("%s requires %d arg%s, but %d passed",
+ this.name(),
+ this.params.length,
+ this.params.length != 1 ? "s" : "",
+ args.length));
ByteBuffer byteBuffer = ByteBuffer.allocate(32); // 32 should easily be enough
diff --git a/Java/src/test/java/org/ciyam/at/BlockchainFunctionCodeTests.java b/Java/src/test/java/org/ciyam/at/BlockchainFunctionCodeTests.java
index 5df06f8..4ae7402 100644
--- a/Java/src/test/java/org/ciyam/at/BlockchainFunctionCodeTests.java
+++ b/Java/src/test/java/org/ciyam/at/BlockchainFunctionCodeTests.java
@@ -86,13 +86,27 @@ public class BlockchainFunctionCodeTests extends ExecutableTest {
@Test
public void testPutPreviousBlockHashIntoA() throws ExecutionException {
- int previousBlockHeight = TestAPI.DEFAULT_INITIAL_BLOCK_HEIGHT - 1;
+ // Generate some blocks containing transactions (but none to AT)
+ TestBlock newBlock = api.generateBlockWithNonAtTransactions();
+ api.addBlockToChain(newBlock);
+ api.bumpCurrentBlockHeight();
+
+ newBlock = api.generateBlockWithNonAtTransactions();
+ api.addBlockToChain(newBlock);
+ api.bumpCurrentBlockHeight();
+
+ // Generate a block containing transaction to AT
+ newBlock = api.generateBlockWithAtTransaction();
+ api.addBlockToChain(newBlock);
+ int previousBlockHeight = api.getCurrentBlockHeight();
+ api.bumpCurrentBlockHeight();
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.PUT_PREVIOUS_BLOCK_HASH_INTO_A.value);
codeByteBuffer.put(OpCode.FIN_IMD.value);
execute(true);
+ // previousBlockHeight - 1 because index into blockchain starts at 0, whereas block heights start at 1
byte[] expectedBlockHash = api.blockchain.get(previousBlockHeight - 1).blockHash;
byte[] aBytes = api.getA(state);
diff --git a/Java/src/test/java/org/ciyam/at/MiscTests.java b/Java/src/test/java/org/ciyam/at/MiscTests.java
index 21841cd..9ccc5dd 100644
--- a/Java/src/test/java/org/ciyam/at/MiscTests.java
+++ b/Java/src/test/java/org/ciyam/at/MiscTests.java
@@ -35,18 +35,55 @@ public class MiscTests extends ExecutableTest {
@Test
public void testFreeze() throws ExecutionException {
+ // Choose initial balance so it used up before max-steps-per-round triggers
+ long initialBalance = 5L;
+ api.accounts.get(TestAPI.AT_ADDRESS).balance = initialBalance;
+
// Infinite loop
codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(0);
- // We need enough rounds to exhaust balance
- long minRounds = TestAPI.DEFAULT_INITIAL_BALANCE / TestAPI.MAX_STEPS_PER_ROUND + 1;
- for (long i = 0; i < minRounds; ++i)
+ // Test a few rounds to make sure AT is frozen and stays frozen
+ for (int i = 0; i < 3; ++i) {
execute(true);
+ assertTrue(state.isFrozen());
+
+ Long frozenBalance = state.getFrozenBalance();
+ assertNotNull(frozenBalance);
+ }
+ }
+
+ @Test
+ public void testUnfreeze() throws ExecutionException {
+ // Choose initial balance so it used up before max-steps-per-round triggers
+ long initialBalance = 5L;
+ api.setCurrentBalance(initialBalance);
+
+ // Infinite loop
+ codeByteBuffer.put(OpCode.JMP_ADR.value).putInt(0);
+
+ // Execute to make sure AT is frozen and stays frozen
+ execute(true);
+
assertTrue(state.isFrozen());
Long frozenBalance = state.getFrozenBalance();
assertNotNull(frozenBalance);
+
+ // Send payment to AT to allow unfreezing
+ // Payment needs to be enough to trigger max-steps-per-round so we can detect unfreezing
+ api.setCurrentBalance(TestAPI.MAX_STEPS_PER_ROUND * api.getFeePerStep() * 2);
+
+ // Execute AT
+ execute(true);
+
+ // We expect AT to be sleeping, not frozen
+ assertFalse(state.isFrozen());
+
+ frozenBalance = state.getFrozenBalance();
+ assertNull(frozenBalance);
+
+ assertTrue(state.isSleeping());
}
@Test
diff --git a/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java b/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java
index cc5bff8..c1cba48 100644
--- a/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java
+++ b/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java
@@ -4,17 +4,18 @@ import java.nio.ByteBuffer;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.ciyam.at.AtLoggerFactory;
import org.ciyam.at.MachineState;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
-public abstract class ExecutableTest {
+public class ExecutableTest {
- private static final int DATA_OFFSET = MachineState.HEADER_LENGTH; // code bytes are not present
- private static final int CALL_STACK_OFFSET = DATA_OFFSET + TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE;
+ public static final int DATA_OFFSET = MachineState.HEADER_LENGTH; // code bytes are not present
+ public static final int CALL_STACK_OFFSET = DATA_OFFSET + TestUtils.NUM_DATA_PAGES * MachineState.VALUE_SIZE;
- public TestLoggerFactory loggerFactory;
+ public AtLoggerFactory loggerFactory;
public TestAPI api;
public MachineState state;
public ByteBuffer codeByteBuffer;
@@ -24,6 +25,7 @@ public abstract class ExecutableTest {
public int userStackOffset;
public int userStackSize;
public byte[] packedState;
+ public byte[] codeBytes;
@BeforeClass
public static void beforeClass() {
@@ -50,28 +52,39 @@ public abstract class ExecutableTest {
loggerFactory = null;
}
- protected void execute(boolean onceOnly) {
- byte[] headerBytes = TestUtils.HEADER_BYTES;
- byte[] codeBytes = codeByteBuffer.array();
- byte[] dataBytes = dataByteBuffer.array();
-
+ public void execute(boolean onceOnly) {
if (packedState == null) {
// First time
System.out.println("First execution - deploying...");
+ byte[] headerBytes = TestUtils.HEADER_BYTES;
+ codeBytes = codeByteBuffer.array();
+ byte[] dataBytes = dataByteBuffer.array();
+
state = new MachineState(api, loggerFactory, headerBytes, codeBytes, dataBytes);
packedState = state.toBytes();
}
do {
- state = MachineState.fromBytes(api, loggerFactory, packedState, codeBytes);
+ execute_once();
+ } while (!onceOnly && !state.isFinished());
- System.out.println("Starting execution round!");
- System.out.println("Current block height: " + api.getCurrentBlockHeight());
- System.out.println("Previous balance: " + TestAPI.prettyAmount(state.getPreviousBalance()));
- System.out.println("Current balance: " + TestAPI.prettyAmount(state.getCurrentBalance()));
+ unwrapState(state);
+ }
+ public void execute_once() {
+ state = MachineState.fromBytes(api, loggerFactory, packedState, codeBytes);
+
+ System.out.println("Starting execution round!");
+ System.out.println("Current block height: " + api.getCurrentBlockHeight());
+ System.out.println("Previous balance: " + TestAPI.prettyAmount(state.getPreviousBalance()));
+ System.out.println("Current balance: " + TestAPI.prettyAmount(api.getCurrentBalance(state)));
+
+ // Actual execution
+ if (api.willExecute(state, api.getCurrentBlockHeight())) {
// Actual execution
+ api.preExecute(state);
state.execute();
+ packedState = state.toBytes();
System.out.println("After execution round:");
System.out.println("Steps: " + state.getSteps());
@@ -94,19 +107,23 @@ public abstract class ExecutableTest {
long newBalance = state.getCurrentBalance();
System.out.println("New balance: " + TestAPI.prettyAmount(newBalance));
+
+ // Update AT balance due to execution costs, etc.
api.setCurrentBalance(newBalance);
+ } else {
+ System.out.println("Skipped execution round");
+ }
- // Bump block height
- api.bumpCurrentBlockHeight();
+ // Add block, possibly containing AT-created transactions, to chain to at least provide block hashes
+ api.addCurrentBlockToChain();
- packedState = state.toBytes();
- System.out.println("Execution round finished\n");
- } while (!onceOnly && !state.isFinished());
+ // Bump block height
+ api.bumpCurrentBlockHeight();
- unwrapState(state);
+ System.out.println("Execution round finished\n");
}
- protected byte[] unwrapState(MachineState state) {
+ public byte[] unwrapState(MachineState state) {
// Ready for diagnosis
byte[] stateBytes = state.toBytes();
@@ -121,30 +138,30 @@ public abstract class ExecutableTest {
return stateBytes;
}
- protected long getData(int address) {
+ public long getData(int address) {
int index = DATA_OFFSET + address * MachineState.VALUE_SIZE;
return stateByteBuffer.getLong(index);
}
- protected void getDataBytes(int address, byte[] dest) {
+ public void getDataBytes(int address, byte[] dest) {
int index = DATA_OFFSET + address * MachineState.VALUE_SIZE;
stateByteBuffer.slice().position(index).get(dest);
}
- protected int getCallStackPosition() {
+ public int getCallStackPosition() {
return TestUtils.NUM_CALL_STACK_PAGES * MachineState.ADDRESS_SIZE - callStackSize;
}
- protected int getCallStackEntry(int address) {
+ public int getCallStackEntry(int address) {
int index = CALL_STACK_OFFSET + 4 + address - TestUtils.NUM_CALL_STACK_PAGES * MachineState.ADDRESS_SIZE + callStackSize;
return stateByteBuffer.getInt(index);
}
- protected int getUserStackPosition() {
+ public int getUserStackPosition() {
return TestUtils.NUM_USER_STACK_PAGES * MachineState.VALUE_SIZE - userStackSize;
}
- protected long getUserStackEntry(int address) {
+ public long getUserStackEntry(int address) {
int index = userStackOffset + 4 + address - TestUtils.NUM_USER_STACK_PAGES * MachineState.VALUE_SIZE + userStackSize;
return stateByteBuffer.getLong(index);
}
diff --git a/Java/src/test/java/org/ciyam/at/test/QuietTestLogger.java b/Java/src/test/java/org/ciyam/at/test/QuietTestLogger.java
new file mode 100644
index 0000000..f27c809
--- /dev/null
+++ b/Java/src/test/java/org/ciyam/at/test/QuietTestLogger.java
@@ -0,0 +1,37 @@
+package org.ciyam.at.test;
+
+import org.ciyam.at.AtLogger;
+
+import java.util.function.Supplier;
+
+public class QuietTestLogger implements AtLogger {
+
+ @Override
+ public void error(String message) {
+ System.err.println("ERROR: " + message);
+ }
+
+ @Override
+ public void error(Supplier messageSupplier) {
+ System.err.println("ERROR: " + messageSupplier.get());
+ }
+
+ @Override
+ public void debug(String message) {
+ }
+
+ @Override
+ public void debug(Supplier messageSupplier) {
+ }
+
+ @Override
+ public void echo(String message) {
+ System.err.println("ECHO: " + message);
+ }
+
+ @Override
+ public void echo(Supplier messageSupplier) {
+ System.err.println("ECHO: " + messageSupplier.get());
+ }
+
+}
diff --git a/Java/src/test/java/org/ciyam/at/test/QuietTestLoggerFactory.java b/Java/src/test/java/org/ciyam/at/test/QuietTestLoggerFactory.java
new file mode 100644
index 0000000..d1325d1
--- /dev/null
+++ b/Java/src/test/java/org/ciyam/at/test/QuietTestLoggerFactory.java
@@ -0,0 +1,13 @@
+package org.ciyam.at.test;
+
+import org.ciyam.at.AtLogger;
+import org.ciyam.at.AtLoggerFactory;
+
+public class QuietTestLoggerFactory implements AtLoggerFactory {
+
+ @Override
+ public AtLogger create(Class> loggerName) {
+ return new QuietTestLogger();
+ }
+
+}
diff --git a/Java/src/test/java/org/ciyam/at/test/TestAPI.java b/Java/src/test/java/org/ciyam/at/test/TestAPI.java
index d500eef..7ade50c 100644
--- a/Java/src/test/java/org/ciyam/at/test/TestAPI.java
+++ b/Java/src/test/java/org/ciyam/at/test/TestAPI.java
@@ -19,14 +19,14 @@ import org.ciyam.at.Timestamp;
public class TestAPI extends API {
/** Average period between blocks, in seconds. */
- public static final int BLOCK_PERIOD = 10 * 60;
+ public static final int BLOCK_PERIOD = 60;
/** Maximum number of steps before auto-sleep. */
public static final int MAX_STEPS_PER_ROUND = 500;
/** Op-code step multiplier for calling functions. */
public static final int STEPS_PER_FUNCTION_CALL = 10;
/** Initial balance for simple test scenarios. */
- public static final long DEFAULT_INITIAL_BALANCE = 1234L;
+ public static final long DEFAULT_INITIAL_BALANCE = 10_0000_0000L;
/** Initial block height for simple test scenarios. */
public static final int DEFAULT_INITIAL_BLOCK_HEIGHT = 10;
/** AT creation block height for simple test scenarios. */
@@ -102,29 +102,36 @@ public class TestAPI extends API {
}
}
- public List blockchain;
- public Map accounts;
- public Map transactions;
+ public List blockchain = new ArrayList<>();
+ public Map accounts = new HashMap<>();
+ public Map transactions = new HashMap<>();
+ public List atTransactions = new ArrayList<>();
+ private TestBlock currentBlock = new TestBlock();
private int currentBlockHeight;
public TestAPI() {
this.currentBlockHeight = DEFAULT_INITIAL_BLOCK_HEIGHT;
// Fill block chain from block 1 to initial height with empty blocks
- blockchain = new ArrayList<>();
for (int h = 1; h <= this.currentBlockHeight; ++h)
blockchain.add(new TestBlock());
// Set up test accounts
- accounts = new HashMap<>();
- new TestAccount(AT_CREATOR_ADDRESS, 1000000L).addToMap(accounts);
+ new TestAccount(AT_CREATOR_ADDRESS, DEFAULT_INITIAL_BALANCE).addToMap(accounts);
new TestAccount(AT_ADDRESS, DEFAULT_INITIAL_BALANCE).addToMap(accounts);
- new TestAccount("Initiator", 100000L).addToMap(accounts);
- new TestAccount("Responder", 200000L).addToMap(accounts);
- new TestAccount("Bystander", 300000L).addToMap(accounts);
+ new TestAccount("Initiator", DEFAULT_INITIAL_BALANCE * 2).addToMap(accounts);
+ new TestAccount("Responder", DEFAULT_INITIAL_BALANCE * 3).addToMap(accounts);
+ new TestAccount("Bystander", DEFAULT_INITIAL_BALANCE * 4).addToMap(accounts);
+ }
- transactions = new HashMap<>();
+ // Hook to be overridden
+ protected boolean willExecute(MachineState state, int blockHeight) {
+ return true;
+ }
+
+ // Hook to be oveerridden
+ protected void preExecute(MachineState state) {
}
public static byte[] encodeAddress(String address) {
@@ -153,9 +160,19 @@ public class TestAPI extends API {
this.currentBlockHeight = blockHeight;
}
+ public void addTransactionToCurrentBlock(TestTransaction testTransaction) {
+ currentBlock.transactions.add(testTransaction);
+ }
+
+ public void addCurrentBlockToChain() {
+ addBlockToChain(currentBlock);
+ currentBlock = new TestBlock();
+ }
+
public TestBlock addBlockToChain(TestBlock newBlock) {
blockchain.add(newBlock);
final int blockHeight = blockchain.size();
+ StringBuilder sb = new StringBuilder(256);
for (int seq = 0; seq < newBlock.transactions.size(); ++seq) {
TestTransaction transaction = newBlock.transactions.get(seq);
@@ -165,6 +182,48 @@ public class TestAPI extends API {
// Add to transactions map
transactions.put(stringifyHash(transaction.txHash), transaction);
+
+ // Transaction sent/received by AT? Add to AT transactions list
+ if (transaction.sender.equals(AT_ADDRESS) || transaction.recipient.equals(AT_ADDRESS))
+ atTransactions.add(transaction);
+
+ // Process PAYMENT transactions
+ if (transaction.txType == ATTransactionType.PAYMENT) {
+ sb.setLength(0);
+ sb.append(transaction.sender)
+ .append(" sent ")
+ .append(prettyAmount(transaction.amount));
+
+ // Subtract amount from sender
+ TestAccount senderAccount = accounts.get(transaction.sender);
+ if (senderAccount == null)
+ throw new IllegalStateException(String.format("Can't send from unknown sender %s: no funds!",
+ transaction.sender));
+
+ // Do not apply if sender is AT because balance update already performed during execution
+ if (!transaction.sender.equals(AT_ADDRESS)) {
+ senderAccount.balance -= transaction.amount;
+ }
+
+ if (senderAccount.balance < 0)
+ throw new IllegalStateException(String.format("Can't send %s from %s: insufficient funds (%s)",
+ prettyAmount(transaction.amount),
+ transaction.sender,
+ prettyAmount(senderAccount.balance)));
+
+ // Add amount to recipient
+ sb.append(" to ");
+ TestAccount recipientAccount = accounts.get(transaction.recipient);
+ if (recipientAccount == null) {
+ sb.append("(new) ");
+ recipientAccount = new TestAccount(transaction.recipient, 0);
+ accounts.put(transaction.recipient, recipientAccount);
+ }
+ recipientAccount.balance += transaction.amount;
+ sb.append(transaction.recipient);
+
+ System.out.println(sb.toString());
+ }
}
return newBlock;
@@ -288,7 +347,13 @@ public class TestAPI extends API {
if (transaction.recipient.equals("AT")) {
// Found a transaction
- System.out.println("Found transaction at height " + blockHeight + " sequence " + transactionSequence);
+ System.out.println(String.format("Found transaction at height %d, sequence %d: %s %s from %s",
+ blockHeight,
+ transactionSequence,
+ transaction.txType.equals(ATTransactionType.PAYMENT) ? prettyAmount(transaction.amount) : "",
+ transaction.txType.name(),
+ transaction.sender
+ ));
// Generate pseudo-hash of transaction
this.setA(state, transaction.txHash);
@@ -392,12 +457,28 @@ public class TestAPI extends API {
if (recipient == null)
throw new IllegalStateException("Refusing to pay to unknown account: " + address);
- recipient.balance += amount;
- System.out.println(String.format("Paid %s to '%s', their balance now: %s", prettyAmount(amount), recipient.address, prettyAmount(recipient.balance)));
+ if (amount < 0)
+ throw new IllegalStateException(String.format("Refusing to pay negative amount: %s", amount));
+
+ if (amount == 0) {
+ System.out.println(String.format("Skipping zero-amount payment to account %s", address));
+ return;
+ }
+
+ System.out.println(String.format("Creating PAYMENT of %s to %s", prettyAmount(amount), recipient.address));
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)));
+ System.out.println(String.format("AT current balance was %s, now: %s", prettyAmount(previousBalance), prettyAmount(newBalance)));
+
+ // Add suitable transaction to currentBlock
+
+ // Generate tx hash
+ byte[] txHash = new byte[32];
+ RANDOM.nextBytes(txHash);
+
+ TestTransaction testTransaction = new TestTransaction(txHash, AT_ADDRESS, recipient.address, amount);
+ addTransactionToCurrentBlock(testTransaction);
}
@Override
@@ -409,7 +490,17 @@ public class TestAPI extends API {
if (recipient == null)
throw new IllegalStateException("Refusing to send message to unknown account: " + address);
- recipient.messages.add(this.getA(state));
+ byte[] message = this.getA(state);
+ recipient.messages.add(message);
+
+ // Add suitable transaction to currentBlock
+
+ // Generate tx hash
+ byte[] txHash = new byte[32];
+ RANDOM.nextBytes(txHash);
+
+ TestTransaction testTransaction = new TestTransaction(txHash, AT_ADDRESS, recipient.address, message);
+ addTransactionToCurrentBlock(testTransaction);
}
@Override
@@ -423,10 +514,16 @@ public class TestAPI extends API {
System.out.println("Finished - refunding remaining to creator");
TestAccount atCreatorAccount = accounts.get(AT_CREATOR_ADDRESS);
- atCreatorAccount.balance += amount;
- System.out.println(String.format("Paid %s to AT creator '%s', their balance now: %s", prettyAmount(amount), atCreatorAccount.address, prettyAmount(atCreatorAccount.balance)));
+ System.out.println(String.format("Creating PAYMENT of %s to AT creator %s", prettyAmount(amount), atCreatorAccount.address));
- accounts.get(AT_ADDRESS).balance -= amount;
+ // Add suitable transaction to currentBlock
+
+ // Generate tx hash
+ byte[] txHash = new byte[32];
+ RANDOM.nextBytes(txHash);
+
+ TestTransaction testTransaction = new TestTransaction(txHash, AT_ADDRESS, atCreatorAccount.address, amount);
+ addTransactionToCurrentBlock(testTransaction);
}
@Override