使用java创建一个模拟区块链的理解

使用到的.java文件及其用处

Block.java 生成一个新的区块
NoobChain.java 将生成的区块穿成链状,就是区块链
StringUtil.java 对各种字符串的操作,包括生成hashcode,包括生成merkle树
Tansaction.java 这是一个交易类,生成一个新交易
TransactionInput.java 交易输入类,比特币规定每一笔新的交易的输入必须是某笔交易未花费的输出
TransactionOutput.java 交易输出类
Wallet.java 使用者的钱包,在这里,钱包就相当于进行交易的人

1、看看每一个文件的代码,而且他们都有什么具体功能:

Block.java:

package My_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>();//数据为交易的列表
	//private String data;
	private long timeStamp;//时间戳
	private int nonce;
	
	public Block(String previousHash) {
		this.previousHash=previousHash;
		this.timeStamp=new Date().getTime();//返回一个long型的毫秒数,1970年01月01日8点以来的毫秒数
		this.hash=calculateHash();
	}
	
	public String calculateHash() {
		//String calculatedHash=StringUtil.applySha256(previousHash+Long.toString(timeStamp)+Integer.toString(nonce)+data);
		String calculatedHash=StringUtil.applySha256(previousHash+Long.toString(timeStamp)+Integer.toString(nonce)+merkleRoot);//生成这个区块的hash值
		return calculatedHash;
	}
	
	public void mineBlock(int difficulty) {
		//创建一个string值由难度的位数决定
		merkleRoot=StringUtil.getMerkleRoot(transactions);//对于这个交易列表求出一个merklehash值
		String target=new String(new char[difficulty]).replace('\0', '0');//创建一个字符串前几个字符全是0
		while(!hash.substring(0,difficulty).equals(target)) {//string的substring方法将字符串从0-difficulty索引之间的子字符串拿出来
			nonce++;
			hash=calculateHash();
		}
		System.out.println("Block Mined!!!!! : "+hash);
	}
	
	public boolean addTransaction(Transaction transaction) {
		//处理交易并检查是否有效,创世纪区块将被忽略
		if(transaction==null) {
			return false;
		}
		if(previousHash!="0") {
			if(transaction.processTransaction()!=true) {//在这里通过交易信息进行交易
				System.out.println("Transaction failed to proess.Discarded");
				return false;
			}
		}
		transactions.add(transaction);//向交易信息列表中添加这次交易
		System.out.println("Transaction Successfully added to Block");
		return true;
	}
}

Block.java文件主要功能就是生成这个区块的信息,这个区块的hash值是上个区块的hash值+一个随机数+当前的时间戳+这个区块所存储的交易信息对应的哈希值merklehash通过sha256加密生成一个新的字符串,这个字符串就是当前区块的hash值。在calculateHash()函数中调用StringUtil类中的方法生成新hash值。

是否可以创建一个区块的条件是:计算机进行一种特殊的算法计算,当计算出来的hash值的前几位0的个数等于设置的难度梯度要求的0的个数是,这个区块将被创建出来,并且添加到区块链中

NoobChain.java:

package My_Block;

import java.security.Security;
import java.util.ArrayList;
import java.util.HashMap;

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 = 6;
	public static double time = 0;
	public static float minimumTransaction=0.1f;
	public static Wallet walletA;
	public static Wallet walletB;
	public static Transaction genesisTransaction;
	


	public static void main(String[] args) {
		// 调用Bouncey castle作为安全性的提供类
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		// 创建两个钱包
		walletA = new Wallet();
		walletB = new Wallet();
		Wallet coinbase=new Wallet();//创建一对私钥和公钥
		
		//创建创世纪交易,将100个货币发给walletA
		genesisTransaction=new Transaction(coinbase.publicKey,walletA.publicKey,100f,null);
		genesisTransaction.generateSignature(coinbase.privateKey);//对创世纪交易进行签名
		genesisTransaction.transactionID="0";//默认设置创世纪交易的输入为0
		//手动添加交易输出
		genesisTransaction.outputs.add(new TransactionOutput(genesisTransaction.reciepient,genesisTransaction.value,genesisTransaction.transactionID));
		UTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0));//保存我们第一个交易到UTXO列表中
		System.out.println("Creating and Mining Genesis block..");
		Block genesis=new Block("0");
		genesis.addTransaction(genesisTransaction);
		addBlock(genesis);//进行区块的挖取
		
		//测试
		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));//sendFunds返回的是一个创建的交易
		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;// public类型的Block类型可以在包内任意地方访问
		Block previousBlock;
		String hashTarget=new String(new char[difficulty]).replace('\0', '0');
		HashMap<String,TransactionOutput> tempUTXOs=new HashMap<String,TransactionOutput>();//未完成的临时交易列表
		tempUTXOs.put(genesisTransaction.outputs.get(0).id,genesisTransaction.outputs.get(0));
		//循环便利区块链进行hash检查
		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("#Current Hashes not equal");
				return false;
			}
			if(!previousBlock.hash.equals(currentBlock.previousHash)) {
				System.out.println("#Previous Hashes not equal");
				return false;
			}
			if(!currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) {
				System.out.println("#This block hasn't been mined");
				return false;
			}
			//循环遍历区块链交易
			TransactionOutput tempOutput;
			for(int t=0;t<currentBlock.transactions.size();t++) {
				Transaction currentTransaction=currentBlock.transactions.get(t);
				if(!currentTransaction.verifySignature()) {
					System.out.println("#Signature on Trnasacrtion("+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);
	}
}

这个类相当于区块链的主体:区块串成的链,其中的blockchain即为我们所说的区块链。

StringUtil.java:

package My_Block;

import java.security.Key;
/*
 * 每一个区块生成独一无二的hash值
 */
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Base64;

public class StringUtil {
	// 应用ECDSA签名并产生私钥对应的字符串数组
	// applyECDSASig方法的输入参数为付款人的私钥和需要加密的数据信息,签名后返回字符数组
	public static byte[] applyECDSASig(PrivateKey privateKey, String data) {
		Signature dsa;
		byte[] output = new byte[0];
		try {
			dsa = Signature.getInstance("ECDSA", "BC");
			dsa.initSign(privateKey);// 初始化签署签名的私钥
			byte[] strByte = data.getBytes();// 将输入的字符串变成byte类型
			dsa.update(strByte);// update方法将dsa更新成strByte里面的数据
			byte[] realSig = dsa.sign();// 返回byte里面所有已更新数据的签名字节
			output = realSig;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return output;
	}

	// 应用ECDSA验证数字签名,用公钥解开数字签名并验证签名是否有效
	// 输入参数为签名、公钥和需要加密的数据
	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的hash值
	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);
		}
	}

	// 返回key对应的编码字符串
	public static String getStringFromKey(Key key) {
		return Base64.getEncoder().encodeToString(key.getEncoded());
	}
	
	/*可追踪的交易列表并返回一个merkleroot
	但是在一个单一的区块中可能存放了1000个交易,
	这就会导致大量的hash计算,
	不用担心在这里我们使用了交易的merkle树*/
	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;
	}
}

StringUtil.java中存储的是对字符串的各种操作,比如说应用ECDSA签名并产生私钥对应的字符串数组,应用ECDSA验证数字签名,用公钥解开数字签名并验证签名是否有效,将字符串转化为sha256的hash值,返回key对应的编码字符串,生成merkle树来存储大量的交易。

Transaction.java:

package My_Block;
/*
 * 交易类,是中间的服务器
 */
import java.security.*;
import java.util.ArrayList;

public class Transaction {
	
	public String transactionID;//这个交易的hash编号
	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>();
	
	//被创建交易的数量
	public 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;
	}
	
	//计算交易的hash值,用作交易的编号
	public String calculateHash() {
		sequence++;//增加sequence的值,防止出现hash值一样的情况
		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 verifySignature() {
		String data=StringUtil.getStringFromKey(sender)+StringUtil.getStringFromKey(reciepient)+Float.toString(value);
		return StringUtil.verifyECDSASig(sender, data, signature);
	}
	
	
	//用boolean值来说明新的交易是否被创建
	public boolean processTransaction() {
		//验证签名
		if(verifySignature()==false) {
			System.out.println("#Transaction Signature failed to verify");
			return false;
		}
		//收集交易的输入(必须注意的是输入是未被使用的)
		for(TransactionInput i:inputs) {
			i.UTXO=NoobChain.UTXOs.get(i.transactionOutputId);
		}
		//检查交易是否有效
		if(getInputsValue()<NoobChain.minimumTransaction) {
			System.out.println("Transaction Inputs to small:"+getInputsValue());
			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) {
			NoobChain.UTXOs.put(o.id, o);
		}
		//把已经使用的交易输入从UTXO中移除
		for(TransactionInput i:inputs) {
			if(i.UTXO==null)continue;//如果交易找不到就跳过
			NoobChain.UTXOs.remove(i.UTXO.id);
		}
		return true;
	}
	
		//返回余额
		public float getInputsValue() {
			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;
		}
	}


这个类主要功能是创建一个新的交易,其中包含这个交易的id,付款人的地址(公钥),接收人的地址(公钥),要转移的金额,数字签名。processTransaction()函数说明这个新的交易是否被创建。processTransaction()中对应功能如下:1、验证签名,不一样就停止函数。2、收集输入集中的每一个输出id,而且每一个输入是一个UTXO。3、检查交易是否有效。4、创建交易输出,扣除钱,产生一个交易,将交易金额发给收款人,剩余金额返回给付款人,将输出增加到未使用的输出列表中,把已经使用的交易从UTXOS中移除。

一般是在wallet中发起一个交易,Transation中的processTransaction来判断这个交易是否被创建,如果没有创建,则进行创建,并交易。

TransactionInput.java:

package My_Block;
//交易输入类
//比特币规定每一笔新的交易的输入必须是某笔交易未花费的输出

public class TransactionInput {
	public String transactionOutputId;//指向交易输出类->transactionId
	public TransactionOutput UTXO;//包含所有未使用的交易输出
	public TransactionInput(String transactionOutputId) {
		this.transactionOutputId=transactionOutputId;
	}
}

TransactionInput类中,每一笔新的交易的输入必须是某笔交易未花费的输出,所以可以看到,输入存储了输出的ID和UTXO。UTXO指的就是未使用的交易输出。一个TransactionInput()类对应一个未进行使用的输出,这样的一个类列表组成一个输入

TransactionOutput.java:

package My_Block;
//增加一个交易输出类
//一个交易输出被创建用来显示一个比特币发送给你的地址
//你账户的余额是所有指向你的未使用交易的输出。
import java.security.*;

public class TransactionOutput {
	
	public String id;//这个交易输出的id
	public PublicKey reciepient;//持有者的公钥
	public float value;//持有者的金额
	public String parentTransactionId;//交易编号
	
	//构造函数
	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);
	}
}

你账户的余额是所有指向你的未使用交易的输出。所以这个类存储了一个交易输出的id,这个交易持有者的公钥reciepient,持有者只有这个交易输出的金额value,和parentTransactionId是交易编号。

Wallet.java:

package My_Block;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
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;//你的地址,用于分享你的公钥给别人来获得付款,别人用公钥来验证你的签名是否有效
	//钱包自身的UTXOs,用来存储没有交易的输出
	public HashMap<String,TransactionOutput> UTXOs=new HashMap<String,TransactionOutput>();
	/*
	 * 即私钥用来签名不想被篡改的数据,而公钥是用来验证签名的,签名=私钥+(from+to+value)
	 * 我们通过密钥对的方式来创建一对公钥和私钥,密钥对采用的是椭圆曲线加密算法(ECDSA)
	 */
	public Wallet() {
		generateKeyPair();
	}
	//generateKeyPair函数生成一对私钥和公钥
	public void generateKeyPair() {
		try {
			KeyPairGenerator keyGen=KeyPairGenerator.getInstance("ECDSA","BC");
			SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
			ECGenParameterSpec ecSpec=new ECGenParameterSpec("prime192v1");
			keyGen.initialize(ecSpec,random);
			KeyPair keyPiar=keyGen.generateKeyPair();
			privateKey=keyPiar.getPrivate();
			publicKey=keyPiar.getPublic();
			
		}
		catch(Exception e) {
			throw new RuntimeException(e);
		}
	}
	//返回余额并保存钱包自身的UTXO
	public float getBalance() {
		float total=0;
		for(Map.Entry<String, TransactionOutput>item: NoobChain.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("#Not Enough funds to send transaction.Transaction Discarded");
		return null;
		}
		//创建输入列表
		ArrayList<TransactionInput> inputs=new ArrayList<TransactionInput>();
		float total=0;
		//entrySet()的返回值是返回一个Set集合,也是键值对对应,此集合的类型为Map.Entry
		//HashMap不支持Iterator所以要通过其他方式迭代Map中的Key和Value
		//Map.Entry<>方法表示Map中的一个实体(一个key-一个value对,是Set<Map.Entry<K,V>>
		for(Map.Entry<String, TransactionOutput> item:UTXOs.entrySet()) {
			TransactionOutput UTXO=item.getValue();
			total+=UTXO.value;
			inputs.add(new TransactionInput(UTXO.id));
			if(total>value) //将拿出的钱相加,发现大于需要支付的钱的时候,停止拿出钱,将那些拿出的钱(未交易的输出)的id记录下来,当作输入列表
				break;
			}
		
			Transaction newTransaction=new Transaction(publicKey,_recipient,value,inputs);
			newTransaction.generateSignature(privateKey);//用你的私钥对你不想被改变的数据加密,生成一个数字签名
			for(TransactionInput input:inputs) {
				UTXOs.remove(input.transactionOutputId);//将这个输出从你的钱包中去除
			}
			return newTransaction;//返回一个新交易
		}
	}

Wallet.java中getBalance()返回钱包余额。sendFunds()创建一个新的交易(再此过程中,检查是否有足够的钱,如果是则继续,否就return)。创建一个列表,列表元素为未交易的输出对应的id。拿出所有家当进入交易,当打出的钱大于需要支付的时候,将这些钱放入一个tarnsactioninput列表。用Transaction创建一个交易。将你想要加密的数据进行私钥加密。

2、接下来是完成一个交易的流程:

在NoobChain文件中,首先创建了一个叫做blockchain的ArryList区块链,其中的元素是Block对象。挖取区块难度为6,这个难度可以调整,创建两个钱包walletA和walletB,还有一个创世纪钱包coinbase。

然后创建创世纪交易,将100个货币发给walletA,步骤为:先实例化Transaction类的一个对象,创建出创世纪交易genesisTransaction,然后用创世纪钱包的私钥对交易中的一些数据进行加密,使用Transaction中的generateSignature()方法实现,接下来设置创世纪交易的ID为0,因为每个交易都有一个很特殊的hash值,所以0是为了用于区分它和之后的交易。因为创世纪钱包的特殊性,我们利用交易genesisTransation中的信息手动创建一个输出,即TransactionOutput名为的对象,将这个对象添加到genesisTransaction的outputs列表中作为输出列表。保存这个交易到UTXOs中,创建第一个区块,区块传入的previoushash值为0(默认第一块区块的previoushash为0),然后调用genesis中的addTransaction()方法,因为previoushash为0,所以就不通过transaction.processTransaction()方法来创建交易输出,而在上面选择手动创建输出,这个区块名叫genesis。接下来就是对区块的挖掘,使用addBlock()方法调用区块的mineBlock()方法,进行挖取,如果挖取成功,则将这个区块加入区块链。

现在walletA中就有了100块钱,walletB中还是0块钱,接下来我们创建walletA和walletB之间的交易。

先实例化一个Block,名为block1,传入的previoushash为genesis的hash值,然后使用Wallet中的sendFunds()方法来创建一个交易,比如:walletA.sendFunds(walletB.publicKey,40f)
就是创建了一个walletA转40元到walletB的交易,为Transaction类的对象,然后调用block1中的addTransaction()方法,这个方法中又调用了transaction.processTransaction()方法,在这个方法中完成各种交易,当返回true时表示完成交易,将这次交易信息添加到block1的交易列表transactions中,并完成这次交易。使用addBlock()方法进行区块block1的挖掘,当计算成功(挖掘成功)后将这个区块添加到blockchain中。

之后的交易都是这样进行的,完成一次交易,然后将这次交易的信息存入一个区块中,然后进行区块的挖掘,当计算机经过复杂的计算挖掘到这个区块时,将这个区块加入区块链中,这样就构成了一个区块链。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值