首页 > 其他分享 >智能合约编写高级篇(二)区块哈希介绍

智能合约编写高级篇(二)区块哈希介绍

时间:2023-09-07 14:06:26浏览次数:37  
标签:transaction num 哈希 编写 区块 ref block

本文档从区块哈希基本概念出发,详细介绍了中移链的区块哈希交易接口和应用方向。适用于EOS区块链智能合约高级开发人员,熟悉如何获取当前发生交易所在的区块号和区块哈希前缀,并通过Tapos机制验证交易的有效性。

01

概述

(一)哈希算法

哈希算法是可以将任意长度的二进制数据映射为固定长度二进制数据(哈希值)的一种算法。在这个过程中,哈希函数将输入数据通过一系列的复杂运算变换成固定长度的输出,这个值等同于存放数据的地址,这个地址里面再将输入的数据进行存储,所以哈希函数可以将互联网上的数据以固定长度字符串的形式来保存。同时,哈希函数可以用于密码学、数据完整性验证、信息指纹等领域,常见的哈希算法有MD5、SHA-1、SHA-256等。

(二)区块哈希

区块哈希是通过哈希算法对区块中的所有数据进行计算得出的固定长度的字符串。具体来说,区块哈希是指在区块链技术中,对于每一个新生产的区块,会给这个区块计算一个固定长度的哈希值,这个哈希值包含了这个区块中所有的数据,包括交易记录、上一个区块的哈希值、时间戳等,并且只要这些数据有任何一点改变,那么这个区块的哈希值就会发生变化,所以它在保证网络安全性、防止篡改和验证数据完整性方面起着非常重要的作用。

(三)区块哈希的特点

区块哈希的存在保证了区块链的数据安全性,任何篡改数据的行为都会被立即发现,同时它也具备以下特点:

唯一性:每个区块哈希值都是唯一的,即使是区块链上有极其微小的一点数据改变,也会导致哈希值的变化。这保证了数据的不可变性和唯一标识性。

不可逆性:区块哈希函数是一个单向函数,可以将任意长度的数据转换为固定长度的哈希值。对于哈希值无法进行反向计算推导恢复原始数据,这保证了数据的安全性。

不可篡改性:如果输入数据发生了任何改变,计算得到的哈希值都会发生变化。

02

环境依赖

  • eosio_2.1.0-1
  • eosio.cdt v1.8.x

03

区块哈希接口

与区块哈希相关的交易接口分别有tapos_block_num()和tapos_block_prefix(),它们用于生成区块哈希及验证交易执行的前提条件,这有助于确保交易的有效性和安全性,并提供区块链的相关信息以支持智能合约的开发。

(一)tapos_block_num()

它是一个用于获取当前交易所引用的区块号的函数,它返回一个无符号整数值,代表当前执行交易的区块高度。块高度表示当前执行的块在整个区块链上的位置,可以用于构建块摘要和验证交易。在交易处理过程中,每个块都有一个唯一的块高度。

  • 源码描述
/**
    *  Gets the block number used for TAPOS on the currently executing transaction.
    *
    *  @ingroup transaction
    *  @return block number used for TAPOS on the currently executing transaction
    *  Example:
    *  @code
    *  int tbn = tapos_block_num();
    *  @endcode
    */
   inline int tapos_block_num() {
      return internal_use_do_not_use::tapos_block_num();
   }
  • 调用方式
#include <eosio/transaction.hpp>
#include <eosio/eosio.hpp>
uint16_t current_block_num = tapos_block_num(); // 获取用于当前执行交易所在的区块号

(二)tapos_block_prefix()

它是一个用于获取当前交易所在区块哈希前缀的函数,它返回一个无符号整数值,代表当前执行交易的区块哈希前缀。区块哈希前缀是区块哈希的一部分,用于构建block summary。它通常作为一个随机数,用于增加区块哈希的难度,以保持加密的安全性。

  • 源码描述
/**
    *  Gets the block prefix used for TAPOS on the currently executing transaction.
    *
    *  @ingroup transaction
    *  @return block prefix used for TAPOS on the currently executing transaction
    *  Example:
    *  @code
    *  int tbp = tapos_block_prefix();
    *  @endcode
    */
   inline int tapos_block_prefix() {
      return internal_use_do_not_use::tapos_block_prefix();
   }
  • 调用方式
#include <eosio/transaction.hpp>
#include <eosio/eosio.hpp>
uint32_t current_block_prefix = tapos_block_prefix(); // 获取用于当前执行交易所在的区块哈希前缀

(三)什么是TaPos机制?

TaPos是“交易作为权益证明”(Transaction-as-Proof-of-Stake)的缩写。TaPos是在EOS的交易处理过程中引入的一个概念,它是一种用于实现去中心化共识的机制,目的是确保交易执行的前提条件和验证交易的有效性。在去中心化的区块链网络中,由于网络可能存在延迟和分叉,交易的确认和执行顺序可能会有所不同,所以引入TaPos机制来防止在不包含引用区块的分叉上重放交易,从而增加了安全性和可靠性。下面我们来看一下EOS白皮书是如何对TaPos进行描述的。

Transaction as Proof of Stake (TaPoS)
The EOS.IO software requires every transaction to include part of the hash of a recent block header. This hash serves two purposes:
1. prevents a replay of a transaction on forks that do not include the referenced block.
//防止在不包含引用区块的分叉上重放交易。
2. signals the network that a particular user and their stake are on a specific fork.
//向网络发出信号,表明特定用户及其权益位于哪条特定分叉上。
Over time all users end up directly confirming the blockchain which makes it difficult to forge counterfeit chains as the counterfeit would not be able to migrate transactions from the legitimate chain.

(四)如何通过TaPos验证交易

通过TaPoS机制,EOS网络可以确保交易的顺序性并防止在不同块之间重放交易,所以每个交易都必须包含正确的TaPoS字段,即交易作为股权证明的一部分,在交易签名过程中,这些字段会与其他的交易信息一起打包进入交易,以便验证它们的有效性。为了让链更稳固,也让用户交易更安全,当链中每发生一笔交易时,都会验证两个字段,分别是ref_block_num和ref_block_prefix,以下是eosio.cdt中对它们的声明。

// eosio.cdt/1.8.1/include/eosiolib/contracts/eosio/transaction.hpp
class transaction_header {
   public:
      /**
       * Construct a new transaction_header with an expiration of now + 60 seconds.
       *
       * @brief Construct a new transaction_header object initialising the transaction header expiration to now + 60 seconds
       */
      transaction_header( time_point_sec exp = time_point_sec(current_time_point()) + 60)
         :expiration(exp)
      {}

      time_point_sec  expiration;
      /// the time at which a transaction expires
      uint16_t        ref_block_num; 
      /// specifies a block num in the last 2^16 blocks
      uint32_t        ref_block_prefix; 
      /// specifies the lower 32 bits of the block id at get_ref_blocknum
      unsigned_int    max_net_usage_words = 0UL; /// number of 8 byte words this transaction can serialize into after compressions
      uint8_t         max_cpu_usage_ms = 0UL; /// number of CPU usage units to bill transaction for
      unsigned_int    delay_sec = 0UL; /// number of seconds to delay transaction, default: 0

      EOSLIB_SERIALIZE( transaction_header, (expiration)(ref_block_num)(ref_block_prefix)(max_net_usage_words)(max_cpu_usage_ms)(delay_sec) )
   };

以下代码是在交易初始化时候验证ref_block_num和ref_block_prefix两个字段。

// eos/libraries/chain/transaction.cpp
void transaction_header::set_reference_block( const block_id_type& reference_block ) {
   ref_block_num    = fc::endian_reverse_u32(reference_block._hash[0]);
   ref_block_prefix = reference_block._hash[1];
}

bool transaction_header::verify_reference_block( const block_id_type& reference_block )const {
   return ref_block_num    == (decltype(ref_block_num))fc::endian_reverse_u32(reference_block._hash[0]) &&
          ref_block_prefix == (decltype(ref_block_prefix))reference_block._hash[1];
}
// eos/libraries/chain/transaction_context.cpp
void transaction_context::init_for_input_trx_common( uint64_t initial_net_usage, bool skip_recording )
   {
      published = control.pending_block_time();
      is_input = true;
      const transaction& trx = packed_trx.get_transaction();
      if (!control.skip_trx_checks()) {
         control.validate_expiration(trx);
         control.validate_tapos(trx);
         validate_referenced_accounts( trx, enforce_whiteblacklist && control.is_producing_block() );
      }
      init( initial_net_usage );
      
      if (!skip_recording)
         record_transaction( packed_trx.id(), trx.expiration ); /// checks for dupes
   }
// eos/libraries/chain/controller.cpp
void controller::validate_tapos( const transaction& trx )const { try {
   const auto& tapos_block_summary = db().get<block_summary_object>((uint16_t)trx.ref_block_num);

   //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
   EOS_ASSERT(trx.verify_reference_block(tapos_block_summary.block_id), invalid_ref_block_exception,
              "Transaction's reference block did not match. Is this transaction from a different fork?",
              ("tapos_summary", tapos_block_summary));
} FC_CAPTURE_AND_RETHROW() }

(五)测试用例

编写智能合约测试用例,通过调用上文中介绍的tapos_block_num()和tapos_block_prefix()两个函数来获取验证交易有效性的两个字段ref_block_num和ref_block_prefix。

#include <eosio/transaction.hpp>
#include <eosio/eosio.hpp>

using namespace eosio;

class [[eosio::contract("test")]] test: public contract {
public:
  using contract::contract;

  [[eosio::action]]
  void checktapos() {
    uint32_t current_block_num = tapos_block_num(); // 获取当前交易执行所在的区块号
    uint32_t current_block_prefix = tapos_block_prefix(); // 获取当前交易执行所在的区块哈希前缀
    print("ref_block_num: ", current_block_num,"\t\t\t");
    print("ref_block_prefix: ", current_block_prefix);
  }
};

返回结果如下:

executed transaction: b35a59a178af7dedbbad641952146470adbe5e4d48316382afec5941fcdf2372  96 bytes  146 us
#          test <= test::checktapos             ""
>> ref_block_num: 43983             ref_block_prefix: 448306994

以下是对应区块结构transaction中的ref_block_num和ref_block_prefix。

root@VM-24-16-ubuntu:/home/ubuntu/biosboot/genesis# cleos get transaction b35a59a178af7dedbbad641952146470adbe5e4d48316382afec5941fcdf2372
{
  "id": "b35a59a178af7dedbbad641952146470adbe5e4d48316382afec5941fcdf2372",
  "trx": {
    "receipt": {
      "status": "executed",
      "cpu_usage_us": 146,
      "net_usage_words": 12,
      "trx": [
        1,{
          "compression": "none",
          "prunable_data": {
            "prunable_data": [
              0,{
                "signatures": [
                  "SIG_K1_Jzmoz3cfi8H7tA3V8zidyihS7XYv29h1bnxdoFSM7ddvRXRo7q4GTJrgPKDsvbbXMFsaFxzCZeAqwwhG2GNVdjHXiL41y1"
                ],
                "packed_context_free_data": ""
              }
            ]
          },
          "packed_trx": "6b6eae64cfab329fb81a0000000001000000000090b1ca0000a6d56488544301000000000090b1ca00000000a8ed32320000"
        }
      ]
    },
    "trx": {
      "expiration": "2023-07-12T09:12:11",
      "ref_block_num": 43983,
      "ref_block_prefix": 448306994,
      "max_net_usage_words": 0,
      "max_cpu_usage_ms": 0,
      "delay_sec": 0,
      "context_free_actions": [],
      "actions": [{
          "account": "test",
          "name": "checktapos",
          "authorization": [{
              "actor": "test",
              "permission": "active"
            }
          ],
          "data": ""
        }
      ],
      "signatures": [
        "SIG_K1_Jzmoz3cfi8H7tA3V8zidyihS7XYv29h1bnxdoFSM7ddvRXRo7q4GTJrgPKDsvbbXMFsaFxzCZeAqwwhG2GNVdjHXiL41y1"
      ],
      "context_free_data": []
    }
  },
  "block_time": "2023-07-12T09:11:41.500",
  "block_num": 43985,
  "last_irreversible_block": 44144,
  "traces": [{
      "action_ordinal": 1,
      "creator_action_ordinal": 0,
      "closest_unnotified_ancestor_action_ordinal": 0,
      "receipt": {
        "receiver": "test",
        "act_digest": "f0d16f853ef72e7be5d8c84219cdcaa75d9b13d89b59c9385aebaecc20bc34f7",
        "global_sequence": 43996,
        "recv_sequence": 8,
        "auth_sequence": [[
            "test",
            11
          ]
        ],
        "code_sequence": 2,
        "abi_sequence": 1
      },
      "receiver": "test",
      "act": {
        "account": "test",
        "name": "checktapos",
        "authorization": [{
            "actor": "test",
            "permission": "active"
          }
        ],
        "data": ""
      },
      "context_free": false,
      "elapsed": 39,
      "console": "ref_block_num: 43983             ref_block_prefix: 448306994",
      "trx_id": "b35a59a178af7dedbbad641952146470adbe5e4d48316382afec5941fcdf2372",
      "block_num": 43985,
      "block_time": "2023-07-12T09:11:41.500",
      "producer_block_id": null,
      "account_ram_deltas": [],
      "account_disk_deltas": [],
      "except": null,
      "error_code": null,
      "return_value_hex_data": ""
    }
  ]
}

需要注意,ref_block_num表示的是交易所引用的区块的区块号码,而ref_block_prefix是这个区块的哈希前缀。在交易签名过程中,这些字段会与其他的交易信息一起打包进入交易中,以便验证这个交易是否合法。而在cleos get transaction命令所显示的交易信息中,另一个名为block_num的字段则表示最终执行该交易的区块的区块号码,此字段与交易提交时所引用的区块号码ref_block_num是不同的。因为在提交交易时,可能会发生交易被延迟,所以最终执行该交易的区块可能与该交易所引用的区块不同。

END

标签:transaction,num,哈希,编写,区块,ref,block
From: https://blog.51cto.com/u_15476243/7396443

相关文章

  • 编写求阶乘函数
    ​ ,计算并返回1!+2!+3!+……+n!的值。函数fact()实现计算并返回123*……*n的值;函数fun()实现计算并返回1!+2!+3!+……+n!的值;函数main()从后台获取整数n,调用函数fun(),输出结果并保留0位小数。#include<stdio.h>floatfact(floatm){floati,s=1;for(i=1;i<=m......
  • 自己编写一个发get请求案例
    在线用户的文件夹上一篇的咨询在线用户列表我们打印一下,发现确实打印了这个对象省流:如果我想要发请求,只要在api里面新建就行了。那么我们试一下创建一个test1在api下的monitor下引入对axios的封装(第一行)复制list方法,命名其为list1,为了做测试打印11111111(已经有list了)引......
  • 谷歌优化之如何编写和提交 robots.txt 文件
    您可以使用robots.txt文件控制抓取工具可以访问您网站上的哪些文件。robots.txt文件应位于网站的根目录下。因此,对于网站 www.example.com,robots.txt文件的路径应为 www.example.com/robots.txt。robots.txt是一种遵循漫游器排除标准的纯文本文件,由一条或多条规则组成。每条......
  • 编写软件检测报告有哪些注意事项?软件检测报告获取
    软件检测报告是指把测试的过程和结果写成文档,对发现的问题和缺陷进行分析,为纠正软件的存在的质量问题提供依据,同时为软件验收和交付打下基础。一、编写软件检测报告的注意事项1、报告的结构要合理和清晰。应该按照一定的逻辑顺序,将软件的测试目标、测试环境、测试方法......
  • 编写涉及多个数据库的 MySQL 存储过程注意事项
    在编写涉及多个数据库的MySQL存储过程时,有一些注意事项需要考虑。以下是一些重要的注意事项:数据库权限:确保您在存储过程中使用的数据库用户具有足够的权限来访问和操作涉及的所有数据库。这包括对表、视图、存储过程等对象的读取和写入权限。数据库连接:在存储过程中,您可能......
  • 使用python自动根据数据库的成品重量编写一个ppt并保存在"d:\test.ppt"
    要使用Python自动创建一个PPT并根据数据库中的成品重量生成内容,你可以使用Python的`python-pptx`库来实现。首先,你需要确保已经安装了这个库。你可以使用以下命令安装它:```pythonpipinstallpython-pptx```接下来,你可以按照以下步骤创建一个Python脚本来实现你的需求:```py......
  • 20.2 设备树中的 platform 驱动编写
    一、设备树下的platform驱动  platform驱动框架分为总线、设备和驱动,总线不需要我们去管理,这个是Linux内核提供。在有了设备树的前提下,我们只需要实现platform_driver即可。 1. 修改pinctrl-stm32.c文件   先复习一下pinctrl子系统和gpio子系统,pinctrl子......
  • 【原创】基于QT编写的支持IPv4/IPv6双协议栈,TCP/UDP双模式,DLL内存加载的模块化远控木
    本人已经本科毕业一年有余,在平常实习过程中,发现大佬都对我的本科毕设--双协议栈远控木马感兴趣。据我所知,目前流行的C2远控软件中,MSF支持IPv4和IPv6,但是MSF生成的单个木马只是支持其中的一种协议,而不是双协议栈。CobaltStrike目前尚无IPv6的使用案例。其他支持双协议栈的C2软件......
  • Python开发实例(十一)单词记忆游戏:编写一个简单的游戏,测试用户对一组随机单词的记忆能力
    在这个实例中,我们将创建一个简单的单词记忆游戏。游戏的规则是随机展示一组单词,然后要求用户在一定时间内尽可能多地记住这些单词。时间到后,再询问用户输入这些单词。最后,计算并显示用户正确记住的单词数量。下面是单词记忆游戏的Python程序:pythonCopycodeimportrandomimport......
  • 2023-09-05:请用go语言编写。一个图像有n个像素点,存储在一个长度为n的数组arr里, 每个像
    2023-09-05:请用go语言编写。一个图像有n个像素点,存储在一个长度为n的数组arr里,每个像素点的取值范围[0,s]的整数,请你给图像每个像素点值加上一个整数k(可以是负数),像素值会自动截取到[0,s]范围,当像素值<0,会更改为0,当新像素值>s,会更改为s,这样就可以得到新的arr,想让所有像素点的......