Java实现比特币区块链工具包
结合上一篇的介绍:https://blog.csdn.net/NEU_LightBulb/article/details/103086867
以下就是代码介绍想看源码直接:
https://github.com/zjw271208550/learn/tree/master/blockchain-core
-
工程结构
-
实体类
Block | 区块的数据结构的模板,也是上一篇文章中介绍的最原始的区块结构,其内部的方法会根据实际业务情况修改 |
BlockChain | 上一篇文章中介绍的最原始的链结构,实际中链不可能长时间的存在于内存中,而是存在本地的,所以这个仅仅是一个模板,其内部的方法会根据实际业务情况修改 |
TransactionGroup | 交易的实体类,也是上一篇文章中介绍的最原始的交易结构 |
TransactionIn | 输入交易的实体类,也是上一篇文章中介绍的最原始的输入交易结构 |
TransactionOut | 输出交易的实体类,也是上一篇文章中介绍的最原始的输出交易结构 |
TransactionOutUnspent | 未输出交易的实体类,也是上一篇文章中介绍的最原始的未输出交易结构 |
-
静态方法类
-
Crypt
加密、签名相关的方法,使用bouncycastle的椭圆曲线算法进行非对称加密,包含的方法如下
/**
* Java 的非对称加密的密钥是配对生成的
*/
public static Map<String, Key> generateECKeyPair()
throws NoSuchAlgorithmException,NoSuchProviderException {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE_EC,new SecureRandom());
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
//私钥
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
//将密钥存储在map中
Map<String, Key> keyMap = new HashMap<String, Key>();
keyMap.put(Consts.KEY_PUBLIC_MAP, publicKey);
keyMap.put(Consts.KEY_PRIVATE_MAP, privateKey);
return keyMap;
}
/**
* 找到一种 GayGay的方法实现由随机串生成私钥,实际上是生成密钥对但只返回私钥
*/
public static byte[] generateECPrivateKeyFromRandom(byte[] random)
throws NoSuchAlgorithmException,NoSuchProviderException{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
keyPairGenerator.initialize(KEY_SIZE_EC,new SecureRandom(random));
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
return privateKey.getEncoded();
}
/**
* 从生成的密钥对中获得私钥
*/
public static byte[] getECPrivateKey(Map<String, Key> keyMap) {
ECPrivateKey key = (ECPrivateKey) keyMap.get(Consts.KEY_PRIVATE_MAP);
return key.getEncoded();
}
/**
* 从生成的密钥对中获得公钥
*/
public static byte[] getECPublicKey(Map<String, Key> keyMap){
ECPublicKey key = (ECPublicKey) keyMap.get(Consts.KEY_PUBLIC_MAP);
return key.getEncoded();
}
/**
* 使用签名者私钥对数据散列值签名
*/
public static byte[] doSignature(byte[] data,byte[] privateKey)
throws NoSuchAlgorithmException,NoSuchProviderException,
InvalidKeySpecException,InvalidKeyException,SignatureException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
PrivateKey pKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Signature signature = Signature.getInstance(KEY_SIGNATURE_INS_EC);
signature.initSign(pKey);
signature.update(data);
return signature.sign();
}
/**
* 使用签名者公钥、数据散列值来验证签名
*/
public static boolean verifySignature(byte[] data,byte[] publicKey,byte[] sign)
throws NoSuchAlgorithmException,NoSuchProviderException,
InvalidKeySpecException,InvalidKeyException,SignatureException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
PublicKey pKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(KEY_SIGNATURE_INS_EC);
signature.initVerify(pKey);
signature.update(data);
return signature.verify(sign);
}
/**
* 使用私钥对散列化数据加密
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey)
throws NoSuchAlgorithmException,NoSuchProviderException, NoSuchPaddingException,
InvalidKeySpecException,InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
PrivateKey pKey = parsePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_CIPHER_EC,KEY_PROVIDER_CIPHER_EC);
cipher.init(Cipher.ENCRYPT_MODE, pKey);
return cipher.doFinal(data);
}
/**
* 使用公钥对加密数据解密
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey)
throws NoSuchAlgorithmException,NoSuchProviderException, NoSuchPaddingException,
InvalidKeySpecException,InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
PublicKey pKey = parsePublicKey(publicKey);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_CIPHER_EC,KEY_PROVIDER_CIPHER_EC);
cipher.init(Cipher.ENCRYPT_MODE, pKey);
return cipher.doFinal(data);
}
/**
* Base64编码
*/
public static String bytes2Base64String(byte[] key){
return (new BASE64Encoder()).encodeBuffer(key);
}
/**
* Base64解码
*/
public static byte[] base64String2Bytes(String key) throws IOException {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* 由公钥获得私钥
*/
public static byte[] getECPublicKeyFromPrivateKey(byte[] privateKey)
throws NoSuchAlgorithmException,NoSuchProviderException,InvalidKeySpecException{
ECPrivateKey pKey = (ECPrivateKey)parsePrivateKey(privateKey);
ECParameterSpec parameter = pKey.getParameters();
ECPoint Q = parameter.getG().multiply(pKey.getD());
byte[] publicDerBytes = Q.getEncoded(false);
ECPoint point = parameter.getCurve().decodePoint(publicDerBytes);
ECPublicKeySpec publicKeySpec =
new ECPublicKeySpec(point,pKey.getParameters());
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
ECPublicKey publicKey = (ECPublicKey)keyFactory.generatePublic(publicKeySpec);
return publicKey.getEncoded();
}
private static PrivateKey parsePrivateKey(byte[] key)
throws NoSuchAlgorithmException,NoSuchProviderException,InvalidKeySpecException{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
//生成私钥
return keyFactory.generatePrivate(pkcs8KeySpec);
}
private static PublicKey parsePublicKey(byte[] key)
throws NoSuchAlgorithmException,NoSuchProviderException,InvalidKeySpecException{
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_EC,KEY_PROVIDER_EC);
//生成私钥
return keyFactory.generatePublic(x509KeySpec);
}
-
HashFunction
Hash相关操作的方法:
/**
* 生成该区块的 Hash
* @param block 区块
* @return Hash
*/
public static String generateHash(Block block){
MessageDigest messageDigest;
if(null==block.getData() && null==block.getPreviousHash())
throw new RuntimeException("data or previous-hash is null");
StringBuilder nocodeBuilder = new StringBuilder();
nocodeBuilder.append(block.getIndex());
nocodeBuilder.append(block.getCreateTimestamp());
for(TransactionGroup tx:block.getData()) {
nocodeBuilder.append(tx.toString());
}
nocodeBuilder.append(block.getPreviousHash());
nocodeBuilder.append(block.getHashDifficulty());
String nocode = nocodeBuilder.toString();
int hashOffset = 0;
String encode = "";
try{
messageDigest = MessageDigest.getInstance(Consts.ENCRYPT_INSTANCE);
while (true) {
String nocodetmp = nocode + hashOffset;
messageDigest.update(nocodetmp.getBytes(Consts.ENCRYPT_BYTES_CODING));
encode = bytes2Hex(messageDigest.digest());
if(checkHashDifficulty(encode,block.getHashDifficulty())) {
block.setHashOffset(hashOffset);
break;
}
else {
hashOffset = hashOffset + 1;
encode = "";
nocodetmp = "";
}
}
}catch (NoSuchAlgorithmException e){
throw new RuntimeException("error on loading SHA-256");
}catch (UnsupportedEncodingException e) {
throw new RuntimeException("error on loading UTF-8");
}
return encode;
}
/**
* 检查生成的 Hash是否符合 Difficulty的要求
* @param hash Hash
* @param difficulty 难度
* @return 是否
*/
private static boolean checkHashDifficulty(String hash,int difficulty){
String hashBinary = hex2Binary(hash);
return hashBinary.startsWith(generatePrefix(difficulty));
}
/**
* bytes转16进制数的字符串
* @param bytes bytes
* @return 16进制数的字符串
*/
private static String bytes2Hex(byte[] bytes){
StringBuilder stringBuilder = new StringBuilder("");
if (bytes == null || bytes.length <= 0) {
return null;
}
for (byte b : bytes) {
int v = b & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* bytes转2进制数的字符串
* @param bytes bytes
* @return 2进制数的字符串
*/
private static String bytes2Binary(byte[] bytes){
StringBuilder stringBuilder = new StringBuilder("");
if (bytes == null || bytes.length <= 0) {
return null;
}
for (byte b : bytes) {
stringBuilder.append(Long.toString(b & 0xff, 2));
}
return stringBuilder.toString().substring(0, stringBuilder.length() - 1);
}
/**
* 16进制数的字符串转2进制数的字符串
* @param hex 16进制数的字符串
* @return 2进制数的字符串
*/
private static String hex2Binary(String hex){
char[] strChar=hex.toCharArray();
StringBuilder stringBuilder = new StringBuilder("");
for(char c:strChar){
int ihex = Integer.valueOf(String.valueOf(c),16);
String sbin = Integer.toBinaryString(ihex);
String sbinf = String.format("%04d",Integer.valueOf(sbin));
stringBuilder.append(sbinf);
}
return stringBuilder.toString();
}
/**
* 生成难度对应的 Hash前缀用于难度验证
* @param offset 难度
* @return 前缀字符串
*/
private static String generatePrefix(int offset){
StringBuilder prefix = new StringBuilder();
for (int i=0;i<offset;i++){
prefix.append('0');
}
return prefix.toString();
}
/**
* SHA256编码
* @param nocode 未编码字符串
* @return 编码后字符串
*/
public static String getSHA256Code(String nocode){
String encode;
try{
MessageDigest messageDigest = MessageDigest.getInstance(Consts.ENCRYPT_INSTANCE);
messageDigest.update(nocode.getBytes(Consts.ENCRYPT_BYTES_CODING));
encode = bytes2Hex(messageDigest.digest());
}catch (NoSuchAlgorithmException e){
throw new RuntimeException("error on loading SHA-256");
}catch (UnsupportedEncodingException e) {
throw new RuntimeException("error on loading UTF-8");
}
return encode;
}
-
Transaction
交易的相关方法
/**
* 生成交易组的 ID
*/
public static String generateId(TransactionGroup transaction){
StringBuilder inId = new StringBuilder(transaction.getFromAddress());
for(TransactionIn txIn:transaction.getTxIns()){
inId.append(txIn.getOutFromTxId()+txIn.getOutFromIndex());
}
StringBuilder outId = new StringBuilder("");
for(TransactionOut txOut:transaction.getTxOuts()){
inId.append(txOut.getToAddress()+txOut.getInfo().getDataEncryptString());
}
return HashFunction.getSHA256Code(inId.toString()+outId.toString());
}
/**
* 给一个 TxInORM 签名
*/
public static String signForTransactionIn(String bodyId,
TransactionIn txIn,
String privateKey,
List<TransactionOutUnspent> unspents)
throws IOException, NoSuchAlgorithmException, NoSuchProviderException,
InvalidKeySpecException, InvalidKeyException, SignatureException {
String idToSign = bodyId;
TransactionOutUnspent referencedUnspentTxOut =
findUnspentTxOut(txIn.getOutFromTxId(), txIn.getOutFromIndex(), unspents);
if(null == referencedUnspentTxOut)
throw new RuntimeException("没有匹配的未使用TxOut");
String referencedAddress = referencedUnspentTxOut.getToAddress();
if(!getPublicKey(privateKey).equals(referencedAddress))
throw new RuntimeException("私钥生成的公钥与地址不符");
String signature = bytes2Base64String(
doSignature(
base64String2Bytes(idToSign),
base64String2Bytes(privateKey)
)
);
return signature;
}
/**
* 给一个 创始TxInORM 签名
*/
public static String signForTransactionIn0(String bodyId,
String addressTo,
String privateKey)
throws IOException, NoSuchAlgorithmException, NoSuchProviderException,
InvalidKeySpecException, InvalidKeyException, SignatureException {
String idToSign = bodyId;
if(!getPublicKey(privateKey).equals(addressTo))
throw new RuntimeException("私钥生成的公钥与地址不符");
String signature = bytes2Base64String(
doSignature(
base64String2Bytes(idToSign),
base64String2Bytes(privateKey)
)
);
return signature;
}
/**
* 根据私钥 Base64编码字符串获得 Base64编码后的公钥字符串
*/
public static String getPublicKey(String privateKey)
throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
byte[] privateK = base64String2Bytes(privateKey);
byte[] publicK = getECPublicKeyFromPrivateKey(privateK);
return bytes2Base64String(publicK);
}
/**
* 判断是否是合法的交易组
*/
public static boolean isLegalTransaction(TransactionGroup transaction,
List<TransactionOutUnspent> unspents)
throws IOException,NoSuchAlgorithmException,NoSuchProviderException,
InvalidKeySpecException,InvalidKeyException,SignatureException{
//判断 Id是否合法
if(!generateId(transaction).equals(transaction.getId()) ){
log.error("交易序列不合法:交易序列 ID不合法");
return false;
}
//遍历所有的 txIn判断是否合法
List<BlockData> txInInfos = new ArrayList<>();
for(TransactionIn in:transaction.getTxIns()){
//some of the txIns are invalid in t
if(!isLegalTransactionIn(in,transaction,unspents)){
log.error("交易序列不合法:发现签名不合法的 TxInORM,idx为{}",in.getOutFromIndex());
return false;
}
txInInfos.add(getTransactionInInfo(in,unspents));
}
List<BlockData> txOutInfos = new ArrayList<>();
for(TransactionOut out :transaction.getTxOuts()){
txOutInfos.add(out.getInfo());
}
if(txInInfos.size()!=txOutInfos.size()) {
log.error("交易输入 TxIn与交易输出 TxOut数量不符");
return false;
}
for(BlockData txInInfo:txInInfos){
int tmp=0;
for(BlockData txOutInfo:txOutInfos) {
if (txInInfo.equals(txOutInfo)){
break;
}else {
tmp+=1;
}
}
if(tmp==txOutInfos.size()) {
return false;
}
}
return true;
}
/**
* 判断是否是合法的 txIn
*/
private static boolean isLegalTransactionIn(TransactionIn txIn,
TransactionGroup transaction,
List<TransactionOutUnspent> unspents)
throws IOException,NoSuchAlgorithmException,NoSuchProviderException,
InvalidKeySpecException,InvalidKeyException,SignatureException{
//找到 txIn 引用的 txOutUnspent
TransactionOutUnspent referencedUTxOut =
findUnspentTxOut(txIn.getOutFromTxId(),txIn.getOutFromIndex(),unspents);
//如果找不到说明 txIn 不合法
if(null == referencedUTxOut) {
log.error("交易输入 TxIn不合法:找不到引用的未使用交易输出 TxOutUnspent");
return false;
}
String senderAddress = referencedUTxOut.getToAddress();
//判断签名合法性
return verifySignature(
base64String2Bytes(transaction.getId()),
base64String2Bytes(senderAddress),
base64String2Bytes(txIn.getSignature()));
}
/**
* 找到与交易输入匹配的未使用的交易输出
*/
private static BlockData getTransactionInInfo(TransactionIn txIn,
List<TransactionOutUnspent> unspents){
TransactionOutUnspent referencedUTxOut =
findUnspentTxOut(txIn.getOutFromTxId(),txIn.getOutFromIndex(),unspents);
return referencedUTxOut.getInfo();
}
/**
* 找到与 txOut 匹配的 txOutUnspent
*/
private static TransactionOutUnspent findUnspentTxOut(String txInId,
int txInIndex,
List<TransactionOutUnspent> unspents){
for(TransactionOutUnspent unspent: unspents){
if(unspent.getOutFromTxId().equals(txInId) &&
unspent.getOutFromIndex() == txInIndex)
return unspent;
}
return null;
}
-
使用关系型数据库作为元数据载体的工具
-
元数据结构
public abstract class BlockData {
private String dataId;
private String sha256_data;
public BlockData(String dataId, String dataString) {
this.dataId = dataId;
this.sha256_data = HashFunction.getSHA256Code(dataString);
}
public BlockData(String dataId, String dataString,boolean hasCoding) {
this.dataId = dataId;
if(hasCoding)
this.sha256_data = dataString;
else
this.sha256_data = HashFunction.getSHA256Code(dataString);
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getSHA256Data() {
return sha256_data;
}
public void setData(String dataString) {
this.sha256_data = HashFunction.getSHA256Code(dataString);
}
public void setSHA256Data(String sha256_data) {
this.sha256_data = sha256_data;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof BlockData){
return this.dataId.equals(((BlockData) obj).getDataId())&&
this.sha256_data.equals(((BlockData) obj).getSHA256Data());
}else return false;
}
public String getDataEncryptString(){
return this.sha256_data;
}
}
-
ORM
BlockORM | int:index long:createTimestamp long:createTimeUse LinkedList<TxORM>:data String:hash String:previousHash int:hashOffset int:hashDifficulty | 与数据库交互的区块实体 只有构造方法和Getter、Setter |
TxInORM | String:txId String:preTxId int:preTxIdx String:sign | 与数据库交互的输入交易实体 只有构造方法和Getter、Setter |
TxOutORM | String:txId String:addressTo String:dataId String:data | 与数据库交互的输出交易实体 只有构造方法和Getter、Setter |
TxORM | int:blockIdx String:id String:fromAddress List<TxInORM>:txInORMS List<TxOutORM>:txOutORMS | 与数据库交互的交易实体 只有构造方法和Getter、Setter |
UTxOutORM | String:publicKey String:dataId String:data String:txId int:txIdx | 与数据库交互的未输出交易实体 只有构造方法和Getter、Setter |
-
Mybatis Mapper
这里要首先说一下RDB中区块、输入交易、输出交易、交易、未输出交易的表结构从上文的ORM结构就能看出来,这些都是预先建好的表:
根据区块位置获得区块信息 | SELECT n_index,dt_create,n_use_time,c_hash,c_prehash,n_offset,n_difficaulty FROM db_data.t_blockchain WHERE n_index=#{index} |
根据区块位置获得区块内交易列表 | SELECT n_block_index,c_id,c_from_addr FROM db_data.t_txs WHERE n_block_index=#{n_index} |
获取未上链的交易 | SELECT n_block_index,c_id,c_from_addr FROM db_data.t_txs WHERE n_block_index ISNULL |
根据交易ID获取该交易的输入交易列表 | SELECT c_tx_id,c_pretx_id,n_pretx_idx,c_sign FROM db_data.t_txins WHERE c_tx_id=#{c_id} |
根据交易ID获取该交易的输出交易列表 | SELECT c_tx_id,c_address_to,c_data_id,c_data FROM db_data.t_txouts WHERE c_tx_id=#{c_id} |
插入一个区块 | INSERT INTO db_data.t_blockchain (n_index,dt_create,n_use_time,c_hash,c_prehash,n_offset,n_difficaulty) VALUES(#{index},#{createTimestamp},#{createTimeUse},#{hash},#{previousHash},#{hashOffset},#{hashDifficulty}) |
更新刚刚上链的交易的区块位置 | UPDATE db_data.t_txs SET n_block_index=#{bid} WHERE c_id=#{cid} |
新增一个输入交易 | INSERT INTO db_data.t_txins (c_tx_id,c_pretx_id,n_pretx_idx,c_sign) VALUES(#{txId},#{preTxId},#{preTxIdx},#{sign}) |
新增一个输出交易 | INSERT INTO db_data.t_txouts (c_tx_id,c_address_to,c_data_id,c_data) VALUES(#{txId},#{addressTo},#{dataId},#{data}) |
根据数据所有者(公钥)获取未输出交易列表 | SELECT c_publickey,c_data_id,c_data,c_txid,n_txidx FROM db_data.t_utxos WHERE c_publickey=#{publicKey} |
根据交易ID获取未输出交易列表 | SELECT c_publickey,c_data_id,c_data,c_txid,n_txidx FROM db_data.t_utxos WHERE c_txid=#{txId} |
根据交易ID和输出交易位置获取未输出交易 | SELECT c_publickey,c_data_id,c_data,c_txid,n_txidx FROM db_data.t_utxos WHERE c_txid=#{txId} AND n_txidx=#{txIds} |
根据数据ID取未输出交易 | SELECT c_publickey,c_data_id,c_data,c_txid,n_txidx FROM db_data.t_utxos WHERE c_data_id=#{dataId} |
插入一个未输出交易 | INSERT INTO db_data.t_utxos (c_publickey,c_data_id,c_data,c_txid,n_txidx) VALUES(#{publicKey},#{dataId},#{data},#{txId},#{txIdx}) |
根据数据ID删除一个未输出交易 | DELETE FROM db_data.t_utxos WHERE c_data_id=#{dataId} |
获取链尾区块信息 | SELECT n_index,dt_create,n_use_time,c_hash,c_prehash,n_offset,n_difficaulty FROM db_data.t_blockchain ORDER BY n_index DESC LIMIT 1 |
获取创世块信息 | SELECT n_index,dt_create,n_use_time,c_hash,c_prehash,n_offset,n_difficaulty FROM db_data.t_blockchain WHERE n_index=1 |
-
给Service调用的静态方法
/**
* 检查数据是否被篡改
* @param mapper 继承 BCMapper 的 Mapper
* @param dataInDB 要被检验的数据
* @return 是否
*/
public static boolean checkDataById(BCMapper mapper, BlockData dataInDB){
UTxOutORM uTxOut = mapper.getUTxOutByDataId(dataInDB.getDataId());
if(null == uTxOut){
logger.error("该数据未在账本或未被挖掘");
return false;
}else {
return uTxOut.getData().equals(dataInDB.getSHA256Data());
}
}
/**
* 检查是否已经创世
* @param mapper 继承 BCMapper 的 Mapper
* @return 是否
*/
public static boolean hasInit(BCMapper mapper){
BlockORM initBlockInfo = mapper.getInitBlockInfo();
if(null==initBlockInfo){
return false;
}
Block initBlock = ORM2EntityFunction.BlockOrm2Entity(initBlockInfo);
String initHash = HashFunction.generateHash(initBlock);
if(initHash.equals(initBlockInfo.getHash())){
return true;
}else {
System.out.println("创世块HASH不合法");
return false;
}
}
/**
* 创世
* @param mapper 继承 BCMapper 的 Mapper
* @return 成功与否
*/
public static boolean initBlock(BCMapper mapper){
Block initBlock = new Block(1,0,0,null,
"","",0, Consts.HASH_DIFFICULTY_INIT);
long start = new Date().getTime();
String hash = HashFunction.generateHash(initBlock);
long end = new Date().getTime();
BlockORM initBlockORM = new BlockORM(1,0,end-start,hash,
"",initBlock.getHashOffset(),Consts.HASH_DIFFICULTY_INIT);
mapper.addOneBlock(initBlockORM);
return true;
}
/**
* 添加一个新的数据,数据准备上链
* @param mapper 继承 BCMapper 的 Mapper
* @param publicKey 数据所有者公钥
* @param privateKey 数据所有者私钥
* @param data 数据
* @return 成功与否
*/
public static boolean addNewData(BCMapper mapper,String publicKey,String privateKey, BlockData data)
throws Exception{
TransactionIn txIn = new TransactionIn("",1,null);
TransactionOut txOut = new TransactionOut(publicKey,data);
TransactionGroup tx = new TransactionGroup(
publicKey,
Arrays.asList(new TransactionIn[] {txIn}),
Arrays.asList(new TransactionOut[] {txOut})
);
String sign = Transaction.signForTransactionIn0(tx.getId(), publicKey, privateKey);
txIn.setSignature(sign);
mapper.addOneTx(
new TxORM(null,publicKey,tx.getId()));
mapper.addOneTxIn(
new TxInORM(tx.getId(), txIn.getOutFromTxId(), txIn.getOutFromIndex(), txIn.getSignature()));
mapper.addOneTxOut(
new TxOutORM(tx.getId(),txOut.getToAddress(),txOut.getInfo().getDataId(),txOut.getInfo().getSHA256Data()));
return true;
}
/**
* 挖掘未上链的数据,数据上链
* @param mapper 继承 BCMapper 的 Mapper
* @param publicKey 挖掘者公钥
* @return 成功与否
*/
public static boolean mineTransactions(BCMapper mapper,String publicKey) throws Exception{
List<TxORM> txOrms = mapper.getTxsByNull();
if(null == txOrms || txOrms.size()==0){
return true;
}
/*
* 此处会广播 txOrms给全部节点,一起算
*/
BlockORM lastBlockInfo = mapper.getLastBlockInfo();
LinkedList<TransactionGroup> txs = new LinkedList<>();
for(TxORM txORM: txOrms){
TransactionGroup tx = ORM2EntityFunction.TxOrm2Entity(txORM);
//正确的TxId
if(tx.getId().equals(txORM.getId())){
for(TransactionIn txIn:tx.getTxIns()){
boolean check = Crypt.verifySignature(
Crypt.base64String2Bytes(tx.getId()),
Crypt.base64String2Bytes(txORM.getFromAddress()),
Crypt.base64String2Bytes(txIn.getSignature())
);
if(!check){
throw new RuntimeException("交易ID"+tx.getId()+",签名不合法");
}
}
txs.add(tx);
}else {
throw new RuntimeException("交易ID:"+txORM.getId()+",不合法");
}
}
Block newBlock = new Block(
lastBlockInfo.getIndex()+1,
txs,
lastBlockInfo.getHash(),
lastBlockInfo.getHashDifficulty());
BlockORM newBlockOrm = new BlockORM(
newBlock.getIndex(),
newBlock.getCreateTimestamp(),
newBlock.getCreateTimeUse(),
null,
newBlock.getHash(),
newBlock.getPreviousHash(),
newBlock.getHashOffset(),
newBlock.getHashDifficulty());
mapper.addOneBlock(newBlockOrm);
for(TxORM txORM: txOrms){
List<TxOutORM> txOutOrms = txORM.getTxOutORMS();
mapper.updateTxBlockId(newBlockOrm.getIndex(),txORM.getId());
int idx=1;
for(TxOutORM txOutORM:txOutOrms){
mapper.addOneUTxOut(
new UTxOutORM(publicKey,txOutORM.getDataId(),txOutORM.getData(),txORM.getId(),idx));
idx += 1;
}
}
return true;
}