3
0
mirror of https://github.com/Qortal/AT.git synced 2025-02-16 04:05:51 +00:00
AT/Java/tests/common/ACCTAPI.java
catbref f0e031599d Initial upload of Java re-implementation
Note that this is unfinished, requiring fee-per-opcode support
and some refactoring.
2018-09-27 13:59:43 +01:00

329 lines
9.4 KiB
Java

package common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import org.ciyam.at.API;
import org.ciyam.at.ExecutionException;
import org.ciyam.at.FunctionData;
import org.ciyam.at.IllegalFunctionCodeException;
import org.ciyam.at.MachineState;
import org.ciyam.at.Timestamp;
public class ACCTAPI implements API {
private class Account {
public String address;
public long balance;
public Account(String address, long amount) {
this.address = address;
this.balance = amount;
}
}
private class Transaction {
public int txType;
public String creator;
public String recipient;
public long amount;
public long[] message;
}
private class Block {
public List<Transaction> transactions;
public Block() {
this.transactions = new ArrayList<Transaction>();
}
}
//
private List<Block> blockchain;
private Map<String, Account> accounts;
private long balanceAT;
private long previousBalanceAT;
//
public ACCTAPI() {
// build blockchain
this.blockchain = new ArrayList<Block>();
Block genesisBlock = new Block();
this.blockchain.add(genesisBlock);
// generate accounts
this.accounts = new HashMap<String, Account>();
Account initiator = new Account("Initiator", 0);
this.accounts.put(initiator.address, initiator);
Account responder = new Account("Responder", 10000);
this.accounts.put(responder.address, responder);
Account bystander = new Account("Bystander", 999);
this.accounts.put(bystander.address, bystander);
this.balanceAT = 50000;
this.previousBalanceAT = this.balanceAT;
}
public void generateNextBlock(byte[] secret) {
Random random = new Random();
Block block = new Block();
System.out.println("Block " + (this.blockchain.size() + 1));
int transactionCount = random.nextInt(5);
for (int i = 0; i < transactionCount; ++i) {
Transaction transaction = new Transaction();
transaction.txType = random.nextInt(2);
switch (transaction.txType) {
case 0: // payment
transaction.amount = random.nextInt(1000);
System.out.print("Payment Tx [" + transaction.amount + "]");
break;
case 1: // message
System.out.print("Message Tx [");
transaction.message = new long[4];
if (random.nextInt(3) == 0) {
// correct message
transaction.message[0] = fromBytes(secret, 0);
transaction.message[1] = fromBytes(secret, 8);
transaction.message[2] = fromBytes(secret, 16);
transaction.message[3] = fromBytes(secret, 24);
} else {
// incorrect message
transaction.message[0] = 0xdeadbeefdeadbeefL;
transaction.message[1] = 0xdeadbeefdeadbeefL;
transaction.message[2] = 0xdeadbeefdeadbeefL;
transaction.message[3] = 0xdeadbeefdeadbeefL;
}
System.out.print(String.format("%016x", transaction.message[0]));
System.out.print(String.format("%016x", transaction.message[1]));
System.out.print(String.format("%016x", transaction.message[2]));
System.out.print(String.format("%016x", transaction.message[3]));
System.out.print("]");
break;
}
transaction.creator = getRandomAccount();
transaction.recipient = getRandomAccount();
System.out.println(" from " + transaction.creator + " to " + transaction.recipient);
block.transactions.add(transaction);
}
this.blockchain.add(block);
this.previousBalanceAT = this.balanceAT;
}
/** Convert long to little-endian byte array */
private byte[] toByteArray(long value) {
return new byte[] { (byte) (value), (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24), (byte) (value >> 32), (byte) (value >> 40),
(byte) (value >> 48), (byte) (value >> 56) };
}
/** Convert part of little-endian byte[] to long */
private long fromBytes(byte[] bytes, int start) {
return (bytes[start] & 0xffL) | (bytes[start + 1] & 0xffL) << 8 | (bytes[start + 2] & 0xffL) << 16 | (bytes[start + 3] & 0xffL) << 24
| (bytes[start + 4] & 0xffL) << 32 | (bytes[start + 5] & 0xffL) << 40 | (bytes[start + 6] & 0xffL) << 48 | (bytes[start + 7] & 0xffL) << 56;
}
private String getRandomAccount() {
int numAccounts = this.accounts.size();
int accountIndex = new Random().nextInt(numAccounts);
List<Account> accounts = this.accounts.values().stream().collect(Collectors.toList());
return accounts.get(accountIndex).address;
}
@Override
public int getCurrentBlockHeight() {
return this.blockchain.size();
}
@Override
public int getATCreationBlockHeight(MachineState state) {
return 1;
}
@Override
public void putPreviousBlockHashInA(MachineState state) {
state.a1 = this.blockchain.size() - 1;
state.a2 = state.a1;
state.a3 = state.a1;
state.a4 = state.a1;
}
@Override
public void putTransactionAfterTimestampInA(Timestamp timestamp, MachineState state) {
int blockHeight = timestamp.blockHeight;
int transactionSequence = timestamp.transactionSequence + 1;
while (blockHeight <= this.blockchain.size()) {
Block block = this.blockchain.get(blockHeight - 1);
List<Transaction> transactions = block.transactions;
if (transactionSequence > transactions.size() - 1) {
// No more transactions at this height
++blockHeight;
transactionSequence = 0;
continue;
}
Transaction transaction = transactions.get(transactionSequence);
if (transaction.recipient.equals("Initiator")) {
// Found a transaction
System.out.println("Found transaction at height " + blockHeight + " sequence " + transactionSequence);
// Generate pseudo-hash of transaction
state.a1 = new Timestamp(blockHeight, transactionSequence).longValue();
state.a2 = state.a1;
state.a3 = state.a1;
state.a4 = state.a1;
return;
}
++transactionSequence;
}
// Nothing found
state.a1 = 0L;
state.a2 = 0L;
state.a3 = 0L;
state.a4 = 0L;
}
@Override
public long getTypeFromTransactionInA(MachineState state) {
Timestamp timestamp = new Timestamp(state.a1);
Block block = this.blockchain.get(timestamp.blockHeight - 1);
Transaction transaction = block.transactions.get(timestamp.transactionSequence);
return transaction.txType;
}
@Override
public long getAmountFromTransactionInA(MachineState state) {
Timestamp timestamp = new Timestamp(state.a1);
Block block = this.blockchain.get(timestamp.blockHeight - 1);
Transaction transaction = block.transactions.get(timestamp.transactionSequence);
return transaction.amount;
}
@Override
public long getTimestampFromTransactionInA(MachineState state) {
// Transaction hash in A is actually just 4 copies of transaction's "timestamp"
Timestamp timestamp = new Timestamp(state.a1);
return timestamp.longValue();
}
@Override
public long generateRandomUsingTransactionInA(MachineState state) {
// NOT USED
return 0L;
}
@Override
public void putMessageFromTransactionInAIntoB(MachineState state) {
Timestamp timestamp = new Timestamp(state.a1);
Block block = this.blockchain.get(timestamp.blockHeight - 1);
Transaction transaction = block.transactions.get(timestamp.transactionSequence);
state.b1 = transaction.message[0];
state.b2 = transaction.message[1];
state.b3 = transaction.message[2];
state.b4 = transaction.message[3];
}
@Override
public void putAddressFromTransactionInAIntoB(MachineState state) {
Timestamp timestamp = new Timestamp(state.a1);
Block block = this.blockchain.get(timestamp.blockHeight - 1);
Transaction transaction = block.transactions.get(timestamp.transactionSequence);
state.b1 = transaction.creator.charAt(0);
state.b2 = state.b1;
state.b3 = state.b1;
state.b4 = state.b1;
}
@Override
public void putCreatorAddressIntoB(MachineState state) {
// Dummy creator
state.b1 = "C".charAt(0);
state.b2 = state.b1;
state.b3 = state.b1;
state.b4 = state.b1;
}
@Override
public long getCurrentBalance(MachineState state) {
return this.balanceAT;
}
@Override
public long getPreviousBalance(MachineState state) {
return this.previousBalanceAT;
}
@Override
public void payAmountToB(long value1, MachineState state) {
char firstChar = String.format("%c", state.b1).charAt(0);
Account recipient = this.accounts.values().stream().filter((account) -> account.address.charAt(0) == firstChar).findFirst().get();
recipient.balance += value1;
System.out.println("Paid " + value1 + " to " + recipient.address + ", their balance now: " + recipient.balance);
this.balanceAT -= value1;
System.out.println("Our balance now: " + this.balanceAT);
}
@Override
public void payCurrentBalanceToB(MachineState state) {
// NOT USED
}
@Override
public void payPreviousBalanceToB(MachineState state) {
// NOT USED
}
@Override
public void messageAToB(MachineState state) {
// NOT USED
}
@Override
public long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state) {
timestamp.blockHeight += (int) minutes;
return timestamp.longValue();
}
@Override
public void onFatalError(MachineState state, ExecutionException e) {
System.out.println("Fatal error: " + e.getMessage());
System.out.println("No error address set - refunding to creator and finishing");
}
@Override
public void platformSpecificPreExecuteCheck(short functionCodeValue, int paramCount, boolean returnValueExpected) throws IllegalFunctionCodeException {
// NOT USED
}
@Override
public void platformSpecificPostCheckExecute(short functionCodeValue, FunctionData functionData, MachineState state) throws ExecutionException {
// NOT USED
}
}