A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

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.*;有就行了

这里需要导入

这个包,导入之后 一样也许依然标红  但可以运行

运行结果:





4 个回复

倒序浏览
回复 使用道具 举报
奈斯,很赞
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马