@Test public void validateGenesisBlock() { // CREATE EMPTY DATABASE DBSet databaseSet = DBSet.createEmptyDatabaseSet(); // CREATE GENESIS BLOCK Block genesisBlock = new GenesisBlock(); // CHECK IF VALID assertEquals(true, genesisBlock.isValid(databaseSet)); // ADD INVALID GENESIS TRANSACTION Transaction transaction = new GenesisTransaction( new Account("XUi2oga2pnGNcZ9es6pBqxydtRZKWdkL2g"), BigDecimal.valueOf(-1000).setScale(8), NTP.getTime()); genesisBlock.addTransaction(transaction); // CHECK IF INVALID assertEquals(false, genesisBlock.isValid(databaseSet)); // CREATE NEW BLOCK genesisBlock = new GenesisBlock(); // CHECK IF VALID assertEquals(true, genesisBlock.isValid(databaseSet)); // PROCESS genesisBlock.process(databaseSet); // CHECK IF INVALID assertEquals(false, genesisBlock.isValid(databaseSet)); }
public void orphan(DBSet db) { // ORPHAN AT TRANSACTIONS LinkedHashMap<Tuple2<Integer, Integer>, AT_Transaction> atTxs = DBSet.getInstance().getATTransactionMap().getATTransactions(this.getHeight(db)); Iterator<AT_Transaction> iter = atTxs.values().iterator(); while (iter.hasNext()) { AT_Transaction key = iter.next(); Long amount = key.getAmount(); if (key.getRecipientId() != null && !Arrays.equals(key.getRecipientId(), new byte[AT_Constants.AT_ID_SIZE]) && !key.getRecipient().equalsIgnoreCase("1")) { Account recipient = new Account(key.getRecipient()); recipient.setConfirmedBalance( recipient.getConfirmedBalance(db).subtract(BigDecimal.valueOf(amount, 8)), db); if (Arrays.equals(recipient.getLastReference(db), new byte[64])) { recipient.removeReference(db); } } Account sender = new Account(key.getSender()); sender.setConfirmedBalance( sender.getConfirmedBalance(db).add(BigDecimal.valueOf(amount, 8)), db); } // ORPHAN TRANSACTIONS this.orphanTransactions(this.getTransactions(), db); // REMOVE FEE BigDecimal blockFee = this.getTotalFee(); if (blockFee.compareTo(BigDecimal.ZERO) == 1) { // UPDATE GENERATOR BALANCE WITH FEE this.generator.setConfirmedBalance( this.generator.getConfirmedBalance(db).subtract(blockFee), db); } // DELETE AT TRANSACTIONS FROM DB db.getATTransactionMap().delete(this.getHeight(db)); // DELETE TRANSACTIONS FROM FINAL MAP db.getTransactionFinalMap().delete(this.getHeight(db)); // DELETE BLOCK FROM DB db.getBlockMap().delete(this); // SET PARENT AS LAST BLOCK db.getBlockMap().setLastBlock(this.getParent(db)); for (Transaction transaction : this.getTransactions()) { // ADD ORPHANED TRANASCTIONS BACK TO DATABASE db.getTransactionMap().add(transaction); // DELETE ORPHANED TRANASCTIONS FROM PARENT DATABASE db.getTransactionParentMap().delete(transaction.getSignature()); } }
@Test public void orphanBlock() { // CREATE EMPTY MEMORY DATABASE DBSet databaseSet = DBSet.createEmptyDatabaseSet(); // PROCESS GENESISBLOCK GenesisBlock genesisBlock = new GenesisBlock(); genesisBlock.process(databaseSet); // CREATE KNOWN ACCOUNT byte[] seed = Crypto.getInstance().digest("test".getBytes()); byte[] privateKey = Crypto.getInstance().createKeyPair(seed).getA(); PrivateKeyAccount generator = new PrivateKeyAccount(privateKey); // PROCESS GENESIS TRANSACTION TO MAKE SURE GENERATOR HAS FUNDS Transaction transaction = new GenesisTransaction(generator, BigDecimal.valueOf(1000).setScale(8), NTP.getTime()); transaction.process(databaseSet); // GENERATE NEXT BLOCK BlockGenerator blockGenerator = new BlockGenerator(); Block block = blockGenerator.generateNextBlock(databaseSet, generator, genesisBlock); // FORK DBSet fork = databaseSet.fork(); // GENERATE PAYMENT 1 Account recipient = new Account("XUi2oga2pnGNcZ9es6pBqxydtRZKWdkL2g"); long timestamp = block.getTimestamp(); byte[] signature = PaymentTransaction.generateSignature( databaseSet, generator, recipient, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp); Transaction payment1 = new PaymentTransaction( generator, recipient, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp, generator.getLastReference(databaseSet), signature); payment1.process(fork); block.addTransaction(payment1); // GENERATE PAYMENT 2 Account recipient2 = new Account("XLPYYfxKEiDcybCkFA7jXcxSdePMMoyZLt"); signature = PaymentTransaction.generateSignature( fork, generator, recipient2, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp); Transaction payment2 = new PaymentTransaction( generator, recipient2, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp, generator.getLastReference(fork), signature); block.addTransaction(payment2); // ADD TRANSACTION SIGNATURE byte[] transactionsSignature = blockGenerator.calculateTransactionsSignature(block, generator); block.setTransactionsSignature(transactionsSignature); // CHECK VALID assertEquals(true, block.isSignatureValid()); assertEquals(true, block.isValid(databaseSet)); // PROCESS BLOCK block.process(databaseSet); // ORPHAN BLOCK block.orphan(databaseSet); // CHECK BALANCE GENERATOR assertEquals( true, generator.getConfirmedBalance(databaseSet).compareTo(BigDecimal.valueOf(1000)) == 0); // CHECK LAST REFERENCE GENERATOR assertEquals( true, Arrays.equals(generator.getLastReference(databaseSet), transaction.getSignature())); // CHECK BALANCE RECIPIENT assertEquals( true, recipient.getConfirmedBalance(databaseSet).compareTo(BigDecimal.valueOf(1000)) == 0); // CHECK LAST REFERENCE RECIPIENT assertEquals( false, Arrays.equals(recipient.getLastReference(databaseSet), payment1.getSignature())); // CHECK BALANCE RECIPIENT2 assertEquals( true, recipient2.getConfirmedBalance(databaseSet).compareTo(BigDecimal.valueOf(0)) == 0); // CHECK LAST REFERENCE RECIPIENT assertEquals(true, Arrays.equals(recipient2.getLastReference(databaseSet), new byte[0])); // CHECK LAST BLOCK assertEquals( true, Arrays.equals( genesisBlock.getSignature(), databaseSet.getBlockMap().getLastBlock().getSignature())); }
@Test public void parseBlock() { // CREATE EMPTY MEMORY DATABASE DBSet databaseSet = DBSet.createEmptyDatabaseSet(); // PROCESS GENESISBLOCK GenesisBlock genesisBlock = new GenesisBlock(); genesisBlock.process(databaseSet); // CREATE KNOWN ACCOUNT byte[] seed = Crypto.getInstance().digest("test".getBytes()); byte[] privateKey = Crypto.getInstance().createKeyPair(seed).getA(); PrivateKeyAccount generator = new PrivateKeyAccount(privateKey); // PROCESS GENESIS TRANSACTION TO MAKE SURE GENERATOR HAS FUNDS Transaction transaction = new GenesisTransaction(generator, BigDecimal.valueOf(1000).setScale(8), NTP.getTime()); transaction.process(databaseSet); // GENERATE NEXT BLOCK BlockGenerator blockGenerator = new BlockGenerator(); Block block = blockGenerator.generateNextBlock(databaseSet, generator, genesisBlock); // FORK DBSet fork = databaseSet.fork(); // GENERATE PAYMENT 1 Account recipient = new Account("XUi2oga2pnGNcZ9es6pBqxydtRZKWdkL2g"); long timestamp = block.getTimestamp(); byte[] signature = PaymentTransaction.generateSignature( databaseSet, generator, recipient, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp); Transaction payment1 = new PaymentTransaction( generator, recipient, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp, generator.getLastReference(databaseSet), signature); payment1.process(fork); block.addTransaction(payment1); // GENERATE PAYMENT 2 Account recipient2 = new Account("XLPYYfxKEiDcybCkFA7jXcxSdePMMoyZLt"); signature = PaymentTransaction.generateSignature( fork, generator, recipient2, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp); Transaction payment2 = new PaymentTransaction( generator, recipient2, BigDecimal.valueOf(100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp, generator.getLastReference(fork), signature); block.addTransaction(payment2); // ADD TRANSACTION SIGNATURE byte[] transactionsSignature = Crypto.getInstance().sign(generator, block.getGeneratorSignature()); block.setTransactionsSignature(transactionsSignature); // CONVERT TO BYTES byte[] rawBlock = block.toBytes(); try { // PARSE FROM BYTES Block parsedBlock = BlockFactory.getInstance().parse(rawBlock); // CHECK INSTANCE assertEquals(false, parsedBlock instanceof GenesisBlock); // CHECK SIGNATURE assertEquals(true, Arrays.equals(block.getSignature(), parsedBlock.getSignature())); // CHECK GENERATOR assertEquals(block.getGenerator().getAddress(), parsedBlock.getGenerator().getAddress()); // CHECK BASE TARGET assertEquals(block.getGeneratingBalance(), parsedBlock.getGeneratingBalance()); // CHECK FEE assertEquals(block.getTotalFee(), parsedBlock.getTotalFee()); // CHECK REFERENCE assertEquals(true, Arrays.equals(block.getReference(), parsedBlock.getReference())); // CHECK TIMESTAMP assertEquals(block.getTimestamp(), parsedBlock.getTimestamp()); // CHECK TRANSACTIONS COUNT assertEquals(block.getTransactionCount(), parsedBlock.getTransactionCount()); } catch (Exception e) { fail("Exception while parsing transaction."); } // PARSE TRANSACTION FROM WRONG BYTES rawBlock = new byte[50]; try { // PARSE FROM BYTES BlockFactory.getInstance().parse(rawBlock); // FAIL fail("this should throw an exception"); } catch (Exception e) { // EXCEPTION IS THROWN OKE } }
@Test public void validateBlock() { // CREATE EMPTY MEMORY DATABASE DBSet databaseSet = DBSet.createEmptyDatabaseSet(); // PROCESS GENESISBLOCK GenesisBlock genesisBlock = new GenesisBlock(); genesisBlock.process(databaseSet); // CREATE KNOWN ACCOUNT byte[] seed = Crypto.getInstance().digest("test".getBytes()); byte[] privateKey = Crypto.getInstance().createKeyPair(seed).getA(); PrivateKeyAccount generator = new PrivateKeyAccount(privateKey); // PROCESS GENESIS TRANSACTION TO MAKE SURE GENERATOR HAS FUNDS Transaction transaction = new GenesisTransaction(generator, BigDecimal.valueOf(1000).setScale(8), NTP.getTime()); transaction.process(databaseSet); // GENERATE NEXT BLOCK BlockGenerator blockGenerator = new BlockGenerator(); Block newBlock = blockGenerator.generateNextBlock(databaseSet, generator, genesisBlock); // ADD TRANSACTION SIGNATURE byte[] transactionsSignature = Crypto.getInstance().sign(generator, newBlock.getGeneratorSignature()); newBlock.setTransactionsSignature(transactionsSignature); // CHECK IF VALID assertEquals(true, newBlock.isValid(databaseSet)); // CHANGE REFERENCE Block invalidBlock = BlockFactory.getInstance() .create( newBlock.getVersion(), new byte[128], newBlock.getTimestamp(), newBlock.getGeneratingBalance(), newBlock.getGenerator(), newBlock.getGeneratorSignature()); // CHECK IF INVALID assertEquals(false, invalidBlock.isValid(databaseSet)); // CHANGE TIMESTAMP invalidBlock = BlockFactory.getInstance() .create( newBlock.getVersion(), newBlock.getReference(), 1L, newBlock.getGeneratingBalance(), newBlock.getGenerator(), newBlock.getGeneratorSignature()); // CHECK IF INVALID assertEquals(false, invalidBlock.isValid(databaseSet)); // CHANGE BASETARGET invalidBlock = BlockFactory.getInstance() .create( newBlock.getVersion(), newBlock.getReference(), newBlock.getTimestamp(), 1L, newBlock.getGenerator(), newBlock.getGeneratorSignature()); // CHECK IF INVALID assertEquals(false, invalidBlock.isValid(databaseSet)); // ADD INVALID TRANSACTION invalidBlock = blockGenerator.generateNextBlock(databaseSet, generator, genesisBlock); Account recipient = new Account("XUi2oga2pnGNcZ9es6pBqxydtRZKWdkL2g"); long timestamp = newBlock.getTimestamp(); byte[] signature = PaymentTransaction.generateSignature( databaseSet, generator, recipient, BigDecimal.valueOf(-100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp); Transaction payment = new PaymentTransaction( generator, recipient, BigDecimal.valueOf(-100).setScale(8), BigDecimal.valueOf(1).setScale(8), timestamp, generator.getLastReference(databaseSet), signature); invalidBlock.addTransaction(payment); // CHECK IF INVALID assertEquals(false, invalidBlock.isValid(databaseSet)); // ADD GENESIS TRANSACTION invalidBlock = blockGenerator.generateNextBlock(databaseSet, generator, genesisBlock); transaction = new GenesisTransaction( generator, BigDecimal.valueOf(1000).setScale(8), newBlock.getTimestamp()); invalidBlock.addTransaction(transaction); // CHECK IF INVALID assertEquals(false, invalidBlock.isValid(databaseSet)); }
public void process(DBSet db) { // PROCESS TRANSACTIONS for (Transaction transaction : this.getTransactions()) { // PROCESS transaction.process(db); // SET PARENT db.getTransactionParentMap().set(transaction, this); // REMOVE FROM UNCONFIRMED DATABASE db.getTransactionMap().delete(transaction); } // DELETE CONFIRMED TRANSACTIONS FROM UNCONFIRMED TRANSACTIONS LIST List<Transaction> unconfirmedTransactions = new ArrayList<Transaction>(db.getTransactionMap().getValues()); for (Transaction transaction : unconfirmedTransactions) { if (db.getTransactionParentMap().contains(transaction.getSignature())) { db.getTransactionMap().delete(transaction); } } // PROCESS FEE BigDecimal blockFee = this.getTotalFee(); if (blockFee.compareTo(BigDecimal.ZERO) == 1) { // UPDATE GENERATOR BALANCE WITH FEE this.generator.setConfirmedBalance(this.generator.getConfirmedBalance(db).add(blockFee), db); } Block parent = this.getParent(db); int height = 1; if (parent != null) { // SET AS CHILD OF PARENT db.getChildMap().set(parent, this); // SET BLOCK HEIGHT height = parent.getHeight(db) + 1; db.getHeightMap().set(this, height); } else { // IF NO PARENT HEIGHT IS 1 db.getHeightMap().set(this, 1); } // PROCESS TRANSACTIONS int seq = 1; for (Transaction transaction : this.getTransactions()) { db.getTransactionFinalMap().add(height, seq, transaction); seq++; } // ADD TO DB db.getBlockMap().add(this); // UPDATE LAST BLOCK db.getBlockMap().setLastBlock(this); }
public void orphan() { this.orphan(DBSet.getInstance()); }
public boolean isValid(DBSet db) { // CHECK IF PARENT EXISTS if (this.reference == null || this.getParent(db) == null) { return false; } // CHECK IF TIMESTAMP IS VALID -500 MS ERROR MARGIN TIME if (this.timestamp - 500 > NTP.getTime() || this.timestamp < this.getParent(db).timestamp) { return false; } // CHECK IF TIMESTAMP REST SAME AS PARENT TIMESTAMP REST if (this.timestamp % 1000 != this.getParent(db).timestamp % 1000) { return false; } // CHECK IF GENERATING BALANCE IS CORRECT if (this.generatingBalance != BlockGenerator.getNextBlockGeneratingBalance(db, this.getParent(db))) { return false; } // CHECK IF VERSION IS CORRECT if (this.version != this.getParent(db).getNextBlockVersion(db)) { return false; } if (this.version < 2 && (this.atBytes.length > 0 || this.atFees != 0)) { return false; } // CREATE TARGET byte[] targetBytes = new byte[32]; Arrays.fill(targetBytes, Byte.MAX_VALUE); BigInteger target = new BigInteger(1, targetBytes); // DIVIDE TARGET BY BASE TARGET BigInteger baseTarget = BigInteger.valueOf(BlockGenerator.getBaseTarget(this.generatingBalance)); target = target.divide(baseTarget); // MULTIPLY TARGET BY USER BALANCE target = target.multiply(this.generator.getGeneratingBalance(db).toBigInteger()); // MULTIPLE TARGET BY GUESSES long guesses = (this.timestamp - this.getParent(db).getTimestamp()) / 1000; BigInteger lowerTarget = target.multiply(BigInteger.valueOf(guesses - 1)); target = target.multiply(BigInteger.valueOf(guesses)); // CONVERT PROOF HASH TO BIGINT BigInteger hashValue = new BigInteger(1, getProofHash()); // CHECK IF HASH LOWER THEN TARGET if (hashValue.compareTo(target) >= 0) { return false; } // CHECK IF FIRST BLOCK OF USER if (hashValue.compareTo(lowerTarget) < 0) { return false; } if (this.atBytes != null && this.atBytes.length > 0) { try { AT_Block atBlock = AT_Controller.validateATs( this.getBlockATs(), db.getBlockMap().getLastBlock().getHeight(db) + 1, db); this.atFees = atBlock.getTotalFees(); } catch (NoSuchAlgorithmException | AT_Exception e) { e.printStackTrace(); return false; } } // CHECK TRANSACTIONS DBSet fork = db.fork(); for (Transaction transaction : this.getTransactions()) { // CHECK IF NOT GENESISTRANSACTION if (transaction instanceof GenesisTransaction) { return false; } Integer min = 0; if (db.getBlockMap().getParentList() != null) { min = AT_API_Platform_Impl.getForkHeight(db); } // CHECK IF VALID if (transaction instanceof DeployATTransaction) { DeployATTransaction atTx = (DeployATTransaction) transaction; if (atTx.isValid(fork, min) != Transaction.VALIDATE_OK) { return false; } } else if (transaction.isValid(fork) != Transaction.VALIDATE_OK) { return false; } // CHECK TIMESTAMP AND DEADLINE if (transaction.getTimestamp() > this.timestamp || transaction.getDeadline() <= this.timestamp) { return false; } // PROCESS TRANSACTION IN MEMORYDB TO MAKE SURE OTHER TRANSACTIONS VALIDATE PROPERLY transaction.process(fork); } // BLOCK IS VALID return true; }
public void process() { this.process(DBSet.getInstance()); }
public int getHeight(DBSet db) { if (db.getHeightMap().contains(this.getSignature())) return db.getHeightMap().get(this); else return -1; }
public boolean isValid() { return this.isValid(DBSet.getInstance()); }
public int getHeight() { return this.getHeight(DBSet.getInstance()); }
public Block getChild(DBSet db) { return db.getChildMap().get(this); }
public Block getChild() { return this.getChild(DBSet.getInstance()); }
public Block getParent(DBSet db) { return db.getBlockMap().get(this.reference); }
public Block getParent() { return this.getParent(DBSet.getInstance()); }