您正在查看: EOS 分类下的文章

EOS 根据链Mongo数据判断交易状态

根据几个测试交易对比

  1. 正常发起普通交易(余额充足,非延迟)
  2. 正常发起延迟交易(余额充足,延迟)
  3. 正常发起延迟交易,交易到期前,将转出账户的余额转空(余额不足,延迟)

对比链Mongo表transactionstransaction_traces

对于transactions体现了交易所在块是否不可逆irreversible,而transaction_traces

 "receipt" : {
        "status" : "hard_fail",
        ...
    },

status状态来判断交易状态。
对于交易对比的1和2对于数据来说只是delay_sec数量问题,并且交易到期前,数据库中无法查到延迟交易。
1或2与3进行比较,transactions无关键差别,transaction_tracesstatus状态存在差别。

清洗数据

为了方便数据的使用,最好还是需要单独新洗出一个交易状态表,此表中关键包含,

  1. 交易id
  2. 所在块号
  3. 交易状态
  4. 是否不可逆

测试数据如下

库名:trx_status

{
    "_id" : ObjectId("5fbb8ef5349bfe65ef744506"),
    "trx_id" : "c1811e16a77288e3f374faf2eb2b0308d2f480b3414a07357d6e45cf29ffe56e",
    "block_num" : 11414152,
    "status" : 0,
    "irreversible" : true,
    "block_time" : ISODate("2020-11-23T10:29:10.000Z"),
    "block_timestamp" : NumberLong(1606127350000),
    "createdAt" : ISODate("2020-11-23T10:29:09.952Z"),
    "updatedAt" : ISODate("2020-11-23T10:31:05.985Z"),
    "block_id" : "00ae2a8818a6bd8cd33e9873419ac7258813ea9578dde95c8e0771397351b952"
}

交易等待不可逆

status==0 && irreversible == fasle

交易丢失(丢块)

status==0 && irreversible == fasle && ((current_time - block_time) >((head_block_num - last_irreversible_block_num) * 0.5 + 容差值))
// current_time - block_time 为当前时间与交易创建时间的秒数
// 容差值是为了避免误判,通常为 1-2分钟即可

交易失败

status!=0

因为交易到执行时间前,数据库中没有插入数据,所以没有delayed事件

当交易不可逆

status==0 && irreversible == true

备注

status的状态

status_enum {
    // 这个表示执行成功(所以不需要执行出错逻辑)
    executed  = 0,
    // 客观的来说,执行失败了(或者没有执行),某一个出错逻辑执行了
    soft_fail = 1,
    // 执行失败了,并且执行的出错逻辑也失败了,所以并没有状态改变
    hard_fail = 2,
    // 交易被延迟了,计划到未来的某个时间执行
    delayed   = 3,
    // 交易过期了,并且存储空间返还给用户
    expired   = 4  ///< transaction expired and storage space refuned to user
};

修改配置

config.ini配置中,去掉onblock交易,较少垃圾数据

mongodb-filter-out = eosio:onblock:

参考

https://www.bcskill.com/index.php/archives/621.html

在eosio合约中解析json

在合约中常有解析json的需求,此文章介绍使用
nlohmann/json

原版本
https://github.com/nlohmann/json
适配eosio合约后的版本源码地址
https://github.com/bcskill/eosio_json

下载完json.hpp文件后,将代码文件放到合约目录,通常放到项目合约代码同级的common目录

然后在项目合约中直接包含此json.hpp文件

#include "./common/json.hpp"

在使用的合约指名json的命名空间

using json = nlohmann::json;

然后就可以直接解析json字符串了

json memoJson = json::parse(memo);
   // 为了业务的判定,合约账户内不允许非限定交易,如有特殊需求再做变更
   eosio::check(memoJson.count("transfer_type") == 1, get_assert_msg(ASSERT_ERROR_CODE::MISSING_PARAMETERS, "Missing parameters"));
   transfer_type = memoJson["transfer_type"].get<uint8_t>();

Error 3010004: Invalid authority

当设置一个账户的权限为多个账户时

cleos set account permission bcskillsurou active  '{"threshold":1,"keys":[{"key":"EOS5bu13CujrEUdKB57LnvihUAqkdUycwSz2p7vPuac7vNBvfh7M7","weight":1}],"accounts":[{"permission":{"actor":"222222222222","permission":"eosio.code"},"weight":1},{"permission":{"actor":"111111111111","permission":"eosio.code"},"weight":1}],"waits":[{"wat_sec":1,"weight":1}]}'  owner -p bcskillsurou

报错如下

Error 3010004: Invalid authority
Ensure that your authority JSON is valid follows the following format!
{
  "threshold":      <INTEGER [1-2^32): the threshold that must be met to satisfy this authority>,
  "keys": [         <keys must be alpha-numerically sorted by their string representations and unique>
    ...
    {
      "key":        <STRING: EOS.IO compatible Public Key>,
      "weight":     <INTEGER [1-2^16): a signature from this key contributes this to satisfying the threshold>
    }
    ...
  ],
  "accounts": [     <accounts must be alpha-numerically sorted by their permission (actor, then permission) and unique>
    ...
    {
      "permission": {
        "actor":      <STRING: account name of the delegated signer>,
        "permission": <STRING: permission level on the account that must be satisfied>,
      },
      "weight":     <INTEGER [1-2^16): satisfying the delegation contributes this to satisfying the threshold>
    }
    ...
  ],
  "waits": [        <waits must be sorted by wait_sec, largest first, and be unique>
    ...
    {
      "wait_sec":   <INTEGER [1-2^32): seconds of delay which qualifies as passing this wait>
      "weight":     <INTEGER [1-2^16): satisfying the delay contributes this to satisfying the threshold>
    }
    ...
  ]
}

Error Details:
Authority failed validation! ensure that keys, accounts, and waits are sorted and that the threshold is valid and satisfiable!

解决问题

将添加的accounts按照ascii顺序添加
例如上面的例子,先添加 111111111111 再添加 222222222222

cleos set account permission bcskillsurou active  '{"threshold":1,"keys":[{"key":"EOS5bu13CujrEUdKB57LnvihUAqkdUycwSz2p7vPuac7vNBvfh7M7","weight":1}],"accounts":[{"permission":{"actor":"111111111111","permission":"eosio.code"},"weight":1},{"permission":{"actor":"222222222222","permission":"eosio.code"},"weight":1}],"waits":[{"wat_sec":1,"weight":1}]}'  owner -p bcskillsurou

参考

https://github.com/EOSIO/eos/issues/4433

基于 EOS 公钥加密,私钥解密示例

eos-crypto-java 使用指南

源码:https://github.com/yanjunli/eos-crypto-java

eos-crypto-java 目前可以支持 基于 ECC+AES 的加解密方式。

在本压缩包中,包含基于jdk1.5 打好的jar 包。

要求

jdk 1.5+

基于 EOS 公钥加密,私钥解密示例

        String privateKey =  "5KTZYCDdcfNrmEpcf97SJBCtToZjYHjHm8tqTWvzUbsUJgkxcfk";
        EosPrivateKey eosPrivateKey = new EosPrivateKey(privateKey);
        EosPublicKey  eosPublicKey = eosPrivateKey.getPublicKey();
        // 转换成 EC privatekey
        ECPrivateKey ecPrivateKey = eosPrivateKey.getECPrivateKey();
        ECPublicKey ecPublicKey = eosPublicKey.getECPublicKey();

        byte[] plaindata = "{\"age\": 1,\"12345\":\"24qqwazzxdtttdxkaskjewuizckczxnlsdosasda4!!!@#$$%^&&*(()(^#\"}".getBytes("utf8");

        System.out.println("加密原文:" + new String(plaindata));

        byte[] encryptdata = ECCUtil.publicEncrypt(plaindata,ecPublicKey);

        System.out.println("加密后密文:" + HexUtils.toHex(encryptdata));

        plaindata = ECCUtil.privateDecrypt(encryptdata,ecPrivateKey);

        System.out.println("解密后原文: "+ new String(plaindata));

基于ECC+AES 双向验证 加解密示例

/**
*
* sender  发起方密钥对
*
* EOS8g1u3ktAGHs4QsVp9aeaWNebFLtprQHwpaSjegx6iEuoTNhjXU
* 5KTZYCDdcfNrmEpcf97SJBCtToZjYHjHm8tqTWvzUbsUJgkxcfk
*
* receiver 接收方一密钥对
*
* EOS7ez2gagfoXw9XdW3kRx3EsCoWvupGR6u6ZJhFPEe9Q12V8JgUL
* 5JUrqxYcssR9LLVtWDeQcc9HCX4FEqBG7d9GW6t7mvmB1rUuZr9
*
* receiver 接收方二 密钥对
* EOS5WMHqw6jDDBPBm7JXTHemAwtSo2tp93pRysJMRhiT1zUYb24vL
* 5HrcVeuHHNwHsivrMoJ9XvU6EM7Q2wQ2ECiy8GeoiuamhNiSuZq
*/

// 1.  调用钱包获取 发送方私钥
  String senderPrivateKey =  "5KTZYCDdcfNrmEpcf97SJBCtToZjYHjHm8tqTWvzUbsUJgkxcfk";
  EosPrivateKey senderECPrivateKey = new EosPrivateKey(senderPrivateKey);
//        EosPublicKey senderECPublicKey = new EosPublicKey(senderPublicKey);
        // 2.  根据私钥 生成公钥。 或者直接根据公钥 调用钱包获取私钥。 都可以。
  EosPublicKey senderECPublicKey = senderECPrivateKey.getPublicKey();

  String senderPublicKey = senderECPublicKey.toString();
  /**
   * 调用钱包获取 接收方私钥   获取公私钥方式 根据需求确定。
   *  1. 可以根据公钥,从钱包里获取私钥
   *  2. 也可以直接从钱包里取出私钥,反向生成公钥
   *  
   *  实际业务场景,发起方只会有接收方公钥,并没有接收方私钥. 
   *  此时 可以通过 new EosPublicKey(receiverPublicKey) 方式 生成EosPublicKey 对象。
   */
  String receiverPrivateKey = "5JUrqxYcssR9LLVtWDeQcc9HCX4FEqBG7d9GW6t7mvmB1rUuZr9";
  EosPrivateKey receiverECPrivateKey = new EosPrivateKey(receiverPrivateKey);
  EosPublicKey receiverECPublicKey = receiverECPrivateKey.getPublicKey();
  String receiverPublicKey = receiverECPublicKey.toString();
  //        String receiverPublicKey =  "EOS7ez2gagfoXw9XdW3kRx3EsCoWvupGR6u6ZJhFPEe9Q12V8JgUL";


  /**
   * 使用 发送者方私钥 和接收方公钥,生成 aes key, 对数据进行加密
   * nonce  为初始化向量,可以使用固定值,
   *                      也可以使用随机值,并使用私有协议。根据业务需求选择。
   */
  byte[] nonce = new byte[16];
  MTRandom random=new MTRandom();
  random.nextBytes(nonce);

  // 待加密 数据
  byte[] params = "{\"test1\": 1,\"test2\":\"24qqwazzxdtttdxkaskjewuizckczxnlsdosasda4!!!@#$$%^&&*((){}(^#\"}".getBytes("utf8");

  System.out.println("原始加密数据: " + new String(params,"utf8"));

  byte[] encrypted = new byte[0];
  try {
      encrypted = CryptUtil.encrypt(senderECPrivateKey,receiverECPublicKey,nonce,params);
  } catch (InvalidCipherTextException e) {
      e.printStackTrace();
      System.out.println("  do something!!!!");
  }

  System.out.println("加密后数据: " + new String(encrypted,"utf8"));
  try {
      byte[] plainText = CryptUtil.decrypt(receiverECPrivateKey,senderECPublicKey,nonce,encrypted);
      // 解密后数据
      System.out.println("解密后数据 :  "+new String(plainText, "utf8"));
  } catch (InvalidCipherTextException e) {
      e.printStackTrace();
      System.out.println("  do something!!!!");
  }

基于数字信封的 加解密示例


/**
         *
         * sender
         *
         * EOS8g1u3ktAGHs4QsVp9aeaWNebFLtprQHwpaSjegx6iEuoTNhjXU
         * 5KTZYCDdcfNrmEpcf97SJBCtToZjYHjHm8tqTWvzUbsUJgkxcfk
         *
         * receiver 平台公私钥对
         *
         * EOS7ez2gagfoXw9XdW3kRx3EsCoWvupGR6u6ZJhFPEe9Q12V8JgUL
         * 5JUrqxYcssR9LLVtWDeQcc9HCX4FEqBG7d9GW6t7mvmB1rUuZr9
         *
         * receiver 省侧公私钥对
         * EOS5WMHqw6jDDBPBm7JXTHemAwtSo2tp93pRysJMRhiT1zUYb24vL
         * 5HrcVeuHHNwHsivrMoJ9XvU6EM7Q2wQ2ECiy8GeoiuamhNiSuZq
         */

        // 1.  调用钱包获取 发送方私钥
        String senderPrivateKey =  "5KTZYCDdcfNrmEpcf97SJBCtToZjYHjHm8tqTWvzUbsUJgkxcfk";
        EosPrivateKey senderECPrivateKey = new EosPrivateKey(senderPrivateKey);
//        EosPublicKey senderECPublicKey = new EosPublicKey(senderPublicKey);
        // 2.  根据私钥 生成公钥。 或者直接根据公钥 调用钱包获取私钥。 都可以,看具体业务需求
        EosPublicKey senderECPublicKey = senderECPrivateKey.getPublicKey();

        String senderPublicKey = senderECPublicKey.toString();
        /**
         * 调用钱包获取 接收方私钥   获取公私钥方式 根据业务需求确定。
         *  1. 可以根据公钥,从钱包里获取私钥
         *  2. 也可以直接从钱包里取出私钥,反向生成公钥
         */
        String receiverPrivateKey = "5JUrqxYcssR9LLVtWDeQcc9HCX4FEqBG7d9GW6t7mvmB1rUuZr9";
        EosPrivateKey receiverECPrivateKey = new EosPrivateKey(receiverPrivateKey);

        EosPublicKey receiverECPublicKey = receiverECPrivateKey.getPublicKey();

        /**
         * 生成对称密钥
         */
        byte[] nonce = new byte[16];
        MTRandom random=new MTRandom();
        random.nextBytes(nonce);

        // 待加密 数据
        byte[] params = "{\"age\": 1,\"汉字\":\"为初始化向量,可以使用固定值,\",\"12345\":\"24qqwazzxdtttdxkaskjewuizckczxnlsdosasda4!!!@#$$%^&&*(()(^#\"}".getBytes("utf8");

        System.out.println("加密前原始数据: " + new String(params,"utf8"));

        // 发起方使用对称密钥,对原始数据进行加密
        byte[] encryptedData = null;
        try {
            encryptedData = CryptUtil.aesEncryptWithNOIV(nonce,params);
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            System.out.println("  do something!!!!");
        }

        System.out.println("加密后数据: " + HexUtils.toHex(encryptedData));


        System.out.println("加密前对称密钥: " + HexUtils.toHex(nonce));

        // 发起方使用 接收方公钥,对对称密钥进行加密
        byte[] encryptedKey = null;
        try {
            encryptedKey = ECCUtil.publicEncrypt(nonce,receiverECPublicKey.getECPublicKey());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("  do something!!!!");
        }

        System.out.println("加密后对称密钥: " + HexUtils.toHex(encryptedKey));

        // 将对称密钥加密后的数据,密文组装后,进行网络传输。
        // 组装 demo
        /**
         *    4 byte                       |      encryptedKey                 |       4 byte              | encryptedData
         *    对称密钥加密后的数据长度      |      ECC 加密后的对称秘钥           |       密文数据长度         | AES 加密后的密文
         */

        ByteBuffer bytebuffer = ByteBuffer.allocate( 4 + encryptedKey.length + 4 +encryptedData.length);
        bytebuffer.putInt(encryptedKey.length);
        bytebuffer.put(encryptedKey);
        bytebuffer.putInt(encryptedData.length);
        bytebuffer.put(encryptedData);

//        String base58encode = Base58.encode(bytebuffer.array());
//        System.out.println("base58 编码后的:   " + base58encode);

        // 进行 16 进制编码
        String hexencode = HexUtils.toHex(bytebuffer.array());

        System.out.println(" 将数字信封和密文组装后的报文 16进制格式:" + hexencode);

        System.out.println("发送方数据加密完成,可以将数据发送出去 ");

        /**
         *****************************************************  以下为接收方 代码  *************************************
         */

//        byte[] base58decode = Base58.decode(hexencode);
        byte[] hexdecode = HexUtils.toBytes(hexencode);
        ByteBuffer receiveBuffer = ByteBuffer.wrap(hexdecode);

        // 获取到对称秘钥长度
        int receivedEncryptedKeyLength = receiveBuffer.getInt();
        // 加密后的对称密钥key
        byte[] receivedEncryptKey = new byte[receivedEncryptedKeyLength];
        receiveBuffer.get(receivedEncryptKey,0,receivedEncryptedKeyLength);

        System.out.println(" 接收到的 加密后的对称密钥 :" + HexUtils.toHex(receivedEncryptKey));
        // 获取到的 密文的长度
        int contextLength = receiveBuffer.getInt();
        // 密文
        byte[] receivedEncryptContext = new byte[contextLength];
        receiveBuffer.get(receivedEncryptContext,0,contextLength);

        System.out.println(" 接收到的 密文:" + HexUtils.toHex(receivedEncryptContext));


        // 使用接收方私钥,解密对称密钥
        byte[] receiveddecryptKey = null;
        try {
            receiveddecryptKey = ECCUtil.privateDecrypt(receivedEncryptKey,receiverECPrivateKey.getECPrivateKey());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("  do something!!!!");
        }

        System.out.println(" 解密后的对称密钥 :" + HexUtils.toHex(receiveddecryptKey));

        // 使用对称密钥,对密文进行解密

        try {
            byte[] plainText = CryptUtil.aesDecryptWithNOIV(receiveddecryptKey,receivedEncryptContext);
            // 解密后数据
            System.out.println("解密后数据 :  "+new String(plainText, "utf8"));
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            System.out.println("  do something!!!!");
        }

EOS js name To Uint64 OR Uint64 to name

源代码仓库地址:
https://github.com/bcskill/eosjs-name

演示地址
Try on run-kit https://npm.runkit.com/eosjs-account-name

测试代码
// name To Uint64

const eosjsAccountName = require("eosjs-account-name")
const n = eosjsAccountName.nameToUint64('eosio');
console.log('eosio to uint64: ' + n);
console.log('uint64 to name: ' + eosjsAccountName.uint64ToName(n));

// Uint64 to name

const uint64 = '6138663577826885632';
const name = eosjsAccountName.uint64ToName(uint64);