用spring boot 2从零开始创建区块链

区块链这么火的技术,大java怎能落后,所以有了本文,主要代码参考自 Learn Blockchains by Building One , 中文翻译:用Python从零开始创建区块链 。

一、区块链对象模型的基础属性(BlockChain)

区块链的基本数据模型参考:最基本的区块链hello world(python3实现) 。主要属性如下:

    @ApiModelProperty(value = "当前交易列表", dataType = "List<Transaction>")
    @JSONField(serialize = false)
    @JsonIgnore
    private List<Transaction> currentTransactions;

    @ApiModelProperty(value = "所有交易列表", dataType = "List<Transaction>")
    private List<Transaction> transactions;

    @ApiModelProperty(value = "区块列表", dataType = "List<BlockChain>")
    @JSONField(serialize = false)
    @JsonIgnore
    private List<BlockChain> chain;

    @ApiModelProperty(value = "集群的节点列表", dataType = "Set<String>")
    @JSONField(serialize = false)
    @JsonIgnore
    private Set<String> nodes;

    @ApiModelProperty(value = "上一个区块的哈希值", dataType = "String", example = "f461ac428043f328309da7cac33803206cea9912f0d4e8d8cf2786d21e5ff403")
    private String previousHash = "";

    @ApiModelProperty(value = "工作量证明", dataType = "Integer", example = "100")
    private Integer proof = 0;

    @ApiModelProperty(value = "当前区块的索引序号", dataType = "Long", example = "2")
    private Long index = 0L;

    @ApiModelProperty(value = "当前区块的时间戳", dataType = "Long", example = "1526458171000")
    private Long timestamp = 0L;

    @ApiModelProperty(value = "当前区块的哈希值", dataType = "String", example = "g451ac428043f328309da7cac33803206cea9912f0d4e8d8cf2786d21e5ff401")
    private String hash;

注:上面有些注解来自swagger,主要为了方便生成在线文档以及直接调试rest接口。相对之前最基本的区块链hello world(python3实现)一文,每个区块中的data,在这里细分为transactions、currentTransactions。另外区块“链”本质上可以理解为链表,所以得有一个List<?> chain;此外这里引入了所谓“工作量证明”,用于验证每个区域的hash值不是随便来的,而是要达到一定规则的运算量才能获取,可以理解为控制挖矿速度的难度系数。 

 

二、BlockChain的常规操作

2.1 生成新块newBlock

    public BlockChain newBlock(Integer proof, String previousHash) {
        BlockChain block = new BlockChain();
        block.index = chain.size() + 1L;
        block.timestamp = System.currentTimeMillis();
        block.transactions.addAll(currentTransactions);
        block.proof = proof;
        block.previousHash = previousHash;
        currentTransactions.clear();
        chain.add(block);
        return block;
    }

  

2.2 生成第1个"创世"块

链表总归要有一个Head节点,区块链也不例外

   public void newSeedBlock() {
        newBlock(100, "1");
    }

约定previousHash=1的,即为所谓的"创世"块  

 

2.3 生成hash值

    public String getHash() {
        String json = jsonUtil.toJson(this.getCurrentTransactions()) +
                jsonUtil.toJson(this.getTransactions()) +
                jsonUtil.toJson(this.getChain()) +
                this.getPreviousHash() + this.getProof() + this.getIndex() + this.getTimestamp();
        hash = SHAUtils.getSHA256Str(json);
        return hash;
    }

这里把区块的主要属性:交易数据、链表中所有元素、工作量证明、区块索引号、时间戳 拼在一起,然后计算sha256。总之,这些主要属性中的任何一个属性发生变化,整个hash值就变了。

 

2.4 工作量证明

相信对区块链有了解的同学,都知道“挖矿”。为了控制挖矿的难度,得有一个规则来约束下,所以就有了这个工作量证明,这里我们模拟一个简单的策略:

    public Boolean validProof(Integer lastProof, Integer proof) {
        System.out.println("validProof==>lastProof:" + lastProof + ",proof:" + proof);
        String guessHash = SHAUtils.getSHA256Str(String.format("{%d}{%d}", lastProof, proof));
        return guessHash.startsWith("00");
    }

把上一块的proof值与本区块的proof在一起,算sha256值,如果正好前2位是00,表示证明通过。(注:0的个数越多,挖矿难度越大,有兴趣的同学可以自己调整试下)

 

2.5 区块链验证数据是否正确

为了防止区块链的节点中混入非法脏数据(或被篡改),需要一个检测数据完整性的方法

    public boolean validChain(List<BlockChain> chain) {
        if (CollectionUtils.isEmpty(chain)) {
            return false;
        }

        BlockChain previousBlock = chain.get(0);
        int currentIndex = 1;
        while (currentIndex < chain.size()) {
            BlockChain block = chain.get(currentIndex);
            if (!block.getPreviousHash().equals(previousBlock.getHash())) {
                return false;
            }

            if (!validProof(previousBlock.getProof(), block.getProof())) {
                return false;
            }
            previousBlock = block;
            currentIndex += 1;
        }
        return true;
    }

规则很简单:

a)每个区块的previousHash值,必须等于前一个块的hash值

b)  验证每个块上的proof值是否有效

 

2.6 集群中的分叉校验

区块链是一个去中心化的分布式体系,每个节点都能挖矿,挖出来的“新区块”都能加入链中,如果出现节点之间的区块链数据不一致,需要一个策略来做仲裁,可以定一个简单的规则:链最长的节点认定为有效的,其它节点都以此为准。

为了模拟这种情况,在BlockChain类的属性中,特地留了一个nodes节点列表,用于登记集群中的其它节点信息。

 public void registerNode(String address) {
        nodes.add(address);
    }

上面的方法,将把其它节点的实例(类似http://localhost:8081/),登记到节点列表中。知道了集群中所有其它节点,就可以一一检查谁的链条最长,代码如下:

    public boolean resolveConflicts() {
        int maxLength = getChain().size();
        List<BlockChain> newChain = new ArrayList<>();
        for (String node : getNodes()) {
            RestTemplate template = new RestTemplate();
            Map map = template.getForObject(node + "chain", Map.class);
            int length = MapUtils.getInteger(map, "length");
            String json = jsonUtil.toJson(MapUtils.getObject(map, "chain"));
            List<BlockChain> chain = jsonUtil.fromJson(json, new TypeReference<List<BlockChain>>() {
            });
            if (length > maxLength && validChain(chain)) {
                maxLength = length;
                newChain = chain;
            }
        }
        if (!CollectionUtils.isEmpty(newChain)) {
            this.chain = newChain;
            return true;
        }
        return false;
    }

大意是遍历整个节点,逐一请求其它节点的rest接口,获取其完整的链表,然后跟自己对比,如果比自己长的,就把自己给换掉。这样轮一圈后,自身的链表,就被替换为整个集群中最长的那个。

 

三、调试运行

为了方便调试,本文引入了swagger(不熟悉的同学可以参考spring cloud 学习(10) - 利用springfox集成swagger一文),然后加一堆rest api,跑起来,就可以直接测了:

点击查看原图

3.1 调用/chain查看下初始值:

{
  "chain": [
    {
      "transactions": [],
      "previousHash": "1",
      "proof": 100,
      "index": 1,
      "timestamp": 1527427873298,
      "hash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479"
    }
  ],
  "length": 1
}

可以看到就只有一个“创世”块,其previousHash为特定值1

 

3.2 调用/mine挖一块矿

{
  "previousHash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479",
  "index": 2,
  "proof": 172,
  "message": "New Block Forged",
  "transactions": [
    {
      "sender": "0",
      "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
      "amount": 1
    }
  ]
}

挖到矿(即:产生一个新的区块block),系统自动奖励本节点1个币(从transaction可以看出这一点),同时这笔奖励的交易被写入新块中。这时再来看下/chain

{
    "chain": [
        {
            "transactions": [],
            "previousHash": "1",
            "proof": 100,
            "index": 1,
            "timestamp": 1527427873298,
            "hash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479"
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479",
            "proof": 172,
            "index": 2,
            "timestamp": 1527427956435,
            "hash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93"
        }
    ],
    "length": 2
}

可以看到,有二个区块加入"链表"中了,可以继续再挖一块,最终/chain可能长成这样:

{
    "chain": [
        {
            "transactions": [],
            "previousHash": "1",
            "proof": 100,
            "index": 1,
            "timestamp": 1527427873298,
            "hash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479"
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479",
            "proof": 172,
            "index": 2,
            "timestamp": 1527427956435,
            "hash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93"
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93",
            "proof": 153,
            "index": 3,
            "timestamp": 1527428128077,
            "hash": "d2b50c6ae768fd48591d07a054de78d058c11f889cec15588d270f72b6e420f1"
        }
    ],
    "length": 3
}

  

3.3 调用/transactions/new 发起一笔新交易

参数如下:

{
  "amount": 1.0,
  "recepient": "block-on-other-node",
  "sender": "50130c5283e640779b4e5e7a5afd2e6b"
}

注:sender一般取为当前矿机的标识,即本节点的nodeId,接收方一般指其它节点(这里我们随便输入点内容,当作演示),然后交易的金额为“1”个币,成功后,将返回

{
  "message": "Transaction will be added to Block 4"
}  

但这时,如果调用/chain查看整个链的数据,会发现没有变化,因为这笔交易数据,只是放在本区块的currentTransactions列表中(注:该属性并未json序列化输出,忘记的同学,可以拉到本文最开头,复习下几个重要的属性)。只有下一个可用区块产生时,这笔交易才会写入新的区块中,so,我们再继续挖一块新矿,调用/mine,然后再查看/chain

{
    "chain": [
        {
            "transactions": [],
            "previousHash": "1",
            "proof": 100,
            "index": 1,
            "timestamp": 1527427873298,
            "hash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479"
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479",
            "proof": 172,
            "index": 2,
            "timestamp": 1527427956435,
            "hash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93"
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93",
            "proof": 153,
            "index": 3,
            "timestamp": 1527428128077,
            "hash": "d2b50c6ae768fd48591d07a054de78d058c11f889cec15588d270f72b6e420f1"
        },
        {
            "transactions": [
                {
                    "sender": "50130c5283e640779b4e5e7a5afd2e6b",
                    "recepient": "block-on-other-node",
                    "amount": 1
                },
                {
                    "sender": "0",
                    "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
                    "amount": 1
                }
            ],
            "previousHash": "d2b50c6ae768fd48591d07a054de78d058c11f889cec15588d270f72b6e420f1",
            "proof": 86,
            "index": 4,
            "timestamp": 1527428477991,
            "hash": "4b3c261d2f878cebbdc1ade1a09809bb647abbd990353e529f630220a53d60ed"
        }
    ],
    "length": 4
}

刚才的交易,已经被写入最后一个刚挖出的Block中。

 

3.4 模拟多节点数据不一致,使用/resolve仲裁解决

a) 再启动一个新端口的运行实例

方法一:参考下图,idea中设置运行时的环境变量,填上server.port=8081,就可以在另一个端口上启动

点击查看原图

方法二:在build.gradle里加一个task

task 8081 << {
	bootRun.systemProperty 'server.port', '8081'
}

然后就可以命令行下,直接gradle 8081 bootRun 

方法三:java -jar xxx.jar --name="Spring" --server.port=8081 直接在运行jar的时候指定端口

b) 调用/register 将新节点实例(即:8081端口的节点),注册到8080的节点上

参数如下:

{
  "nodes": [
    "http://localhost:8081/"
  ]
}  

反过来,把8080老节点也注册到新节点上(即:相当于两两相互注册)。注册成功后,这时调用8081新节点上的/chain ,因为这是个新节点,里面只有一个创世块,显然跟8080老节点上的数据不一致

c) 新8081节点上调用/resolve 

输出如下:

{
  "newChain": [
    {
      "transactions": [],
      "previousHash": "1",
      "proof": 100,
      "index": 1,
      "timestamp": 1527427873298,
      "hash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479"
    },
    {
      "transactions": [
        {
          "sender": "0",
          "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
          "amount": 1
        }
      ],
      "previousHash": "9fbb08a5f332baf42012b8541122eccb60a603834fa98e9b5789898022b23479",
      "proof": 172,
      "index": 2,
      "timestamp": 1527427956435,
      "hash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93"
    },
    {
      "transactions": [
        {
          "sender": "0",
          "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
          "amount": 1
        }
      ],
      "previousHash": "df079acca767eaca611383f7b1bb2b37daa3b01502f3219cb79ecddd010f7d93",
      "proof": 153,
      "index": 3,
      "timestamp": 1527428128077,
      "hash": "d2b50c6ae768fd48591d07a054de78d058c11f889cec15588d270f72b6e420f1"
    },
    {
      "transactions": [
        {
          "sender": "50130c5283e640779b4e5e7a5afd2e6b",
          "recepient": "block-on-other-node",
          "amount": 1
        },
        {
          "sender": "0",
          "recepient": "50130c5283e640779b4e5e7a5afd2e6b",
          "amount": 1
        }
      ],
      "previousHash": "d2b50c6ae768fd48591d07a054de78d058c11f889cec15588d270f72b6e420f1",
      "proof": 86,
      "index": 4,
      "timestamp": 1527428477991,
      "hash": "4b3c261d2f878cebbdc1ade1a09809bb647abbd990353e529f630220a53d60ed"
    }
  ],
  "message": "Our chain was replaced"
}

最后一行的message: Our chain was replaced 表示,本节点的区块链已经被集群其它节点中最长的那个替换掉了。

 

最后,文中演示的所有代码,已经托管在github上,地址:https://github.com/yjmyzz/springboot-blockchain-helloworld 欢迎大家Fork.

转载于:https://www.cnblogs.com/yjmyzz/p/build-a-block-chain-demo-using-spring-boot-2.html

<think>嗯,用户想了解如何从零开始搭建电商独立站或个人网站,从一个小白到高手的过程。首先,我需要考虑用户的基础。作为小白,可能对技术不太熟悉,所以需要从最基础的步骤开始讲起,比如域名注册、选择主机、搭建平台等等。然后,可能需要分步骤来讲解,每个步骤详细说明,同时结合用户提到的引用内容,比如引用[2]中的技术解决方案,可以推荐一些相关的技术栈,比如Spring全家桶、MongoDB、RocketMQ这些,但也要考虑到用户是新手,可能需要更基础的工具,比如WordPress或者Shopify,这样更容易上手。 接下来,用户可能对成本、时间和技术要求有疑问。所以需要解释不同搭建方式的优缺点,比如使用SaaS平台和自主开发的对比。同时,要提到如何选择域名和主机,这里可能需要推荐一些常见的服务商,比如GoDaddy、阿里云等,并说明注意事项,比如域名后缀、主机的性能需求等。 然后,关于网站开发部分,用户可能需要了解前端和后端的基础知识,但作为新手,可能需要推荐一些现成的模板和建站工具,比如Elementor、WooCommerce,这些可以降低技术门槛。同时,引用[2]里提到的区块链和智能合约技术可能对于电商平台的支付和合同部分有帮助,但这对新手来说可能比较复杂,可以稍微提及,作为进阶的内容。 另外,测试和上线也是重要的步骤,需要指导用户如何进行基本的功能测试、性能测试,以及如何部署到生产环境。后续的维护和优化,比如SEO、数据分析工具,这些也是提升网站效果的关键,需要提到常用的工具如Google Analytics和SEMrush。 还要考虑用户可能遇到的问题,比如安全性、支付集成、法律合规等,这些都需要在步骤中提醒用户注意。例如,SSL证书的重要性,支付接口的选择,以及隐私政策的合规性。 最后,根据引用[1]中的观点,用户可能在决策时会有战略上的考虑,比如是否值得投入时间和资源,是否有确定感。所以在回答中可以强调分阶段实施,先验证需求再逐步优化,降低初期风险,这可能符合用户的心理预期,即“人生逻辑大于商业逻辑”。 总结下来,回答的结构应该是分步骤,从规划到开发到上线维护,每个步骤给出具体的建议和工具推荐,同时结合用户提供的引用内容中的技术方案,适当引入进阶技术的方向,但保持对新手友好。此外,生成的相关问题需要围绕用户可能进一步关心的方面,比如成本控制、技术学习路径、推广方法等,帮助他们继续深入学习。</think>### 从零开始搭建电商独立站或个人网站的步骤指南 #### 一、明确需求规划 1. **确定目标** - 电商站需支持商品展示、支付、订单管理等功能;个人网站侧重内容展示(如博客、作品集)。 - 参考案例:好客租房平台通过区块链技术实现无中介化[^2],可借鉴其功能模块(如智能推荐、电子合同)。 2. **选择搭建方式** - **SaaS平台(适合小白)** 使用Shopify、Wix等工具,无需代基础,模板丰富,但灵活性较低,需支付月费。 - **自主开发(需技术基础)** 采用Spring全家桶、MongoDB等分布式技术,适合定制化需求,但学习成本高。 #### 二、域名主机准备 1. **注册域名** - 选择简短易记的名称,后缀建议`.com`或`.cn`。 - 推荐平台:GoDaddy、阿里云(国内需备案)。 2. **购买主机/服务器** - 小型网站可选虚拟主机(如Bluehost);高流量站点需云服务器(AWS、阿里云ECS)。 - 注意:国内服务器需完成ICP备案,耗时约15天。 #### 三、网站开发部署 1. **使用建站工具(快速上手)** - WordPress + WooCommerce:通过插件实现电商功能,支持自定义主题(如Elementor页面编辑器)。 - 代示例(基础HTML结构): ```html <!DOCTYPE html> <html> <head> <title>我的网站</title> </head> <body> <h1>欢迎访问!</h1> </body> </html> ``` 2. **自主开发技术栈(进阶)** - 前端:Vue.js/React + Bootstrap - 后端:Spring Boot(参考好客租房技术方案) - 数据库:MySQL/MongoDB - 部署:Docker容器化 + Nginx反向代理 #### 四、测试上线 1. **功能测试** - 检查支付接口(如支付宝、Stripe)、表单提交等核心功能。 2. **性能优化** - 使用CDN加速静态资源,压缩图片(工具:TinyPNG)。 3. **正式上线** - 将域名解析到服务器IP,配置SSL证书(免费工具:Let's Encrypt)。 #### 五、运营维护 1. **SEO优化** - 使用Google Analytics分析流量,通过SEMrush优化关键词。 2. **安全防护** - 定期备份数据,安装防火墙(如Wordfence插件)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值