美文网首页
用Java实现一个简单的区块链

用Java实现一个简单的区块链

作者: jerry的技术与思维 | 来源:发表于2018-10-07 20:29 被阅读0次

    前面的文章,笔者花了不少的文字来介绍区块链的基础概念,以太坊的概念和开发,大家是否感觉区块链开发还是挺复杂的呢。但其实区块链技术本质就是一个分布式账本,在技术上本质就是一个链表。链表里面有一个个的区块,每个区块有自己的数字签名(涉及到加密技术)和交易数据和一些其他的数据,然后把一堆区块串起来就是区块链,同时把区块数据能够进行广播,基本上就完成一个最简单的区块链系统。

    那么今天,笔者用java开发语言来简单实现一个简易版的区块链。

    首先,定义好区块的结构,我们先回顾下一个区块有哪些信息:

    1. 区块hash
    2. 上一个区块hash
    3. 时间戳
    4. 交易信息列表

    其中交易信息(以UTXO模型为例)包括以下信息:

    1. 交易hash
    2. 交易发送者
    3. 交易接受者
    4. 时间戳
    5. 交易额
    6. 签名
    7. 交易输入
    8. 交易输出

    OK,回顾完了区块的数据内容,我们用Java代码编写出来数据结构,很明显我们需要定义两个Java对象:交易和区块。

    看下面的代码

    交易类:Transaction

    public class Transaction {
    
        public String transactionHash; //交易hash
        public PublicKey sender; //发送这地址
        public PublicKey receiver; //接受者地址
        public double value;  //交易额
        public byte[] signature; //签名数据
        public long timeStamp; //时间戳
    
        public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>(); //交易输入
        public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>(); //交易输出
    }
    

    区块类:Block

    public class Block {
       
       public String hash;
       public String previousHash; 
       public List<Transaction> data; //交易列表
       public long timeStamp; //时间戳
     
       
    }
    

    交易输出类:定义UTXO中的交易输出

    public class TransactionOutput {
        public String id;
        public PublicKey receiver; //
        public double value; //接受者的拥有的余额
        public String parentTransactionHash; //产生该交易输出的交易hash
    
        
        public TransactionOutput(PublicKey receiver, float value, String parentTransactionHash) {
            this.receiver = receiver;
            this.value = value;
            this.parentTransactionHash = parentTransactionHash;
            this.id = StringUtil.applySha256(StringUtil.getStringFromKey(receiver)+Float.toString(value)+parentTransactionHash);
        }
    

    交易输入类:

    public class TransactionInput {
        public String transactionOutputId; //交易输出的id
        public TransactionOutput UTXO; //Contains the Unspent transaction output
    
        public TransactionInput(String transactionOutputId) {
            this.transactionOutputId = transactionOutputId;
        }
    }
    

    钱包类:
    钱包包括公钥和私钥,已经余额

    public class Wallet {
    
        public PrivateKey privateKey;
        public PublicKey publicKey;
    
        public HashMap<String,TransactionOutput> UTXOs = new HashMap<String,TransactionOutput>();
    
        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
                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);
            }
        }
    
        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;
        }
    
        public Transaction sendFunds(PublicKey _recipient,float value ) {
            if(getBalance() < value) {
                System.out.println("#Not Enough funds to send transaction. Transaction Discarded.");
                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;
        }
    }
    

    加密工具类:

    public class StringUtil {
    
        public static String applySha256(String input) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
                byte[] hash = digest.digest(input.getBytes("UTF-8"));
                StringBuffer hexString = new StringBuffer();
                for (int i = 0; i < hash.length; i++) {
                    String hex = Integer.toHexString(0xff & hash[i]);
                    if (hex.length() == 1) hexString.append('0');
                    hexString.append(hex);
                }
                return hexString.toString();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
    
        //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);
            }
        }    
    
    }
    

    这个工具类里面就包括我们所需要生成数字签名函数,有很多种的加密算法来生成数字签名。这里我们就选择SHA256。

    定义完成了基本的数据结果,我们编写一个main函数跑起来。

    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();
    
        }
    }
    

    相关文章

      网友评论

          本文标题:用Java实现一个简单的区块链

          本文链接:https://www.haomeiwen.com/subject/elvlaftx.html