Skip to content

Latest commit

 

History

History
637 lines (562 loc) · 22.4 KB

advanced.md

File metadata and controls

637 lines (562 loc) · 22.4 KB

星火链进阶教程

1.nonce管理

星火链网底层区块链平台(下文简称星火链)作为“许可”公有链,拥有一部分的公有属性,使用星火令(类似于以太坊的gas)作为智能合约执行的消耗,同时不同账户之间可以通过交易相互转移星火令,星火链的账户模型设计了连续递增的nonce值的方式来防止出现重放攻击。

同一账户的发送的交易需要保证nonce值的连续递增,否则交易会因为nonce值太小或者太大导致失败。我们推荐用户使用多个已激活的账号做为账号池,随机使用某一未被加锁账号进行交易,交易完成时释放该账号锁,这样可以保证上链交易的成功率和性能。

具体方案

image-20220704175507796.png

分布式锁之Redisson

使用Redisson框架实现分布式锁。

1.1 加入jar包依赖

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.11.0</version>
</dependency>

1.2 配置Redisson

public class RedissonManager {
    private static Config config = new Config();
    //声明redisso对象
    private static Redisson redisson = null;
   //实例化redisson
 static{
     config.useSingleServer().setAddress("redis://127.0.0.1:6379");
   //得到redisson对象
 redisson = (Redisson) Redisson.create(config);

}

 //获取redisson对象的方法
    public static Redisson getRedisson(){
 return redisson;
    }
}

1.3 锁的获取和释放

public class RedissonLock {
    //从配置类中获取redisson对象
    private static Redisson redisson = RedissonManager.getRedisson();
    private static final String LOCK_TITLE = "redisLock_";
    //加锁
    public static boolean acquire(String lockName){
 //声明key对象
 String key = LOCK_TITLE + lockName;
 //获取锁对象
 RLock mylock = redisson.getLock(key);
 //加锁,并且设置锁过期时间,防止死锁的产生
 mylock.lock(2, TimeUnit.MINUTES);
 System.err.println("======lock======"+Thread.currentThread().getName());
 //加锁成功
 return  true;
    }
    //锁的释放
    public static void release(String lockName){
 //必须是和加锁时的同一个key
 String key = LOCK_TITLE + lockName;
 //获取所对象
 RLock mylock = redisson.getLock(key);
 //释放锁(解锁)
 mylock.unlock();
 System.err.println("======unlock======"+Thread.currentThread().getName());
    }
}

1.4 模拟获取分布式锁

/**
 * 获取分布式锁
 */
public class TransactionDemo {
    private static BIFSDK sdk = BIFSDK.getInstance(SampleConstant.SDK_INSTANCE_URL);
    private static Redisson redisson = RedissonManager.getRedisson();

    public static void main(String[] args) {

 //参数
 String senderAddress1="did:bid:efLrPu7LNR4YwA5M1Kfx6BYb1JP7aPKp";
 String senderPrivateKey1="priSPKteVqGoNgtKE68ZjNHAbGJsNvV9nTBkTLMYTGhVjsBY5R";

 String senderAddress2="did:bid:efBdagu8sVkJWEw5kLt1w69bxa85Kuag";
 String senderPrivateKey2="priSPKmCQMrjCcRgV3u2VsYhujf7QsG7Kr6Tgm94AbzCge46d8";
 //账号集合
 List<String> availableAccAddr = new ArrayList<String>();
 availableAccAddr.add(senderAddress1+";"+senderPrivateKey1);
 availableAccAddr.add(senderAddress2+";"+senderPrivateKey2);
 //目的地址
 String destAddress="did:bid:efnVUgqQFfYeu97ABf6sGm3WFtVXHZB2";
 Long feeLimit=1000000L;
 Long gasPrice=100L;
 //交易对象
 BIFGasSendOperation gasSendOperation= new BIFGasSendOperation();
 gasSendOperation.setAmount(1L);
 gasSendOperation.setDestAddress(destAddress);
 while (true) {
     try {
  Thread.sleep(1000);
  new transaction(availableAccAddr,feeLimit,gasPrice,0,gasSendOperation).start();
     }catch (Exception e) {
  e.printStackTrace();
  break;
     }
 }
 System.out.println("END");
    }

    static class transaction extends Thread{
 List<String> availableAccAddr;
 Long feeLimit;
 Long gasPrice;
 BIFBaseOperation operation;
 Integer domainId;
 public transaction(List<String> availableAccAddr,Long feeLimit,Long gasPrice,Integer domainId,BIFBaseOperation operation ) {
     this.availableAccAddr = availableAccAddr;
     this.feeLimit = feeLimit;
     this.gasPrice = gasPrice;
     this.domainId = domainId;
     this.operation = operation;
 }

 @Override
 public void run() {
     //随机获取交易账号
     int index = new Random().nextInt(availableAccAddr.size());
     String senderAddress=availableAccAddr.get(index).split(";")[0];
     String senderPrivateKey=availableAccAddr.get(index).split(";")[1];
     //加锁
     RedissonLock.acquire(senderAddress);
     System.out.println("线程"+ Thread.currentThread().getName() +"获得分布式锁:"+senderAddress);
     try {
  //获取账号nonce值
  Long nonce=0L;
  BIFAccountGetNonceRequest request = new BIFAccountGetNonceRequest();
  request.setAddress(senderAddress);
  RMap<Object, Object> redisHash = redisson.getMap(senderAddress);
  if(!redisHash.isEmpty()){
      //设置过期时间
      redisHash.expire(60, TimeUnit.SECONDS);
      nonce=Long.parseLong(redisHash.get("nonce").toString());
  }else{
      // 调用getNonce接口
      BIFAccountGetNonceResponse response = sdk.getBIFAccountService().getNonce(request);
      if (0 == response.getErrorCode()) {
   nonce=response.getResult().getNonce()+1;
      }
  }
  //序列化交易
  BIFTransactionSerializeRequest serializeRequest = new BIFTransactionSerializeRequest();
  serializeRequest.setSourceAddress(senderAddress);
  serializeRequest.setNonce(nonce);
  serializeRequest.setFeeLimit(feeLimit);
  serializeRequest.setGasPrice(gasPrice);
  serializeRequest.setOperation(operation);
  serializeRequest.setDomainId(domainId);
  // 调用buildBlob接口
  BIFTransactionSerializeResponse serializeResponse = sdk.getBIFTransactionService().BIFSerializable(serializeRequest);
  System.out.println("serializeResponse:"+ JsonUtils.toJSONString(serializeResponse.getResult()));
  if (!serializeResponse.getErrorCode().equals(Constant.SUCCESS)) {
      throw new SDKException(serializeResponse.getErrorCode(), serializeResponse.getErrorDesc());
  }
  String transactionBlob = serializeResponse.getResult().getTransactionBlob();
  System.out.println("transactionBlob:"+transactionBlob);
  //签名交易
  byte[] signBytes = PrivateKeyManager.sign(HexFormat.hexToByte(transactionBlob), senderPrivateKey);
  String publicKey = PrivateKeyManager.getEncPublicKey(senderPrivateKey);
  //提交交易
  BIFTransactionSubmitRequest submitRequest = new BIFTransactionSubmitRequest();
  submitRequest.setSerialization(transactionBlob);
  submitRequest.setPublicKey(publicKey);
  submitRequest.setSignData(HexFormat.byteToHex(signBytes));
  // 调用bifSubmit接口
  BIFTransactionSubmitResponse transactionSubmitResponse = sdk.getBIFTransactionService().BIFSubmit(submitRequest);
  if (transactionSubmitResponse.getErrorCode() == 0) {
      System.out.println(senderAddress+ " ,hash: "+transactionSubmitResponse.getResult().getHash());

      BIFTransactionGetInfoRequest requestHash = new BIFTransactionGetInfoRequest();
      requestHash.setHash(transactionSubmitResponse.getResult().getHash());
      BIFTransactionGetInfoResponse response = sdk.getBIFTransactionService().getTransactionInfo(requestHash);
      int num=0;
      while (response.getErrorCode() != 0) {
   try{
       Thread.sleep(1000);
       response= sdk.getBIFTransactionService().getTransactionInfo(requestHash);
       num++;
       System.out.println("num    "+num);
       if(num>=120){
    break;
       }
   }catch (Exception e) {
       e.printStackTrace();
   }
      }
      while (response.getErrorCode() != 0) {
   try{
       Thread.sleep(300000);
       BIFTransactionCacheRequest cacheRequest=new BIFTransactionCacheRequest();
       cacheRequest.setHash(transactionSubmitResponse.getResult().getHash());
       BIFTransactionCacheResponse responseTxCacheDataHash = sdk.getBIFTransactionService().getTxCacheData(cacheRequest);
       if (responseTxCacheDataHash.getErrorCode() != 0) {
    response= sdk.getBIFTransactionService().getTransactionInfo(requestHash);
    if(response.getErrorCode()!=0){
        break;
    }
       }
   }catch (Exception e) {
       e.printStackTrace();
   }
      }
      if(response.getErrorCode() == 99){ //99:nonce值过小
   // 调用getNonce接口
   BIFAccountGetNonceResponse  nonceResponse = sdk.getBIFAccountService().getNonce(request);
   if (0 == nonceResponse.getErrorCode()) {
       nonce=nonceResponse.getResult().getNonce();
   }
      }
      //更新nonce值
      nonce=nonce+1;
      redisHash.put("nonce",Long.toString(nonce));
      RedissonLock.release(senderAddress);
      System.out.println("线程"+Thread.currentThread().getName()+"释放分布式锁");
  }
     } catch (Exception e) {
  e.printStackTrace();
     }
 }
    }
}

2.账号权限管理

场景说明:账号A可授权账号B权利值,通过设定权利值可限制账号B的交易权限,账号B可替账号A完成交易操作。

该操作用于设置账户权限。包括签名者权重列表、交易门限、指定类型交易门限。

"source_address": "did:bid:efGksjVp8DzBCDAgPZHMs4zkC6MoTrr9",//操作源账户,即操作的执行方--账号A
"set_privilege": {
 "master_weight": "10",//可选,当前账户的自身权力值
 "signers"://可选,需要操作的签名者列表
 [
   {
     "address": "did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//需要操作的签名者地址,--账号B
     "weight": 8 //可选,签名者的权力值
   }
 ],
 "tx_threshold": "2",//可选,发起交易需要权力值
 "type_thresholds"://可选,不同操作需要的权力值
 [
   {
     "type": 1,//创建账户操作类型
     "threshold": 8 //可选,该操作需要的权力值
   },
   {
     "type": 4,//设置metadata需要权利值
     "threshold": 9 //可选该操作需要的权力值
   },
    {
         "type": 7,//设置转移星火令需要权利值
         "threshold": 10 //可选该操作需要的权力值
    }
 ]
      }

初始化SDK

import cn.bif.api.BIFSDK;

BIFSDK sdk = BIFSDK.getInstance("http://test.bifcore.bitfactory.cn");   //星火链测试网RPC地址

设置账号权限

 // 初始化参数
 String senderAddress = "did:bid:zf2bbxDwdzm4g4fJNTH2ah6gbHu6PdAX2";
 String senderPrivateKey = "priSrrfxxMAvnify3iRtTEW2zy87qo4N4B2gwhXSN4WFbXFJUs";
 String masterWeight = "";
 BIFSigner[] signers = new BIFSigner[1];
 BIFSigner s=new BIFSigner();
 s.setAddress("did:bid:efAsXt5zM2Hsq6wCYRMZBS5Q9HvG2EmK");
 s.setWeight(8L);
 signers[0]=s;

 String txThreshold = "2";
 BIFTypeThreshold[] typeThresholds = new BIFTypeThreshold[1];
 BIFTypeThreshold d=new BIFTypeThreshold();
 d.setThreshold(8L);
 d.setType(1);
 typeThresholds[0]=d;

 BIFAccountSetPrivilegeRequest request = new BIFAccountSetPrivilegeRequest();
 request.setSenderAddress(senderAddress);
 request.setPrivateKey(senderPrivateKey);
 request.setSigners(signers);
 request.setTxThreshold(txThreshold);
 request.setMasterWeight(masterWeight);
 request.setTypeThresholds(typeThresholds);
 request.setRemarks("set privilege");
      
 // 调用 setPrivilege 接口
 BIFAccountSetPrivilegeResponse response = sdk.getBIFAccountService().setPrivilege(request);
 if (response.getErrorCode() == 0) {
     System.out.println(JsonUtils.toJSONString(response.getResult()));
 } else {
     System.out.println(JsonUtils.toJSONString(response));
 }

查询账号权限

 // 初始化请求参数
 String accountAddress = "did:bid:zf2bbxDwdzm4g4fJNTH2ah6gbHu6PdAX2";
 BIFAccountPrivRequest request = new BIFAccountPrivRequest();
 request.setAddress(accountAddress);
      
 // 调用getAccountPriv接口
 BIFAccountPrivResponse response = sdk.getBIFAccountService().getAccountPriv(request);

 if (response.getErrorCode() == 0) {
     BIFAccountPrivResult result = response.getResult();
     System.out.println(JsonUtils.toJSONString(result));
 } else {
     System.out.println(JsonUtils.toJSONString(response));
 }

3.合约批量调用

场景说明:用户处理事件过程中涉及多个合约调用、同一合约多次调用时,可使用该接口,保证事务的一致性,同时可减少交易次数,节省交易费用。

该操作用于转移星火令并触发合约。

示例

 // 初始化参数
 String senderAddress = "did:bid:ef7zyvBtyg22NC4qDHwehMJxeqw6Mmrh";
 String contractAddress = "did:bid:eftzENB3YsWymQnvsLyF4T2ENzjgEg41";
 String senderPrivateKey = "priSPKr2dgZTCNj1mGkDYyhyZbCQhEzjQm7aEAnfVaqGmXsW2x";
 Long amount = 0L;
 String destAddress1 = KeyPairEntity.getBidAndKeyPair().getEncAddress();
 String destAddress2 = KeyPairEntity.getBidAndKeyPair().getEncAddress();
 String input1 = "{\"method\":\"creation\",\"params\":{\"document\":{\"@context\": [\"https://w3.org/ns/did/v1\"],\"context\": \"https://w3id.org/did/v1\"," +
  "\"id\": \""+destAddress1+"\", \"version\": \"1\"}}}";
 String input2 = "{\"method\":\"creation\",\"params\":{\"document\":{\"@context\": [\"https://w3.org/ns/did/v1\"],\"context\": \"https://w3id.org/did/v1\"," +
  "\"id\": \""+destAddress2+"\", \"version\": \"1\"}}}";

 List<BIFContractInvokeOperation> operations = new ArrayList<BIFContractInvokeOperation>();
 //操作对象1
 BIFContractInvokeOperation operation1=new BIFContractInvokeOperation();
 operation1.setContractAddress(contractAddress);
 operation1.setBIFAmount(amount);
 operation1.setInput(input1);
 //操作对象2
 BIFContractInvokeOperation operation2=new BIFContractInvokeOperation();
 operation2.setContractAddress(contractAddress);
 operation2.setBIFAmount(amount);
 operation2.setInput(input2);

 operations.add(operation1);
 operations.add(operation2);

 BIFBatchContractInvokeRequest request = new BIFBatchContractInvokeRequest();
 request.setSenderAddress(senderAddress);
 request.setPrivateKey(senderPrivateKey);
 request.setOperations(operations);
 request.setRemarks("contract invoke");

 // 调用 bifContractInvoke 接口
 BIFContractInvokeResponse response = sdk.getBIFContractService().batchContractInvoke(request);
 if (response.getErrorCode() == 0) {
     System.out.println(JsonUtils.toJSONString(response.getResult()));
 } else {
     System.out.println(JsonUtils.toJSONString(response));
 }

4.交易回执

交易回执结构如下:

{
	"actual_fee": 776000, //交易实际花费的费用
	"close_time": 1664352470872350, //交易执行完成的时间
	"error_code": 0, //交易的错误码,0表示交易执行成功,非0表示交易执行失败
	"error_desc": "", //交易的错误描述
	"hash": "0f3477d3a6708168ce7f694a7eaf87129f7373548be0b085d3422809c03ea8d1", //交易hash值
	"ledger_seq": 932290, //交易所在的区块高度
	"signatures": [ //签名列表
		{
			"public_key": "b0656670063fd619ae607e39187477eabb70f45a657879af8c7dfe1c0dbb105dbccf23", //公钥
			"sign_data": "2c212c987688c176e8b444b0e7f977d8c290ccf88a29305aadac2b693ce983827b46842e403703fd0456e23b6121cb242b1610f1926421577ee6714950dd4900" //签名数据
		}
	],
	"transaction": { //交易内容,交易类型包括:创建账号/合约、合约调用、星火令转移、设置metadata、设置权限、记录日志
		.....
	},
	"tx_size": 776 //交易字节数
}

4.1 创建账号

{
  "source_address":"did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//交易源账号,即交易的发起方
  "nonce":2, //交易源账户的nonce值
  "fee_limit" : 1000000, //愿为交易花费的手续费
  "gas_price": 1000,//gas价格(不小于配置的最低值)
  "ceil_ledger_seq": 100, //可选,区块高度限制, 如果大于0,则交易只有在该区块高度之前(包括该高度)才有效
  "metadata":"0123456789abcdef", //可选,用户自定义给交易的备注,16进制格式
  "operations":[
    {
      "type": 1, // 创建账户操作类型
      "source_address": "did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//可选,操作源账户,即操作的执行方
      "metadata": "0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
      "create_account": {
        "dest_address": "did:bid:ef9nCb6Ge1Pj2wv7XW3hnzka6G2MkPZs",//待创建的目标账户地址
        "init_balance": 100000,//目标账户的初始化余额
        "priv":  {
          "master_weight": 1,//目标账户拥有的权力值
          "thresholds": {
            "tx_threshold": 1//发起交易需要的权力值
          }
        }
      }
    }
  ]
}

4.2 创建合约

{
  "source_address":"did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//交易源账号,即交易的发起方
  "nonce":2, //交易源账户的nonce值
  "fee_limit" : 1000000, //愿为交易花费的手续费
  "gas_price": 1000,//gas价格(不小于配置的最低值)
  "ceil_ledger_seq": 100, //可选,区块高度限制, 如果大于0,则交易只有在该区块高度之前(包括该高度)才有效
  "metadata":"0123456789abcdef", //可选,用户自定义给交易的备注,16进制格式
  "operations":[
    {
      "type": 1, // 创建账户操作类型
      "source_address": "did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//可选,操作源账户,即操作的执行方
      "metadata": "0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
      "create_account": {
        "contract": { //合约
          "payload": "
                 'use strict';
                 function init(bar)
                 {
                   return;
                 }

                 function main(input)
                 {
                   return;
                 }

                 function query()
                 {
                   return;
                 }
                 "//合约代码
        },
        "init_balance": 100000,  //合约账户初始化资产
        "init_input" : "{\"method\":\"toWen\",\"params\":{\"feeType\":0}}",//可选,合约代码init函数的入参
        "priv": {
          "master_weight": 0,//待创建的合约账户拥有的权力值
          "thresholds": {
            "tx_threshold": 1 //发起交易需要的权力值
          }
        }
      }
    }
  ]
}

4.3合约调用

{
    "source_address": "did:bid:ef27dchGWy9WY5xVznyPScZ6MP5CATsJF",//交易源账号,即交易的发起方
    "fee_limit": 200000000,//愿为交易花费的手续费
    "gas_price": 200,//gas价格(不小于配置的最低值)
    "nonce": 111028, //交易源账户的nonce值
    "operations": [
      {
        "type": 7,//合约调用类型
        "pay_coin": {
          "dest_address": "did:bid:efMTBwMHE7audr53KyZK5cjw3ysZUXPF", //合约地址
          "input": "{\"function\":\"safeTransferFrom(address,address,string)\",\"args\":\"did:bid:ef27dchGWy9WY5xVznyPScZ6MP5CATsJF,did:bid:efdcPDsvv2RnMwAt5D1PYxMtbGvW8JjT,\u0027did:bid:efdudDHcUc4EXUyW2hTCP9JtRxfiRsDB\u0027\"}" //合约调用方法
        }
      }
    ]
}

4.4 星火令转移

{
  "fee_limit": 10253000,//愿为交易花费的手续费
  "gas_price": 1000,//gas价格(不小于配置的最低值)
  "nonce": 110, //交易源账户的nonce值
  "operations": [
    {
      "pay_coin": {
        "amount": 1000000000, //转账金额
        "dest_address": "did:bid:ef3LqNzb4ssNf2vqwNwBfqngrA3Sx8yD" //目的地址
      },
      "type": 7// 交易操作类型
    }
  ],
  "source_address": "did:bid:ef249EXxmYEuvazPM5Qr77H5xN18Zba3f"//交易源账号,即交易的发起方
}

4.5 设置metadata

{
  "source_address":"did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//交易源账号,即交易的发起方
  "nonce":2, //交易源账户的nonce值
  "fee_limit" : 1000000, //愿为交易花费的手续费
  "gas_price": 1000,//gas价格(不小于配置的最低值)
  "ceil_ledger_seq": 100,//可选,区块高度限制, 如果大于0,则交易只有在该区块高度之前(包括该高度)才有效
  "metadata":"0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
  "operations":[
    {
      "type": 4,//设置 metadata 操作类型
      "source_address": "did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//可选,操作源账户,即操作的执行方
      "metadata": "0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
      "set_metadata": {
        "key": "abc",//metadata关键字
        "value": "hello abc!",//metadata内容
        "version": 0 //可选,metadata版本号
      }
    }
  ]
}

4.6 设置权限

{
  "source_address":"did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//交易源账号,即交易的发起方
  "nonce":2, //交易源账户的nonce值
  "fee_limit" : 1000000, //愿为交易花费的手续费
  "gas_price": 1000,//gas价格(不小于配置的最低值)
  "ceil_ledger_seq": 100,//可选,区块高度限制, 如果大于0,则交易只有在该区块高度之前(包括该高度)才有效
  "metadata":"0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
  "operations":[
    {
      "type": 9,//设置权限操作类型
      "source_address": "did:bid:efEB8Lvnzw8pzDCWUmqe1XJfNZMAYRvR",//可选,操作源账户,即操作的执行方
      "metadata": "0123456789abcdef",//可选,用户自定义给交易的备注,16进制格式
      "set_privilege": {
        "master_weight": "10",//可选,当前账户的自身权力值
        "signers"://可选,需要操作的签名者列表
        [
          {
            "address": "did:bid:ef9nCb6Ge1Pj2wv7XW3hnzka6G2MkPZs",//需要操作的签名者地址
            "weight": 8 //可选,签名者的权力值
          }
        ],
        "tx_threshold": "2",//可选,发起交易需要权力值
        "type_thresholds"://可选,不同操作需要的权力值
        [
          {
            "type": 1,//创建账户操作类型
            "threshold": 8 //可选,该操作需要的权力值
          },
          {
            "type": 2,//发行资产操作类型
            "threshold": 9 //可选该操作需要的权力值
          }
        ]
      }
    }
  ]
}

4.7 记录日志

{
  "transaction": {
    "nonce": 511546,
    "operations": [
      {
        "log": {
          "datas": [
            "{\"data\":\"\",\"topics\":[\"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"00000000000000006566ee21773c3a96a1440a669dee46e40ca3b1d28caa5e70\",\"0000000000000000656686e34bc4e3a92a07cf162c463518a9491f0c4ad56aca\",\"0000000000000000000000000000000000000000000000000000000000061131\"]}"
          ],//日志内容
          "topic": "evmLog_dced48d56925087f9883d7d9b66e71f8ea93681adc4b0d8a64771928202f74a0"// 日志主题
        },
        "type": 8 //记录日志操作类型
      }
    ],
    "source_address": "did:bid:efMTBwMHE7audr53KyZK5cjw3ysZUXPF"
  }
}