public byte[] getProofHash() { if (this.version < 3) { return Crypto.getInstance().digest(this.generatorSignature); } else { // newSig = sha256(prevSig || pubKey) byte[] data = Bytes.concat(this.reference, generator.getPublicKey()); return Crypto.getInstance().digest(data); } }
public boolean isSignatureValid() { // VALIDATE BLOCK SIGNATURE byte[] data = new byte[0]; // WRITE PARENT GENERATOR SIGNATURE byte[] generatorSignature = Arrays.copyOfRange(this.reference, 0, GENERATOR_SIGNATURE_LENGTH); data = Bytes.concat(data, generatorSignature); // WRITE GENERATING BALANCE byte[] baseTargetBytes = Longs.toByteArray(this.generatingBalance); data = Bytes.concat(data, baseTargetBytes); // WRITE GENERATOR byte[] generatorBytes = Bytes.ensureCapacity(this.generator.getPublicKey(), GENERATOR_LENGTH, 0); data = Bytes.concat(data, generatorBytes); if (!Crypto.getInstance() .verify(this.generator.getPublicKey(), this.generatorSignature, data)) { return false; } // VALIDATE TRANSACTIONS SIGNATURE data = this.generatorSignature; for (Transaction transaction : this.getTransactions()) { // CHECK IF TRANSACTION SIGNATURE IS VALID if (!transaction.isSignatureValid()) { return false; } // ADD SIGNATURE TO DATA data = Bytes.concat(data, transaction.getSignature()); } if (!Crypto.getInstance() .verify(this.generator.getPublicKey(), this.transactionsSignature, data)) { return false; } return true; }
@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)); }