您正在查看: EOS-新手教程 分类下的文章

EOS 空投

修改eosjs2 支持其他默认平台币符号,系统名,公钥前缀

前景

目前基于EOS搭建的链,由于修改了默认的系统代币符号,所以js库这边也要做对应的修改

测试数据

为了方便讲解,我们假设修改版与原版的对比数据如下

平台币符号 系统名 公钥前缀 js库名 ecc库名
EOS原版 EOS eosio EOS eosjs eosjs-ecc
修改版 BSC bscio BSC bscjs bscjs-ecc

依赖库

eosjs
版本:20.0 release
https://github.com/EOSIO/eosjs/tree/20.0.0
eosjs-ecc
版本:最新release
https://github.com/EOSIO/eosjs-ecc

eosjs部分修改

eosjs-numeric.ts

https://github.com/EOSIO/eosjs/blob/849c03992e6ce3cb4b6a11bf18ab17b62136e5c9/src/eosjs-numeric.ts#L276

export function stringToPublicKey(s: string): Key {
    if (typeof s !== 'string') {
        throw new Error('expected string containing public key');
    }
    if (s.substr(0, 3) === 'EOS') { // 中的EOS修改为自己的代币符号 BSC
        const whole = base58ToBinary(publicKeyDataSize + 4, s.substr(3));

https://github.com/EOSIO/eosjs/blob/849c03992e6ce3cb4b6a11bf18ab17b62136e5c9/src/eosjs-numeric.ts#L311

export function convertLegacyPublicKey(s: string) {
    if (s.substr(0, 3) === 'EOS') { // 中的EOS修改为自己的代币符号 BSC
        return publicKeyToString(stringToPublicKey(s));
    }
    return s;
}

修改eosjs包名为bscjs

package.json

"name": "eosjs"// 修改为 bscjs
"eosjs-ecc": "4.0.4", // 修改为 bscjs-ecc

src\eosjs-ecc.d.ts

declare module "eosjs-ecc"; // 修改为 bscjs-ecc

src\eosjs-jssig.ts

import * as ecc from 'eosjs-ecc'; // 修改为 bscjs-ecc

src\eosjs-serialize.ts

export function supportedAbiVersion(version: string) {
    return version.startsWith('eosio::abi/1.'); // eosio修改为 bscio
}

src\tests\eosjs-jssig.test.ts

import * as ecc from 'eosjs-ecc';

此时已经将eosjs修改为了我们的bscjs,下面我们在处理下依赖的ecc包

eosjs-ecc部分修改

前言

原本想不修改ecc,直接内部指定pubkey_prefix,但是
由于src\eosjs-jssig.ts 中
https://github.com/EOSIO/eosjs/blob/849c03992e6ce3cb4b6a11bf18ab17b62136e5c9/src/eosjs-jssig.ts#L21

const pub = convertLegacyPublicKey(ecc.PrivateKey.fromString(k).toPublic().toString());

使用了 toPublic,
https://github.com/EOSIO/eosjs-ecc/blob/2063257e8d02e82ce4ca1d0fdadf451281b33d1e/src/key_private.js#L62

function toPublic() {
        if (public_key) {
            // cache
            // S L O W in the browser
            return public_key
        }
        const Q = secp256k1.G.multiply(d);
        return public_key = PublicKey.fromPoint(Q);
    }

而这个函数里,又没有参数可以指定 自定义的 pubkey_prefix
https://github.com/EOSIO/eosjs-ecc/blob/2063257e8d02e82ce4ca1d0fdadf451281b33d1e/src/key_public.js#L18

function PublicKey(Q, pubkey_prefix = 'EOS') {

所以eosjs-ecc 还是得改下默认的平台币符号
已经提交issue(github)

开始修改默认的代币符号

src\api_common.js

 privateToPublic: (wif, pubkey_prefix = 'EOS') => // 中的EOS修改为自己的代币符号 BSC
      PrivateKey(wif).toPublic().toString(pubkey_prefix),

    /**
        @arg {pubkey} pubkey - like EOSKey..
        @arg {string} [pubkey_prefix = 'EOS']

        @return {boolean} valid

        @example ecc.isValidPublic(pubkey) === true
    */
    isValidPublic: (pubkey, pubkey_prefix = 'EOS') => // 中的EOS修改为自己的代币符号 BSC
      PublicKey.isValid(pubkey, pubkey_prefix),

src\key_private.js

assert.equal(pub.toString(), 'EOS859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM', pubError)

修改下前缀 EOS为BSC 不然推npmjs时验证不通过

src\key_public.js

以下pubkey_prefix都改成BSC

function PublicKey(Q, pubkey_prefix = 'EOS') {
function toString(pubkey_prefix = 'EOS') {
PublicKey.isValid = function(pubkey, pubkey_prefix = 'EOS') {
PublicKey.fromString = function(public_key, pubkey_prefix = 'EOS') {
PublicKey.fromStringOrThrow = function(public_key, pubkey_prefix = 'EOS') {

src\object.test.js

describe('secp256k1 keys', () => {
    it('randomKey', function() {
      this.timeout(1100)
      return PrivateKey.randomKey()
    })

    it('private to public', () => {
      assert.equal(
        pub.toString(),
        // 'PUB_K1_6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5BoDq63',
        'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV', // 前缀改为 BSC
        'pub.toString'
      )
    })

修改eosjs-ecc的包名相关

package.json

"name": "eosjs-ecc", // 修改为 bscjs-cc

下面内容中的eosjs-ecc 都替换成 bscjs-ecc

 "build_browser": "mkdir -p lib && browserify -o lib/eosjs-ecc.js -s eosjs_ecc lib/index.js",
    "build_browser_test": "yarn build && browserify -o dist/test.js lib/*.test.js",
    "documentation": "node_modules/documentation/bin/documentation.js",
    "minimize": "terser lib/eosjs-ecc.js -o lib/eosjs-ecc.min.js --source-map --compress --mangle",
    "docs": "yarn documentation -- readme src/api_common.js --section \"Common API\" --shallow",
    "srisum": "npx srisum lib/eosjs-ecc.*",
    "prepublishOnly": "yarn build && yarn minimize && yarn test_lib && yarn docs && yarn srisum"
  },
  "repository": {
    "type": "git",
    "url": "git://github.com/EOSIO/eosjs-ecc.git"
  },

src\common.test.js

 it('privateToPublic', () => {
    // const pub = 'PUB_K1_859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2Ht7beeX'
    const pub = 'EOS859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM' // 前缀改BSC
    assert.equal(ecc.privateToPublic(wif), pub)
  })

  it('isValidPublic', () => {
    const keys = [
      [true, 'PUB_K1_859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2Ht7beeX'],
      [true, 'EOS859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM'], // 前缀改BSC
      [false, 'MMM859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM'],
      [false, 'EOS859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVm', 'EOS'], // 前缀改BSC
      [true, 'PUB859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVM', 'PUB'],
      [false, 'PUB859gxfnXyUriMgUeThh1fWv3oqcpLFyHa3TfFYC4PK2HqhToVm', 'PUB'],
    ]

然后参考https://www.bcskill.com/index.php/archives/437.html 将 bscjs-ecc 提交到npmjs,即可编译bscjs

编译步骤

编译bscjs-ecc

npm i 或 yarn install // 安装依赖
npm run build // 编译
npm publish // 提交npmjs

编译bscjs

npm i 或 yarn install // 安装依赖
yarn build-web // 编译web使用

此时会在 eosjs\dist-web 生成

测试例子

<html>
<head>
  <meta charset="utf-8">


<script src='dist-web/eosjs-api.js'></script>
<script src='dist-web/eosjs-jsonrpc.js'></script>
<script src='dist-web/eosjs-jssig.js'></script>
<script>
  let pre = document.getElementsByTagName('pre')[0];
  const defaultPrivateKey = "xxx"; // 私钥
  const rpc = new eosjs_jsonrpc.JsonRpc('https://xxx.com'); // RPC 地址
  const signatureProvider = new eosjs_jssig.JsSignatureProvider([defaultPrivateKey]);
  const api = new eosjs_api.Api({ rpc, signatureProvider });

  (async () => {
    try {   
      console.log(await rpc.get_account('bcskillsurou')); // 测试账号
      const result = await api.transact({
        actions: [{
            account: 'fscio',
            name: 'buyrambytes',
            authorization: [{
                actor: 'bcskillsurou',
                permission: 'active',
            }],
            data: {
                payer: 'bcskillsurou',
                receiver: 'bcskillsurou',
                kbytes: 8192
            },
        }]
      }, {
        blocksBehind: 3,
        expireSeconds: 30,
      });
      console.log('\n\nTransaction pushed!\n\n' + JSON.stringify(result, null, 2));
      //pre.textContent += '\n\nTransaction pushed!\n\n' + JSON.stringify(result, null, 2);
    } catch (e) {
      console.log( '\nCaught exception: ' + e);
      if (e instanceof eosjs_jsonrpc.RpcError)
        console.log( '\n\n' + JSON.stringify(e.json, null, 2));
    }
  })();
</script>
</head>
<body>
<pre style="width: 100%; height: 100%; margin:0px; "></pre>
  <pre style="width: 100%; height: 100%; margin:0px; "></pre>
</body>
</html>

参考

https://eosio.github.io/eosjs/
https://eosio.github.io/eosjs/guides/2.-Transaction-Examples.html

eos rpc get_table_rows index_position

https://github.com/EOSIO/eos/blob/686f0deb5dac097cc292f735ccb47c238e763de0/plugins/chain_plugin/chain_plugin.cpp#L1043

uint64_t read_only::get_table_index_name(const read_only::get_table_rows_params& p, bool& primary) {
   using boost::algorithm::starts_with;
   // see multi_index packing of index name
   const uint64_t table = p.table;
   uint64_t index = table & 0xFFFFFFFFFFFFFFF0ULL;
   EOS_ASSERT( index == table, chain::contract_table_query_exception, "Unsupported table name: ${n}", ("n", p.table) );

   primary = false;
   uint64_t pos = 0;
   if (p.index_position.empty() || p.index_position == "first" || p.index_position == "primary" || p.index_position == "one") {
      primary = true;
   } else if (starts_with(p.index_position, "sec") || p.index_position == "two") { // second, secondary
   } else if (starts_with(p.index_position , "ter") || starts_with(p.index_position, "th")) { // tertiary, ternary, third, three
      pos = 1;
   } else if (starts_with(p.index_position, "fou")) { // four, fourth
      pos = 2;
   } else if (starts_with(p.index_position, "fi")) { // five, fifth
      pos = 3;
   } else if (starts_with(p.index_position, "six")) { // six, sixth
      pos = 4;
   } else if (starts_with(p.index_position, "sev")) { // seven, seventh
      pos = 5;
   } else if (starts_with(p.index_position, "eig")) { // eight, eighth
      pos = 6;
   } else if (starts_with(p.index_position, "nin")) { // nine, ninth
      pos = 7;
   } else if (starts_with(p.index_position, "ten")) { // ten, tenth
      pos = 8;
   } else {
      try {
         pos = fc::to_uint64( p.index_position );
      } catch(...) {
         EOS_ASSERT( false, chain::contract_table_query_exception, "Invalid index_position: ${p}", ("p", p.index_position));
      }
      if (pos < 2) {
         primary = true;
         pos = 0;
      } else {
         pos -= 2;
      }
   }
   index |= (pos & 0x000000000000000FULL);
   return index;
}

Position of the index used, accepted parameters primary, secondary, tertiary, fourth, fifth, sixth, seventh, eighth, ninth , tenth

index_position 可以以某前缀的名字指定对应多级索引,或者数字(从2开始)

EOS 合约 table primary key支持的类型

https://github.com/EOSIO/eos/blob/5082391c60b0fa5e68157c385cd402bf25aea934/plugins/chain_plugin/chain_plugin.cpp#L1158

read_only::get_table_rows_result read_only::get_table_rows( const read_only::get_table_rows_params& p )const {
   const abi_def abi = eosio::chain_apis::get_abi( db, p.code );
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
   bool primary = false;
   auto table_with_index = get_table_index_name( p, primary );
   if( primary ) {
      EOS_ASSERT( p.table == table_with_index, chain::contract_table_query_exception, "Invalid table name ${t}", ( "t", p.table ));
      auto table_type = get_table_type( abi, p.table );
      if( table_type == KEYi64 || p.key_type == "i64" || p.key_type == "name" ) {
         return get_table_rows_ex<key_value_index>(p,abi);
      }
      EOS_ASSERT( false, chain::contract_table_query_exception,  "Invalid table type ${type}", ("type",table_type)("abi",abi));
   } else {
      EOS_ASSERT( !p.key_type.empty(), chain::contract_table_query_exception, "key type required for non-primary index" );

      if (p.key_type == chain_apis::i64 || p.key_type == "name") {
         return get_table_rows_by_seckey<index64_index, uint64_t>(p, abi, [](uint64_t v)->uint64_t {
            return v;
         });
      }
      else if (p.key_type == chain_apis::i128) {
         return get_table_rows_by_seckey<index128_index, uint128_t>(p, abi, [](uint128_t v)->uint128_t {
            return v;
         });
      }
      else if (p.key_type == chain_apis::i256) {
         if ( p.encode_type == chain_apis::hex) {
            using  conv = keytype_converter<chain_apis::sha256,chain_apis::hex>;
            return get_table_rows_by_seckey<conv::index_type, conv::input_type>(p, abi, conv::function());
         }
         using  conv = keytype_converter<chain_apis::i256>;
         return get_table_rows_by_seckey<conv::index_type, conv::input_type>(p, abi, conv::function());
      }

结论

primary key 只支持uint64_tcapi_name
如果想支持其他类型只能用
二级索引支持了 index_position + key_type

官方解释

它们是多索引表,因为它们支持在数据上使用多个索引,主索引类型必须是uint64_t并且必须是唯一的,但其他次要索引可以具有重复项。最多可以有16个附加索引,字段类型可以是uint64_t,uint128_t,eosio::checksum256,double或long double

参考

https://developers.eos.io/eosio-cpp/docs/using-multi-index-tables#section-introduction
https://developers.eos.io/eosio-nodeos/reference#get_table_rows
https://eosio.stackexchange.com/questions/3091/is-it-possible-to-use-a-type-other-than-uint64-t-for-a-table-primary-key/3097#3097