您正在查看: surou 发布的文章

EOS 合约中使用eosio::checksum256作为查询索引,查询不到问题

演示合约代码

合约中Table表结构

struct [[eosio::table, eosio::contract("bcskillsurou")]] extract_token {
    uint64_t id;
    eosio::checksum256 transaction_id;                   // 交易单id

    auto primary_key() const { return id; }
    eosio::checksum256 second_key() const { return transaction_id; } // 已hash作为二级索引

    EOSLIB_SERIALIZE(extract_token, (id)(transaction_id))
};
typedef eosio::multi_index<"xxxxtb"_n, extract_token, eosio::indexed_by<"bysubkey"_n, eosio::const_mem_fun<extract_token, eosio::checksum256, &extract_token::second_key>>> xxxx_table;

合约中查询代码

ACTION findtrxid(eosio::checksum256 trx_id){
    ....
    xxx_table xxxtb(_self, _self.value);
    auto sub_extract_index = xxxtb.get_index<"bysubkey"_n>();
    auto itr_xxx = sub_xxx_index.find(trx_id);

    eosio_assert(itr_extract != sub_xxx_index.end(), "Record not found");
    ....
}

使用Cleos 复现问题

cleos push action 合约账户 findtrxid '{"trx_id":0bb0e5901c3b627484c6a1a473ba8e3e3a3ffb4b96f849e08afb2d1e2671b88d}' -p 合约账户

返回

Error 3050003: eosio_assert_message assertion failure
Error Details:
assertion failure with message: {"code":102,"msg":"Record not found 563"}

经过测试发现是因为eosio::checksum256 trx_id传参最终打包不是已字符串打包导致,交易id上传后被识别成了

1116029241376226714100000000000000000000000000000000000000000000

解决方案

修改eosio::checksum256 trx_id为字符串即可

cleos push action 合约账户 findtrxid '{"trx_id":"0bb0e5901c3b627484c6a1a473ba8e3e3a3ffb4b96f849e08afb2d1e2671b88d"}' -p 合约账户

对于其他语言封装,处理eosio::checksum256 trx_id统一字符串打包即可

备注

特殊hash复现

参考

https://eosio.github.io/eosio.cdt/1.6.0/group__multiindex.html

received a go away message from xxxx, reason = authentication failure

主动连接方提示:

info  2019-09-03T07:20:45.197 thread-0  net_plugin.cpp:2293           handle_message       ] received a go away message from 17.1.0.9:7860, reason = authentication failure

被连接方提示:

error 2019-09-04T09:30:24.112 thread-0  net_plugin.cpp:2715           authenticate_peer    ] Peer web0_92:6886 - 5a05387 sent a handshake with a timestamp skewed by more than 1 second.
error 2019-09-04T09:30:24.112 thread-0  net_plugin.cpp:2251           handle_message       ] Peer not authenticated.  Closing connection.

由于BP节点config设置了对等网络验证

allowed-connection = producers
allowed-connection = specified

此时被链接方已经添加了主动连接方的peer-key

peer-key = "FSC71Uiuk23RACZ2PKDZQUeAgyA4w8g9DYurjtBktNes382zn1tMP"

跟下代码
https://github.com/EOSIO/eos/blob/be804bf63c5092a123c3e1a468559a8164bcd3be/plugins/net_plugin/net_plugin.cpp#L2788

namespace sc = std::chrono;
      sc::system_clock::duration msg_time(msg.time);
      auto time = sc::system_clock::now().time_since_epoch();
      if(time - msg_time > peer_authentication_interval) {
         fc_elog( logger, "Peer ${peer} sent a handshake with a timestamp skewed by more than ${time}.",
                  ("peer", msg.p2p_address)("time", "1 second")); // TODO Add to_variant for std::chrono::system_clock::duration
         return false;
      }

Peer clock may be no more than 1 second skewed from our clock, including network latency.
对等时钟与我们的时钟相差不超过1秒,包括网络延迟。

主动连接方发起连接时(当前机器本地时间)到被连接方处理时(被连方本地时间)不能超过1秒(peer_authentication_interval
如果超时就直接拒绝

解决方案

各节点开启定时同步时间服务

sudo apt-get install chrony

参考:https://blog.csdn.net/kinglyjn/article/details/53606791

eos build tag --dirty

git describe --tags --dirty
git update-index --refresh
git describe --tags --dirty

参考

https://stackoverflow.com/questions/16035240/why-is-git-describe-dirty-adding-a-dirty-suffix-when-describing-a-clean-ch

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