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..04854dd 100644 --- a/Java/src/main/java/org/ciyam/at/OpCode.java +++ b/Java/src/main/java/org/ciyam/at/OpCode.java @@ -1070,7 +1070,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/test/ExecutableTest.java b/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java index cc5bff8..96a52c8 100644 --- a/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java +++ b/Java/src/test/java/org/ciyam/at/test/ExecutableTest.java @@ -96,6 +96,9 @@ public abstract class ExecutableTest { System.out.println("New balance: " + TestAPI.prettyAmount(newBalance)); api.setCurrentBalance(newBalance); + // Add block, possibly containing AT-created transactions, to chain to at least provide block hashes + api.addCurrentBlockToChain(); + // Bump block height api.bumpCurrentBlockHeight(); 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..316dca0 100644 --- a/Java/src/test/java/org/ciyam/at/test/TestAPI.java +++ b/Java/src/test/java/org/ciyam/at/test/TestAPI.java @@ -19,7 +19,7 @@ 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. */ @@ -102,29 +102,27 @@ 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_ADDRESS, DEFAULT_INITIAL_BALANCE).addToMap(accounts); new TestAccount("Initiator", 100000L).addToMap(accounts); new TestAccount("Responder", 200000L).addToMap(accounts); new TestAccount("Bystander", 300000L).addToMap(accounts); - - transactions = new HashMap<>(); } public static byte[] encodeAddress(String address) { @@ -153,6 +151,15 @@ 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(); @@ -165,6 +172,10 @@ 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); } return newBlock; @@ -288,7 +299,11 @@ 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 from %s", + blockHeight, + transactionSequence, + transaction.txType.name(), + transaction.sender)); // Generate pseudo-hash of transaction this.setA(state, transaction.txHash); @@ -392,12 +407,29 @@ public class TestAPI extends API { if (recipient == null) throw new IllegalStateException("Refusing to pay to unknown account: " + address); + 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; + } + recipient.balance += amount; System.out.println(String.format("Paid %s to '%s', their balance now: %s", prettyAmount(amount), recipient.address, prettyAmount(recipient.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))); + + // 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 +441,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