Block.java的代码:
package noodchain;
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>();//我们的数据将是一个简单的信息。
public long timeStamp;//从1970年1月1日读取的毫秒数
public int nonce;
//块构造函数
public Block(String previousHash) {
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
this.hash = calculateHash();//确保在设置了其他值之前做这步
}
//根据块内容计算新的哈希
public String calculateHash() {
// TODO Auto-generated method stub
String calculatedhash = StringUtil.applySha256(
previousHash + Long.toString(timeStamp)
+Integer.toString(nonce)
+ merkleRoot);
return calculatedhash;
}
//增加nonce值,直到达到散列目标为止。
public void mineBlock(int difficulty) {
merkleRoot = StringUtil.getMerkleRoot(transactions);
String target = StringUtil.getDificultyString(difficulty);//设定难度系数
while(!hash.substring(0,difficulty).equals(target)) {
nonce++;
hash = calculateHash();
}
System.out.println("挖掘出区块:" + hash);
}
//将事务添加到这个块中
public boolean addTransaction(Transaction transaction) {
//处理事务并检查是否有效,除非块是起源块,否则忽略。
if(transaction == null)
return false;
if((!"0".equals(previousHash))) {
if((transaction.processTransaction() != true)) {
System.out.println("事务失败.丢弃。");
return false;
}
}
transactions.add(transaction);
System.out.println("事务成功添加到Block中");
return true;
}
}
NoodChain代码:
package noodchain;
import java.security.Security;
import java.util.ArrayList;
import com.google.gson.*;
import java.util.Base64;
import java.util.HashMap;
public class NoodChain {
public static ArrayList<Block> blockchain = new ArrayList<Block>();
//所有未使用的交易清单。
public static HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>();
public static int difficulty = 2;
public static float minimumTransaction = 0.1f;
public static Wallet walletA;
public static Wallet walletB;
public static Transaction genesisTransaction;
public static void main(String[] args) {
//设置bouncecastle作为安全供应商
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//创建新的钱包
walletA = new Wallet();
walletB = new Wallet();
Wallet coinbase = new Wallet();
//创建genesis事务,向walletA发送100个NoobCoin:
genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
genesisTransaction.generateSignature(coinbase.privateKey);//手工签署起源事务
genesisTransaction.transactionId = "0"; //手动设置事务id
genesisTransaction.outputs.add(new TransactionOutput(genesisTransaction.reciepient, genesisTransaction.value, genesisTransaction.transactionId)); //手动添加事务输出
UTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); //在UTXOs列表中存储第一个事务是很重要的。
System.out.println("创建和挖掘起源块... ");
Block genesis = new Block("0");
genesis.addTransaction(genesisTransaction);
addBlock(genesis);
//测试
Block block1 = new Block(genesis.hash);
System.out.println("\n钱包A的余额: " + walletA.getBalance());
System.out.println("\n钱包A试图向钱包B提供资金40...");
block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f));
addBlock(block1);
System.out.println("\n钱包A的余额: " + walletA.getBalance());
System.out.println("\n钱包B的余额: " + walletB.getBalance());
Block block2 = new Block(block1.hash);
System.out.println("\n钱包A试图发送比它所拥有的更多的资金(1000)...");
block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f));
addBlock(block2);
System.out.println("\n钱包A的余额: " + walletA.getBalance());
System.out.println("\n钱包B的余额: " + walletB.getBalance());
Block block3 = new Block(block2.hash);
System.out.println("\n钱包B正试图向钱包A提供资金20...");
block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20));
System.out.println("\n钱包A的余额: " + walletA.getBalance());
System.out.println("\n钱包B的余额: " + walletB.getBalance());
isChainValid();
}
public static Boolean isChainValid() {
// TODO Auto-generated method stub
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));
//通过区块链循环检查哈希:
for(int i=1; i < blockchain.size(); i++) {
currentBlock = blockchain.get(i);
previousBlock = blockchain.get(i-1);
//比较注册哈希和计算哈希:
if(!currentBlock.hash.equals(currentBlock.calculateHash()) ){
System.out.println("#当前哈希不相等");
return false;
}
//比较之前的哈希和注册之前的哈希
if(!previousBlock.hash.equals(currentBlock.previousHash) ) {
System.out.println("#之前的哈希不相等");
return false;
}
//检查哈希是否被解决
if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
System.out.println("#这个区块尚未开矿");
return false;
}
//循环通过blockchains事务:
TransactionOutput tempOutput;
for(int t=0; t <currentBlock.transactions.size(); t++) {
Transaction currentTransaction = currentBlock.transactions.get(t);
if(!currentTransaction.verifiySignature()) {
System.out.println("#事务签名(" + t + ") 有效");
return false;
}
if(currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
System.out.println("#输入等于交易的输出(" + t + ")");
return false;
}
for(TransactionInput input: currentTransaction.inputs) {
tempOutput = tempUTXOs.get(input.transactionOutputId);
if(tempOutput == null) {
System.out.println("#引用输入到事务(" + t + ") 消失");
return false;
}
if(input.UTXO.value != tempOutput.value) {
System.out.println("#引用输入到事务(" + t + ") 的值有效");
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("#事务(" + t + ") 的输出接受者不应该是谁");
return false;
}
if( currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) {
System.out.println("#事务(" + t + ") 输出“change”不是发送者.");
return false;
}
}
}
System.out.println("区块链有效");
return true;
}
public static void addBlock(Block block) {
// TODO Auto-generated method stub
block.mineBlock(difficulty);
blockchain.add(block);
}
}
StringUtil.java代码:
package noodchain;
import java.security.Key;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import com.google.gson.*;
public class StringUtil {
//将sha256应用于字符串并返回结果。
public static String applySha256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
//将sha-256应用到我们的输入
byte[] hash = digest.digest(input.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();//这将包含哈希值作为16进制
for (int i = 0;i < hash.length;i++) {
String hex = Integer.toHexString(0xff & hash);
if(hex.length()==1)
hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
catch (Exception e) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
//应用ECDSA签名并返回结果(作为字节)。
//理解不了这段没关系
//只需要记住:applyECDSASig接受发送者私钥和字符串输入,对其签名并返回一个字节数组。
//verifyECDSASig接受签名、公钥和字符串数据,如果签名有效,则返回true或false。
//getStringFromKey从任何键返回编码的字符串。
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;
}
//验证签名字符串
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) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
//将对象转换为json字符串的简短助手
public static String getJson(Object o) {
return new GsonBuilder().setPrettyPrinting().create().toJson(o);
}
public static String getStringFromkey(Key key) {
// TODO Auto-generated method stub
return Base64.getEncoder().encodeToString(key.getEncoded());
}
//在事务数组中使用Tacks并返回一个merkle根。
public static String getMerkleRoot(ArrayList<Transaction> transactions) {
int count = transactions.size();
List<String> previousTreeLayer = new ArrayList<String>();
for(Transaction transaction : transactions) {
previousTreeLayer.add(transaction.transactionId);
}
List<String> treeLayer = previousTreeLayer;
while(count > 1) {
treeLayer = new ArrayList<String>();
for(int i=1; i < previousTreeLayer.size(); i+=2) {
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;
}
//返回比较哈希的困难字符串目标。5的难度将返回“00000”
public static String getDificultyString(int difficulty) {
// TODO Auto-generated method stub
return new String(new char[difficulty]).replace('\0', '0');
}
}
Transaction的代码:
package noodchain;
import java.security.*;
import java.util.ArrayList;
public class Transaction {
public String transactionId;//这也是事务的嘻哈
public PublicKey sender;//发送方地址/公钥
public PublicKey reciepient;//接收方地址/公钥
public float value;
public byte[] signature;//这是为了防止别人在我们的钱包里花钱。
public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
private static int sequence = 0;//生成了多少事务的粗略计数。
//构造:
public Transaction(PublicKey from,PublicKey to,float value,ArrayList<TransactionInput>inputs) {
this.sender = from;
this.reciepient = to;
this.value = value;
this.inputs = inputs;
}
//这将计算事务哈希(将用作其Id)
private String calculateHash() {
sequence++;//增加序列以避免两个具有相同哈希的相同事务
return StringUtil.applySha256(
StringUtil.getStringFromkey(sender) +
StringUtil.getStringFromkey(reciepient) +
Float.toString(value) + sequence
);
}
//签上我们不希望被篡改的所有数据。
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromkey(sender) + StringUtil.getStringFromkey(reciepient) + Float.toString(value);
signature = StringUtil.applyECDSASig(privateKey, data);
}
//验证我们签署的数据没有被篡改。
public boolean verifiySignature() {
String data = StringUtil.getStringFromkey(sender) + StringUtil.getStringFromkey(reciepient) + Float.toString(value);
return StringUtil.verifyECDSASig(sender, data, signature);
}
//实际上,您可能想要签署更多的信息,比如所使用的输出/输入和/或时间戳(目前我们只是签署了最低要求)
public boolean processTransaction() {
// TODO Auto-generated method stub
if(verifiySignature() == false) {
System.out.println("#事务签名无法验证");
return false;
}
//收集交易输入(确保未使用):
for(TransactionInput i : inputs) {
i.UTXO = NoodChain.UTXOs.get(i.transactionOutputId);
}
//检查事务是否有效
if(getInputsValue() < NoodChain.minimumTransaction) {
System.out.println("事务输入太小: " + getInputsValue());
System.out.println("请输入大于 " + NoodChain.minimumTransaction);
return false;
}
//生成事务输出:
float leftOver = getInputsValue() - value; //得到输入的值,然后剩下会发生更改:
transactionId = calculateHash();
outputs.add(new TransactionOutput( this.reciepient, value,transactionId)); //值发送给接收方
outputs.add(new TransactionOutput( this.sender, leftOver,transactionId)); //将剩余的“更改”发送回发送方
//将输出添加到未使用的列表
for(TransactionOutput o : outputs) {
NoodChain.UTXOs.put(o.id , o);
}
//删除UTXO列表中的事务输入:
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; // 如果找不到事务,就跳过它
NoodChain.UTXOs.remove(i.UTXO.id);
}
return true;
}
public float getInputsValue() {
// TODO Auto-generated method stub
float total = 0;
for(TransactionInput i : inputs) {
if(i.UTXO == null) continue; //如果找不到事务,则跳过它,这种方法可能不是最佳的.
total += i.UTXO.value;
}
return total;
}
public float getOutputsValue() {
float total = 0;
for(TransactionOutput o : outputs) {
total += o.value;
}
return total;
}
private String calulateHash() {
sequence++; //增加序列以避免两个具有相同散列的相同事务
return StringUtil.applySha256(
StringUtil.getStringFromkey(sender) +
StringUtil.getStringFromkey(reciepient) +
Float.toString(value) + sequence
);
}
}
TransactionInput.java的代码:
package noodchain;
/*这个类将用于引用尚未使用的事务输出。
* transactionOutputId将用于查找相关的事务输出,
* 允许矿工检查您的所有权。
*/
public class TransactionInput {
public String transactionOutputId;//对transactionoutputs的引用-> transactionId
public TransactionOutput UTXO;//包含未使用的事务输出
public TransactionInput(String transactionOutputId) {
this.transactionOutputId = transactionOutputId;
}
}
TransactionOutput.java的代码:
package noodchain;
import java.security.PublicKey;
public class TransactionOutput {
public String id;
public PublicKey reciepient;//也被称为这些硬币的新主人。
public float value;//他们拥有的硬币数量
public String parentTransactionId;//此输出是在其中创建的事务的id
//构造函数
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);
}
//检查这个币是否属于你
public boolean isMine(PublicKey publicKey) {
return (publicKey == reciepient);
}
}
Wallet.java的代码:
package noodchain;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.management.RuntimeErrorException;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
//只有这个钱包里的UTXOs。
public HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>();
public Wallet() {
generateKeyPair();
//关于这个方法,您需要了解的是它使用Java.security。
//密钥对发生器产生一个椭圆曲线密钥对。
//该方法生成并设置我们的公钥和私钥。
}
private void generateKeyPair() {
// TODO Auto-generated method stub
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA","BC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
//初始化密钥生成器并生成密钥对
keyGen.initialize(ecSpec,random);//256个字节提供了可接受的安全级别
KeyPair keyPair = keyGen.generateKeyPair();
//从密钥对中设置公钥和私钥
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
} catch (Exception e) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
public float getBalance() {
float total = 0;
for (Map.Entry<String, TransactionOutput> item: NoodChain.UTXOs.entrySet()){
TransactionOutput UTXO = item.getValue();
if(UTXO.isMine(publicKey)) { //如果输出是我的(如果硬币是我的)
UTXOs.put(UTXO.id,UTXO); //将它添加到我们的未使用事务列表中。
total += UTXO.value ;
}
}
return total;
}
public Transaction sendFunds(PublicKey _recipient,float value ) {
if(getBalance() < value) {
System.out.println("#没有足够的资金进行交易。事务被丢弃。");
return null;
}
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;
}
}
慢慢去看吧 代码能运行,是全的,只不过注意两个地方:
① 这里需要导入一个包, 导入之后可能还是有红色下划线,但可以忽略,好像是版本问题,此时可以运行,只要import com.google.gson.*;有就行了 ② 这里需要导入 这个包,导入之后 一样也许依然标红 但可以运行 运行结果:
|
|