挖矿做的工作
在一个block的结构中, version prev_block merkle_root time bits都是很容易计算的. 只有 nonce这个随机数不确定.
bitcoin的 pow 就是 找到一个合适的 nonce, 使得 version prev_block merkle_root time bits nonce合并的结果 reuslt,再经过2次sha256计算, 达到一个符合bitcoin网络难度的数值. bitcoin的网络难度通过 bits计算, 难度即最后计算的hash的前n位是零.
每一个block hash的计算, 都包含了prev_block_hash,这也是链的体现, 增加了攻击bitcoin的难度. 如果有人改了一个block的hash, 这个块之后的所有的块hash都要重新计算.
比特币历史区块timestamp以https://btc.com给出的可以计算出
bits前两位为指数,后6位为系数
currentDifficulty:当前难度,
currentDifficulty = diff_1_target/TARGET,
diff_1_target为0x1d00ffff,TARGET是一个2016次不变,但总体而言一直在变的值,它的目标就是让产生一个区块的时间大概在10分钟左右
比特币节点求解工作量证明问题的步骤大致如下:
1)生成coinbase交易,并与其他所有准备打包进区块的交易组成交易列表,通过Merkle树算法生成Merkle根哈希;
2)把Merkle根哈希及其他相关字段组装成区块头,将区块头的80字节数据作为工作量证明的输入;
3)不停地变更区块头中的随机数,即nonce的数值,并对每次变更后的区块头做双重SHA256运算
(即SHA256(SHA256(Block_Header))),将结果值与当前网络的目标值做对比,
如果小于目标值,则解题成功,工作量证明完成
比特币工作量证明的目标值(target)的计算公式如下:
目标值=最大目标值/难度值
其中,最大目标值为一个恒定值:
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
目标值的大小与难度值成反比。比特币工作量证明的达成就是矿工计算出来的区块哈希值必须小于目标值。
package mine;
import encrypt.HashUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
public class MiningDemo {
public static void main(String[] args) {
// bits 网络的难度
String bits = "1a44b9f2";
String version = "00000001";
String preHash = "00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81";
String merkleRoot = "2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3";
String timestamp = "1305998791";
// String nonce="2504433986";
String nonce = "9546a142";
System.out.println(getMiningTarget(bits));
timestamp = Utils.decimalStr2HexStr(timestamp);
// nonce = Utils.decimalStr2HexStr(nonce);
System.out.println(nonce);
System.out.println(timestamp);
String blockHash = calculateHash(version, preHash, merkleRoot, timestamp, bits, nonce);
System.out.println("block:" + blockHash);
}
//获取难度目标
public static String getMiningTarget(String bits) {
//目标难度系数
String coefficient = bits.substring(2);
System.out.println("系数:" + coefficient);
//目标难度指数
String exponent = bits.substring(0, 2);
System.out.println("指数:" + exponent);
//目标数值
String targetDecimal = Integer.parseInt(coefficient, 16) * Math.pow(2, 8 * (Integer.parseInt(exponent, 16) - 3)) + "";
System.out.println("科学计数法:" + targetDecimal);
//科学计数法转普通数值
BigDecimal bigDecimal = new BigDecimal(targetDecimal);
BigInteger bigInteger = new BigInteger(bigDecimal.toPlainString());
String targetHex = bigInteger.toString(16);
System.out.println("16进制:" + targetHex);
//计算长度,高位补0
int targetLen = targetHex.length();
System.out.println(targetLen);
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < 64 - targetLen; i++) {
prefix.append("0");
}
String targetHash = prefix + targetHex;
System.out.println("64位目标hash:" + targetHash);
return targetHash;
}
public static String calculateHash(String version, String preHash, String merkleRoot, String timestamp, String bits, String nonce) {
version = Utils.hexStrReverse(version);
preHash = Utils.hexStrReverse(preHash);
merkleRoot = Utils.hexStrReverse(merkleRoot);
timestamp = Utils.hexStrReverse(timestamp);
bits = Utils.hexStrReverse(bits);
nonce = Utils.hexStrReverse(nonce);
String header_hex = version + preHash + merkleRoot + timestamp +bits+ nonce;
String result = "";
try {
result = HashUtils.sha256HexString(HashUtils.sha256HexString(header_hex));
} catch (Exception e) {
e.printStackTrace();
}
return Utils.hexStrReverse(result);
}
}
bits=“1a44b9f2”
获取高位补零的个数:0x17转10进制为23,高位补零的个数为:64-23*2=18
获取后缀补零的格式(凑够64位):64-高位补零个数
根据bits快速计算目标hash和难度目标:(近似,标准实现见上面)
public static void main(String[] args) {
// bits 网络的难度
String bits = "1a44b9f2";
//系数
String coefficient = bits.substring(2);
//指数
String exponent = bits.substring(0, 2);
int exp = Integer.parseInt(exponent, 16);
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < 64 - exp * 2; i++) {
prefix.append("0");
}
StringBuilder suffix = new StringBuilder();
for (int i = 0; i < 64 - prefix.length() - 6; i++) {
suffix.append("0");
}
String strTargetHash = prefix + coefficient + suffix;
System.out.println(strTargetHash);
BigInteger difficulty = calculateDifficulty(strTargetHash);
System.out.println(difficulty);
}
public static BigInteger calculateDifficulty(String strTargetHash){
String geniusBlockHash="00000000ffff0000000000000000000000000000000000000000000000000000";
BigInteger bigGeniusHash = new BigInteger(geniusBlockHash, 16);
BigInteger bigTargetHash = new BigInteger(strTargetHash, 16);
return bigGeniusHash.divide(bigTargetHash);
}