EOSIO 自建数据结构

你有没有发现合约中的参数类型明明是uint64,可是执行合约的时候明明传入的是字符串?明明合约参数类型是个结构体,执行合约的时候我传入的还是字符串?这到底是怎么回事?

EOSIO 针对数据流有两套机制,一套是针对客户端和 EOSIO 中的服务进行交互的时候,一套是 EOSIO 中的服务和智能合约进行交互的时候。

目前我知道的是,客户端和服务端交互的时候使用的是 JSON 格式的字符串!基于此我们修改源码增加自己的自定义的数据结构。

  1. 修改 ~/eos/libraries/chain/include/eosio/chain/asset.hpp, 增加 customdata 自定义数据结构,并且增加宏FC_REFLECT
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
#include 
#include 
#include 

namespace eosio { namespace chain {

    struct customdata {
        int n;

        static customdata from_string(const string& from);
        string       to_string()const ;
    };

/**

asset includes amount and currency symbol

asset::from_string takes a string of the form "10.0000 CUR" and constructs an asset 
with amount = 10 and symbol(4,"CUR")

*/

struct asset
{
   static constexpr int64_t max_amount = (1LL << 62) - 1;

   explicit asset(share_type a = 0, symbol id = symbol(CORE_SYMBOL)) :amount(a), sym(id) {
      EOS_ASSERT( is_amount_within_range(), asset_type_exception, "magnitude of asset amount must be less than 2^62" );
      EOS_ASSERT( sym.valid(), asset_type_exception, "invalid symbol" );
   }

   bool is_amount_within_range()const { return -max_amount <= amount && amount <= max_amount; }
   bool is_valid()const               { return is_amount_within_range() && sym.valid(); }

   double to_real()const { return static_cast(amount) / precision(); }

   uint8_t     decimals()const;
   string      symbol_name()const;
   int64_t     precision()const;
   const symbol& get_symbol() const { return sym; }
   share_type get_amount()const { return amount; }

   static asset from_string(const string& from);
   string       to_string()const;

   asset& operator += (const asset& o)
   {
      EOS_ASSERT(get_symbol() == o.get_symbol(), asset_type_exception, "addition between two different asset is not allowed");
      amount += o.amount;
      return *this;
   }

   asset& operator -= (const asset& o)
   {
      EOS_ASSERT(get_symbol() == o.get_symbol(), asset_type_exception, "subtraction between two different asset is not allowed");
      amount -= o.amount;
      return *this;
   }
   asset operator -()const { return asset(-amount, get_symbol()); }

   friend bool operator == (const asset& a, const asset& b)
   {
      return std::tie(a.get_symbol(), a.amount) == std::tie(b.get_symbol(), b.amount);
   }
   friend bool operator < (const asset& a, const asset& b)
   {
      EOS_ASSERT(a.get_symbol() == b.get_symbol(), asset_type_exception, "logical operation between two different asset is not allowed");
      return std::tie(a.amount,a.get_symbol()) < std::tie(b.amount,b.get_symbol());
   }
   friend bool operator <= (const asset& a, const asset& b) { return (a == b) || (a < b); }
   friend bool operator != (const asset& a, const asset& b) { return !(a == b); }
   friend bool operator > (const asset& a, const asset& b)  { return !(a <= b); }
   friend bool operator >= (const asset& a, const asset& b) { return !(a < b);  }

   friend asset operator - (const asset& a, const asset& b) {
      EOS_ASSERT(a.get_symbol() == b.get_symbol(), asset_type_exception, "subtraction between two different asset is not allowed");
      return asset(a.amount - b.amount, a.get_symbol());
   }

   friend asset operator + (const asset& a, const asset& b) {
      EOS_ASSERT(a.get_symbol() == b.get_symbol(), asset_type_exception, "addition between two different asset is not allowed");
      return asset(a.amount + b.amount, a.get_symbol());
   }

   friend std::ostream& operator << (std::ostream& out, const asset& a) { return out << a.to_string(); }

   friend struct fc::reflector;

   void reflector_verify()const {
      EOS_ASSERT( is_amount_within_range(), asset_type_exception, "magnitude of asset amount must be less than 2^62" );
      EOS_ASSERT( sym.valid(), asset_type_exception, "invalid symbol" );
   }

private:
   share_type amount;
   symbol     sym;

};

struct extended_asset  {
  extended_asset(){}
  extended_asset( asset a, name n ):quantity(a),contract(n){}
  asset quantity;
  name contract;
};

bool  operator <  (const asset& a, const asset& b);
bool  operator <= (const asset& a, const asset& b);

}} // namespace eosio::chain

namespace fc {
inline void to_variant(const eosio::chain::asset& var, fc::variant& vo) { vo = var.to_string(); }
inline void from_variant(const fc::variant& var, eosio::chain::asset& vo) {
   vo = eosio::chain::asset::from_string(var.get_string());
}
}

namespace fc {
    inline void to_variant(const eosio::chain::customdata& var, fc::variant& vo) { vo = var.to_string(); }
    inline void from_variant(const fc::variant& var, eosio::chain::customdata& vo) {
        vo = eosio::chain::customdata::from_string(var.get_string());
    }
}

FC_REFLECT(eosio::chain::asset, (amount)(sym))
FC_REFLECT(eosio::chain::extended_asset, (quantity)(contract) )

FC_REFLECT(eosio::chain::customdata, (n))

  1. 修改 /home/juzi/eos/libraries/chain/asset.cpp,实现 to_string 和 from_string,这里建议将 from_string 设置成静态的,因为 from_string 可能会失败
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#include 
#include 
#include 
#include 
namespace eosio { namespace chain {

uint8_t asset::decimals()const {
   return sym.decimals();
}

string asset::symbol_name()const {
   return sym.name();
}

int64_t asset::precision()const {
   return sym.precision();
}

string asset::to_string()const {
   string sign = amount < 0 ? "-" : "";
   int64_t abs_amount = std::abs(amount);
   string result = fc::to_string( static_cast(abs_amount) / precision());
   if( decimals() )
   {
      auto fract = static_cast(abs_amount) % precision();
      result += "." + fc::to_string(precision() + fract).erase(0,1);
   }
   return sign + result + " " + symbol_name();
}

asset asset::from_string(const string& from)
{
   try {
      string s = fc::trim(from);

      // Find space in order to split amount and symbol
      auto space_pos = s.find(' ');
      EOS_ASSERT((space_pos != string::npos), asset_type_exception, "Asset's amount and symbol should be separated with space");
      auto symbol_str = fc::trim(s.substr(space_pos + 1));
      auto amount_str = s.substr(0, space_pos);

      // Ensure that if decimal point is used (.), decimal fraction is specified
      auto dot_pos = amount_str.find('.');
      if (dot_pos != string::npos) {
         EOS_ASSERT((dot_pos != amount_str.size() - 1), asset_type_exception, "Missing decimal fraction after decimal point");
      }

      // Parse symbol
      string precision_digit_str;
      if (dot_pos != string::npos) {
         precision_digit_str = eosio::chain::to_string(amount_str.size() - dot_pos - 1);
      } else {
         precision_digit_str = "0";
      }

      string symbol_part = precision_digit_str + ',' + symbol_str;
      symbol sym = symbol::from_string(symbol_part);

      // Parse amount
      safe int_part, fract_part;
      if (dot_pos != string::npos) {
         int_part = fc::to_int64(amount_str.substr(0, dot_pos));
         fract_part = fc::to_int64(amount_str.substr(dot_pos + 1));
         if (amount_str[0] == '-') fract_part *= -1;
      } else {
         int_part = fc::to_int64(amount_str);
      }

      safe amount = int_part;
      amount *= safe(sym.precision());
      amount += fract_part;

      return asset(amount.value, sym);
   }
   FC_CAPTURE_LOG_AND_RETHROW( (from) )
}

        customdata customdata::from_string(const string& from)
        {
            customdata o;

            std::stringstream ss(from);

            ss >> o.n;

            return o;
        }
        string       customdata::to_string()const
        {
            std::stringstream ss;
            ss << n;
            return ss.str();
        }

} }  // eosio::types

  1. 修改 ~/eos/libraries/chain/abi_serializer.cpp,将 customdata 添加到 built_in_types,这个文件必须修改,原因是这个文件和生成 abi 文件有关的,具体怎么实现的暂时没有研究。
   void abi_serializer::configure_built_in_types() {

      built_in_types.emplace("bool",                      pack_unpack());
      built_in_types.emplace("int8",                      pack_unpack());
      built_in_types.emplace("uint8",                     pack_unpack());
      built_in_types.emplace("int16",                     pack_unpack());
      built_in_types.emplace("uint16",                    pack_unpack());
      built_in_types.emplace("int32",                     pack_unpack());
      built_in_types.emplace("uint32",                    pack_unpack());
      built_in_types.emplace("int64",                     pack_unpack());
      built_in_types.emplace("uint64",                    pack_unpack());
      built_in_types.emplace("int128",                    pack_unpack());
      built_in_types.emplace("uint128",                   pack_unpack());
      built_in_types.emplace("varint32",                  pack_unpack());
      built_in_types.emplace("varuint32",                 pack_unpack());

      // TODO: Add proper support for floating point types. For now this is good enough.
      built_in_types.emplace("float32",                   pack_unpack());
      built_in_types.emplace("float64",                   pack_unpack());
      built_in_types.emplace("float128",                  pack_unpack());

      built_in_types.emplace("time_point",                pack_unpack());
      built_in_types.emplace("time_point_sec",            pack_unpack());
      built_in_types.emplace("block_timestamp_type",      pack_unpack());

      built_in_types.emplace("name",                      pack_unpack());

      built_in_types.emplace("bytes",                     pack_unpack());
      built_in_types.emplace("string",                    pack_unpack());

      built_in_types.emplace("checksum160",               pack_unpack());
      built_in_types.emplace("checksum256",               pack_unpack());
      built_in_types.emplace("checksum512",               pack_unpack());

      built_in_types.emplace("public_key",                pack_unpack());
      built_in_types.emplace("signature",                 pack_unpack());

      built_in_types.emplace("symbol",                    pack_unpack());
      built_in_types.emplace("symbol_code",               pack_unpack());
      built_in_types.emplace("asset",                     pack_unpack());
      built_in_types.emplace("extended_asset",            pack_unpack());
      built_in_types.emplace("customdata",                pack_unpack());

   }
  1. 在智能合约中增加一个测试:
#include 

using namespace eosio;

struct customdata {
    int n;

//    template
//    friend DataStream &operator<<(DataStream &ds, const customdata &t) { return ds << t.n; }
//
//    template
//    friend DataStream &operator>>(DataStream &ds, customdata &t) { return ds >> t.n; }
};

class hello : public eosio::contract {
public:
    using contract::contract;

    /// @abi action
    void hi(account_name user) {
        print("Hello, ", name{user});
    }

    /// @abi action
    void hi2(account_permission per) {
        require_auth2(per.account, per.permission);

        print("Hello, ", name{per.account});
    }

    /// @abi action
    void hi3(account_name user) {
        require_auth(user);

        print("Hello, ", name{user});
    }

    void hi4(customdata cd) {
        print(cd.n);
    }
};

EOSIO_ABI(hello, (hi)(hi2)(hi3)(hi4))
  1. 重新编译所有,然后安装,使用生成后的 eosiocpp 生成 hello.abi,然后搭建环境测试吧!!!

你可能感兴趣的:(EOSIO 自建数据结构)