区块链的java简单实现

区块链的简单实现

定义

区块链就是一串或者是一系列区块的集合,类似于链表的概念,每个区块都指向于后面一个区块,然后顺序的连接在一起。在区块链中的每一个区块都存放了很多很有价值的信息,主要包括三个部分:自己的数字签名,上一个区块的数字签名,还有一切需要加密的数据(这些数据在比特币中就相当于是交易的信息,它是加密货币的本质)。每个数字签名不但证明了自己是特有的一个区块,而且指向了前一个区块的来源,让所有的区块在链条中可以串起来,而数据就是一些特定的信息,你可以按照业务逻辑来保存业务数据。这里的hash指的就是数字签名所以每一个区块不仅包含前一个区块的hash值,同时包含自身的一个hash值,自身的hash值是通过之前的hash值和数据data通过hash计算出来的。如果前一个区块的数据一旦被篡改了,那么前一个区块的hash值也会同样发生变化(因为数据也被计算在内),这样也就导致了所有后续的区块中的hash值。所以计算和比对hash值会让我们检查到当前的区块链是否是有效的,也就避免了数据被恶意篡改的可能性,因为篡改数据就会改变hash值并破坏整个区块链。

区块链的实现

定义区块的结构体

  • 包含的数据
    • 自己的hash值,由前一个区块的hash值,自己保存的数据,时间戳,工作量一起使用数字摘要计算而来
    • 前一个区块的hash值 pervhash
    • 保存的数据 data
    • 时间戳 timestamp
    • 工作量的证明 nonce
  • 实现的方法
    • calculateHash(),使用数字摘要算法计算自己的hash值
    • mineBlock(int difficult),挖矿算法,我写的是计算出前difficult个字符为0的hash值作为区块的hash值作为挖矿算法,例如mineBlock(3),就是计算出前三个字符为0的hash值。difficult代表挖矿的难度
@Data
public class Block {
    private String hash;
    private String prevHash;
    private String data;
    private final long timestamp;
    private long nonce;
    
    public Block(String data, String prevHash) throws Exception {
        this.data = data;
        this.prevHash = prevHash;
        this.timestamp = System.currentTimeMillis();
        this.hash = calculateHash();
    }

    public String calculateHash() throws Exception {
        return Digest.getDigest(prevHash + data + timestamp + nonce, "SHA-256");
    }

    public void mineBlock(int difficult) throws Exception {
        String target = new String(new char[difficult]).replace('\0', '0');
        while(!hash.substring(0, difficult).equals(target)) {
            nonce++;
            hash = calculateHash();
        }
        System.out.println("mine:" + hash);
    }

}

数字摘要算法

用于计算hash值,使用指定的数字摘要算法计算出hash值,并转换成16进制显示

public class Digest {

    private static String toHex(byte[] digest) throws Exception {
        // 创建对象用来拼接
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1){
                // 如果生成的字符只有一个,前面补0
                s = "0"+s;
            }
            sb.append(s);
        }
//        System.out.println("16进制数据的长度:" + sb.toString().getBytes().length);
        return sb.toString();
    }

    public static String getDigest(String input, String algorithm) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
//        System.out.println("密文的字节长度:" + digest.length);
        return toHex(digest);
    }
}

区块链的实现

  • 实现一个简单的区块链需要的数据
    • 一个用于保存区块的集合chain
    • 表示挖矿的难度 DIFFICTLT 变量
    • 保存数据存储位置的变量DATA_PATH,用于保存在区块链中的数据,需要使用到数据时,根据路径找到需要的数据,然后反序列化使用该数据
  • 实现的方法
    • serialized(Object obj, String path)将指定的对象序列化到指定的路径
    • deserialized(Class c, String methodName, int index),根据索引区块链中查找到指定的区块数据,通过反序列化执行对象的方法
    • isValidChain(),判断是否是一条有效的区块链,通过判断当前块的hash值是否符合difficult难度指定的hash值形式,前一个块的hash值是否正确,当前块的hash值是否正确,三个条件判断是否为有效的区块链
    • isPreviousValid(Block curBlock, Block prevBlock),判断前一个块是否有效
    • isCurrentValid(Block curBlock, String hashTarget),判断当前块是否有效
  • 主方法
    首先序列化一个需要存储的对象,然后创建一个块来保存对象序列化的路径,第一个块没有前驱hash值,我们用0来填充,通过循环创建10个区块,每个区块都保存着一个序列化的对象,然后调用挖矿算法获取指定难度的hash值,最后装入区块链中
    创建完成后判断区块链是否有效,然后就可以使用区块链了,通过反序列化指定区块保存的对象,就可以调用该对象的方法。
public class BlockChain {
    private static final List<Block> chain = new ArrayList<>();
    private static final int DIFFICULTY = 3;
    private static final String DATA_PATH = "F:\\Java_WorkSpace_idea\\java_cryptography\\src\\main\\java\\com\\lzl\\";

    public static void main(String[] args) throws Exception {
        serialized(new TestBlock(), DATA_PATH + "testBlock.txt");
        Block first = new Block(DATA_PATH + "testBlock.txt", "0");
        first.mineBlock(DIFFICULTY);
        chain.add(first);
        for(int i = 0; i < 10; i++) {
            TestBlock testBlock = new TestBlock();
            serialized(testBlock, DATA_PATH + i + ".txt");
            Block block = new Block(DATA_PATH + i + ".txt", chain.get(i).getHash());
            block.mineBlock(DIFFICULTY);
            chain.add(block);
        }
        if (!isValidChain()) {
            System.out.println("不是有效的区块链");
        }
        System.out.println(JSON.toJSON(chain));
        deserialized(TestBlock.class, "sayHello", 5);
        deserialized(TestBlock.class, "sayHello", 2);
        deserialized(TestBlock.class, "sayHello", 3);
    }


    private static void deserialized(Class c, String methodName, int index) throws Exception {
        Block block = chain.get(index);
        String path = block.getData();
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
        Object o = objectInputStream.readObject();
        c.getDeclaredMethod(methodName).invoke(o);
    }

    private static void serialized(Object obj, String path) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
        objectOutputStream.writeObject(obj);
    }

    public static boolean isValidChain() throws Exception {
        Block curBlock;
        Block prevBlock;
        String hashTarget = new String(new char[DIFFICULTY]).replace('\0', '0');
        for(int i = 1; i < chain.size(); i++) {
            curBlock = chain.get(i);
            prevBlock = chain.get(i - 1);
            if (!isCurrentValid(curBlock, hashTarget) || !isPreviousValid(curBlock, prevBlock)) return false;
        }
        return true;
    }

    private static boolean isPreviousValid(Block curBlock, Block prevBlock) {
        if (!prevBlock.getHash().equals(curBlock.getPrevHash())) {
            System.out.println("前驱块的hash不相等");
            return false;
        }
        return true;
    }

    private static boolean isCurrentValid(Block curBlock, String hashTarget) throws Exception {
        if (!curBlock.getHash().equals(curBlock.calculateHash()) || !curBlock.getHash().substring(0, DIFFICULTY).equals(hashTarget)) {
            System.out.println("当前块hash不相等");
            return false;
        }
        return true;
    }
}

用于序列化的对象

public class TestBlock implements Serializable {
    private int count = 0;
    private static final long serialVersionUID = -8324022222992414658L;

    public void sayHello() throws Exception {
        System.out.println(count + " : Hello world!");
    }

    public TestBlock() {
        count += new Random().nextInt(10);
    }
}

运行截图:
通过运行可以看到,生成的hash值都是指定difficult的hash值,然后生成了一条包含11个区块的区块链,每一个区块都包含保存的数据路径,前驱hash值,工作量的证明,自己的hash值以及时间戳,最后输出了调用指定hash块的方法的输出信息。
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值