@ -19,14 +19,14 @@ import org.ciyam.at.Timestamp;
public class TestAPI extends API {
public class TestAPI extends API {
/** Average period between blocks, in seconds. */
/** 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. */
/** Maximum number of steps before auto-sleep. */
public static final int MAX_STEPS_PER_ROUND = 500 ;
public static final int MAX_STEPS_PER_ROUND = 500 ;
/** Op-code step multiplier for calling functions. */
/** Op-code step multiplier for calling functions. */
public static final int STEPS_PER_FUNCTION_CALL = 10 ;
public static final int STEPS_PER_FUNCTION_CALL = 10 ;
/** Initial balance for simple test scenarios. */
/** Initial balance for simple test scenarios. */
public static final long DEFAULT_INITIAL_BALANCE = 1234 L ;
public static final long DEFAULT_INITIAL_BALANCE = 10_0000_0000 L ;
/** Initial block height for simple test scenarios. */
/** Initial block height for simple test scenarios. */
public static final int DEFAULT_INITIAL_BLOCK_HEIGHT = 10 ;
public static final int DEFAULT_INITIAL_BLOCK_HEIGHT = 10 ;
/** AT creation block height for simple test scenarios. */
/** AT creation block height for simple test scenarios. */
@ -102,29 +102,36 @@ public class TestAPI extends API {
}
}
}
}
public List < TestBlock > blockchain ;
public List < TestBlock > blockchain = new ArrayList < > ( ) ;
public Map < String , TestAccount > accounts ;
public Map < String , TestAccount > accounts = new HashMap < > ( ) ;
public Map < String , TestTransaction > transactions ;
public Map < String , TestTransaction > transactions = new HashMap < > ( ) ;
public List < TestTransaction > atTransactions = new ArrayList < > ( ) ;
private TestBlock currentBlock = new TestBlock ( ) ;
private int currentBlockHeight ;
private int currentBlockHeight ;
public TestAPI ( ) {
public TestAPI ( ) {
this . currentBlockHeight = DEFAULT_INITIAL_BLOCK_HEIGHT ;
this . currentBlockHeight = DEFAULT_INITIAL_BLOCK_HEIGHT ;
// Fill block chain from block 1 to initial height with empty blocks
// Fill block chain from block 1 to initial height with empty blocks
blockchain = new ArrayList < > ( ) ;
for ( int h = 1 ; h < = this . currentBlockHeight ; + + h )
for ( int h = 1 ; h < = this . currentBlockHeight ; + + h )
blockchain . add ( new TestBlock ( ) ) ;
blockchain . add ( new TestBlock ( ) ) ;
// Set up test accounts
// Set up test accounts
accounts = new HashMap < > ( ) ;
new TestAccount ( AT_CREATOR_ADDRESS , DEFAULT_INITIAL_BALANCE ) . addToMap ( accounts ) ;
new TestAccount ( AT_CREATOR_ADDRESS , 1000000L ) . addToMap ( accounts ) ;
new TestAccount ( AT_ADDRESS , DEFAULT_INITIAL_BALANCE ) . addToMap ( accounts ) ;
new TestAccount ( AT_ADDRESS , DEFAULT_INITIAL_BALANCE ) . addToMap ( accounts ) ;
new TestAccount ( "Initiator" , 100000L ) . addToMap ( accounts ) ;
new TestAccount ( "Initiator" , DEFAULT_INITIAL_BALANCE * 2 ) . addToMap ( accounts ) ;
new TestAccount ( "Responder" , 200000L ) . addToMap ( accounts ) ;
new TestAccount ( "Responder" , DEFAULT_INITIAL_BALANCE * 3 ) . addToMap ( accounts ) ;
new TestAccount ( "Bystander" , 300000L ) . addToMap ( accounts ) ;
new TestAccount ( "Bystander" , DEFAULT_INITIAL_BALANCE * 4 ) . addToMap ( accounts ) ;
}
// Hook to be overridden
protected boolean willExecute ( MachineState state , int blockHeight ) {
return true ;
}
transactions = new HashMap < > ( ) ;
// Hook to be oveerridden
protected void preExecute ( MachineState state ) {
}
}
public static byte [ ] encodeAddress ( String address ) {
public static byte [ ] encodeAddress ( String address ) {
@ -153,9 +160,19 @@ public class TestAPI extends API {
this . currentBlockHeight = blockHeight ;
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 ) {
public TestBlock addBlockToChain ( TestBlock newBlock ) {
blockchain . add ( newBlock ) ;
blockchain . add ( newBlock ) ;
final int blockHeight = blockchain . size ( ) ;
final int blockHeight = blockchain . size ( ) ;
StringBuilder sb = new StringBuilder ( 256 ) ;
for ( int seq = 0 ; seq < newBlock . transactions . size ( ) ; + + seq ) {
for ( int seq = 0 ; seq < newBlock . transactions . size ( ) ; + + seq ) {
TestTransaction transaction = newBlock . transactions . get ( seq ) ;
TestTransaction transaction = newBlock . transactions . get ( seq ) ;
@ -165,6 +182,48 @@ public class TestAPI extends API {
// Add to transactions map
// Add to transactions map
transactions . put ( stringifyHash ( transaction . txHash ) , transaction ) ;
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 ;
return newBlock ;
@ -288,7 +347,13 @@ public class TestAPI extends API {
if ( transaction . recipient . equals ( "AT" ) ) {
if ( transaction . recipient . equals ( "AT" ) ) {
// Found a transaction
// 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
// Generate pseudo-hash of transaction
this . setA ( state , transaction . txHash ) ;
this . setA ( state , transaction . txHash ) ;
@ -392,12 +457,28 @@ public class TestAPI extends API {
if ( recipient = = null )
if ( recipient = = null )
throw new IllegalStateException ( "Refusing to pay to unknown account: " + address ) ;
throw new IllegalStateException ( "Refusing to pay to unknown account: " + address ) ;
recipient . balance + = amount ;
if ( amount < 0 )
System . out . println ( String . format ( "Paid %s to '%s', their balance now: %s" , prettyAmount ( amount ) , recipient . address , prettyAmount ( recipient . balance ) ) ) ;
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 previousBalance = state . getCurrentBalance ( ) ;
final long newBalance = previousBalance - amount ;
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
@Override
@ -409,7 +490,17 @@ public class TestAPI extends API {
if ( recipient = = null )
if ( recipient = = null )
throw new IllegalStateException ( "Refusing to send message to unknown account: " + address ) ;
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
@Override
@ -423,10 +514,16 @@ public class TestAPI extends API {
System . out . println ( "Finished - refunding remaining to creator" ) ;
System . out . println ( "Finished - refunding remaining to creator" ) ;
TestAccount atCreatorAccount = accounts . get ( AT_CREATOR_ADDRESS ) ;
TestAccount atCreatorAccount = accounts . get ( AT_CREATOR_ADDRESS ) ;
atCreatorAccount . balance + = amount ;
System . out . println ( String . format ( "Creating PAYMENT of %s to AT creator %s" , prettyAmount ( amount ) , atCreatorAccount . address ) ) ;
System . out . println ( String . format ( "Paid %s to AT creator '%s', their balance now: %s" , prettyAmount ( amount ) , atCreatorAccount . address , prettyAmount ( atCreatorAccount . balance ) ) ) ;
// Add suitable transaction to currentBlock
// Generate tx hash
byte [ ] txHash = new byte [ 32 ] ;
RANDOM . nextBytes ( txHash ) ;
accounts . get ( AT_ADDRESS ) . balance - = amount ;
TestTransaction testTransaction = new TestTransaction ( txHash , AT_ADDRESS , atCreatorAccount . address , amount ) ;
addTransactionToCurrentBlock ( testTransaction ) ;
}
}
@Override
@Override