Advanced Smart Contract Writing (1) Obtaining block time

Starting from the basic concept of block time, this document introduces the block time interface and application direction of China Mobile Chain. Applicable to senior developers of EOS smart contracts, familiar with how to obtain the current block time, the block time of the next block, the conversion of timestamp, etc.

01

overview

(1) Timestamp

A timestamp is a sequence of characters or encoded information that identifies when a particular event occurred. Today, usage of the term has expanded to refer to digital date and time information superimposed on digital information. For example, computer files contain timestamps that indicate when the file was last modified. However, with the birth of digital documents, electronic data has the characteristics of fragility, variability, concealment, and carrier diversity, and is easy to be copied, deleted, and tampered with, and difficult to be discovered. Therefore, in the actual judicial identification process of electronic data, it is difficult to accurately confirm the time of its generation and the authenticity and integrity of its content.

(2) Blockchain timestamp

The blockchain timestamp system is actually a distributed timestamp service implemented on a P2P network through a consensus algorithm between nodes. It is a chain composed of blocks that are ordered in time by using timestamps. When each new block is generated, it will be stamped with a time stamp, and finally connected into a blockchain according to the sequence of block generation time, and each independent node establishes a connection through the P2P network, thus forming a go-to for the record of information data. Centralized distributed timestamp service system.

(3) Features of blockchain timestamp

Because the blockchain has the following characteristics, the time stamp can be completely trusted: (1) It cannot be destroyed/modified; (2) Block has natural time characteristics, and the time stamp is one of the Block meta fields; (3) Block can store transaction information, Transactions are data that can be "written"; timestamps make it exponentially harder to change a record, with older records being harder to change.

02

environment dependent

  • eosio_2.1.0-1:

https://github.com/eosio/eos/releases/download/v2.1.0/eosio_2.1.0-1-ubuntu-18.04_amd64.deb

  • eosio.cdt v1.8.x:

https://github.com/EOSIO/eosio.cdt/releases/tag/v1.8.1

03

Block time class and interface

The classes related to block time are mainly:

  • eosio::time_point

  • eosio::time_point_sec

  • eosio::block_timestamp

Where block_timestamp is the block timestamp, time_pointUnix epoch timestamp (millisecond precision), time_point_secUnix epoch timestamp (second level precision).

The time can be obtained through the external interface for business processing, among which current_block_time() and current_time_point() are mainly used to operate on the block time, and return block_timestamp instance and time_point instance respectively. expiration() is mainly used to operate on the transaction time in the block, and returns uint32_t integer.

(1) External interface

1、current_block_time()

Get the Unix epoch timestamp (block_timestamp instance, in microseconds) of the current block.

  • calling method

#include<eosio/time.hpp>
#include<eosio/system.hpp>
auto cur_timestamp = current_block_time();

2、current_time_point()

Get the Unix epoch timestamp (time_point instance in microseconds) of the current block.

  • calling method

#include<eosio/time.hpp>
#include<eosio/system.hpp>
auto cur_timepoint = current_time_point();

3、expiration()

Get the Unix epoch timestamp (uint32_t in seconds) of the current transaction.

  • calling method

#include<eosio/transaction.hpp>
auto trx_time = eosio::expiration();

transaction_header _trx_header;

4. Detailed explanation of external interface comparison

Both current_block_time() and current_time_point() represent the block time, which corresponds to the timestamp in the block structure, and the difference lies in the specific instances. (Note: timestamp can represent half a second, and the to_string() function provided in the block_timestamp and time_point classes can only output string format time with a precision of seconds, although the precision of the timestamp can represent half a second)

  • current_block_time() obtains the current block time (block_timestamp class instance), and provides related operation functions, next() obtains the block time of the next block (block_timestamp class instance), converts it into an integer timestamp by converting it into a time_point instance, and performs Business operations or output timestamps.

  • current_time_point() directly obtains the current block time (time_point instance), which can perform business operations or output timestamps. The time_point instance can be converted into a block_timestamp class instance through the constructor of the block_timestamp class.

  • expiration() obtains the timestamp of the transaction to which the current action belongs, corresponding to the expiration in the transaction in the block structure.

The following is the block structure (including transactions):

jasmine@Jasmine:~/eosio/eos/build/bin$ ./cleos get block 110
{
  "timestamp": "2023-03-31T01:31:15.000",
  "producer": "eosio",
  "confirmed": 0,
  "previous": "0000006d5b29b623251bea57a48155377081a1b34fa3be9aa5cde21e7d50cb18",
  "transaction_mroot": "706eb8ba97278c9f2a6a5ca4c255394403a3abd60bdd6e09265101d9d0626f3c",
  "action_mroot": "357eb8fba52b02a68930c6d577dbc53523463534026c60100bf84f28b2ad42ae",
  "schedule_version": 0,
  "new_producers": null,
  "producer_signature": "SIG_K1_KgCxbksk9ttikb1WkLvaAtv9doCcjD9CAgBznTyitbfPNWfkQfPrQpW8NE2WPqmNbJx8onM23uKSmRi3X3zfH5PS2LXstu",
  "transactions": [{
      "status": "executed",
      "cpu_usage_us": 40787,
      "net_usage_words": 25,
      "trx": {
        "id": "e2205dd5cfd536ac07adde8bdf9fa341cc736a5751ec4f6aadd17e8ec6601dcf",
        "signatures": [
          "SIG_K1_KVxTaDLMu65ZrPnkrtxmZe7BrjdBbF3XztEdYNo9XkN8SJAnPF3cHoMyC5PWKUCF7UCqQgRi6etMTF3dnjooEVEe7QFA4b"
        ],
        "compression": "none",
        "packed_context_free_data": "",
        "context_free_data": [],
        "packed_trx": "003826646c009a80664500000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed3232660000000000ea3055000000008440a54a01000000010002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf0100000001000000010002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf0100000000",
        "transaction": {
          "expiration": "2023-03-31T01:31:44",
          "ref_block_num": 108,
          "ref_block_prefix": 1164345498,
          "max_net_usage_words": 0,
          "max_cpu_usage_ms": 0,
          "delay_sec": 0,
          "context_free_actions": [],
          "actions": [{
              "account": "eosio",
              "name": "newaccount",
              "authorization": [{
                  "actor": "eosio",
                  "permission": "active"
                }
              ],
              "data": {
                "creator": "eosio",
                "name": "demo11",
                "owner": {
                  "threshold": 1,
                  "keys": [{
                      "key": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
                      "weight": 1
                    }
                  ],
                  "accounts": [],
                  "waits": []
                },
                "active": {
                  "threshold": 1,
                  "keys": [{
                      "key": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
                      "weight": 1
                    }
                  ],
                  "accounts": [],
                  "waits": []
                }
              },
              "hex_data": "0000000000ea3055000000008440a54a01000000010002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf0100000001000000010002c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf01000000"
            }
          ]
        }
      }
    }
  ],
  "id": "0000006e062a01ae4fa1829b16a28dc1cb19b253c4612541590d7d25e9f0fa53",
  "block_num": 110,
  "ref_block_prefix": 2609029455
}

(2) block_timestamp class

1、block_timestamp()

Constructor

  • Source code description

block_timestamp(const time_point& t) {
    set_time_point(t);
}
block_timestamp(const time_point_sec& t) {
    set_time_point(t);
}

2、block_interval_ms

In the EOS blockchain, a block is generated every 0.5 seconds, and block_interval_ms is the block interval (500 milliseconds)

  • Source code description

static constexpr int32_t block_interval_ms = 500;

3、from_iso_string()

Convert time in string format to block_timestamp instance (Note: The format of the incoming string is %Y-%m-%dT%H:%M:%S, and the precision is seconds)

  • Source code description

static block_timestamp from_iso_string(const std::string& date_str) {
    auto time_p = time_point::from_iso_string(date_str);
    return block_timestamp{ time_p };
}

4、next()

Return the block time of the next block (block_timestamp instance)

The block time of the next block = current block time + block_interval_ms

  • Source code description

block_timestamp next() const {
    eosio::check( std::numeric_limits<uint32_t>::max() - slot >= 1, "block timestamp overflow" );
    auto result = block_timestamp(*this);
    result.slot += 1;
    return result;
}

5、to_time_point()

return time_point instance

  • Source code description

time_point to_time_point() const {
    return (time_point)(*this);
}

6、to_string()

Return time string (note: the string format is %Y-%m-%dT%H:%M:%S, the block interval is 500 milliseconds because the precision is seconds, and some blocks have error loss when converting to string)

  • Source code description

std::string to_string() const {
    return to_time_point().to_string();
}

7. Test cases

/*
测试block_timestamp类,由current_block_time()获取当前区块时间的block_timestamp实例,并进行如下测试:
  测试获取静态变量区块间隔时间block_interval_ms
  测试from_iso_string()验证时间转换精度
  测试next()获取下一个区块时间戳
  测试to_time_point()转换block_timestamp实例为time_point实例
  测试to_string()返回时间字符串
  测试block_timestamp重载运算符
*/
ACTION tstime::gettime(){
    block_timestamp current_timestamp = current_block_time();               //获取当前区块时间的block_timestamp实例
    
    //测试获取静态变量区块间隔时间block_interval_ms
    int32_t ms = eosio::block_timestamp::block_interval_ms;                      //current_timestamp.block_interval_ms:500(毫秒)
  
  //测试from_iso_string()验证时间转换精度
    auto my_timestamp = eosio::block_timestamp::from_iso_string("2023-03-31T01:31:15.000").to_time_point().time_since_epoch().count();     //时间戳:1680226275000000
    auto my_timestamp3 = eosio::block_timestamp::from_iso_string("2023-03-31T01:31:15.999").to_time_point().time_since_epoch().count();    //时间戳:1680226275000000(微秒) 结果相同,时间字符串的解析精度为秒
    
    //测试next()验证下一个区块的时间戳与当前区块时间戳关系
  auto next_interval = current_timestamp.next().to_time_point() - current_timestamp.to_time_point();  //500 下一个区块与当前区块的间隔时间 
    
    // 测试to_time_point()转换block_timestamp实例为time_point实例
  auto cur_timestamp_timepoint = current_timestamp.to_time_point();               //当前区块block_timestamp实例
    auto next_timestamp_timepoint = current_timestamp.next().to_time_point();       //下一个区块block_timestamp实例
    
    //测试to_string()返回时间字符串
    eosio::print("block_timestamp is:",current_timestamp.to_string(),"\t");             //返回时间字符串2023-05-05T08:13:15
    
  //测试block_timestamp实例的重载运算符    
  check(next_timestamp > current_timestamp,"next_timestamp <= current_timestamp");   
    check(next_timestamp < current_timestamp,"next_timestamp >= current_timestamp"); 
    check(next_timestamp != current_timestamp,"next_timestamp == current_timestamp");
    check(next_timestamp >= current_timestamp,"next_timestamp < current_timestamp");
}
}

(3) time_point class

1、time_point()

Constructor

  • Source code description

explicit time_point( microseconds e = microseconds() ) :elapsed(e){}

2、elapsed

The expiration time of the current instance (in microseconds)

  • Source code description

microseconds elapsed;

3、from_iso_string()

Convert the time in string format to a time_point instance (Note: The format of the incoming string is %Y-%m-%dT%H:%M:%S, and the precision is seconds)

  • Source code description

static time_point from_iso_string(const std::string& date_str) {
    std::tm tm;
    check(strptime(date_str.c_str(), "%Y-%m-%dT%H:%M:%S", &tm), "date parsing failed");

    auto tp = std::chrono::system_clock::from_time_t( ::mktime( &tm ) );
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>( tp.time_since_epoch() );
    return time_point{ microseconds{ static_cast<int64_t>(duration.count()) } };
}

4、time_since_epoch()

Returns the Unix epoch timestamp of the current instance (precision: microseconds)

  • Source code description

const microseconds& time_since_epoch()const { return elapsed; }

5、sec_since_epoch()

Returns the Unix epoch timestamp (precision: seconds) of the current instance

  • Source code description

uint32_t    sec_since_epoch()const  { return uint32_t(elapsed.count() / 1000000); }

6、to_string()

Return time string (note: the string format is %Y-%m-%dT%H:%M:%S, the block interval is 500 milliseconds because the precision is seconds, and some blocks have error loss when converting to string)

  • Source code description

std::string to_string() const {
    time_t rawtime = sec_since_epoch();

    char buf[100];
    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", gmtime ( &rawtime ));

    return std::string{buf};
}

7. Test cases

/*
测试time_point类,current_time_point()获取当前区块时间的time_point实例,并进行如下测试:
  测试获取变量过期时间elapsed
  测试from_iso_string()验证时间转换精度
  测试time_point实例转换为block_timestamp实例
  测试sec_since_epoch()的返回时间戳精度
  测试time_since_epoch()的返回时间戳精度
  测试to_string()返回时间字符串
  测试time_point重载运算符
*/
ACTION tstime::gettime(){
    time_point block_time_point = current_time_point();                                 //获取当前区块time_point实例
    
    //测试获取变量过期时间elapsed
    auto cur_elapsed = block_time_point.elapsed.count();                                // 当前区块time_point实例时间戳 1683274397500000(微秒)
    
    //测试from_iso_string()验证时间转换精度
    auto my_time_point = eosio::time_point::from_iso_string("2023-03-31T01:31:15.000").time_since_epoch().count();//时间戳:1680226275000000(微秒) 
    auto my_time_point2 = eosio::time_point::from_iso_string("2023-03-31T01:31:15.999").time_since_epoch().count();//时间戳:1680226275000000(微秒)结果相同,时间字符串的解析精度为秒
    
    //测试time_point实例转换为block_timestamp实例
    auto blocktimestamp_trs = eosio::block_timestamp(block_time_point);                 //time_point 转block_timestamp
    
    //测试sec_since_epoch()的返回时间戳精度
    uint32_t cur_time_point_sec_since = block_time_point.sec_since_epoch();             //返回时间戳1683274397(秒)
    
    //测试time_since_epoch()的返回时间戳精度
    int64_t cur_time_point_time_since = block_time_point.time_since_epoch().count();    //返回时间戳1683274397500000(微秒)
    
    //测试to_string()返回时间字符串
    eosio::print("block_time_point is:",block_time_point.to_string(),"\t");             //返回时间字符串2023-05-05T08:13:15
    
    //测试time_point重载运算符
    eosio::time_point resadd1 = my_time_point4 + my_time_point;             
    eosio::time_point resadd2 = my_time_point4 + my_time_point.elapsed;
    eosio::time_point ressub1 = my_time_point4 - my_time_point.elapsed;
    eosio::microseconds ressub2 = my_time_point4 - my_time_point;
    check(my_time_point4 > my_time_point,"my_time_point4 <= my_time_point");
    check(my_time_point4 < my_time_point,"my_time_point4 >= my_time_point"); 
    check(my_time_point4 != my_time_point,"my_time_point4 == my_time_point");
    check(my_time_point4 >= my_time_point,"my_time_point4 < my_time_point");
}
}

(4) time_point_sec class

1、time_point_sec()

Constructor

  • Source code description

time_point_sec():
    utc_seconds(0){}
explicit time_point_sec(uint32_t seconds )
    :utc_seconds(seconds){}
time_point_sec( const time_point& t )
    :utc_seconds( uint32_t(t.time_since_epoch().count() / 1000000ll) ){}

2、from_iso_string()

Convert the time in string format to a time_point instance (Note: The format of the incoming string is %Y-%m-%dT%H:%M:%S, and the precision is seconds)

  • Source code description

static time_point_sec from_iso_string(const std::string& date_str) {
    auto time_p = time_point::from_iso_string(date_str);
    return time_point_sec{ time_p };
}

3、to_string()

Return time string (note: the string format is %Y-%m-%dT%H:%M:%S, and the precision is seconds)

  • Source code description

std::string to_string() const {
    return ((time_point)(*this)).to_string();
}

4、sec_since_epoch()

Returns the Unix epoch timestamp (precision: seconds) of the current instance

  • Source code description

uint32_t sec_since_epoch()const { return utc_seconds; }

5. Test cases

/*
测试time_point_sec类,由transaction_header.expiration获取当前交易的time_point_sec实例,并进行如下测试:
  测试from_iso_string()验证时间转换精度
  测试to_string()返回时间字符串
  测试sec_since_epoch()返回时间戳
*/
ACTION tstime::gettime(){
    transaction_header _trx_header;
    eosio::time_point_sec trx_time_sec = _trx_header.expiration;
    //测试from_iso_string()验证时间转换精度
    auto trx_time_secmy01 = eosio::time_point_sec::from_iso_string("2023-03-31T01:31:15.000").sec_since_epoch();//返回时间戳1680226275(秒)
    auto trx_time_secmy02 = eosio::time_point_sec::from_iso_string("2023-03-31T01:31:15.500").sec_since_epoch();//返回时间戳1680226275(秒)结果相同,时间字符串的解析精度为秒
    
    //测试to_string()返回时间字符串
    auto trx_time_str = trx_time_sec.to_string(); 
    
    //测试sec_since_epoch()返回时间戳
    auto trx_timestamp = trx_time_sec.sec_since_epoch();    // 返回当前交易时间戳16832743977(秒)
}

-END-

Guess you like

Origin blog.csdn.net/BSN_yanxishe/article/details/131721506