您正在查看: EOS 分类下的文章

eos 离线计算交易id

场景

由于一些场景需要提前计算出上传的交易ID,
类似问题,已提交issue https://github.com/EOSIO/eos/issues/7816
由于一些问题,(猜测单个nodeos推送了太多事务)导致事务推送返回超时,但实际上事务已成功推送。我暂时想到的解决方案是,我希望在提交交易之前预先计算交易的ID,以便在重复交易时首先确定交易ID是否已经存在。搜索之后,我发现'cleos get transaction_id'(#6830)似乎是我想要的功能,但是测试不同的数据,总是返回相同的id。我用方法问题?或者这个功能没有像这样使用

跟进

先提前跟一下代码
cleos get transaction_id
https://github.com/EOSIO/eos/blob/1418543149b7caf8fc69a23621e3db7f3c6d18ad/programs/cleos/main.cpp#L1306

struct get_transaction_id_subcommand {
   string trx_to_check;

   get_transaction_id_subcommand(CLI::App* actionRoot) {
      auto get_transaction_id = actionRoot->add_subcommand("transaction_id", localized("Get transaction id given transaction object"));
      get_transaction_id->add_option("transaction", trx_to_check, localized("The JSON string or filename defining the transaction which transaction id we want to retrieve"))->required();

      get_transaction_id->set_callback([&] {
         try {
            fc::variant trx_var = json_from_file_or_string(trx_to_check);
            if( trx_var.is_object() ) {
               fc::variant_object& vo = trx_var.get_object();
               // if actions.data & actions.hex_data provided, use the hex_data since only currently support unexploded data
               if( vo.contains("actions") ) {
                  if( vo["actions"].is_array() ) {
                     fc::mutable_variant_object mvo = vo;
                     fc::variants& action_variants = mvo["actions"].get_array();
                     for( auto& action_v : action_variants ) {
                        if( !action_v.is_object() ) {
                           std::cerr << "Empty 'action' in transaction" << endl;
                           return;
                        }
                        fc::variant_object& action_vo = action_v.get_object();
                        if( action_vo.contains( "data" ) && action_vo.contains( "hex_data" ) ) {
                           fc::mutable_variant_object maction_vo = action_vo;
                           maction_vo["data"] = maction_vo["hex_data"];
                           action_vo = maction_vo;
                           vo = mvo;
                        } else if( action_vo.contains( "data" ) ) {
                           if( !action_vo["data"].is_string() ) {
                              std::cerr << "get transaction_id only supports un-exploded 'data' (hex form)" << std::endl;
                              return;
                           }
                        }
                     }
                  } else {
                     std::cerr << "transaction json 'actions' is not an array" << std::endl;
                     return;
                  }
               } else {
                  std::cerr << "transaction json does not include 'actions'" << std::endl;
                  return;
               }
               auto trx = trx_var.as<transaction>();
               transaction_id_type id = trx.id(); // 计算交易id
               if( id == transaction().id() ) {
                  std::cerr << "file/string does not represent a transaction" << std::endl;
               } else {
                  std::cout << string( id ) << std::endl;
               }
            } else {
               std::cerr << "file/string does not represent a transaction" << std::endl;
            }
         } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_check))
      });
   }
};

再查看主链代码
https://github.com/EOSIO/eos/blob/1e9ca55cc35b003c81ff4da780fb3cb869a16607/libraries/chain/transaction.cpp#L66

transaction_id_type transaction::id() const {
   digest_type::encoder enc;
   fc::raw::pack( enc, *this );
   return enc.result();
}

digest_type transaction::sig_digest( const chain_id_type& chain_id, const vector<bytes>& cfd )const {
   digest_type::encoder enc;
   fc::raw::pack( enc, chain_id );
   fc::raw::pack( enc, *this );
   if( cfd.size() ) {
      fc::raw::pack( enc, digest_type::hash(cfd) );
   } else {
      fc::raw::pack( enc, digest_type() );
   }
   return enc.result();
}

看实现代码,应该没问题
再看看关键词,每次都是返回374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb
搜一下
https://github.com/EOSIO/eos/issues/6603
过期交易错误,会返回这种?
猜测是当前获取交易id的数据过期了,那使用新的交易体计算下。
待补充

等等,先去eosjs 那边看下,

反向到eosjs找下线索

const serializedTransaction = await api.transact({
        ....
      }, {
        blocksBehind: 3,
        expireSeconds: 3600,
        broadcast: false
      });
console.log(Crypto.createHash("sha256").update(serializedTransaction).digest("hex"));

前端可使用 Crypto https://github.com/crypto-browserify/createHash

java
https://github.com/adyliu/jeos/blob/f0ef9bd72b97a118b077f6d585146d36b9739015/src/main/java/io/jafka/jeos/util/SHA.java#L19

 public static byte[] sha256(final byte[] b) {
        Objects.requireNonNull(b);
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(b);
            return messageDigest.digest();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

添加 contextFreeActions
https://github.com/EOSIO/eosio-java/blob/a9202879f31edb4122e768df0dc7fca391eff7e7/eosiojava/src/main/java/one/block/eosiojava/session/TransactionProcessor.java#L261

public void prepare(@NotNull List<Action> actions, @NotNull List<Action> contextFreeActions) throws TransactionPrepareError {

serializedTransaction 计算
https://github.com/EOSIO/eosio-java/blob/a9202879f31edb4122e768df0dc7fca391eff7e7/eosiojava/src/test/java/one/block/eosiojava/session/TransactionProcessorTest.java#L175

@Test
    public void serialize() {
        this.mockDefaultSuccessData();
        TransactionProcessor processor = createAndPrepareTransaction(this.defaultActions());
        assertNotNull(processor);

        try {
            String serializedTransaction = processor.serialize();
            assertEquals(MOCKED_TRANSACTION_HEX, serializedTransaction);
        } catch (TransactionSerializeError transactionSerializeError) {
            transactionSerializeError.printStackTrace();
        }
    }

参考

https://github.com/EOSIO/eosjs/issues/460
https://github.com/EOSIO/eos/issues/5935
https://github.com/EOSIO/eos/issues/6830
https://eosio.stackexchange.com/questions/1941/transaction-id-is-a-hash-of-what
https://github.com/EOSIO/eos/issues/7816

先进的EOSIO编程理念

在审查EOS智能合约时,我注意到每个开发人员都有自己的编程风格,并且有许多不同的方法可以执行相同的操作,例如处理时间或将操作发送到其他合约。这篇文章是一些EOSIO库函数的思想转述,我觉得这些函数很优雅且有用。

1. eosio::same_payer

第一个只是一个常量表达式,可以在修改多索引表的条目时使用。使用eosio::same_payer时,将使用的新RAM(如果有)将分配给已为表条目支付的同一帐户。
用法:

statstable.modify( st, eosio::same_payer, [&]( auto& s ) {
    s.supply += quantity;
});

它在[multi_index.hpp]中定义,只是空name(value:0)""_n 或者name(0)的常量表达式,一些开发人员仍然使用它来表示同一个付款人。

2. get_first_receiver,get_self()

在contracts.hpp中定义的两个getters get_selfget_first_receiver返回正在运行的操作的执行上下文的一部分。(在EOSIO.CDT1.6中实现了get_first_receiver以支持旧的get_code,现在不推荐使用。)get_self方法返回当前运行代码的合约,而get_first_receiver返回动作源自的帐户。除非涉及通过require_recipient的通知,否则这两个帐户是相同的。

例如,通过侦听eosio.token的传输操作的通知,get_self()返回你的合约部署到的帐户,而get_first_receiver()返回eosio.token帐户。这是因为该行为来自向你的合约帐户发送转移操作到eosio.token帐户的帐户。

用法:

[[eosio::on_notify("eosio.token::transfer")]] void cryptoship::transfer(name from, name to, const asset &quantity,
                          string memo) {
  print(get_self()); // cryptoship
  print(get_first_receiver()); // eosio.token
}

3.action_wrapper

许多用例需要将合约代码中的新操作发送到另一个合约。这是合约可以相互积极沟通的唯一方式。同样,有很多方法可以做到这一点,但最优雅的方法之一是使用eosio::action_wrappers。它为特定智能合约代码的特定操作创建操作模板,然后可用于调用此操作。

第一个参数是操作名称,第二个参数是操作的方法声明。
用法
eosio.token标头为eosio.token.hpp header file中的所有操作定义了动作包装器:

[[eosio::action]]
void create( name   issuer,
            asset  maximum_supply);

[[eosio::action]]
void issue( name to, asset quantity, string memo );

[[eosio::action]]
void retire( asset quantity, string memo );

[[eosio::action]]
void transfer( name    from,
              name    to,
              asset   quantity,
              string  memo );

// ...

using create_action = eosio::action_wrapper<"create"_n, &token::create>;
using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>;
using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>;
using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>;
// ...

我们现在可以通过包含此头文件向任何eosio.token合约发送内联操作。

需要注意的是,只需要包含带有声明的头文件。意思是,即使对于具有未知实现细节的闭源合约,也可以轻松编写动作包装器。只需要写出声明,动作签名,可以从ABI获得。

使用eosio-cpp的-I标志包含头文件的附加包含目录。

包含头文件后,将发送内联传输操作,如下所示:

#include <eosio_token/include/eosio_token.hpp>

// can specify the contract to send the action to as first argument
token::transfer_action payout("eosio.token"_n, {get_self(), "active"_n});
// transfer arguments are now passed as postional arguments
payout.send(get_self(), to, quantity, memo);

对于使用to_action方法的延迟交易,同样适用:

token::transfer_action payout("eosio.token"_n, {get_self(), "active"_n});

transaction t{};
t.actions.emplace_back(payout.to_action(get_self(), to, quantity, memo));
t.delay_sec = 10;
t.send(0 /* sender id */, get_self(), false);

4. EOSIO时间类time_point,time_point_sec,microseconds

EOSIO库在time.hpp header中定义了两个日期类,它们的精度不同。time_point_sec类是一个标准的UNIX时间戳,用于存储自1970年1月1日以来在uint32_t中的秒数,time_point具有更精确的精度,用于存储uint64_t中经过的microseconds(而不是毫秒数)。从两个类转换都很容易。

要使用时间算术,可以使用microseconds,它带有有用的助手,如secondsminuteshours

用法:

eosio::time_point tp = eosio::current_time_point();
eosio::time_point_sec tps = eosio::current_time_point();
eosio::microseconds micros = tp.time_since_epoch();
uint64_t count_micros = micros.count();
uint32_t count_seconds = tps.sec_since_epoch();

// no more 60*60*24*1e6
const auto MICROSECONDS_IN_DAY = hours(24);
count_micros += MICROSECONDS_IN_DAY;
// no more 60*60*24
count_seconds += hours(24).to_seconds();

eosio::time_point_sec lastGame = /* ... */;
check((eosio::time_point_sec)(current_time_point() + minutes(1)) >= lastGame,
      "last game not finished");

使用microseconds及其助手可以避免任何类型的常量,如const auto SECONDS_PER_DAY = 60*60*24,使代码更容易推理。

转载自:http://blog.hubwiz.com/2019/06/28/eos-programming-concepts/

EOS Detective——EOS 的主要追踪工具包

EOS Nation 刚刚推出了 eosdetective.io 工具包,可以让 EOS 区块链的取证数据分析触手可及。使用 EOS
Detective 时,您将能够识别 EOS 帐户之间的关系并在图表中显示结果。该工具包免费提供,能让用户自己成为侦探。

https://medium.com/@eosnationbp/introducing-eos-detective-db9e709d68d3

https://eosdetective.io

如何使用Keycat

使用Keycat登录Bloks.io
Keycat是一个基于钥匙串的身份验证器。使用Keycat,您可以像登录电子邮件一样访问任何区块链中的DApps。

在本教程中,我们将演示如何使用Keycat 登录Bloks(EOS资源管理器)以及如何在Bloks中的EOSDAQ(基于EOS的DEX)上交易令牌。

  1. 首先,访问bloks.io并单击页面右上角的“登录”。

  2. 弹出如下所示,单击“Keycat”图标。

  3. 首先,单击“导入帐户”以将您的EOS帐户导入Keycat。然后,输入您的EOS帐户和私钥,然后按“下一步”。

  4. 弹出“保存密码”时,必须单击“保存”以便以后连续单击登录。

  5. 您现在已完成使用Keycat登录Bloks.io,并且您的用户帐户显示在右上角。

开源地址:https://github.com/EOSDAQ/keycat