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

EOS 签名中获取公钥 recover,验证登录,token

ecc

https://github.com/EOSIO/eosjs-ecc#recover

recover

Recover the public key used to create the signature.

Parameters

  • signature (String | Buffer) (EOSbase58sig.., Hex, Buffer)
  • data (String | Buffer) full data
  • encoding String data encoding (if data is a string) (optional, default 'utf8')

Examples

ecc.recover(signature, 'I am alive') === pubkey
Returns pubkey

Java

https://github.com/eosforce/eosforce-monitor/blob/9b35fcb3db0ae246b56de83485bad99502f8f5cd/src/main/java/client/SignedTransactionTest.java#L120

public void signTransactionNew_matchSignature(){
        SignedTransaction txn =createTxn( "eos", "newaccount", CREATE_ACCOUNT_DATA_AS_HEX,
                new String[]{ "inita", "eos"}, new String[]{"inita@active"});

        String head_block_id = "000009907e54bb84c7dc993e613e237f65dfbbc0a26f501b2ac7e1eb7b570df3";
        txn.setReferenceBlock(head_block_id);
        txn.setExpiration("2017-09-22T09:04:25");

        EosPrivateKey key = new EosPrivateKey("5KiA2RDrwb9xq2j6Z3k8qaz58HVhwh7mAxjWPag9dwjpBFNCGYp");

        assertEquals( "key parse failed-1",  "EOS6H6WZR2Nme3Sp4F8Krkdn19EYsTZLEyD8KasQYfa2EcqpZMohV", key.getPublicKey().toString());

        key = new EosPrivateKey("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3");
        assertEquals( "key parse failed-2",  "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", key.getPublicKey().toString());

        byte[] data = HexUtils.toBytes("369a790be1b3192fa6eccd0e8e90b39692145d30c75eda7e435df083e30801a3");
        BigInteger r = new BigInteger(HexUtils.toBytes("545c106dfd35900fab318cc12e208140abba086ab1112c543a808e2248ddb62d"));
        BigInteger s = new BigInteger(HexUtils.toBytes("5fe909c18582e792116e418e5491d18a5c98be34e5bdccb577d6fa59806e6a28"));
        CurveParam curveParamK1 = EcTools.getCurveParam( CurveParam.SECP256_K1);
        EcSignature signature = new EcSignature( r,s, curveParamK1,1);
        assertEquals("failed to recover pubKey from sig, (recId 1)", key.getPublicKey(), EcDsa.recoverPubKey(curveParamK1,data, signature, 1) );

        r = new BigInteger(HexUtils.toBytes("40361c656f284cd676d920108d3a63dfcf0779d0296cdb2f9421c0c1fd18244a"));
        s = new BigInteger(HexUtils.toBytes("284db40e8661d75067d45b6559266ba5345e86df0af83343951284121dddd1ec"));
        signature = new EcSignature( r,s, curveParamK1,0);
        assertEquals("failed to recover pubKey from sig, (recId 0)", key.getPublicKey(), EcDsa.recoverPubKey(curveParamK1, data, signature, 0) );
    }

    public void recoverPublic(){
        byte[] data = HexUtils.toBytes("330fbd64ae1b11d2b29d74a1dfa7ca5c0ba184b835e8643fdf362365dd584b0b");
        EcSignature ecSignature = new EcSignature("SIG_K1_JuSQ5GZfvU2w1i16qxYdRQ1tdNzYtP1AwJZRcwJ4viUgbEXR9K5rMdy5bpq7gydMjPxqQqZbmVtcFEDdMY9NBHGehmNdiZ");
        EosPublicKey eosPublicKey =  EcDsa.recoverPubKey(data, ecSignature);
        logger.info("publicKey: {}", eosPublicKey.toString());

    }

其他参考

Java

https://github.com/adyliu/jeos/blob/4eaa9fb4555129f3d0baa56e7ffb70cac213a009/src/main/java/io/jafka/jeos/util/ecc/Ecdsa.java#L188

public int calcPubKeyRecoveryParam(BigInteger e, SignBigInt sign, Point Q) {
        for (int i = 0; i < 4; i++) {
            Point Qprime = recoverPubKey(e, sign, i);
            if (Qprime.equals(Q)) {
                return i;
            }
        }
        throw new RuntimeException( "Unable to find valid recovery factor");
    }

    public Point recoverPubKey(BigInteger e, SignBigInt big, int i) {

        BigInteger n = curve.n();
        Point G = curve.G();

        BigInteger r = big.getR();
        BigInteger s = big.getS();

        if (!(r.signum() > 0 && r.compareTo(n) < 0)) {
            throw new RuntimeException(  "Invalid r value");
        }
        if (!(s.signum() > 0 && s.compareTo(n) < 0)) {
            throw new RuntimeException(  "Invalid r value");
        }

        // A set LSB signifies that the y-coordinate is odd
        int isYOdd = i & 1;

        // The more significant bit specifies whether we should use the
        // first or second candidate key.
        int isSecondKey = i >> 1;

        // 1.1 Let x = r + jn
        BigInteger x = isSecondKey == 1 ? r.add(n) : r;

        Point R = curve.getCurve().pointFromX(isYOdd, x);

        // // 1.4 Check that nR is at infinity
        Point nR = R.multiply(n);

        if (!nR.isInfinity()) {
            throw new RuntimeException(  "nR is not a valid curve point");
        }

        BigInteger eNeg = e.negate().mod(n);

        BigInteger rInv = r.modInverse(n);

        Point Q = R.multiplyTwo(s, G, eNeg).multiply(rInv);

        if (Q.isInfinity()) {
            throw new RuntimeException(  "Point is at infinity");
        }

        return Q;
    }

java (适用于Swift和Java的Native SDK)

https://github.com/EOSIO/eosio-java/blob/a9202879f31edb4122e768df0dc7fca391eff7e7/eosiojava/src/main/java/one/block/eosiojava/utilities/EOSFormatter.java#L1431:27

核心方法 recoverPublicKeyFromSignature

 private static byte[] recoverPublicKeyFromSignature(int recId, BigInteger r, BigInteger s,
            @NotNull Sha256Hash message, boolean compressed, AlgorithmEmployed keyType) {
        checkArgument(recId >= 0, "recId must be positive");
        checkArgument(r.signum() >= 0, "r must be positive");
        checkArgument(s.signum() >= 0, "s must be positive");

        // 1.0 For j from 0 to h   (h == recId here and the loop is outside this function)
        //   1.1 Let x = r + jn

        BigInteger n; // Curve order.
        ECPoint g;
        ECCurve.Fp curve;

        switch (keyType) {
            case SECP256R1:
                n = ecParamsR1.getN();
                g = ecParamsR1.getG();
                curve = (ECCurve.Fp) ecParamsR1.getCurve();
                break;

            default:
                n = ecParamsK1.getN();
                g = ecParamsK1.getG();
                curve = (ECCurve.Fp) ecParamsK1.getCurve();
                break;
        }

        BigInteger i = BigInteger.valueOf((long) recId / 2);
        BigInteger x = r.add(i.multiply(n));

        //   1.2. Convert the integer x to an octet string X of length mlen using the conversion routine
        //        specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or mlen = ⌈m/8⌉.
        //   1.3. Convert the octet string (16 set binary digits)||X to an elliptic curve point R using the
        //        conversion routine specified in Section 2.3.4. If this conversion routine outputs “invalid”, then
        //        do another iteration of Step 1.
        //
        // More concisely, what these points mean is to use X as a compressed public key.
        BigInteger prime = curve.getQ();
        if (x.compareTo(prime) >= 0) {
            // Cannot have point co-ordinates larger than this as everything takes place modulo Q.
            return null;
        }
        // Compressed keys require you to know an extra bit of data about the y-coord as there are two possibilities.
        // So it's encoded in the recId.
        ECPoint R = decompressKey(x, (recId & 1) == 1, keyType);
        //   1.4. If nR != point at infinity, then do another iteration of Step 1 (callers responsibility).
        if (!R.multiply(n).isInfinity()) {
            return null;
        }
        //   1.5. Compute e from M using Steps 2 and 3 of ECDSA signature verification.
        BigInteger e = message.toBigInteger();
        //   1.6. For k from 1 to 2 do the following.   (loop is outside this function via iterating recId)
        //   1.6.1. Compute a candidate public key as:
        //               Q = mi(r) * (sR - eG)
        //
        // Where mi(x) is the modular multiplicative inverse. We transform this into the following:
        //               Q = (mi(r) * s ** R) + (mi(r) * -e ** G)
        // Where -e is the modular additive inverse of e, that is z such that z + e = 0 (mod n). In the above equation
        // ** is point multiplication and + is point addition (the EC group operator).
        //
        // We can find the additive inverse by subtracting e from zero then taking the mod. For example the additive
        // inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and -3 mod 11 = 8.
        BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n);
        BigInteger rInv = r.modInverse(n);
        BigInteger srInv = rInv.multiply(s).mod(n);
        BigInteger eInvrInv = rInv.multiply(eInv).mod(n);
        ECPoint q = ECAlgorithms.sumOfTwoMultiplies(g, eInvrInv, R, srInv);
        return q.getEncoded(compressed);
    }

使用例子

https://github.com/EOSIO/eosio-java/blob/a9202879f31edb4122e768df0dc7fca391eff7e7/eosiojava/src/main/java/one/block/eosiojava/utilities/EOSFormatter.java#L1382:24

/**
     * Getting recovery id from R and S
     *
     * @param r - R in DER of Signature
     * @param s - S in DER of Signature
     * @param sha256HashMessage - Sha256Hash of signed message
     * @param publicKey - public key to validate
     * @param keyType - key type
     * @return - Recovery id of the signature. From 0 to 3. Return -1 if find nothing.
     */
    private static int getRecoveryId(BigInteger r, BigInteger s, Sha256Hash sha256HashMessage,
            byte[] publicKey, AlgorithmEmployed keyType) {
        for (int i = 0; i < NUMBER_OF_POSSIBLE_PUBLIC_KEYS; i++) {
            byte[] recoveredPublicKey = recoverPublicKeyFromSignature(i, r, s, sha256HashMessage,
                    true, keyType);

            if (Arrays.equals(publicKey, recoveredPublicKey)) {
                return i;
            }
        }

        return -1;
    }

https://github.com/EOSIO/eosio-java/blob/a9202879f31edb4122e768df0dc7fca391eff7e7/eosiojava/src/main/java/one/block/eosiojava/utilities/EOSFormatter.java#L468

int recoverId = getRecoveryId(r, s, Sha256Hash.of(signableTransaction), keyData,
                    algorithmEmployed);

if (recoverId < 0) {
    throw new IllegalStateException(
        ErrorConstants.COULD_NOT_RECOVER_PUBLIC_KEY_FROM_SIG);
}

其他库

https://github.com/PegaSysEng/pantheon/blob/6e77605ab9093c8a6772aa6910e3d1846b926a1a/crypto/src/test/java/tech/pegasys/pantheon/crypto/SECP256K1Test.java#L195

public void recoverPublicKeyFromSignature() {
    final SECP256K1.PrivateKey privateKey =
        SECP256K1.PrivateKey.create(
            new BigInteger("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", 16));
    final SECP256K1.KeyPair keyPair = SECP256K1.KeyPair.create(privateKey);

    final BytesValue data =
        BytesValue.wrap("This is an example of a signed message.".getBytes(UTF_8));
    final Bytes32 dataHash = keccak256(data);
    final SECP256K1.Signature signature = SECP256K1.sign(dataHash, keyPair);

    final SECP256K1.PublicKey recoveredPublicKey =
        SECP256K1.PublicKey.recoverFromSignature(dataHash, signature).get();
    assertEquals(keyPair.getPublicKey().toString(), recoveredPublicKey.toString());
  }

附加

如果想做登陆校验的话,可以提供单独的空的action方法,参数如下

  • 账户名
  • 其他参数
  • nonce (随机数 + 时间)

用户签名后发给服务端,服务端接收后根据上面方法可以从签名中获取对应的公钥,然后通过RPCget_key_accounts 或者查讯同步数据库查找当前账户,以及当前公钥对应的权限是否与要求相符。验证通过后返回中心服务器的token做后续处理。

参考

https://medium.com/eosio/eosio-software-release-native-sdks-for-swift-and-java-e6086ddd37b8
https://eosio.github.io/eosio-java/
https://github.com/EOSTribe/java-ecc

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: 'bscio',
            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开始)