Likt's Blog

天空一直都在,是云来了又去。

0%

共识机制学习实践

一、实验目的

  1. 学习PoW,PoS共识机制
  2. PoW共识机制的实践
  3. postman启用接口、navicat连接SQLite数据库

二、实验环境

  1. Windows IDEA
  2. 引导节点:p2p_bootstrap.git
  3. 对等节点:block_chain.git

三、实验步骤

1.本机启动了一个引导节点、一个对等节点,组员启动了一个对等节点

启动引导节点:

1
2
3
server.port=8886
bootstrap.ip= 192.168.227.61
bootstrap.port=8883

启动对等节点:

1
2
3
4
5
6
7
server.port=8001
bootstrap.ip = 192.168.227.61
bootstrap.port = 8883
node.ip= 192.168.227.61
node.port = 8887
spring.servlet.multipart.max-request-size=200MB
spring.servlet.multipart.max-file-size=200MB

组员启动对等节点:

1
2
3
4
5
6
7
server.port=8001
bootstrap.ip = 192.168.227.61
bootstrap.port = 8883
node.ip= 192.168.227.149
node.port = 8887
spring.servlet.multipart.max-request-size=200MB
spring.servlet.multipart.max-file-size=200MB

2.对等节点观察到信息如下:

img

3.使用postman进入接口,开始挖矿:
image-20240929124221124

4.观察到文件目录下多出来两个文件夹db&ws,其中db即SQLite数据库文件,ws即挖到的区块文件。由于对等节点一旦形成之间互相连接,文件共享,挖到的区块数目会一致。

image-20240929124520778

5.使用IDEA调试并明确断点,可以实时观察到各参数信息:

img

6.使用navicat连接SQLite数据库,BlockChain.db测试连接,连接成功之后确认,可以查看到SQLite的三张表:block0,dictionary,pending

img

7.查看结果如下:
img

image-20241002230536999

image-20241002230639096

image-20241002230705849

8.可以对比两个对等节点之间的区块数据会发现确实一致。

四、实验结果与讨论

  1. 引导节点、两个对等节点启动:

    image-20250112173135370

  2. 使用postman进入接口,开始挖矿,成功出现ws区块文件夹

    image-20250112173154955

    image-20241003154513050

  3. 使用navicat连接SQLite数据库:BlockChain.db

    image-20241003154351744

五、总结

  1. PoW的核心原理是基于哈希函数的不可预测性和随机性。在PoW中,矿工需要寻找一个特定的哈希值,使得区块头的哈希满足一定的条件,通常要求哈希值以一定数量的前导零开头。这个条件在数学上是难以预测的,唯一的方法是通过不断尝试不同的随机数来进行哈希计算,直到找到满足条件的哈希值,这需要大量的计算能力。步骤:①选择交易和构建区块头;②挑战难题;③工作量竞赛;④寻找满足条件的哈希值。

    定义一个共同的目标,然后根据不同机器的计算能力来先算出这个目标的结果,计算成功后通知其他节点,来进行验证,一旦验证通过,当前区块的计算即被种植,这个结果会被广泛接受并被用于产生新的区块。

  2. Pos权益证明机制,通过参与者持有的代币数量来决定其创建新区快的权利。步骤:①抵押代币:抵押到网络中,这些代币被锁定,与被选中的概率更高。②选择出块节点:即有权利创建新区块的节点。③负责验证交易和创建新区快。④奖励和惩罚:(抵押的代币)。优势:①能源效率大幅降低;②去中心化,代币持有者积极参与共识机制,避免了算例集中问题;③安全性,攻击的成本和难度,攻击者需要破坏自身的利益来攻击;④能够扩展性,无需进行负责计算,只要进行验证和区块创建,可以更轻松的处理更多的交易和数据;⑤减少硬件竞争,节约了资源。局限性:①权力集中,更多代币的参与者有更大的权力;②节点激励问题,可能缺乏积极性,影响网络的安全性和稳定性;③链长问题,更倾向于选择更多代笔的链,更大话语权;④算法设计复杂性,要考虑权益抵押、随机性,容易不稳定。

  3. 一些其他的共识机制:

    • 权威共识DPoS(代币持有者通过选举一组代表来确认交易和生成区块)
    • 拜占庭容错BFT(强调在节点存在故障或恶意行为时仍然能够保持一致性的共识机制,确保只要节点中的大多数诚实,就能达成共识)
    • 权益授权PoA(基于授权节点)
    • 流动性共识PoL(将流动性提供者纳入共识过程的机制,将代币锁定在智能合约中,以提供流动性支持,同时获得交易手续费和奖励)
    • 实践证明PoET(通过随机等待时间来选择区块生产者的共识机制)
  4. SQLite数据库:

    • block表:存储区块对应的信息
    • dictionary表:存储区块的当前配置
    • pending表:存储等待打包的交易

附上代码

  1. 难题设置以及工作量竞赛

    ①block实体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    package com.example.blockchain.entity.block;
    import lombok.Getter;
    import org.springframework.stereotype.Component;
    @Component
    public class Block
    {
    public String blockIndex;
    public String blockHash;
    public String preBlockHash;
    public String workLoad;
    public String createTime;
    public String path;
    public String dataJson;
    public String tradeIds;
    public int id;
    public String workString() {
    return "Block{" +
    "blockIndex='" + blockIndex + '\'' +
    ", blockPreHash='" + preBlockHash + '\'' +
    ", workLoad='" + workLoad + '\'' +
    ", dataJson='" + dataJson + '\'' +
    ", createTime='" + createTime + '\'' +
    ", path='" + path + '\'' +
    ", randomNumber='" + randomNumber + '\'' +
    ", tradeIds='" + tradeIds + '\'' +
    '}';
    }
    @Override
    public String toString() {
    return "Block{" +
    "blockIndex='" + blockIndex + '\'' +
    ", blockHash='" + blockHash + '\'' +
    ", blockPreHash='" + preBlockHash + '\'' +
    ", workLoad='" + workLoad + '\'' +
    ", dataJson='" + dataJson + '\'' +
    ", createTime='" + createTime + '\'' +
    ", path='" + path + '\'' +
    ", tradeIds='" + tradeIds + '\'' +
    ", randomNumber='" + randomNumber + '\'' +
    '}';
    }
    public String getBlockIndex() {
    return blockIndex;
    }
    public void setBlockIndex(String blockIndex) {
    this.blockIndex = blockIndex;
    }

    public String getBlockHash() {
    return blockHash;
    }

    public void setBlockHash(String blockHash) {
    this.blockHash = blockHash;
    }
    public String getPreBlockHash() {
    return preBlockHash;
    }
    public void setPreBlockHash(String preBlockHash) {
    this.preBlockHash = preBlockHash;
    }
    public String getWorkLoad() {
    return workLoad;
    }
    public String randomNumber;

    public String getPath() {
    return path;
    }
    public void setPath(String path) {
    this.path = path;
    }
    public void setWorkLoad(String workLoad) {
    this.workLoad = workLoad;
    }
    public String getCreateTime() {
    return createTime;
    }
    public void setCreateTime(String createTime) {
    this.createTime = createTime;
    }
    public int getId() {
    return id;
    }
    public void setId(int id) {
    this.id = id;
    }
    public String getRandomNumber() {
    return randomNumber;
    }
    public void setRandomNumber(String randomNumber) {
    this.randomNumber = randomNumber;
    }
    public String getDataJson() {
    return dataJson;
    }
    public void setDataJson(String dataJson) {
    this.dataJson = dataJson;
    }
    public String getTradeIds() {
    return tradeIds;
    }
    public void setTradeIds(String tradeIds) {
    this.tradeIds = tradeIds;
    }
    }

    ②开始运算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    System.out.println("开始运算---------------------");
    int trueCount = 0;
    int falseCount = 0;
    String preBlockIndex = String.valueOf(UpdateTimer.currentMaxBlockIndex);
    BlockServiceImpl.checkBlockTable(preBlockIndex);
    List<Block> blocks = BlockServiceImpl.queryBlockByBlockIndex(preBlockIndex);
    Block currentBlock = null;
    if (blocks.size() > 0) {
    currentBlock = blocks.get(0);
    }
    if (currentBlock == null) {
    currentBlock = new Block();
    currentBlock.setBlockHash("First block hash");
    }
    Dictionary diffWorkload = InitUtils.intiDifficulty();// 字典表的工作量配置
    String maxBlockIndex = currentBlock.getBlockIndex();
    String nextBlockIndex = getNextBlockIndex(maxBlockIndex);
    Block block = new Block();
    Random r = new Random();
    String rand = String.valueOf(r.nextInt(1000000));
    block.setBlockIndex(nextBlockIndex);
    block.setCreateTime(time);
    block.setWorkLoad(diffWorkload.getValue());
    block.setCreateTime(DateUtils.getTime());
    block.setPreBlockHash(currentBlock.getBlockHash());
    block.setRandomNumber(rand);
    Date runDate = new Date();
    String blockPath = DataUtils.getBlockPath(nextBlockIndex, runDate);// gen block file path
    block.setPath(blockPath);

    //取出交易
    List<String> tradeNos = new ArrayList<>();
    List<Pending> list = PendingServiceImpl.queryPendings();
    if (list.size() != 0) {
    for (int i = 0; i < list.size(); i++) {
    tradeNos.add(list.get(i).getOrderNo());
    }
    }
    block.setDataJson(list.toString());

    ③挖到区块

    1
    2
    3
    4
    5
    String outHash = EncryptUtil.encryptSHA256(block.workString());
    System.out.println(outHash);
    if (outHash.startsWith(diffWorkload.getValue())) {
    System.out.println("挖到---------------------");
    }
  2. 计算结果广播以及工作量验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    block.setBlockHash(outHash);
    for (String port : map.getFs().keySet()) {
    Friends f = map.getFs().get(port);
    String ip = f.getIp();
    try {
    String resp = HttpHelper.checkBlock(ip, block);
    JSONObject response = new Gson().fromJson(resp, JSONObject.class);
    if (response.getCode().equals("1")) {
    Boolean isTrue = (Boolean) response.getO();
    if (isTrue) {
    trueCount = trueCount + 1;
    } else {
    falseCount = falseCount + 1;
    }
    }
    } catch (Exception e) {
    System.out.println(ip + "失败");
    map.getFs().get(port).setFriendliness(0);
    }
    }

    int count = trueCount + falseCount;
    boolean isTrueCountMajority = false;
    if (count > 0) {
    isTrueCountMajority = trueCount > (count / 2);
    }

    if (isTrueCountMajority) {
    //正确结果超过半数以上

    //将新区块记录到区块链中
    }

    ②其他节点验证计算结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @RequestMapping(value = "/mining/checkBlock", method = {RequestMethod.POST})
    public ResponseEntity<JSONObject> checkBlock(@RequestBody Block b) {
    JSONObject jo = new JSONObject();

    Dictionary diffWorkload = InitUtils.intiDifficulty();// 字典表的工作量配置
    String blockHash = EncryptUtil.encryptSHA256(b.workString());
    List<Block> bs = BlockServiceImpl.queryBlockByBlockIndex(b.getBlockIndex());

    if (bs.size() != 0 || Integer.valueOf(b.getBlockIndex()) - 1 < UpdateTimer.currentMaxBlockIndex.intValue() || !blockHash.equals(b.getBlockHash()) || !blockHash.startsWith(diffWorkload.getValue())) {
    jo.setO(false);
    } else {
    mining.isWork = false;
    jo.setO(true);
    }

    jo.setCode("1");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);

    }
  3. 定时任务每秒向其他节点请求最新区块

    ①通过已经保存的对等节点的IP进行最新区块的获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public void updateBlock(String blockIndex, Mining mining, MapFriends map) throws Exception {
    BlockDownLoad bdl = null;
    for(String port:map.getFs().keySet()){
    Friends f= map.getFs().get(port);
    String ip=f.getIp();
    if (f.getFriendliness() == 0){
    continue;
    }
    NoticeParams np = new NoticeParams(blockIndex.toString(), ip,"");
    bdl = HttpHelper.downLoadBlock(ip, 8888, np);//获取区块和区块内容
    if(bdl == null) {
    continue;
    }

    //检测当前区块是否已经存在
    TradeBodyPool tbp = BlockBaseUtils.genTbp(bdl);
    List<Block> bs=BlockServiceImpl.queryBlockByBlockIndex(bdl.getBlock().getBlockIndex());
    if(bs.size() > 0 ){
    deletePending(tbp);//删除pending
    return;
    }
    BlockServiceImpl.checkBlockTable(bdl.getBlock().getBlockIndex());//检查表是否存在

    BlockServiceImpl.save(bdl.getBlock());//保存区块DB
    BlockServiceImpl.saveBlockFile(bdl);//保存区块文件
    DicServiceImpl.updateDicBlockIndex(blockIndex);//更新当前更新到的块号
    DicServiceImpl.updateDicMainBockIndex(bdl.getMaxBlockIndex());//更新当前更新到的块号
    UpdateTimer.currentBlockIndex= new BigInteger(blockIndex) ;
    UpdateTimer.currentMaxBlockIndex= new BigInteger(bdl.getMaxBlockIndex()) ;
    deletePending(tbp);//删除pending

    break;
    }
    if(bdl==null){
    mining.updateComplete=true;
    mining.isWork=true;
    //已经更新到最高区块
    throw new Exception();
    }
    }

    ②下载区块的具体方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    public static BlockDownLoad downLoadBlock(String ip, int port, NoticeParams np) {
    BlockDownLoad bdl = new BlockDownLoad();
    String url = "";
    if (StringUtils.isNotBlank(ip)) {
    url = "http://" + ip + ":" + port + "/mining/server/block.zip";
    } else {
    url = "http://" + np.getIp() + ":" + "8001" + "/mining/server/block.zip";
    }
    // create Httpclient object
    CloseableHttpClient httpClient = HttpClients.createDefault();
    CloseableHttpResponse response = null;
    ZipInputStream zis = null;
    HttpPost httpPost = null;
    try {
    System.out.println("Download Block ");
    httpPost = new HttpPost(url);
    httpPost.setHeader("Content-type", "application/json; charset=utf-8");
    httpPost.setHeader("Connection", "Close");
    httpPost.addHeader("Accept-Encoding", "GZIP");
    RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000).setConnectionRequestTimeout(15000).setSocketTimeout(15000).build();
    httpPost.setConfig(requestConfig);
    StringEntity entity = new StringEntity(np.toJSONString(), Charset.forName("UTF-8"));
    entity.setContentEncoding("UTF-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    response = httpClient.execute(httpPost);
    //get zip
    zis = new ZipInputStream(response.getEntity().getContent(), Charset.forName("UTF-8"));
    ZipEntry ze = null;
    Block block = null;
    String blockFileStr = "";
    String maxIndex = "";
    while ((ze = zis.getNextEntry()) != null) {
    if ("blockObject".equals(ze.getName())) {
    //区块对象
    block = BlockServiceImpl.getBlockObeject(zis);
    } else if ("maxblockindex".equals(ze.getName())) {
    //获得最大块编号
    maxIndex = BlockServiceImpl.getMaxBlockIndexStr(zis);
    } else if ("tokblockfile".equals(ze.getName())) {
    //获得区块文件
    blockFileStr = BlockServiceImpl.getBlockFileStr(zis);
    System.out.println(blockFileStr);
    }
    }
    if (block == null || StringUtils.isBlank(blockFileStr)) {

    //throw new Exception("down block is not complete.");
    System.out.println("当前block为空,无需下载");
    }
    bdl.setBlock(block);
    bdl.setBlockFileStr(blockFileStr);
    bdl.setMaxBlockIndex(maxIndex);
    return bdl;
    } catch (Exception e) {
    //e.printStackTrace();
    System.out.println("连接不到对等节点:" + ip);

    } finally {
    try {
    if (httpPost != null) {
    httpPost.releaseConnection();
    }
    if (httpClient != null) {
    httpClient.close();
    }
    if (response != null)
    response.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    return null;
    }

    ③读取流代码

    1
    2
    3
    4
    5
    6
    bos = new ByteArrayOutputStream(); // 创建一个字节数组输出流
    byte[] buffer = new byte[512]; // 创建一个大小为 512 字节的缓冲区
    int len = 0; // 初始化读取长度为 0
    while ((len = zis.read(buffer)) != -1) { // 当还有数据可读时继续循环
    bos.write(buffer, 0, len); // 将已读取的数据写入到字节数组输出流中
    }

    ④下载区块的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    @PostMapping(value = "/mining/server/block.zip")
    @ResponseBody
    public void blockdownload(HttpServletResponse response, @RequestBody NoticeParams noticeParams) {
    OutputStream responseBody = null;
    ZipOutputStream zos = null;
    try {
    List<Block> bs = BlockServiceImpl.queryBlockByBlockIndex(noticeParams.getBn());
    Block b = null;
    //
    if (bs != null && bs.size() > 0) {
    b = bs.get(0);
    if (b != null) {
    zos = new ZipOutputStream(response.getOutputStream());
    //写入块对象
    zos.putNextEntry(new ZipEntry("blockObject"));
    String boStr = new Gson().toJson(b);
    byte[] b_str = boStr.getBytes();
    zos.write(b_str, 0, b_str.length);
    //写入块文件
    zos.putNextEntry(new ZipEntry("tokblockfile"));
    String blockString = DataUtils.getBlockString(DataUtils.getRelativePath(b.getPath()));
    byte[] bs_str = blockString.getBytes();
    zos.write(bs_str, 0, bs_str.length);
    //写入主链上最高的编号
    zos.putNextEntry(new ZipEntry("maxblockindex"));
    Dictionary dic = DicServiceImpl.queryDic(Dictionary.MODUAL_BLOCK, Dictionary.CURRENTBLOCKINDEX);
    byte[] m_str = dic.getValue().getBytes();
    zos.write(m_str, 0, m_str.length);

    zos.closeEntry();
    zos.close();
    }
    }
    } catch (Exception e) {
    e.getMessage();
    } finally {
    if (responseBody != null) {
    try {
    responseBody.close();
    } catch (IOException e) {
    e.getMessage();
    }
    }
    if (zos != null) {
    try {
    zos.close();
    } catch (IOException e) {
    e.getMessage();
    }
    }
    }
    }
  4. 数据库的保存以及修改

    ①保存数据save

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public static boolean save(Block block) {
    Connection connection = null;
    try {
    String tableIndex = DataUtils.getBlockSerial(block.getBlockIndex());
    connection = SQLiteHelper.getConnection();
    connection.setAutoCommit(false);
    String sql = "insert into [block" + tableIndex + "]([blockIndex],[preBlockHash],[path],[createTime],[blockHash],[randomNumber],[onMingChain]) values(?,?,?,?,?,?,?);";
    PreparedStatement statement = connection.prepareStatement(sql);
    statement.setString(1, block.getBlockIndex() + "");
    statement.setString(2, block.getPreBlockHash());
    statement.setString(3, block.getPath());
    statement.setString(4, block.getCreateTime());
    statement.setString(5, block.getBlockHash());
    statement.setString(6, block.getRandomNumber());
    statement.setInt(7, 1);
    statement.execute();
    connection.commit();
    return true;
    } catch (Exception e) {
    if (connection != null) {
    try {
    connection.rollback();
    } catch (SQLException e1) {
    e.getMessage();
    }
    }
    e.getMessage();
    } finally {
    SQLiteHelper.close(connection);
    }
    return false;
    }

    ②修改数据update

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public static boolean update(Dictionary dic) {
    Connection connection = null;
    try {
    connection = SQLiteHelper.getConnection();
    connection.setAutoCommit(false);
    String sql = "update [dictionary] set [value] = ? where [module]=? and [key]=?";
    PreparedStatement statement = connection.prepareStatement(sql);
    statement.setString(1, dic.getValue());
    statement.setString(2, dic.getModule());
    statement.setString(3, dic.getKey());
    statement.execute();
    connection.commit();
    return true;
    } catch (Exception e) {
    if(connection != null) {
    try {
    connection.rollback();
    } catch (SQLException e1) {
    e.getMessage();

    }
    }
    e.getMessage();

    }finally {
    SQLiteHelper.close(connection);
    }
    return false;
    }

    ③删除数据deletePending

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public static boolean deletePendings(List<String> tradeNos) {
    if(tradeNos == null || tradeNos.size() == 0) {
    return true;
    }
    Connection connection = null;
    try {
    connection = SQLiteHelper.getConnection();
    connection.setAutoCommit(false);
    PreparedStatement statement = connection.prepareStatement("delete from [pending] where [orderNo]=?");
    for(int i = 0; i< tradeNos.size(); i++) {
    System.out.println(tradeNos.get(i));
    statement.setString(1, tradeNos.get(i));
    statement.addBatch();
    if(i % 100 ==0) {
    statement.executeBatch();
    statement.clearBatch();
    }
    }
    statement.executeBatch();
    statement.clearBatch();
    connection.commit();
    return true;
    } catch (Exception e) {
    try {
    if(connection != null) {
    connection.rollback();
    }
    } catch (SQLException e1) {
    e.getMessage();

    }
    e.printStackTrace();
    }finally {
    SQLiteHelper.close(connection);
    }
    return false;
    }
  5. 发送交易

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    @RequestMapping(value = "/data/trade", method = {RequestMethod.POST})
    public ResponseEntity<JSONObject> trade(@RequestBody TradeObject tradeObject) {
    JSONObject jo = new JSONObject();
    List<Pending> pes = PendingServiceImpl.queryPendings();


    String no = PendingServiceImpl.genTradeNo(tradeObject);
    tradeObject.setHashNo(no);


    String genTradeNo = PendingServiceImpl.genTradeNo(tradeObject);
    for (Pending p : pes) {
    if (p.getOrderNo().equals(genTradeNo)) {
    jo.setCode("-1");
    jo.setMsg("交易已存在");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    }

    //验证钱包地址


    String body = new Gson().toJson(tradeObject);
    try {
    PendingServiceImpl.validateTradeNo(tradeObject);
    Pending pending = new Pending();
    pending.setTradeBody(body);
    pending.setCreateTime(tradeObject.getJsoncreatetime());
    pending.setOrderNo(tradeObject.getHashNo());
    pending.setTradeType("1");
    PendingServiceImpl.save(pending);
    for (String port : map.getFs().keySet()) {
    Friends f = map.getFs().get(port);
    String ip = f.getIp();
    String url = "http://" + ip + ":8001/data/trade";
    restTemplate.postForEntity(url, tradeObject, TradeObject.class);
    }
    jo.setCode("1");
    jo.setMsg("成功");

    } catch (Exception e) {
    System.out.println(e.getMessage());
    jo.setCode("-1");
    jo.setMsg("失败");
    }
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);

    }
-------------本文结束感谢您的阅读-------------