美文网首页
翻译—区块链(二):用Java创建你的第一个区块链—交易

翻译—区块链(二):用Java创建你的第一个区块链—交易

作者: 过来摸摸头丶 | 来源:发表于2018-08-14 11:08 被阅读0次

    (PS:part3 2月份儿后就见不到影子了…)

    第二篇文章我们将做以下:

    · 创建一个简单的钱包

    · 使用我们的区块链发送已签名的交易

    · 感受更加酷的东西

    以上将会产生我们自己的加密货币(sorta)

    继续我们上篇的,我们已经有了基本的可校验的区块链。但是我们目前的链仅仅存储了相当无用的信息。今天我们打算用交易代替这些无用信息(我们的区块可以容纳多种交易),允许我们创建一个非常简单的加密货币。我们把它取一个新货币的名字"Noob"币。

    · Dependencies:你需要导入包bounceycastle

    准备一个钱包

    在加密货币中,硬币的所有权被转移到了区块链上用来交易,参与者有一个可以进行虚拟货币交易的网址。在他们的基本形式中,钱包仅仅能存储这些网址,但大多数钱包也是一种软件,能够在区块链上做新的交易。

    image.png

    然后让我们创建一个Wallet类来支持我们的公钥和私钥:

    package noobchain;
    import java.security.*;
    
    public class Wallet {
        public PrivateKey privateKey;
        public PublicKey publicKey;
    }
    

    用公钥和私钥来做什么呢?

    对于我们的"Noob"币来说,公钥将作为我们的地址。在与其他人交易的时候分享公钥是可以的。我们的私钥被用来签名我们的交易,来保证没有人可以用他们的私钥来花费我们的"Noob"币。用户必须保存他们自己的私钥!我们还将公钥和交易一起发送,他可以用来验证我们的签名是否有效、数据是否被修改。

    image.png

    我们使用KeyPair类来生成我们的公钥和私钥。使用Elliptic-curve cryptography来生成KeyPairs。让我们在Wallet类中添加generateKeyPair()方法并在构造器中调用它。

    package noobchain;
    import java.security.*;
    
    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);
            }
        }
        
    }
    

    现在我们的Wallet有一个轮廓了,让我们在交易中看看。

    交易 & 签名

    每个交易都携带以下数据:

    · 发送者基金的公钥地址

    · 接受者基金的公钥地址

    · 要交易的价值/基金数量

    · 输入:是交易之前的参考(证明发送方有基金去交易)

    · 输出:表明相关地址接受到的交易量/价值。(这些输出会在一个新的交易中被作为输入)

    · 一个加密的签名,证明地址的所有者发送此交易只有一次,数据没有修改过(例如:防止第三方修改交易的数量)

    接下来创建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
                    );
        }
    }
    

    我们还应该有空的TransactionInput类和TransactionOutput类。不用担心我们将在之后补充它们。

    我们的Transaction类还包含了相关的生成/验证签名的方法和验证交易的方法。

    但是……?

    签名的目的是什么?它怎样工作的?

    签名有两个非常重要的任务在我们的区块链中:第一,它只允许所有者消费他们的虚拟币,第二,他们在新的区块被挖掘出之前防止其他人篡改已经提交的交易。(在切入点?at the point of entry)。

    例如:Bob想要发送2个币给Sally,然后他们的钱包软件会生成这个交易,把它提交给矿工并传入到下一个区块中。矿工试图把2个币的收件人改成John。然而,幸运的是,Bob已经用他的私钥签名了交易数据,允许任何人验证交易数据是否被Bob用他的公钥修改了。(因为没有其他人的公钥就可以验证交易)。

    我们可以从之前的Block类中看到,我们签名是一串字节数组,所以,让我们创建一个生成他们的方法。首先,我们需要一些辅助功能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());
        }
    

    现在,让我们利用这些签名方法在Transaction类中,添加generateSianature()和verifySignature()方法。

    当一个新当交易被添加到区块中到时候,签名将会由矿工去验证。

    测试 Wallets 和 Signatures

    现在我们几乎做了一半让我们测试一些东西。在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());
            
        }
    

    我们创建了两个wallets类walletA和walletB,打印walletA的私钥和公钥。生成一个交易并使用它的私钥进行签名。

    你的输出应该是以下结果:

    image.png

    (Time to pat your self on the back.)现在我们仅仅需要创建/验证输出和输入,然后把交易存储在区块链中。

    如何拥有加密货币…?

    为了拥有1个比特币,你必须收到1个比特币。账簿不是真的给你添加了1个比特币并且给发送者减少了1个比特币,然后交易输出1个比特币被发送到你的地址了。(交易本次输入参考于上一个交易的输出)。

    你的钱包余额是发给你的所有未消费的交易输出的总和。

    从这一点开始,我们将遵循比特币约定并调用未消费的交易输出:UTXO。

    然后我们创建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;
        }
    }
    

    TransactionOutput类:

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

    交易输出将显示发送给每一方后的最终金额。当在新交易中作为输入饮用时,将会作为你要发送硬币的证明。

    输入 & 输出2:进行交易…

    区块链可能接受许多交易并且这条链可能是非常非常长的。它可能需要很长的时间来处理新的交易,因为我们必须找到并检查它的输入。为了解决这个问题,我们额外收集所有可用作输入的为消费的交易。在NoobChain类中添加如下UTXO集合:

    public class NoobChain {
        
        public static ArrayList<Block> blockchain = new ArrayList<Block>();
        public static HashMap<String,TransactionOutputs> UTXOs = new HashMap<String,TransactionOutputs>(); //list of all unspent transactions. 
        public static int difficulty = 5;
        public static Wallet walletA;
        public static Wallet walletB;
    
        public static void main(String[] args) {    
    

    是时候深入了解细节了…

    好的,是时候让我们把所有东西放在一起来处理交易了,在我们的Transaction类中使用boolean类型的processTransaction方法来处理细节:

    //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;
        }
    

    使用这个方法,我们执行一些检查以确保交易有效,然后收集输入并生成输出(查看代码中的注释行以获得更多信息)。

    重要的一步,最后,我们从UTXO列表中丢弃输入,意味着交易输出只能用作输入一次。因此必须使用输入的完整值,所以发送者发送"change"反馈给自己。

    image.png

    最后我们修改以下Wallet类:

    · 收集我们的余额(通过循环UTXO列表检查交易输出isMine)

    · 生成我们的交易…

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

    给我们的区块添加交易

    现在我们有一个可用的交易系统了,我们需要把它结合在我们的区块链中。我们应用一个交易的集合替换掉无用的数据。但是在单个区块中可能有1000个交易,我们的hash计算包含的内容太多了…不过不用担心,我们可以使用交易的merkleroot。

    让我们在StringUtil类中添加一个helper方法去生成merkleroot。

    //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类中做一些修改(merkroot的修改):

    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类构造器,因为我们不再需要String类型的data,并在计算hash的方法中添加上merkleroot。

    我们有一个boolean的addTransaction方法添加交易并返回这个交易是否添加成功。

    最后一步

    我们应该测试一下用钱包发送币,并修改我们区块链的校验方法。首先我们需要一个方法把新币引入进来。有很多办法去创造新币,例如比特币中:矿工把交易包含在内作为每个开采新区块的奖励。

    就目前而言,我们将在第一个区块(创世块)中释放我们所希望拥有的所有货币。就像比特币一样,我们会对创世块进行硬编码。

    修改我们的NoobChain类为他所需要的:

    · 一个创世块释放了100个Noob币给walletA

    · 考虑到交易的账户修改链的校验

    · 用一些交易测试来看看每个东西是不是在正常运行

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

    输出为以下这样:

    image.png

    在你的区块链中钱包现在可以安全的发送货币了,只要他们有资金发送即可。这意味着你拥有资金的本地加密货币了。

    恭喜你完成了区块链上的交易

    你已经成功的创建了自己的加密货币(有点!sort of!),你的区块链现在的状态是:

    · 允许用户用new wallet()创建钱包。

    · 提供有用Elliptic-Curve加密的公钥和私钥的钱包。

    · 通过使用数字签名算法证明所有权,确认资金转移。

    · 允许用户在你的区块链上使用交易:Block.addTransaction(walletA.sendFunds( walletB.publicKey, 20))


    你可以在这里下载作者的源码

    原文链接:https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce

    相关文章

      网友评论

          本文标题:翻译—区块链(二):用Java创建你的第一个区块链—交易

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