6

Does anyone know the correct way to get table rows using checksum256 (fixed_bytes<32>) secondary index through the get_table_rows RPC API?

I found a way using key256, but key256 is deprecated in eosio.cdt 1.5.0.

warning: 'key256' is deprecated: Replaced by fixed_bytes [-Wdeprecated-declarations]

So I implemented a contract with checksum256 secondary index referring to this document, but I can't get proper rows using checksum256 secondary index. My contract is as follows, and I compile it with eosio.cdt 1.5.0.

#include <eosiolib/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] test : public contract
{
  using contract::contract;

public:

  test(name receiver, name code, datastream<const char*> ds) :
      contract(receiver, code, ds),
      items(_self, _self.value)
  {};

  [[eosio::action]]
  void additem(std::string identifier)
  {
    items.emplace(_self, [&](auto& row) {
                           row.key             = items.available_primary_key();
                           row.identifier      = identifier;
                           row.identifier_hash = hash(identifier);
                         });
  }

private:

  struct [[eosio::table]] item {
    uint64_t key;
    std::string identifier;
    checksum256 identifier_hash;
    uint64_t primary_key() const { return key; }
    checksum256 by_identifier_hash() const { return identifier_hash; }
  };
  typedef multi_index<name("items"), item,
                      indexed_by<name("idhash"), const_mem_fun<item, checksum256, &item::by_identifier_hash>>
                      > item_index;

  checksum256 hash(std::string str)
  {
    return sha256(const_cast<char*>(str.c_str()), str.size());
  }

  item_index items;
};

EOSIO_DISPATCH(test, (additem));
$ cleos -u https://api-kylin.eosasia.one push action checksumtest additem '["m0t0k1ch1"]' -p checksumtest@active
$ curl -s https://api-kylin.eosasia.one/v1/chain/get_table_rows -d '{"json":true,"code":"checksumtest","scope":"checksumtest","table":"items","table_key":"idhash","index_position":2,"key_type":"sha256","limit":1}'
{
  "rows": [
    {
      "key": 0,
      "identifier": "m0t0k1ch1",
      "identifier_hash": "c5bae5b3f19a69d45594902726ce983c0948fcda5bbf408f893fff91298688b4"
    }
  ],
  "more": false
}
$ curl -s https://api-kylin.eosasia.one/v1/chain/get_table_rows -d '{"json":true,"code":"checksumtest","scope":"checksumtest","table":"items","table_key":"idhash","index_position":2,"key_type":"sha256","lower_bound":"c5bae5b3f19a69d45594902726ce983c0948fcda5bbf408f893fff91298688b4","upper_bound":"c5bae5b3f19a69d45594902726ce983c0948fcda5bbf408f893fff91298688b4","limit":1}'
{
  "rows": [],
  "more": false
}

Updated on Mar 18, 2019

It seems that the checksum256 data is divided into two parts per 16 bytes, and each part is stored in little endian. So I was able to get the proper row in the following way.

$ curl -s https://api-kylin.eosasia.one/v1/chain/get_table_rows -d '{"json":true,"code":"checksumtest","scope":"checksumtest","table":"items","table_key":"idhash","index_position":2,"key_type":"sha256","lower_bound":"3c98ce2627909455d4699af1b3e5bac5b488862991ff3f898f40bf5bdafc4809","upper_bound":"3c98ce2627909455d4699af1b3e5bac5b488862991ff3f898f40bf5bdafc4809","limit":1}'
{
  "rows": [
    {
      "key": 0,
      "identifier": "m0t0k1ch1",
      "identifier_hash": "c5bae5b3f19a69d45594902726ce983c0948fcda5bbf408f893fff91298688b4"
    }
  ],
  "more": false
}

Thank you for the advice, @CoRe1o3 🙏

See also this issue.

  • 1
    Could you explain how you converted the returned checksum256 into the value you sent to the API? Clicking translate on each tweet is quite cumbersome and it would be useful to have the full explanation in this answer :) – Jason Bert May 6 '19 at 17:20
9

Some further explanation of how the value to pass via cleos was derived. As already mentioned in the original post, this all revolves around endianness. For some reason the checksum256 value is returned in a different endian to what the index key type bounds for sha256 expects.

If you have the below hash (checksum256) returned by a row in the table:

7af12386a82b6337d6b1e4c6a1119e29bb03e6209aa03c70ed3efbb9b74a290c

It's first split into two parts (16 bytes each side):

7af12386a82b6337d6b1e4c6a1119e29 bb03e6209aa03c70ed3efbb9b74a290c

Each part is then reversed in 2 character (1 byte) chunks, using the first part as an example:

7af12386a82b6337d6b1e4c6a1119e29

7a f1 23 86 a8 2b 63 37 d6 b1 e4 c6 a1 11 9e 29

29 9e 11 a1 c6 e4 b1 d6 37 63 2b a8 86 23 f1 7a

The two reversed parts are then concatenated to form the value you can use with the sha256 index key type:

299e11a1c6e4b1d637632ba88623f17a 0c294ab7b9fb3eed703ca09a20e603bb

The final result:

299e11a1c6e4b1d637632ba88623f17a0c294ab7b9fb3eed703ca09a20e603bb

| improve this answer | |
0

2020 Update for eosio 2.0 & eosio.cdt 1.7

I know this post is old but for anyone else who stumbles upon this. The encoding as described by Jason Bert's answer (https://eosio.stackexchange.com/a/4344/3464) is no longer required, as far as I can tell from my tests.

Simply use the the hash directly within lower_bound and/or upper_bound as expected

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.