?本篇是該系列的第二篇钉迷,你可以在這里找到第一篇。原文鏈接在此篷朵。
?在第本篇勾怒,我們將
- 創(chuàng)建一個簡單的錢包
- 在我們的區(qū)塊鏈上簽發(fā)一個交易。
?上面的這些過程声旺,其實就產(chǎn)生了我們自己的加密貨幣笔链。
?在上一篇文章中,我們有了一個可驗證的腮猖、基本的區(qū)塊鏈鉴扫。但是,我們的鏈中僅僅存儲了一些無用的信息澈缺。今天坪创,我們將把這些無用的信息替換為交易數(shù)據(jù)炕婶。這允許我們可以創(chuàng)建一個簡單的加密貨幣,我們稱之為 “NoobCoin”莱预。
?本文中柠掂,還將使用 Bouncy Castle 和 GSON 庫。
1. 準備錢包
?在加密貨幣中依沮,貨幣的所有權(quán)在區(qū)塊鏈上以交易的形式流轉(zhuǎn)
涯贞,交易者擁有一個可以地址可以轉(zhuǎn)入轉(zhuǎn)出。因此危喉,對于一個錢包而言肩狂,至少需要能夠存儲這些地址。更多的姥饰,錢包也可以作為一個軟件用以在區(qū)塊鏈上產(chǎn)生新的交易傻谁。
?現(xiàn)在,讓我們倆創(chuàng)建一個持有我們公鑰和私鑰的 Wallet
類:
package noobchain;
import java.security.PrivateKey;
import java.security.PublicKey;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey ;
}
?公鑰和私鑰有什么用列粪?
?對于我們的加密貨幣 noobcoin
來說审磁,公鑰扮演者我們的錢包地址的角色,我們可以隨意分享自己的公鑰岂座。私鑰态蒂,是用來 簽署(sign) 交易的,沒有人能花費我們的 noobcoin费什,除非钾恢,他擁有我們的私鑰。所以鸳址,用戶的私鑰一定要被保管好瘩蚪。公鑰部分通常會和交易一起發(fā)送的,用以驗證交易前面是否合法稿黍,交易內(nèi)容是否被篡改疹瘦。
?公鑰和私鑰通過 KeyPair 生成,接下來巡球,我們將使用 橢圓曲線加密算法 來生成密鑰對言沐。
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey ;
public Wallet(){
generateKeyPair();
}
public void generateKeyPair() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA","BC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
// Initialize the key generator and generate a KeyPair
keyGen.initialize(ecSpec, random); //256 bytes provides an acceptable security level
KeyPair keyPair = keyGen.generateKeyPair();
// Set the public and private keys from the keyPair
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
}catch(Exception e) {
throw new RuntimeException(e);
}
}
}
?現(xiàn)在,我們的錢包類差不多了酣栈,接下來险胰,讓我們來看看交易。
2. 交易和簽名
?每一個交易矿筝,都要攜帶一定的數(shù)據(jù):
- 發(fā)送者資金的公鑰(譯注:相當發(fā)送者的地址)
- 接受者資金的公鑰(譯注:相當于接受者的地址)
- 交易資金的數(shù)目
- inputs起便,證明發(fā)送者有足夠的幣發(fā)送
- outputs,接收地址收到的總金額
- 加密簽名,保證發(fā)送者簽署的交易不會被惡意篡改
?現(xiàn)在缨睡,我們來創(chuàng)建一個 Transaction 類:
import java.security.*;
import java.util.ArrayList;
public class Transaction {
public String transactionId; // this is also the hash of the transaction.
public PublicKey sender; // senders address/public key.
public PublicKey reciepient; // Recipients address/public key.
public float value;
public byte[] signature; // this is to prevent anybody else from spending funds in our wallet.
public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
private static int sequence = 0; // a rough count of how many transactions have been generated.
// Constructor:
public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
this.sender = from;
this.reciepient = to;
this.value = value;
this.inputs = inputs;
}
// This Calculates the transaction hash (which will be used as its Id)
private String calulateHash() {
sequence++; //increase the sequence to avoid 2 identical transactions having the same hash
return StringUtil.applySha256(
StringUtil.getStringFromKey(sender) +
StringUtil.getStringFromKey(reciepient) +
Float.toString(value) + sequence
);
}
}
?此時,inputs 和 outputs 都是空的陈辱,后面我們會使用到它們奖年。這個 Transaction 類還將包含生成、驗證簽名沛贪、驗證交易等相關(guān)方法陋守。但是,簽名的目的是是什么利赋?它們是如何工作的水评?
簽名的目的和工作原理
?簽名
在區(qū)塊鏈中執(zhí)行了兩個非常重要的任務(wù):首先,允許擁有者們消費他們的幣媚送,它會放置交易信息被篡改中燥。
?私鑰被用來簽署數(shù)據(jù),公鑰被用來驗證數(shù)據(jù)的完整性塘偎。
舉個栗子:Bob 想向 Sally 轉(zhuǎn) 2 個 NoobCoin疗涉,因此,錢包軟件會生成這個交易吟秩,然后將這個交易提交給礦工咱扣,以將該數(shù)據(jù)包含連接到區(qū)塊鏈中。如果礦工企圖將這 2 個幣的接收人改為 John涵防。然而闹伪,幸運的是 Bob 使用私鑰簽署了這個交易數(shù)據(jù),并且允許任何人使用他的公鑰驗證該交易的完整性壮池、合法性偏瓤。
?從上面的代碼中,我們可以看到椰憋,所謂簽名實際上一個 byte 數(shù)組硼补,因此,下面讓我們來生成它熏矿。首先已骇,這里需要一個 StringUtil
類:
//Applies ECDSA Signature and returns the result ( as bytes ).
public static byte[] applyECDSASig(PrivateKey privateKey, String input) {
Signature dsa;
byte[] output = new byte[0];
try {
dsa = Signature.getInstance("ECDSA", "BC");
dsa.initSign(privateKey);
byte[] strByte = input.getBytes();
dsa.update(strByte);
byte[] realSig = dsa.sign();
output = realSig;
} catch (Exception e) {
throw new RuntimeException(e);
}
return output;
}
//Verifies a String signature
public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
try {
Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(data.getBytes());
return ecdsaVerify.verify(signature);
}catch(Exception e) {
throw new RuntimeException(e);
}
}
public static String getStringFromKey(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
?現(xiàn)在,在 Transaction
類的 generateSignature() 和 verifySignature() 方法中運用該簽名方法票编。
//Signs all the data we dont wish to be tampered with.
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
signature = StringUtil.applyECDSASig(privateKey,data);
}
//Verifies the data we signed hasnt been tampered with
public boolean verifiySignature() {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
return StringUtil.verifyECDSASig(sender, data, signature);
}
?當該交易被礦工添加到區(qū)塊鏈上的一個新塊的時候褪储,這個簽名將會被驗證。
3.測試錢包和簽名
?現(xiàn)在慧域,我們基本上已經(jīng)完成了一半的工作鲤竹。在 NoobChain
這個類中,添加一些新的變量,并替換到 main 方法中的一些方法辛藻。
import java.security.Security;
import java.util.ArrayList;
import java.util.Base64;
import com.google.gson.GsonBuilder;
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;
public static void main(String[] args) {
//Setup Bouncey castle as a Security Provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//Create the new wallets
walletA = new Wallet();
walletB = new Wallet();
//Test public and private keys
System.out.println("Private and public keys:");
System.out.println(StringUtil.getStringFromKey(walletA.privateKey));
System.out.println(StringUtil.getStringFromKey(walletA.publicKey));
//Create a test transaction from WalletA to walletB
Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
transaction.generateSignature(walletA.privateKey);
//Verify the signature works and verify it from the public key
System.out.println("Is signature verified");
System.out.println(transaction.verifiySignature());
}
?現(xiàn)在碘橘,我們創(chuàng)建了兩個錢包,walletA 和 walletB吱肌,并且打印了 walletA 的私鑰和公鑰痘拆。你能看到的大概是這樣子的
?回過頭來看,現(xiàn)在我們需要創(chuàng)建/驗證 outputs 和 inputs氮墨,并且將他們存儲到區(qū)塊鏈上的交易上纺蛆。
4. Inputs 和 Outputs
1. 加密貨幣的歸屬
?如果你要擁有一個 bitcoin,首先你要收到一個 bitcoin规揪。這個過程桥氏,并非是在總賬單上將你的 bitcoin 加一 ,將發(fā)送者的 bitcoin 減一的過程猛铅。事實上字支,發(fā)送者肯定是前面也接收到的一個 bitcoin,然后才能將其發(fā)送到你的地址上奸忽。
?錢包的余額祥款,是所有跟你地址(公鑰)相關(guān)的未花費出去的交易輸出的總和。(譯注:后面將會看到月杉,實際上這個鏈會維護一個由 publicKey 做 key刃跛,TransactionOutput 做 value 的 HashMap,這個 map 是所有交易輸出的記錄苛萎,通過 publicKey 可以查找到關(guān)于其擁有者的 bitcoin 數(shù)量)
?接下來桨昙,我們遵從 bitcoin 慣例,將未花費出去的交易輸出命名為:UTXO.
?現(xiàn)在腌歉,我們來創(chuàng)建 TransactionInput 類:
public class TransactionInput {
public String transactionOutputId; //Reference to TransactionOutputs -> transactionId
public TransactionOutput UTXO; //Contains the Unspent transaction output
public TransactionInput(String transactionOutputId) {
this.transactionOutputId = transactionOutputId;
}
}
?然后蛙酪,創(chuàng)建 TransactionOutputs 類:
import java.security.PublicKey;
public class TransactionOutput {
public String id;
public PublicKey reciepient; //also known as the new owner of these coins.
public float value; //the amount of coins they own
public String parentTransactionId; //the id of the transaction this output was created in
//Constructor
public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) {
this.reciepient = reciepient;
this.value = value;
this.parentTransactionId = parentTransactionId;
this.id = StringUtil.applySha256(StringUtil.getStringFromKey(reciepient)+Float.toString(value)+parentTransactionId);
}
//Check if coin belongs to you
public boolean isMine(PublicKey publicKey) {
return (publicKey == reciepient);
}
}
?通過 Transaction outputs 可以獲取到交易雙方通過交易獲取到的各自 bitcoin 的總數(shù)。因此翘盖,它也可以作為新交易的 inputs桂塞,以證明你有足夠多的幣用以交易。
2. 處理交易
?鏈上的區(qū)塊馍驯,可能包含了很多交易阁危,區(qū)塊鏈可能會很長很長很長很長,也因此汰瘫,在處理新塊的時候狂打,可能會花費很長很長的時間,因為我們需要查找并檢查它的輸入混弥。為了繞過這一點趴乡,我們將使用一個額外的集合,以保存未花費的交易。在 NoobChain
中晾捏,我們通過 UTXO
表示:
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>(); //list of all unspent transactions.
public static int difficulty = 5;
public static Wallet walletA;
public static Wallet walletB;
public static void main(String[] args) {
......
?ok蒿涎,現(xiàn)在是時候來揭曉事情的真相了。
?讓我們把所有事情集中起來惦辛,在 Transactoin
處理:
//Returns true if new transaction could be created.
public boolean processTransaction() {
if(verifiySignature() == false) {
System.out.println("#Transaction Signature failed to verify");
return false;
}
//gather transaction inputs (Make sure they are unspent):
for(TransactionInput i : inputs) {
i.UTXO = NoobChain.UTXOs.get(i.transactionOutputId);
}
//check if transaction is valid:
if(getInputsValue() < NoobChain.minimumTransaction) {
System.out.println("#Transaction Inputs to small: " + getInputsValue());
return false;
}
//generate transaction outputs:
float leftOver = getInputsValue() - value; //get value of inputs then the left over change:
transactionId = calulateHash();
outputs.add(new TransactionOutput( this.reciepient, value,transactionId)); //send value to recipient
outputs.add(new TransactionOutput( this.sender, leftOver,transactionId)); //send the left over 'change' back to sender
//add outputs to Unspent list
for(TransactionOutput o : outputs) {
NoobChain.UTXOs.put(o.id , o);
}
//remove transaction inputs from UTXO lists as spent:
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //if Transaction can't be found skip it
NoobChain.UTXOs.remove(i.UTXO.id);
}
return true;
}
//returns sum of inputs(UTXOs) values
public float getInputsValue() {
float total = 0;
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //if Transaction can't be found skip it
total += i.UTXO.value;
}
return total;
}
//returns sum of outputs:
public float getOutputsValue() {
float total = 0;
for(TransactionOutput o : outputs) {
total += o.value;
}
return total;
}
?使用這個方法劳秋,我們執(zhí)行一些檢查,確保交易的合法性裙品,接著,收集輸入俗或,并產(chǎn)生輸出市怎。
?很重要的一點,在結(jié)尾處辛慰,我們從 UTXO 中刪除了 Inputs区匠。這意味著 transaction output 僅有一次機會作為輸入...所有輸入值,都將在本次交易中被使用帅腌,如果沒有使用完驰弄,剩余的部分會返回到自身中。
?最后速客,讓我們來更新下錢包:
- 計算余額(通過對 UTXO 循環(huán)戚篙,計算屬于“我”的余額,判斷是否有足夠的余額進行交易 )溺职。
- 產(chǎn)生新的 transaction(交易)岔擂。
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
public HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>(); //only UTXOs owned by this wallet.
public Wallet() {...
public void generateKeyPair() {...
//returns balance and stores the UTXO's owned by this wallet in this.UTXOs
public float getBalance() {
float total = 0;
for (Map.Entry<String, TransactionOutput> item: NoobChain.UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
if(UTXO.isMine(publicKey)) { //if output belongs to me ( if coins belong to me )
UTXOs.put(UTXO.id,UTXO); //add it to our list of unspent transactions.
total += UTXO.value ;
}
}
return total;
}
//Generates and returns a new transaction from this wallet.
public Transaction sendFunds(PublicKey _recipient,float value ) {
if(getBalance() < value) { //gather balance and check funds.
System.out.println("#Not Enough funds to send transaction. Transaction Discarded.");
return null;
}
//create array list of inputs
ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
float total = 0;
for (Map.Entry<String, TransactionOutput> item: UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
total += UTXO.value;
inputs.add(new TransactionInput(UTXO.id));
if(total > value) break;
}
Transaction newTransaction = new Transaction(publicKey, _recipient , value, inputs);
newTransaction.generateSignature(privateKey);
for(TransactionInput input: inputs){
UTXOs.remove(input.transactionOutputId);
}
return newTransaction;
}
}
6. 將交易添加到區(qū)塊中
?現(xiàn)在,我們有了一個可以工作的交易系統(tǒng)了浪耘,接下來乱灵,需要將其實現(xiàn)到區(qū)塊鏈上。現(xiàn)在七冲,我們可以將以前那些無用的數(shù)據(jù)替換為 ArrayList of transactions(交易列表)痛倚,同時,使用根哈希的方式澜躺,計算區(qū)塊的哈希值蝉稳。
?下面,在 StringUtil 中增加一個獲取根哈希(譯注:可以參考 維基百科)的方法:
//Tacks in array of transactions and returns a merkle root.
public static String getMerkleRoot(ArrayList<Transaction> transactions) {
int count = transactions.size();
ArrayList<String> previousTreeLayer = new ArrayList<String>();
for(Transaction transaction : transactions) {
previousTreeLayer.add(transaction.transactionId);
}
ArrayList<String> treeLayer = previousTreeLayer;
while(count > 1) {
treeLayer = new ArrayList<String>();
for(int i=1; i < previousTreeLayer.size(); i++) {
treeLayer.add(applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i)));
}
count = treeLayer.size();
previousTreeLayer = treeLayer;
}
String merkleRoot = (treeLayer.size() == 1) ? treeLayer.get(0) : "";
return merkleRoot;
}
?再接下來掘鄙,進行 Block 中的修改:
import java.util.ArrayList;
import java.util.Date;
public class Block {
public String hash;
public String previousHash;
public String merkleRoot;
public ArrayList<Transaction> transactions = new ArrayList<Transaction>(); //our data will be a simple message.
public long timeStamp; //as number of milliseconds since 1/1/1970.
public int nonce;
//Block Constructor.
public Block(String previousHash ) {
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
this.hash = calculateHash(); //Making sure we do this after we set the other values.
}
//Calculate new hash based on blocks contents
public String calculateHash() {
String calculatedhash = StringUtil.applySha256(
previousHash +
Long.toString(timeStamp) +
Integer.toString(nonce) +
merkleRoot
);
return calculatedhash;
}
//Increases nonce value until hash target is reached.
public void mineBlock(int difficulty) {
merkleRoot = StringUtil.getMerkleRoot(transactions);
String target = StringUtil.getDificultyString(difficulty); //Create a string with difficulty * "0"
while(!hash.substring( 0, difficulty).equals(target)) {
nonce ++;
hash = calculateHash();
}
System.out.println("Block Mined!!! : " + hash);
}
//Add transactions to this block
public boolean addTransaction(Transaction transaction) {
//process transaction and check if valid, unless block is genesis block then ignore.
if(transaction == null) return false;
if((previousHash != "0")) {
if((transaction.processTransaction() != true)) {
System.out.println("Transaction failed to process. Discarded.");
return false;
}
}
transactions.add(transaction);
System.out.println("Transaction Successfully added to Block");
return true;
}
}
?注意颠区,我們更新了 Block 的構(gòu)造器,不在傳入一個字符串通铲,并且添加了用于計算哈希值的 merkle root(根哈希)屬性毕莱。
?addTransaction 方法將返回一個 boolean,以表示交易是否成功。
7. 華麗的落幕
?最后朋截,我們還需要測試從錢包中消費 noobcoin 蛹稍,更新區(qū)塊鏈合法性檢測。但是部服,首先唆姐,我們還是需要引入這些新的 coins。其實廓八,還是有很多方式引入新的 coins奉芦,比如在比特幣區(qū)塊鏈上,新幣可以作為對礦工挖到礦的獎勵剧蹂。在本文中声功,我們將采用直接在創(chuàng)世區(qū)塊中釋放所有幣的方式。
?我們來更新下 NoobChain
類:
- 創(chuàng)世區(qū)塊將向 walletA 發(fā)放 100 個 Noobcoins.
- 當交易發(fā)生后宠叼,檢測區(qū)塊鏈的合法性
- 通過一些交易先巴,測試是否一切工作 ok
public class NoobChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
public static HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>();
public static int difficulty = 3;
public static float minimumTransaction = 0.1f;
public static Wallet walletA;
public static Wallet walletB;
public static Transaction genesisTransaction;
public static void main(String[] args) {
//add our blocks to the blockchain ArrayList:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); //Setup Bouncey castle as a Security Provider
//Create wallets:
walletA = new Wallet();
walletB = new Wallet();
Wallet coinbase = new Wallet();
//create genesis transaction, which sends 100 NoobCoin to walletA:
genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
genesisTransaction.generateSignature(coinbase.privateKey); //manually sign the genesis transaction
genesisTransaction.transactionId = "0"; //manually set the transaction id
genesisTransaction.outputs.add(new TransactionOutput(genesisTransaction.reciepient, genesisTransaction.value, genesisTransaction.transactionId)); //manually add the Transactions Output
UTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); //its important to store our first transaction in the UTXOs list.
System.out.println("Creating and Mining Genesis block... ");
Block genesis = new Block("0");
genesis.addTransaction(genesisTransaction);
addBlock(genesis);
//testing
Block block1 = new Block(genesis.hash);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("\nWalletA is Attempting to send funds (40) to WalletB...");
block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f));
addBlock(block1);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block2 = new Block(block1.hash);
System.out.println("\nWalletA Attempting to send more funds (1000) than it has...");
block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f));
addBlock(block2);
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
Block block3 = new Block(block2.hash);
System.out.println("\nWalletB is Attempting to send funds (20) to WalletA...");
block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20));
System.out.println("\nWalletA's balance is: " + walletA.getBalance());
System.out.println("WalletB's balance is: " + walletB.getBalance());
isChainValid();
}
public static Boolean isChainValid() {
Block currentBlock;
Block previousBlock;
String hashTarget = new String(new char[difficulty]).replace('\0', '0');
HashMap<String,TransactionOutput> tempUTXOs = new HashMap<String,TransactionOutput>(); //a temporary working list of unspent transactions at a given block state.
tempUTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0));
//loop through blockchain to check hashes:
for(int i=1; i < blockchain.size(); i++) {
currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
//compare registered hash and calculated hash:
if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){
System.out.println("#Current Hashes not equal");
return false;
}
//compare previous hash and registered previous hash
if(!previousBlock.hash.equals(currentBlock.previousHash) ) {
System.out.println("#Previous Hashes not equal");
return false;
}
//check if hash is solved
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("#This block hasn't been mined");
return false;
}
//loop thru blockchains transactions:
TransactionOutput tempOutput;
for(int t=0; t <currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);
if(!currentTransaction.verifiySignature()) {
System.out.println("#Signature on Transaction(" + t + ") is Invalid");
return false;
}
if(currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#Inputs are note equal to outputs on Transaction(" + t + ")");
return false;
}
for(TransactionInput input: currentTransaction.inputs) {
tempOutput = tempUTXOs.get(input.transactionOutputId);
if(tempOutput == null) {
System.out.println("#Referenced input on Transaction(" + t + ") is Missing");
return false;
}
if(input.UTXO.value != tempOutput.value) {
System.out.println("#Referenced input Transaction(" + t + ") value is Invalid");
return false;
}
tempUTXOs.remove(input.transactionOutputId);
}
for(TransactionOutput output: currentTransaction.outputs) {
tempUTXOs.put(output.id, output);
}
if( currentTransaction.outputs.get(0).reciepient != currentTransaction.reciepient) {
System.out.println("#Transaction(" + t + ") output reciepient is not who it should be");
return false;
}
if( currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) {
System.out.println("#Transaction(" + t + ") output 'change' is not sender.");
return false;
}
}
}
System.out.println("Blockchain is valid");
return true;
}
public static void addBlock(Block newBlock) {
newBlock.mineBlock(difficulty);
blockchain.add(newBlock);
}
}
?你可以在 Github 上下載到這個項目。