content: /*
   * Copyright 2023 Ant Group
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.alipay.antchain.bridge.plugins.chainmaker;
  
  import cn.hutool.core.util.HexUtil;
  import cn.hutool.core.util.ObjectUtil;
  import cn.hutool.core.util.StrUtil;
  import com.alibaba.fastjson.JSON;
  import com.alipay.antchain.bridge.commons.bbc.AbstractBBCContext;
  import com.alipay.antchain.bridge.commons.bbc.syscontract.AuthMessageContract;
  import com.alipay.antchain.bridge.commons.bbc.syscontract.ContractStatusEnum;
  import com.alipay.antchain.bridge.commons.bbc.syscontract.SDPContract;
  import com.alipay.antchain.bridge.commons.core.base.CrossChainMessage;
  import com.alipay.antchain.bridge.commons.core.base.CrossChainMessageReceipt;
  import com.alipay.antchain.bridge.plugins.lib.BBCService;
  import com.alipay.antchain.bridge.plugins.spi.bbc.AbstractBBCService;
  import com.google.gson.Gson;
  import com.google.protobuf.InvalidProtocolBufferException;
  import lombok.Getter;
  import org.bouncycastle.util.encoders.Hex;
  import org.chainmaker.pb.common.*;
  import org.chainmaker.pb.config.ChainConfigOuterClass;
  import org.chainmaker.sdk.*;
  import org.chainmaker.sdk.config.*;
  import org.chainmaker.sdk.crypto.ChainMakerCryptoSuiteException;
  import org.chainmaker.sdk.utils.*;
  import org.chainmaker.sdk.utils.Utils;
  import org.web3j.abi.*;
  import org.web3j.abi.datatypes.*;
  import org.web3j.abi.datatypes.generated.Bytes32;
  import org.web3j.abi.datatypes.generated.Uint32;
  import org.web3j.protocol.core.methods.response.Log;
  
  import java.io.IOException;
  import java.lang.reflect.Field;
  import java.math.BigInteger;
  import java.security.NoSuchAlgorithmException;
  import java.security.spec.InvalidKeySpecException;
  import java.util.*;
  import java.util.stream.Collectors;
  
  @BBCService(products = "chainmaker", pluginId = "plugin-chainmaker")
  @Getter
  public class ChainMakerBBCService extends AbstractBBCService {
  
      private static final String OPENSSL_PROVIDER = "openSSL";
      private static final String TLS_NEGOTIATION = "TLS";
      private static final String CONTRACT_ARGS_EVM_PARAM = "data";
      private static final String EMPTY_MATCHER = "0*";
      private static final long rpcCallTimeout = 10000;
      private static final long syncResultTimeout = 10000;
  
      private static final String AM_BYTES = "60806040523480156200001157600080fd5b506000620000246200010960201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000111565b600033905090565b6138c880620001216000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80638406c079116100715780638406c079146101655780638da5cb5b14610183578063a7566e77146101a1578063e41fb517146101bd578063e85df5f7146101d9578063f2fde38b146101f5576100a9565b8063189a0a15146100ae5780636548e9bc146100de578063705c572c146100fa578063715018a61461012a578063747c7ffd14610134575b600080fd5b6100c860048036038101906100c3919061286f565b610211565b6040516100d59190612b99565b60405180910390f35b6100f860048036038101906100f39190612775565b61025a565b005b610114600480360381019061010f919061286f565b61031a565b6040516101219190612b99565b60405180910390f35b61013261034d565b005b61014e60048036038101906101499190612775565b610487565b60405161015c929190612e72565b60405180910390f35b61016d6104c8565b60405161017a9190612b99565b60405180910390f35b61018b6104ee565b6040516101989190612b99565b60405180910390f35b6101bb60048036038101906101b6919061282e565b610517565b005b6101d760048036038101906101d2919061279e565b610610565b005b6101f360048036038101906101ee91906127f2565b610782565b005b61020f600480360381019061020a9190612775565b610974565b005b6000600360008363ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b610262610b1d565b73ffffffffffffffffffffffffffffffffffffffff166102806104ee565b73ffffffffffffffffffffffffffffffffffffffff16146102d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102cd90612d92565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60036020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610355610b1d565b73ffffffffffffffffffffffffffffffffffffffff166103736104ee565b73ffffffffffffffffffffffffffffffffffffffff16146103c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103c090612d92565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60026020528060005260406000206000915090508060000160009054906101000a900463ffffffff16908060000160049054906101000a900460ff16905082565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b3373ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146105a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161059e90612d12565b60405180910390fd5b6105b081610b25565b6000806105bc83610b28565b915091507f2b41cea8d63514764033a161efa4273751d3f20eda0ecd3c514fa43b99f38bb082826040516105f1929190612c1b565b60405180910390a161060b6106068383610c11565b610d9d565b505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160049054906101000a900460ff1661069f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069690612db2565b60405180910390fd5b6106a98282610da0565b60006040518060800160405280600163ffffffff1681526020016106cc85610da4565b8152602001600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900463ffffffff1663ffffffff1681526020018381525090507f79b7516b1b7a6a39fb4b7b22e8667cd3744e5c27425292f8a9f49d1042c0c65161075f82610dbb565b60405161076c9190612bb4565b60405180910390a161077d81610e43565b505050565b61078a610b1d565b73ffffffffffffffffffffffffffffffffffffffff166107a86104ee565b73ffffffffffffffffffffffffffffffffffffffff16146107fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107f590612d92565b60405180910390fd5b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060000160049054906101000a900460ff1615610893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088a90612cd2565b60405180910390fd5b60018160000160046101000a81548160ff021916908315150217905550818160000160006101000a81548163ffffffff021916908363ffffffff16021790555082600360008463ffffffff1663ffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508163ffffffff167f1d5c04c569492046e8119073d290445582589ec4f52031b868ed74b2b7f7bfa8846040516109679190612b99565b60405180910390a2505050565b61097c610b1d565b73ffffffffffffffffffffffffffffffffffffffff1661099a6104ee565b73ffffffffffffffffffffffffffffffffffffffff16146109f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109e790612d92565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610a60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a5790612cb2565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600033905090565b50565b606080600080610b3a82866004610e46565b9050600482610b499190612f29565b91506000610b58600483610e5c565b90506000610b6d84888463ffffffff16610e46565b90508163ffffffff1684610b819190612f29565b93506000610b9185896004610e46565b9050600485610ba09190612f29565b94506000610baf600483610e5c565b90506000610bc4878b8463ffffffff16610e46565b90508163ffffffff1687610bd89190612f29565b965060006040518060400160405280868152602001838152509050610c008160200151610e6a565b995099505050505050505050915091565b610c19612621565b6000610c2483610e9c565b9050600073ffffffffffffffffffffffffffffffffffffffff1660036000836040015163ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610cd9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cd090612c92565b60405180910390fd5b60036000826040015163ffffffff1663ffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c09b261b85836020015184606001516040518463ffffffff1660e01b8152600401610d6193929190612bd6565b600060405180830381600087803b158015610d7b57600080fd5b505af1158015610d8f573d6000803e3d6000fd5b505050508091505092915050565b50565b5050565b600080610db083610f31565b905080915050919050565b60606001826000015163ffffffff161415610de057610dd982610fbd565b9050610e3e565b6002826000015163ffffffff161415610e0357610dfc82611164565b9050610e3e565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e3590612dd2565b60405180910390fd5b919050565b50565b6060610e53848484611341565b90509392505050565b600082820151905092915050565b6060806000610e78846113bc565b90506000610e898260200151611537565b9050816080015181935093505050915091565b610ea4612621565b6000610eaf83611604565b905060018163ffffffff161415610ed157610ec983611618565b915050610f2c565b60028163ffffffff161415610ef157610ee983611756565b915050610f2c565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f2390612e32565b60405180910390fd5b919050565b600080602067ffffffffffffffff811115610f75577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015610fa75781602001600182028036833780820191505090505b5090508260208201526020810151915050919050565b60606001826000015163ffffffff161461100c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161100390612d32565b60405180910390fd5b60006004602060046110218660600151611801565b61102b9190612f29565b6110359190612f29565b61103f9190612f29565b905060008167ffffffffffffffff811115611083577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156110b55781602001600182028036833780820191505090505b50905060008290506110cc81866000015184611859565b6110d66020611870565b816110e191906131b5565b90506110f281866020015184611a9f565b6110fa611aa9565b60ff168161110891906131b5565b905061111981866040015184611859565b6111236020611870565b8161112e91906131b5565b905061113f81866060015184611ab2565b61114c8560600151611801565b8161115791906131b5565b9050819350505050919050565b60606002826000015163ffffffff16146111b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111aa90612cf2565b60405180910390fd5b63ffffffff82606001515111156111ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111f690612c52565b60405180910390fd5b6000600460206004808660600151516112189190612f29565b6112229190612f29565b61122c9190612f29565b6112369190612f29565b905060008167ffffffffffffffff81111561127a577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156112ac5781602001600182028036833780820191505090505b50905060008290506112c381866000015184611859565b6112cd6020611870565b816112d891906131b5565b90506112e981866020015184611a9f565b6112f1611aa9565b60ff16816112ff91906131b5565b905061131081866040015184611859565b61131a6020611870565b8161132591906131b5565b905061133681866060015184611b1d565b819350505050919050565b60608082156000811461135f576040519150602082016040526113b0565b6040519150601f8416801560200281840101858101888315602002848a0101015b8183101561139d5780518352602083019250602081019050611380565b50868552601f19601f8301166040525050505b50809150509392505050565b6113c4612658565b6113cc612658565b6000600690505b835181101561152d576113e461269e565b6113ee8583611bf6565b8093508192505050600461ffff16816000015161ffff161415611425576114188160400151611d3e565b8360000181905250611527565b600561ffff16816000015161ffff16141561144c5780604001518360200181905250611526565b600761ffff16816000015161ffff1614156114945761147861147382604001516000611dd3565b611e04565b836040019063ffffffff16908163ffffffff1681525050611525565b600861ffff16816000015161ffff1614156114bb5780604001518360600181905250611524565b600961ffff16816000015161ffff1614156114e25780604001518360800181905250611523565b600a61ffff16816000015161ffff1614156115225761150e6115098260400151600061213b565b61216a565b8360a0019061ffff16908161ffff16815250505b5b5b5b5b5b506113d3565b8192505050919050565b6060600c82511161157d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161157490612d72565b60405180910390fd5b600061159261158d846008611dd3565b611e04565b905080600c6115a19190612f7f565b63ffffffff16835110156115ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115e190612e52565b60405180910390fd5b6115fc600c848363ffffffff16611341565b915050919050565b6000611611825183610e5c565b9050919050565b611620612621565b600061162c6020611870565b835161163891906131b5565b905060006116468285612353565b905060208261165591906131b5565b915060006116638386610e5c565b905061166f6020611870565b8361167a91906131b5565b925060006116888487612361565b67ffffffffffffffff8111156116c7577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156116f95781602001600182028036833780820191505090505b509050611707848783612390565b61171081611801565b8461171b91906131b5565b93506040518060800160405280600163ffffffff1681526020018481526020018363ffffffff16815260200182815250945050505050919050565b61175e612621565b600061176a6020611870565b835161177691906131b5565b905060006117848285612353565b905060208261179391906131b5565b915060006117a18386610e5c565b90506117ad6020611870565b836117b891906131b5565b925060006117c684876123e8565b90506040518060800160405280600263ffffffff1681526020018481526020018363ffffffff16815260200182815250945050505050919050565b6000602082516118119190612fb9565b90506000602083516118239190613307565b14611837578080611833906132be565b9150505b8080611842906132be565b915050602081611852919061315b565b9050919050565b828101600481038051848352808252505050505050565b6000816008811461197d5760108114611986576018811461198f576020811461199857602881146119a157603081146119aa57603881146119b357604081146119bc57604881146119c557605081146119ce57605881146119d757606081146119e057606881146119e957607081146119f257607881146119fb5760808114611a045760888114611a0d5760908114611a165760988114611a1f5760a08114611a285760a88114611a315760b08114611a3a5760b88114611a435760c08114611a4c5760c88114611a555760d08114611a5e5760d88114611a675760e08114611a705760e88114611a795760f08114611a825760f88114611a8b576101008114611a945760209150611a99565b60019150611a99565b60029150611a99565b60039150611a99565b60049150611a99565b60059150611a99565b60069150611a99565b60079150611a99565b60089150611a99565b60099150611a99565b600a9150611a99565b600b9150611a99565b600c9150611a99565b600d9150611a99565b600e9150611a99565b600f9150611a99565b60109150611a99565b60119150611a99565b60129150611a99565b60139150611a99565b60149150611a99565b60159150611a99565b60169150611a99565b60179150611a99565b60189150611a99565b60199150611a99565b601a9150611a99565b601b9150611a99565b601c9150611a99565b601d9150611a99565b601e9150611a99565b601f9150611a99565b602091505b50919050565b8183820152505050565b60006020905090565b600060208351611ac29190612fb9565b9050600060208451611ad49190613307565b1115611ae9578080611ae5906132be565b9150505b60006001820191505b81811015611b16576020810284015185840152602085039450600181019050611af2565b5050505050565b600082519050611b2e848284611859565b600484611b3b91906131b5565b93508063ffffffff16841015611b86576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b7d90612df2565b60405180910390fd5b8063ffffffff1684611b9891906131b5565b9350801560008114611ba957611bef565b8483018051601f84168015602002818801018581018215602002838601015b81831015611be55782518152602083019250602081019050611bc8565b5083855250505050505b5050505050565b611bfe61269e565b600082845111611c43576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c3a90612c72565b60405180910390fd5b6006831015611c87576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c7e90612d52565b60405180910390fd5b611c8f61269e565b611ca1611c9c868661213b565b61216a565b816000019061ffff16908161ffff1681525050600284611cc19190612f29565b9350611cd5611cd08686611dd3565b611e04565b816020019063ffffffff16908163ffffffff1681525050600484611cf99190612f29565b9350611d108585836020015163ffffffff166124d8565b8160400181905250806020015163ffffffff1684611d2e9190612f29565b9350808492509250509250929050565b611d466126c9565b611d4e6126c9565b6000600690505b8351811015611dc957611d6661269e565b611d708583611bf6565b8093508192505050600161ffff16816000015161ffff161415611d9f5780604001518360000181905250611dc3565b600261ffff16816000015161ffff161415611dc257806040015183602001819052505b5b50611d55565b8192505050919050565b60008251600483611de49190612f29565b1115611def57600080fd5b63ffffffff8260048501015116905092915050565b6000808260e01b90506000600467ffffffffffffffff811115611e50577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015611e825781602001600182028036833780820191505090505b50905081600360048110611ebf577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b81600081518110611efd577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535081600260048110611f66577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b81600181518110611fa4577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508160016004811061200d577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b8160028151811061204b577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350816000600481106120b4577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b816003815181106120f2577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600061212e826000611dd3565b9050809350505050919050565b6000825160028361214c9190612f29565b111561215757600080fd5b61ffff8260028501015116905092915050565b6000808260f01b90506000600267ffffffffffffffff8111156121b6577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156121e85781602001600182028036833780820191505090505b50905081600160028110612225577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b81600081518110612263577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350816000600281106122cc577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b1a60f81b8160018151811061230a577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600061234682600061213b565b9050809350505050919050565b600082820151905092915050565b6000828201519050600160208204016000602083061115612383576001810190505b6020810291505092915050565b60006020905060008085850151925060016020840401905060006020840611156123bb576001810190505b5b808210156123e05785850151602083028501526020860395506001820191506123bc565b505050505050565b606060006123f68484610e5c565b63ffffffff16905060048461240b91906131b5565b935083811115612450576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161244790612e12565b60405180910390fd5b808461245c91906131b5565b9350606081156000811461247b576040519150602082016040526124cc565b6040519150601f8316801560200281840101848101888315602002848a0101015b818310156124b9578051835260208301925060208101905061249c565b50858552601f19601f8301166040525050505b50809250505092915050565b6060835182846124e89190612f29565b11156124f357600080fd5b60008267ffffffffffffffff811115612535577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156125675781602001600182028036833780820191505090505b5090506000806020830191508560208801019050612586828287612593565b8293505050509392505050565b5b602081106125d257815183526020836125ad9190612f29565b92506020826125bc9190612f29565b91506020816125cb91906131b5565b9050612594565b60008114156125e05761261c565b600060018260206125f191906131b5565b6101006125fe919061303d565b61260891906131b5565b905080198351168185511681811786525050505b505050565b6040518060800160405280600063ffffffff16815260200160008019168152602001600063ffffffff168152602001606081525090565b6040518060c0016040528061266b6126c9565b815260200160608152602001600063ffffffff1681526020016060815260200160608152602001600061ffff1681525090565b6040518060600160405280600061ffff168152602001600063ffffffff168152602001606081525090565b604051806040016040528060608152602001606081525090565b60006126f66126f184612ec0565b612e9b565b90508281526020810184848401111561270e57600080fd5b61271984828561324b565b509392505050565b60008135905061273081613864565b92915050565b600082601f83011261274757600080fd5b81356127578482602086016126e3565b91505092915050565b60008135905061276f8161387b565b92915050565b60006020828403121561278757600080fd5b600061279584828501612721565b91505092915050565b600080604083850312156127b157600080fd5b60006127bf85828601612721565b925050602083013567ffffffffffffffff8111156127dc57600080fd5b6127e885828601612736565b9150509250929050565b6000806040838503121561280557600080fd5b600061281385828601612721565b925050602061282485828601612760565b9150509250929050565b60006020828403121561284057600080fd5b600082013567ffffffffffffffff81111561285a57600080fd5b61286684828501612736565b91505092915050565b60006020828403121561288157600080fd5b600061288f84828501612760565b91505092915050565b6128a1816131e9565b82525050565b6128b0816131fb565b82525050565b6128bf81613207565b82525050565b60006128d082612ef1565b6128da8185612f07565b93506128ea81856020860161325a565b6128f3816133c5565b840191505092915050565b600061290982612efc565b6129138185612f18565b935061292381856020860161325a565b61292c816133c5565b840191505092915050565b6000612944602a83612f18565b915061294f826133e3565b604082019050919050565b6000612967602383612f18565b915061297282613432565b604082019050919050565b600061298a601a83612f18565b915061299582613481565b602082019050919050565b60006129ad602683612f18565b91506129b8826134aa565b604082019050919050565b60006129d0601883612f18565b91506129db826134f9565b602082019050919050565b60006129f3602283612f18565b91506129fe82613522565b604082019050919050565b6000612a16602183612f18565b9150612a2182613571565b604082019050919050565b6000612a39602283612f18565b9150612a44826135c0565b604082019050919050565b6000612a5c600e83612f18565b9150612a678261360f565b602082019050919050565b6000612a7f601b83612f18565b9150612a8a82613638565b602082019050919050565b6000612aa2602083612f18565b9150612aad82613661565b602082019050919050565b6000612ac5602683612f18565b9150612ad08261368a565b604082019050919050565b6000612ae8602983612f18565b9150612af3826136d9565b604082019050919050565b6000612b0b603283612f18565b9150612b1682613728565b604082019050919050565b6000612b2e603083612f18565b9150612b3982613777565b604082019050919050565b6000612b51602983612f18565b9150612b5c826137c6565b604082019050919050565b6000612b74603483612f18565b9150612b7f82613815565b604082019050919050565b612b938161323b565b82525050565b6000602082019050612bae6000830184612898565b92915050565b60006020820190508181036000830152612bce81846128c5565b905092915050565b60006060820190508181036000830152612bf081866128fe565b9050612bff60208301856128b6565b8181036040830152612c1181846128c5565b9050949350505050565b60006040820190508181036000830152612c3581856128fe565b90508181036020830152612c4981846128c5565b90509392505050565b60006020820190508181036000830152612c6b81612937565b9050919050565b60006020820190508181036000830152612c8b8161295a565b9050919050565b60006020820190508181036000830152612cab8161297d565b9050919050565b60006020820190508181036000830152612ccb816129a0565b9050919050565b60006020820190508181036000830152612ceb816129c3565b9050919050565b60006020820190508181036000830152612d0b816129e6565b9050919050565b60006020820190508181036000830152612d2b81612a09565b9050919050565b60006020820190508181036000830152612d4b81612a2c565b9050919050565b60006020820190508181036000830152612d6b81612a4f565b9050919050565b60006020820190508181036000830152612d8b81612a72565b9050919050565b60006020820190508181036000830152612dab81612a95565b9050919050565b60006020820190508181036000830152612dcb81612ab8565b9050919050565b60006020820190508181036000830152612deb81612adb565b9050919050565b60006020820190508181036000830152612e0b81612afe565b9050919050565b60006020820190508181036000830152612e2b81612b21565b9050919050565b60006020820190508181036000830152612e4b81612b44565b9050919050565b60006020820190508181036000830152612e6b81612b67565b9050919050565b6000604082019050612e876000830185612b8a565b612e9460208301846128a7565b9392505050565b6000612ea5612eb6565b9050612eb1828261328d565b919050565b6000604051905090565b600067ffffffffffffffff821115612edb57612eda613396565b5b612ee4826133c5565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000612f3482613231565b9150612f3f83613231565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115612f7457612f73613338565b5b828201905092915050565b6000612f8a8261323b565b9150612f958361323b565b92508263ffffffff03821115612fae57612fad613338565b5b828201905092915050565b6000612fc482613231565b9150612fcf83613231565b925082612fdf57612fde613367565b5b828204905092915050565b6000808291508390505b6001851115613034578086048111156130105761300f613338565b5b600185161561301f5780820291505b808102905061302d856133d6565b9450612ff4565b94509492505050565b600061304882613231565b915061305383613231565b92506130807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484613088565b905092915050565b6000826130985760019050613154565b816130a65760009050613154565b81600181146130bc57600281146130c6576130f5565b6001915050613154565b60ff8411156130d8576130d7613338565b5b8360020a9150848211156130ef576130ee613338565b5b50613154565b5060208310610133831016604e8410600b841016171561312a5782820a90508381111561312557613124613338565b5b613154565b6131378484846001612fea565b9250905081840481111561314e5761314d613338565b5b81810290505b9392505050565b600061316682613231565b915061317183613231565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156131aa576131a9613338565b5b828202905092915050565b60006131c082613231565b91506131cb83613231565b9250828210156131de576131dd613338565b5b828203905092915050565b60006131f482613211565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600063ffffffff82169050919050565b82818337600083830152505050565b60005b8381101561327857808201518184015260208101905061325d565b83811115613287576000848401525b50505050565b613296826133c5565b810181811067ffffffffffffffff821117156132b5576132b4613396565b5b80604052505050565b60006132c982613231565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156132fc576132fb613338565b5b600182019050919050565b600061331282613231565b915061331d83613231565b92508261332d5761332c613367565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b60008160011c9050919050565b7f656e636f6465417574684d65737361676556323a20626f6479206c656e67746860008201527f206f7665726c696d697400000000000000000000000000000000000000000000602082015250565b7f6c656e677468206f66207261772064617461206c657373207468616e206f666660008201527f7365740000000000000000000000000000000000000000000000000000000000602082015250565b7f417574684d73673a206e6f2070726f746f636f6c206578697374000000000000600082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f417574684d73673a2070726f746f636f6c206578697374730000000000000000600082015250565b7f656e636f6465417574684d65737361676556323a2077726f6e6720766572736960008201527f6f6e000000000000000000000000000000000000000000000000000000000000602082015250565b7f417574684d73673a2073656e646572206e6f742076616c69642072656c61796560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b7f656e636f6465417574684d65737361676556313a2077726f6e6720766572736960008201527f6f6e000000000000000000000000000000000000000000000000000000000000602082015250565b7f696c6c6567616c206f6666736574000000000000000000000000000000000000600082015250565b7f696c6c6567616c206c656e677468206f66207564616720726573700000000000600082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f417574684d73673a2073656e646572206e6f742076616c6964207375622d707260008201527f6f746f636f6c0000000000000000000000000000000000000000000000000000602082015250565b7f656e636f6465417574684d6573736167653a20616d2076657273696f6e206e6f60008201527f7420737570706f72740000000000000000000000000000000000000000000000602082015250565b7f7661724279746573546f42797465733a206f6666736574206c6573732074686160008201527f6e2074686520696e707574206c656e6774680000000000000000000000000000602082015250565b7f6279746573546f56617242797465733a206f6666736574206c6573732074686160008201527f6e206c656e677468206f6620626f647900000000000000000000000000000000602082015250565b7f6465636f6465417574684d6573736167653a20616d2076657273696f6e206e6f60008201527f7420737570706f72740000000000000000000000000000000000000000000000602082015250565b7f6c656e677468206f6620756461672072657370206c657373207468616e20746860008201527f65206c656e677468206f66206d736720626f6479000000000000000000000000602082015250565b61386d816131e9565b811461387857600080fd5b50565b6138848161323b565b811461388f57600080fd5b5056fea264697066735822122066106196b52a267d3b7f2576b76ef9c8871f1cfab8896437a1b6a7c1ba27d9f764736f6c63430008010033";
      private static final String SDP_BYTES = "60806040523480156200001157600080fd5b50600062000024620000c860201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350620000d0565b600033905090565b614b0d80620000e06000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063838a102c11610097578063c09b261b11610066578063c09b261b14610262578063c1cecc5a1461027e578063f2fde38b1461029a578063f76f703b146102b6576100f5565b8063838a102c146101da5780638da5cb5b146101f657806391198fcd14610214578063a9d662af14610244576100f5565b806356cb2d02116100d357806356cb2d021461017857806360e8b9c914610196578063715018a6146101b25780638037d45e146101bc576100f5565b806305ca8da6146100fa5780630d9ae9e51461012a5780633ac6dfb11461015a575b600080fd5b610114600480360381019061010f9190613791565b6102d2565b6040516101219190613d99565b60405180910390f35b610144600480360381019061013f91906138b5565b610501565b6040516101519190614289565b60405180910390f35b6101626105f9565b60405161016f9190613d99565b60405180910390f35b610180610603565b60405161018d9190613d99565b60405180910390f35b6101b060048036038101906101ab9190613768565b610609565b005b6101ba610739565b005b6101c4610873565b6040516101d19190613d4e565b60405180910390f35b6101f460048036038101906101ef9190613950565b61089d565b005b6101fe610949565b60405161020b9190613d4e565b60405180910390f35b61022e60048036038101906102299190613791565b610972565b60405161023b9190613d99565b60405180910390f35b61024c610b9b565b6040516102599190613d4e565b60405180910390f35b61027c6004803603810190610277919061382c565b610bc1565b005b6102986004803603810190610293919061382c565b610dc3565b005b6102b460048036038101906102af9190613768565b610f2a565b005b6102d060048036038101906102cb919061382c565b6110d3565b005b60006102e1878787868661128e565b6000604051806101200160405280600263ffffffff1681526020016000801b815260200189898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050815260200187815260200186610361576000610364565b60015b60ff1681526020016103bb8a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050338a611295565b63ffffffff1667ffffffffffffffff16815260200163ffffffff8016815260200185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505081526020016040518060200160405280600081525081525090506104506002548261132b90919063ffffffff16565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e41fb51733610498846115be565b6040518363ffffffff1660e01b81526004016104b5929190613d69565b600060405180830381600087803b1580156104cf57600080fd5b505af11580156104e3573d6000803e3d6000fd5b505050506104ef611917565b80602001519150509695505050505050565b60006002548484604051602001610519929190613d1e565b604051602081830303815290604052805190602001201461056f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161056690614149565b60405180910390fd5b60006105c088888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508785611919565b905060006004600083815260200190815260200160002060009054906101000a900463ffffffff16905080925050509695505050505050565b6000600254905090565b60025481565b610611611975565b73ffffffffffffffffffffffffffffffffffffffff1661062f610949565b73ffffffffffffffffffffffffffffffffffffffff1614610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067c90614189565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156106f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ec906141e9565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610741611975565b73ffffffffffffffffffffffffffffffffffffffff1661075f610949565b73ffffffffffffffffffffffffffffffffffffffff16146107b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ac90614189565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6108a5611975565b73ffffffffffffffffffffffffffffffffffffffff166108c3610949565b73ffffffffffffffffffffffffffffffffffffffff1614610919576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091090614189565b60405180910390fd5b8060405160200161092a9190613d37565b6040516020818303038152906040528051906020012060028190555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000610981878787868661197d565b6000604051806101200160405280600263ffffffff1681526020016000801b815260200189898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050815260200187815260200186610a01576000610a04565b60015b60ff16815260200167ffffffffffffffff80168152602001610a6b8a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050338a611984565b63ffffffff16815260200185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508152602001604051806020016040528060008152508152509050610aea6002548261132b90919063ffffffff16565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e41fb51733610b32846115be565b6040518363ffffffff1660e01b8152600401610b4f929190613d69565b600060405180830381600087803b158015610b6957600080fd5b505af1158015610b7d573d6000803e3d6000fd5b50505050610b89611a1a565b80602001519150509695505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610c51576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c4890614109565b60405180910390fd5b610c5e8585858585611a1c565b6000610cad83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611a23565b905060018163ffffffff161415610d1357610d0e86868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611b0d565b610db3565b60028163ffffffff161415610d7757610d7286868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050611bd3565b610db2565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da990614249565b60405180910390fd5b5b610dbb611d21565b505050505050565b610dd0858585858561128e565b6000604051806080016040528087878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050815260200185815260200184848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050815260200163ffffffff80168152509050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e41fb51733610ecb84611d23565b6040518363ffffffff1660e01b8152600401610ee8929190613d69565b600060405180830381600087803b158015610f0257600080fd5b505af1158015610f16573d6000803e3d6000fd5b50505050610f22611917565b505050505050565b610f32611975565b73ffffffffffffffffffffffffffffffffffffffff16610f50610949565b73ffffffffffffffffffffffffffffffffffffffff1614610fa6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f9d90614189565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611016576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161100d90614129565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6110e0858585858561197d565b6000604051806080016040528087878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050815260200185815260200184848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505081526020016111d688888080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503388611984565b63ffffffff16815250905060006111ec82611d23565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e41fb51733836040518363ffffffff1660e01b815260040161124b929190613d69565b600060405180830381600087803b15801561126557600080fd5b505af1158015611279573d6000803e3d6000fd5b50505050611285611a1a565b50505050505050565b5050505050565b6000806112a3858585611e8b565b905060006005600083815260200190815260200160002060009054906101000a900463ffffffff16905060056000838152602001908152602001600020600081819054906101000a900463ffffffff1680929190611300906145cb565b91906101000a81548163ffffffff021916908363ffffffff1602179055505080925050509392505050565b6002826000015163ffffffff1614611378576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161136f90614269565b60405180910390fd5b63ffffffff8260e001515111156113c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113bb90614209565b60405180910390fd5b60008260e001515183604001515160796113de919061433d565b6113e8919061433d565b905060008167ffffffffffffffff81111561142c577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f19166020018201604052801561145e5781602001600182028036833780820191505090505b509050600082905061148a8163ff000000876000015161147e9190614393565b63ffffffff1684611ef4565b6114946020611efe565b8161149f9190614458565b90506000856040015190506114b582828561212d565b805160046114c3919061433d565b826114ce9190614458565b91506114df82876060015185612206565b6114e7612210565b60ff16826114f59190614458565b915061150682876080015185612219565b6001826115139190614458565b9150611524828760a0015185612230565b61152e6040611efe565b826115399190614458565b915061154a828760c0015185612247565b6115546020611efe565b8261155f9190614458565b9150611570828760e001518561212d565b8560e00151516004611582919061433d565b8261158d9190614458565b915061159a82338561225e565b6115a5828685612206565b8280519060200120866020018181525050505050505050565b60606002826000015163ffffffff161461160d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161160490614269565b60405180910390fd5b63ffffffff8260e00151511115611659576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161165090614209565b60405180910390fd5b6000801b826020015114156116a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161169a90614229565b60405180910390fd5b600080600260ff16846080015160ff161190508015611702578361010001515160048560e001515186604001515160596116dd919061433d565b6116e7919061433d565b6116f1919061433d565b6116fb919061433d565b9150611727565b8360e0015151846040015151605961171a919061433d565b611724919061433d565b91505b60008267ffffffffffffffff811115611769577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f19166020018201604052801561179b5781602001600182028036833780820191505090505b50905060008390506117c78163ff00000088600001516117bb9190614393565b63ffffffff1684611ef4565b6117d16020611efe565b816117dc9190614458565b90506117ed81876020015184612206565b6117f5612210565b60ff16816118039190614458565b905060008660400151905061181982828561212d565b80516004611827919061433d565b826118329190614458565b915061184382886060015185612206565b61184b612210565b60ff16826118599190614458565b915061186a82886080015185612219565b6001826118779190614458565b9150611888828860a0015185612230565b6118926040611efe565b8261189d9190614458565b91506118ae828860c0015185612247565b6118b86020611efe565b826118c39190614458565b91506118d4828860e001518561212d565b8660e001515160046118e6919061433d565b826118f19190614458565b9150831561190a57611909828861010001518561212d565b5b8295505050505050919050565b565b60008360405160200161192c9190613d37565b60405160208183030381529060405280519060200120838360405160200161195693929190613ce1565b6040516020818303038152906040528051906020012090509392505050565b600033905090565b5050505050565b600080611992858585611e8b565b905060006003600083815260200190815260200160002060009054906101000a900463ffffffff16905060036000838152602001908152602001600020600081819054906101000a900463ffffffff16809291906119ef906145cb565b91906101000a81548163ffffffff021916908363ffffffff1602179055505080925050509392505050565b565b5050505050565b60008060008351905060048103602085010151915060ff60f81b827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415611b0157600080600467ffffffffffffffff811115611aaa577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015611adc5781602001600182028036833780820191505090505b5090506003830360200186015160218201526004810151915081945050505050611b08565b6001925050505b919050565b611b15613591565b611b28828261226890919063ffffffff16565b6002548160000151604051602001611b409190613d37565b6040516020818303038152906040528051906020012014611b96576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b8d90614149565b60405180910390fd5b63ffffffff8016816060015163ffffffff161415611bbf57611bba85858584612468565b611bcc565b611bcb858585846124eb565b5b5050505050565b611bdb6135c2565b611bee828261273790919063ffffffff16565b6002548160400151604051602001611c069190613d37565b6040516020818303038152906040528051906020012014611c5c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c5390614149565b60405180910390fd5b600060ff16816080015160ff161480611c7f5750600160ff16816080015160ff16145b15611c9557611c90858585846128ec565b611d1a565b600260ff16816080015160ff161415611cb957611cb485858584612a1f565b611d19565b600360ff16816080015160ff161415611cdd57611cd885858584612b1a565b611d18565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d0f90614169565b60405180910390fd5b5b5b5050505050565b565b60606000611d348360000151612c23565b60206004611d458660400151612c7b565b611d4f919061433d565b611d59919061433d565b611d63919061433d565b905060008167ffffffffffffffff811115611da7577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015611dd95781602001600182028036833780820191505090505b5090506000829050611df081866000015184612cd3565b611dfd8560000151612c23565b81611e089190614458565b9050611e1981866020015184612206565b611e21612210565b60ff1681611e2f9190614458565b9050611e4081866060015184612247565b611e4a6020612d3e565b81611e559190614458565b9050611e6681866040015184612cd3565b611e738560400151612c7b565b81611e7e9190614458565b9050819350505050919050565b600080611e9784612d50565b90508085604051602001611eab9190613d37565b6040516020818303038152906040528051906020012084604051602001611ed493929190613ce1565b604051602081830303815290604052805190602001209150509392505050565b8183820152505050565b6000816008811461200b5760108114612014576018811461201d5760208114612026576028811461202f57603081146120385760388114612041576040811461204a5760488114612053576050811461205c5760588114612065576060811461206e5760688114612077576070811461208057607881146120895760808114612092576088811461209b57609081146120a457609881146120ad5760a081146120b65760a881146120bf5760b081146120c85760b881146120d15760c081146120da5760c881146120e35760d081146120ec5760d881146120f55760e081146120fe5760e881146121075760f081146121105760f881146121195761010081146121225760209150612127565b60019150612127565b60029150612127565b60039150612127565b60049150612127565b60059150612127565b60069150612127565b60079150612127565b60089150612127565b60099150612127565b600a9150612127565b600b9150612127565b600c9150612127565b600d9150612127565b600e9150612127565b600f9150612127565b60109150612127565b60119150612127565b60129150612127565b60139150612127565b60149150612127565b60159150612127565b60169150612127565b60179150612127565b60189150612127565b60199150612127565b601a9150612127565b601b9150612127565b601c9150612127565b601d9150612127565b601e9150612127565b601f9150612127565b602091505b50919050565b60008251905061213e848284612247565b60048461214b9190614458565b93508063ffffffff16841015612196576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161218d906141a9565b60405180910390fd5b8063ffffffff16846121a89190614458565b93508015600081146121b9576121ff565b8483018051601f84168015602002818801018581018215602002838601015b818310156121f557825181526020830192506020810190506121d8565b5083855250505050505b5050505050565b8183820152505050565b60006020905090565b828101600181038051848352808252505050505050565b828101600881038051848352808252505050505050565b828101600481038051848352808252505050505050565b8183820152505050565b6000815190506000602061227c8385612ddc565b6122869190614393565b905060008163ffffffff1667ffffffffffffffff8111156122d0577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156123025781602001600182028036833780820191505090505b509050612310838583612dea565b61231981612c7b565b836123249190614458565b925060006123328486612e42565b905061233c612210565b60ff168461234a9190614458565b935060006123588587612ddc565b90506123646020611efe565b8561236f9190614458565b94506000602061237f8789612ddc565b6123899190614393565b905060008163ffffffff1667ffffffffffffffff8111156123d3577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156124055781602001600182028036833780820191505090505b509050612413878983612dea565b61241c81612c7b565b876124279190614458565b96508489600001819052508389602001818152505082896060019063ffffffff16908163ffffffff1681525050808960400181905250505050505050505050565b61247181612e50565b73ffffffffffffffffffffffffffffffffffffffff1663ff098be785858585604001516040518563ffffffff1660e01b81526004016124b39493929190614080565b600060405180830381600087803b1580156124cd57600080fd5b505af11580156124e1573d6000803e3d6000fd5b5050505050505050565b600061254085858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050848460200151612e66565b90508063ffffffff16826060015163ffffffff1614612594576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161258b906140e9565b60405180910390fd5b6000606060006125a385612e50565b905060008173ffffffffffffffffffffffffffffffffffffffff163b141561260657600092506040518060400160405280601481526020017f726563656976657220686173206e6f20636f646500000000000000000000000081525091506126ea565b8073ffffffffffffffffffffffffffffffffffffffff1663c09b261b89898989604001516040518563ffffffff1660e01b81526004016126499493929190614080565b600060405180830381600087803b15801561266357600080fd5b505af1925050508015612674575060015b6126e4576126806146c0565b806308c379a014156126a957506126956149fc565b806126a057506126ab565b809250506126df565b505b3d80600081146126d7576040519150601f19603f3d011682016040523d82523d6000602084013e6126dc565b606091505b50505b6126e9565b600192505b5b7f5c739d2e6f1ca8bbfc6b71f12b702b9c87e02646edd83b3600d624abb67fab0488888884888888604051612725979695949392919061400f565b60405180910390a15050505050505050565b60008151905061274682611a23565b836000019063ffffffff16908163ffffffff16815250506127676020612d3e565b816127729190614458565b905061277e8183612e42565b83602001818152505061278f612210565b60ff168161279d9190614458565b905060006127ab8284612efc565b9050808460400181905250805160046127c4919061433d565b826127cf9190614458565b91506127db8284612e42565b8460600181815250506127ec612210565b60ff16826127fa9190614458565b91506128068284612fec565b846080019060ff16908160ff16815250506001826128249190614458565b91506128308284612ffa565b8460a0019067ffffffffffffffff16908167ffffffffffffffff168152505060088261285c9190614458565b91506128688284612ddc565b8460c0019063ffffffff16908163ffffffff168152505060048261288c9190614458565b91506128988284612efc565b8460e001819052508360e001515160046128b2919061433d565b826128bd9190614458565b9150600260ff16846080015160ff1611156128e6576128dc8284612efc565b8461010001819052505b50505050565b6000606063ffffffff80168360c0015163ffffffff1614156129795761291486868686613008565b8092508193505050600060ff16836080015160ff16141561297457818190612972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161296991906140c7565b60405180910390fd5b505b61298e565b61298586868686613176565b80925081935050505b7fd0831f2b07fd854a865c682cb68bd34a56d746ae81a34d87c820e51ab979c3d3836020015187878787604001516129c58961338c565b8960c001518a60a001518b608001518b8b6040516129ed9b9a99989796959493929190613db4565b60405180910390a1600160ff16836080015160ff161415612a1757612a168387878786866133a2565b5b505050505050565b612a288161338c565b73ffffffffffffffffffffffffffffffffffffffff1663e5512e9782602001518686868660c001518760a001518860e001516040518863ffffffff1660e01b8152600401612a7c9796959493929190613f18565b600060405180830381600087803b158015612a9657600080fd5b505af1158015612aaa573d6000803e3d6000fd5b505050507fd0831f2b07fd854a865c682cb68bd34a56d746ae81a34d87c820e51ab979c3d381602001518585858560400151612ae58761338c565b8760c001518860a0015189608001516001604051612b0c9a99989796959493929190613e67565b60405180910390a150505050565b612b238161338c565b73ffffffffffffffffffffffffffffffffffffffff1663f6d750e482602001518686868660c001518760a001518860e001518961010001516040518963ffffffff1660e01b8152600401612b7e989796959493929190613f89565b600060405180830381600087803b158015612b9857600080fd5b505af1158015612bac573d6000803e3d6000fd5b505050507fd0831f2b07fd854a865c682cb68bd34a56d746ae81a34d87c820e51ab979c3d381602001518585858560400151612be78761338c565b8760c001518860a00151896080015160018b6101000151604051612c159b9a99989796959493929190613db4565b60405180910390a150505050565b600060208251612c3391906143cd565b9050600060208351612c459190614602565b14612c59578080612c5590614582565b9150505b8080612c6490614582565b915050602081612c7491906143fe565b9050919050565b600060208251612c8b91906143cd565b9050600060208351612c9d9190614602565b14612cb1578080612cad90614582565b9150505b8080612cbc90614582565b915050602081612ccc91906143fe565b9050919050565b600060208351612ce391906143cd565b9050600060208451612cf59190614602565b1115612d0a578080612d0690614582565b9150505b60006001820191505b81811015612d37576020810284015185840152602085039450600181019050612d13565b5050505050565b6000612d4982611efe565b9050919050565b600080602067ffffffffffffffff811115612d94577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015612dc65781602001600182028036833780820191505090505b5090508260208201526020810151915050919050565b600082820151905092915050565b6000602090506000808585015192506001602084040190506000602084061115612e15576001810190505b5b80821015612e3a578585015160208302850152602086039550600182019150612e16565b505050505050565b600082820151905092915050565b6000612e5f82602001516134eb565b9050919050565b600080612e74858585611919565b905060006004600083815260200190815260200160002060009054906101000a900463ffffffff16905060046000838152602001908152602001600020600081819054906101000a900463ffffffff1680929190612ed1906145cb565b91906101000a81548163ffffffff021916908363ffffffff1602179055505080925050509392505050565b60606000612f0a8484612ddc565b63ffffffff169050600484612f1f9190614458565b935083811115612f64576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612f5b906141c9565b60405180910390fd5b8084612f709190614458565b93506060811560008114612f8f57604051915060208201604052612fe0565b6040519150601f8316801560200281840101848101888315602002848a0101015b81831015612fcd5780518352602083019250602081019050612fb0565b50858552601f19601f8301166040525050505b50809250505092915050565b600082820151905092915050565b600082820151905092915050565b6000606060006060600061301b8661338c565b73ffffffffffffffffffffffffffffffffffffffff163b141561307957600091506040518060400160405280601481526020017f726563656976657220686173206e6f20636f64650000000000000000000000008152509050613165565b6130828561338c565b73ffffffffffffffffffffffffffffffffffffffff1663ff098be78989898960e001516040518563ffffffff1660e01b81526004016130c49493929190614080565b600060405180830381600087803b1580156130de57600080fd5b505af19250505080156130ef575060015b61315f576130fb6146c0565b806308c379a0141561312457506131106149fc565b8061311b5750613126565b8091505061315a565b505b3d8060008114613152576040519150601f19603f3d011682016040523d82523d6000602084013e613157565b606091505b50505b613164565b600191505b5b818193509350505094509492505050565b6000606060006131cf87878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050868660600151612e66565b90508063ffffffff168460c0015163ffffffff1614613223576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161321a906140e9565b60405180910390fd5b6000606060006132328761338c565b905060008173ffffffffffffffffffffffffffffffffffffffff163b141561329557600092506040518060400160405280601481526020017f726563656976657220686173206e6f20636f64650000000000000000000000008152509150613379565b8073ffffffffffffffffffffffffffffffffffffffff1663c09b261b8b8b8b8b60e001516040518563ffffffff1660e01b81526004016132d89493929190614080565b600060405180830381600087803b1580156132f257600080fd5b505af1925050508015613303575060015b6133735761330f6146c0565b806308c379a0141561333857506133246149fc565b8061332f575061333a565b8092505061336e565b505b3d8060008114613366576040519150601f19603f3d011682016040523d82523d6000602084013e61336b565b606091505b50505b613378565b600192505b5b8282955095505050505094509492505050565b600061339b82606001516134eb565b9050919050565b60006133ad8761338c565b905085858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505087604001819052508387606001818152505082613412576003613415565b60025b876080019060ff16908160ff1681525050826134315781613442565b604051806020016040528060008152505b876101000181905250600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e41fb517826134938a6115be565b6040518363ffffffff1660e01b81526004016134b0929190613d69565b600060405180830381600087803b1580156134ca57600080fd5b505af11580156134de573d6000803e3d6000fd5b5050505050505050505050565b600080602067ffffffffffffffff81111561352f577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156135615781602001600182028036833780820191505090505b50905061357060208483612206565b61357b602082613583565b915050919050565b600082820151905092915050565b6040518060800160405280606081526020016000801916815260200160608152602001600063ffffffff1681525090565b604051806101200160405280600063ffffffff168152602001600080191681526020016060815260200160008019168152602001600060ff168152602001600067ffffffffffffffff168152602001600063ffffffff16815260200160608152602001606081525090565b600061364061363b846142c9565b6142a4565b90508281526020810184848401111561365857600080fd5b61366384828561450f565b509392505050565b60008135905061367a81614a92565b92915050565b60008135905061368f81614aa9565b92915050565b6000813590506136a481614ac0565b92915050565b60008083601f8401126136bc57600080fd5b8235905067ffffffffffffffff8111156136d557600080fd5b6020830191508360018202830111156136ed57600080fd5b9250929050565b60008083601f84011261370657600080fd5b8235905067ffffffffffffffff81111561371f57600080fd5b60208301915083600182028301111561373757600080fd5b9250929050565b600082601f83011261374f57600080fd5b813561375f84826020860161362d565b91505092915050565b60006020828403121561377a57600080fd5b60006137888482850161366b565b91505092915050565b600080600080600080608087890312156137aa57600080fd5b600087013567ffffffffffffffff8111156137c457600080fd5b6137d089828a016136f4565b965096505060206137e389828a01613695565b94505060406137f489828a01613680565b935050606087013567ffffffffffffffff81111561381157600080fd5b61381d89828a016136aa565b92509250509295509295509295565b60008060008060006060868803121561384457600080fd5b600086013567ffffffffffffffff81111561385e57600080fd5b61386a888289016136f4565b9550955050602061387d88828901613695565b935050604086013567ffffffffffffffff81111561389a57600080fd5b6138a6888289016136aa565b92509250509295509295909350565b600080600080600080608087890312156138ce57600080fd5b600087013567ffffffffffffffff8111156138e857600080fd5b6138f489828a016136f4565b9650965050602061390789828a01613695565b945050604087013567ffffffffffffffff81111561392457600080fd5b61393089828a016136f4565b9350935050606061394389828a01613695565b9150509295509295509295565b60006020828403121561396257600080fd5b600082013567ffffffffffffffff81111561397c57600080fd5b6139888482850161373e565b91505092915050565b61399a8161448c565b82525050565b6139a98161449e565b82525050565b6139b8816144aa565b82525050565b6139cf6139ca826144aa565b6145f8565b82525050565b60006139e0826142fa565b6139ea8185614310565b93506139fa81856020860161451e565b613a03816146e2565b840191505092915050565b6000613a1a8385614321565b9350613a2783858461450f565b613a30836146e2565b840190509392505050565b6000613a478385614332565b9350613a5483858461450f565b82840190509392505050565b6000613a6b82614305565b613a758185614321565b9350613a8581856020860161451e565b613a8e816146e2565b840191505092915050565b6000613aa482614305565b613aae8185614332565b9350613abe81856020860161451e565b80840191505092915050565b6000613ad7601a83614321565b9150613ae282614700565b602082019050919050565b6000613afa601d83614321565b9150613b0582614729565b602082019050919050565b6000613b1d602683614321565b9150613b2882614752565b604082019050919050565b6000613b40600783614321565b9150613b4b826147a1565b602082019050919050565b6000613b63601e83614321565b9150613b6e826147ca565b602082019050919050565b6000613b86601683614321565b9150613b91826147f3565b602082019050919050565b6000613ba9602083614321565b9150613bb48261481c565b602082019050919050565b6000613bcc603283614321565b9150613bd782614845565b604082019050919050565b6000613bef603083614321565b9150613bfa82614894565b604082019050919050565b6000613c12601b83614321565b9150613c1d826148e3565b602082019050919050565b6000613c35602783614321565b9150613c408261490c565b604082019050919050565b6000613c58602183614321565b9150613c638261495b565b604082019050919050565b6000613c7b601583614321565b9150613c86826149aa565b602082019050919050565b6000613c9e601f83614321565b9150613ca9826149d3565b602082019050919050565b613cbd816144de565b82525050565b613ccc816144ee565b82525050565b613cdb81614502565b82525050565b6000613ced82866139be565b602082019150613cfd82856139be565b602082019150613d0d82846139be565b602082019150819050949350505050565b6000613d2b828486613a3b565b91508190509392505050565b6000613d438284613a99565b915081905092915050565b6000602082019050613d636000830184613991565b92915050565b6000604082019050613d7e6000830185613991565b8181036020830152613d9081846139d5565b90509392505050565b6000602082019050613dae60008301846139af565b92915050565b600061014082019050613dca600083018e6139af565b8181036020830152613ddd818c8e613a0e565b9050613dec604083018b6139af565b8181036060830152613dfe818a613a60565b9050613e0d6080830189613991565b613e1a60a0830188613cb4565b613e2760c0830187613cc3565b613e3460e0830186613cd2565b613e426101008301856139a0565b818103610120830152613e558184613a60565b90509c9b505050505050505050505050565b600061014082019050613e7d600083018d6139af565b8181036020830152613e90818b8d613a0e565b9050613e9f604083018a6139af565b8181036060830152613eb18189613a60565b9050613ec06080830188613991565b613ecd60a0830187613cb4565b613eda60c0830186613cc3565b613ee760e0830185613cd2565b613ef56101008301846139a0565b818103610120830152613f0781613b33565b90509b9a5050505050505050505050565b600060c082019050613f2d600083018a6139af565b8181036020830152613f4081888a613a0e565b9050613f4f60408301876139af565b613f5c6060830186613cb4565b613f696080830185613cc3565b81810360a0830152613f7b81846139d5565b905098975050505050505050565b600060e082019050613f9e600083018b6139af565b8181036020830152613fb181898b613a0e565b9050613fc060408301886139af565b613fcd6060830187613cb4565b613fda6080830186613cc3565b81810360a0830152613fec81856139d5565b905081810360c08301526140008184613a60565b90509998505050505050505050565b600060c082019050818103600083015261402a81898b613a0e565b905061403960208301886139af565b6140466040830187613991565b6140536060830186613cb4565b61406060808301856139a0565b81810360a08301526140728184613a60565b905098975050505050505050565b6000606082019050818103600083015261409b818688613a0e565b90506140aa60208301856139af565b81810360408301526140bc81846139d5565b905095945050505050565b600060208201905081810360008301526140e18184613a60565b905092915050565b6000602082019050818103600083015261410281613aca565b9050919050565b6000602082019050818103600083015261412281613aed565b9050919050565b6000602082019050818103600083015261414281613b10565b9050919050565b6000602082019050818103600083015261416281613b56565b9050919050565b6000602082019050818103600083015261418281613b79565b9050919050565b600060208201905081810360008301526141a281613b9c565b9050919050565b600060208201905081810360008301526141c281613bbf565b9050919050565b600060208201905081810360008301526141e281613be2565b9050919050565b6000602082019050818103600083015261420281613c05565b9050919050565b6000602082019050818103600083015261422281613c28565b9050919050565b6000602082019050818103600083015261424281613c4b565b9050919050565b6000602082019050818103600083015261426281613c6e565b9050919050565b6000602082019050818103600083015261428281613c91565b9050919050565b600060208201905061429e6000830184613cb4565b92915050565b60006142ae6142bf565b90506142ba8282614551565b919050565b6000604051905090565b600067ffffffffffffffff8211156142e4576142e3614691565b5b6142ed826146e2565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b6000614348826144d4565b9150614353836144d4565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561438857614387614633565b5b828201905092915050565b600061439e826144de565b91506143a9836144de565b92508263ffffffff038211156143c2576143c1614633565b5b828201905092915050565b60006143d8826144d4565b91506143e3836144d4565b9250826143f3576143f2614662565b5b828204905092915050565b6000614409826144d4565b9150614414836144d4565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561444d5761444c614633565b5b828202905092915050565b6000614463826144d4565b915061446e836144d4565b92508282101561448157614480614633565b5b828203905092915050565b6000614497826144b4565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600063ffffffff82169050919050565b600067ffffffffffffffff82169050919050565b600060ff82169050919050565b82818337600083830152505050565b60005b8381101561453c578082015181840152602081019050614521565b8381111561454b576000848401525b50505050565b61455a826146e2565b810181811067ffffffffffffffff8211171561457957614578614691565b5b80604052505050565b600061458d826144d4565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156145c0576145bf614633565b5b600182019050919050565b60006145d6826144de565b915063ffffffff8214156145ed576145ec614633565b5b600182019050919050565b6000819050919050565b600061460d826144d4565b9150614618836144d4565b92508261462857614627614662565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060033d11156146df5760046000803e6146dc6000516146f3565b90505b90565b6000601f19601f8301169050919050565b60008160e01c9050919050565b7f5344504d73673a2073657175656e6365206e6f7420657175616c000000000000600082015250565b7f5344504d73673a206e6f742076616c696420616d20636f6e7472616374000000600082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f7375636365737300000000000000000000000000000000000000000000000000600082015250565b7f5344504d73673a2077726f6e6720726563656976696e6720646f6d61696e0000600082015250565b7f756e65787065637465642061746f6d696320666c616700000000000000000000600082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f7661724279746573546f42797465733a206f6666736574206c6573732074686160008201527f6e2074686520696e707574206c656e6774680000000000000000000000000000602082015250565b7f6279746573546f56617242797465733a206f6666736574206c6573732074686160008201527f6e206c656e677468206f6620626f647900000000000000000000000000000000602082015250565b7f5344504d73673a20696e76616c696420616d20636f6e74726163740000000000600082015250565b7f656e636f64655344504d6573736167653a20626f6479206c656e677468206f7660008201527f65726c696d697400000000000000000000000000000000000000000000000000602082015250565b7f656e636f64655344504d6573736167653a207a65726f206d657373616765206960008201527f6400000000000000000000000000000000000000000000000000000000000000602082015250565b7f756e737570706f7274207364702076657273696f6e0000000000000000000000600082015250565b7f656e636f64655344504d6573736167653a2077726f6e672076657273696f6e00600082015250565b600060443d1015614a0c57614a8f565b614a146142bf565b60043d036004823e80513d602482011167ffffffffffffffff82111715614a3c575050614a8f565b808201805167ffffffffffffffff811115614a5a5750505050614a8f565b80602083010160043d038501811115614a77575050505050614a8f565b614a8682602001850186614551565b82955050505050505b90565b614a9b8161448c565b8114614aa657600080fd5b50565b614ab28161449e565b8114614abd57600080fd5b50565b614ac9816144aa565b8114614ad457600080fd5b5056fea26469706673582212202ebde11d611ce3d012b0d85a76fa00a62ccb42172af3f79b4727ff43ad4b79de64736f6c63430008010033";
  
      // contrast name format
      private static final String AM_NAME = "AuthMsg_{}";
      private static final String SDP_NAME = "SDPMsg_{}";
  
      // AM contract methods
      private static final String AM_METHOD_SET_PROTOCOL = "setProtocol";
      private static final String AM_METHOD_GET_PROTOCOL = "getProtocol";
      private static final String AM_METHOD_RECV_PKG_FROM_RELAYER = "recvPkgFromRelayer";
  
      // SDP contract methods
      private static final String SDP_METHOD_SET_AM_CONTRACT = "setAmContract";
      private static final String SDP_METHOD_GET_AM_CONTRACT = "getAmAddress";
      private static final String SDP_METHOD_SET_LOCAL_DOMAIN = "setLocalDomain";
      private static final String SDP_METHOD_GET_LOCAL_DOMAIN = "getLocalDomain";
      private static final String SDP_METHOD_QUERY_SDP_MSG_SEQ = "querySDPMessageSeq";
  
      // cross chain event for sender
      private static final String SEND_AUTH_MESSAGE_NAME = "SendAuthMessage";
      public static final Event SEND_AUTH_MESSAGE_EVENT = new Event(SEND_AUTH_MESSAGE_NAME,
              Arrays.<TypeReference<?>>asList(new TypeReference<DynamicBytes>() {
              }));
      // "0x79b7516b1b7a6a39fb4b7b22e8667cd3744e5c27425292f8a9f49d1042c0c651"
      private static final String SEND_AUTH_MESSAGE_SIGNATURE = EventEncoder.encode(SEND_AUTH_MESSAGE_EVENT);
  
      // cross chain event for receiver
      private static final String RECEIVE_MESSAGE_NAME = "receiveMessage";
      // "0x5c739d2e6f1ca8bbfc6b71f12b702b9c87e02646edd83b3600d624abb67fab04"
      public static final Event RECEIVE_MESSAGE_EVENT = new Event(RECEIVE_MESSAGE_NAME,
              Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {
                                              }, new TypeReference<Bytes32>() {
                                              },
                      new TypeReference<Address>() {
                      }, new TypeReference<Uint32>() {
                      },
                      new TypeReference<Bool>() {
                      }, new TypeReference<Utf8String>() {
                      }));
      private static final String RECEIVE_MESSAGE_SIGNATURE = EventEncoder.encode(SEND_AUTH_MESSAGE_EVENT);
  
      private String clientAddress;
  
      private ChainMakerConfig config;
  
      private ChainClient chainClient;
  
      private ChainManager chainManager;
  
      private List<User> adminUserList = new ArrayList<User>();
  
      private ChainMakerContext bbcContext;
  
      @Override
      public void startup(AbstractBBCContext abstractBBCContext) {
          getBBCLogger().info("[startup] ChainMaker startup with context: {}",
                  new String(abstractBBCContext.getConfForBlockchainClient()));
  
          if (ObjectUtil.isNull(abstractBBCContext)) {
              throw new RuntimeException("null bbc context");
          }
          if (ObjectUtil.isEmpty(abstractBBCContext.getConfForBlockchainClient())) {
              throw new RuntimeException("empty blockchain client conf");
          }
  
          // 1. Obtain the configuration information
          try {
              config = ChainMakerConfig.fromJsonString(new String(abstractBBCContext.getConfForBlockchainClient()));
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
          if (StrUtil.isEmpty(config.getSdkConfig())) {
              throw new RuntimeException("SdkConfig is empty");
          }
          if (config.getAdminTlsKeyPaths().isEmpty()) {
              throw new RuntimeException("AdminTlsKeyPath is empty");
          }
          if (config.getAdminTlsKeyPaths().size() != config.getAdminTlsCertPaths().size()) {
              throw new RuntimeException("AdminTlsCertPaths size not match");
          }
          if (config.getAdminKeyPaths().isEmpty()) {
              throw new RuntimeException("AdminKeyPath is empty");
          }
          if (config.getAdminKeyPaths().size() != config.getAdminKeyPaths().size()) {
              throw new RuntimeException("AdminKeyPaths size not match");
          }
          if (config.getOrgIds().isEmpty()) {
              throw new RuntimeException("OrgId is empty");
          }
  
          // 2. Connect to the chainmaker network
          Gson gson = new Gson();
          SdkConfig sdkConfig = gson.fromJson(config.getSdkConfig(), SdkConfig.class);
          try {
              chainManager = ChainManager.getInstance();
  
              // 移除chainManager中缓存的链sdk
              for (Field field : ChainManager.class.getDeclaredFields()) {
                  if (StrUtil.equals(field.getName(), "chains")) {
                      field.setAccessible(true);
                      Map map = (Map) field.get(chainManager);
                      map.remove(sdkConfig.getChainClient().getChainId());
                      getBBCLogger().info("[startup] chainManager remove chain, id: {}",
                              sdkConfig.getChainClient().getChainId());
                      break;
                  }
              }
  
              chainClient = chainManager.createChainClient(sdkConfig);
          } catch (ChainClientException | RpcServiceClientException | UtilsException | ChainMakerCryptoSuiteException |
                   IllegalAccessException ex) {
              throw new RuntimeException(ex);
          }
  
          // 3. get client address of chainClient
          try {
              clientAddress = CryptoUtils.certToAddrStr(
                      chainClient.getClientUser().getCertificate(),
                      ChainConfigOuterClass.AddrType.ETHEREUM);
          } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
              throw new RuntimeException("fail to get client address", e);
          }
  
          // 4. Create admin user for endorsement
          int amdinUserNum = config.getAdminTlsKeyPaths().size();
          for (int i = 0; i < amdinUserNum; i++) {
              try {
                  adminUserList.add(
                          new User(
                                  config.getOrgIds().get(i),
                                  config.getAdminKeyPaths().get(i),
                                  config.getAdminCertPaths().get(i),
                                  config.getAdminTlsKeyPaths().get(i),
                                  config.getAdminTlsCertPaths().get(i)));
              } catch (ChainMakerCryptoSuiteException e) {
                  throw new RuntimeException("fail to create admin user for endorsement", e);
              }
          }
  
          // 3. set context
          this.bbcContext = new ChainMakerContext(abstractBBCContext);
  
          this.bbcContext.setAmContractName(this.config.getAmContractName());
          this.bbcContext.setSdpContractName(this.config.getSdpContractName());
  
          // 4. set the pre-deployed contracts into context
          if (ObjectUtil.isNull(abstractBBCContext.getAuthMessageContract())
                  && StrUtil.isNotEmpty(this.config.getAmContractAddressDeployed())) {
              AuthMessageContract authMessageContract = new AuthMessageContract();
              authMessageContract.setContractAddress(this.config.getAmContractAddressDeployed());
              authMessageContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
              this.bbcContext.setAuthMessageContract(authMessageContract);
          }
  
          if (ObjectUtil.isNull(abstractBBCContext.getSdpContract())
                  && StrUtil.isNotEmpty(this.config.getSdpContractAddressDeployed())) {
              SDPContract sdpContract = new SDPContract();
              sdpContract.setContractAddress(this.config.getSdpContractAddressDeployed());
              sdpContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
              this.bbcContext.setSdpContract(sdpContract);
          }
  
          getBBCLogger().info("ChainMaker startup success, (" +
                          "amAddr: {}, amStatus: {}, " +
                          "sdpAddr: {}, sdpStatus: {}," +
                          "amName: {}, sdpName: {})",
                  this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getContractAddress() : "",
                  this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getStatus() : "",
                  this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getContractAddress() : "",
                  this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getStatus() : "",
                  this.bbcContext.getAmContractName() != null ? this.bbcContext.getAmContractName() : "",
                  this.bbcContext.getSdpContractName() != null ? this.bbcContext.getSdpContractName() : ""
          );
      }
  
      @Override
      public void shutdown() {
          getBBCLogger().info("shut down ChainMaker BBCService!");
          chainClient.stop();
      }
  
      @Override
      public AbstractBBCContext getContext() {
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
  
          getBBCLogger().info("ChainMaker BBCService context (" +
                          "amAddr: {}, amStatus: {}, " +
                          "sdpAddr: {}, sdpStatus: {}," +
                          "amName: {}, sdpName: {})",
                  this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getContractAddress() : "",
                  this.bbcContext.getAuthMessageContract() != null ? this.bbcContext.getAuthMessageContract().getStatus() : "",
                  this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getContractAddress() : "",
                  this.bbcContext.getSdpContract() != null ? this.bbcContext.getSdpContract().getStatus() : "",
                  this.bbcContext.getAmContractName() != null ? this.bbcContext.getAmContractName() : "",
                  this.bbcContext.getSdpContractName() != null ? this.bbcContext.getSdpContractName() : ""
          );
  
          return this.bbcContext;
      }
  
      @Override
      public Long queryLatestHeight() {
          Long l;
          try {
              l = chainClient.getCurrentBlockHeight(rpcCallTimeout);
          } catch (ChainMakerCryptoSuiteException | ChainClientException e) {
              throw new RuntimeException("failed to query latest height", e);
          }
          getBBCLogger().info("latest height: {}", l);
          return l;
      }
  
      @Override
      public void setupAuthMessageContract() {
          getBBCLogger().info("[setupAuthMessageContract] start to set up auth message contract");
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNotNull(this.bbcContext.getAuthMessageContract())
                  && StrUtil.isNotEmpty(this.bbcContext.getAuthMessageContract().getContractAddress())) {
              // If the contract has been pre-deployed and the contract address is configured in the configuration file,
              // there is no need to redeploy.
              return;
          }
  
          // 2. call setupContract to deploy and get TxResponse
          String amContractName = Utils.calcContractName(StrUtil.format(AM_NAME, UUID.randomUUID().toString()));
          ContractOuterClass.Contract contract = setupContract(AM_BYTES, amContractName);
  
          // 3. update bbcContext
          AuthMessageContract authMessageContract = new AuthMessageContract();
          authMessageContract.setContractAddress(contract.getAddress());
          authMessageContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
          bbcContext.setAuthMessageContract(authMessageContract);
          bbcContext.setAmContractName(contract.getName());
          config.setAmContractName(contract.getName());
          bbcContext.setConfForBlockchainClient(config.toJsonString().getBytes());
          getBBCLogger().info("setup am contract successful: {}-{}",
                  contract.getName(),
                  contract.getAddress());
      }
  
      @Override
      public void setupSDPMessageContract() {
          getBBCLogger().info("[setupSDPMessageContract] start to set up auth message contract");
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNotNull(this.bbcContext.getSdpContract())
                  && StrUtil.isNotEmpty(this.bbcContext.getSdpContract().getContractAddress())) {
              // If the contract has been pre-deployed and the contract address is configured in the configuration file,
              // there is no need to redeploy.
              return;
          }
  
          // 2. call setupContract to deploy and get TxResponse
          String sdpContractName = Utils.calcContractName(StrUtil.format(SDP_NAME, UUID.randomUUID().toString()));
          ContractOuterClass.Contract contract = setupContract(SDP_BYTES, sdpContractName);
  
          // 3. check whether the deployment is successful
          SDPContract sdpContract = new SDPContract();
          sdpContract.setContractAddress(contract.getAddress());
          sdpContract.setStatus(ContractStatusEnum.CONTRACT_DEPLOYED);
          bbcContext.setSdpContract(sdpContract);
          bbcContext.setSdpContractName(contract.getName());
          config.setSdpContractName(contract.getName());
          bbcContext.setConfForBlockchainClient(config.toJsonString().getBytes());
          getBBCLogger().info("setup sdp contract successful: {}-{}",
                  contract.getName(),
                  contract.getAddress());
      }
  
      @Override
      public long querySDPMessageSeq(String senderDomain, String senderID, String receiverDomain, String receiverID) {
          getBBCLogger().info("[querySDPMessageSeq] start to query sdp msg seq, {}-{}-{}-{}",
                  senderDomain,
                  senderID,
                  receiverDomain,
                  receiverID);
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNull(this.bbcContext.getSdpContract())) {
              throw new RuntimeException("empty sdp contract in bbc context");
          }
  
          // 2. query sequence
          try {
              ResultOuterClass.TxResponse responseInfo = queryContract(
                      this.bbcContext.getSdpContract().getContractAddress(),
                      SDP_METHOD_QUERY_SDP_MSG_SEQ,
                      Arrays.<Type>asList(
                              new Utf8String(senderDomain),
                              new Bytes32(HexUtil.decodeHex(senderID)),
                              new Utf8String(receiverDomain),
                              new Bytes32(HexUtil.decodeHex(receiverID))),
                      Collections.<TypeReference<?>>emptyList());
  
              if (responseInfo.getCode() != ResultOuterClass.TxStatusCode.SUCCESS) {
                  getBBCLogger().error("[querySDPMessageSeq] query sdp seq ({}-{}-{}-{}) tx failed, error msg: {}",
                          senderDomain,
                          senderID,
                          receiverDomain,
                          receiverID,
                          responseInfo.getMessage());
                  return -1;
              }
  
              long seq = new BigInteger(responseInfo.getContractResult().getResult().toByteArray()).longValue();
              getBBCLogger().info("[querySDPMessageSeq] success to query sdp seq ({}-{}-{}-{}) , result: {}",
                      senderDomain,
                      senderID,
                      receiverDomain,
                      receiverID,
                      seq);
              return seq;
  
          } catch (Exception e) {
              getBBCLogger().error("[querySDPMessageSeq] failed to query sdpMsg seq (senderDomain: {}, senderID: {}, receiverDomain: {}, receiverID: {})",
                      senderDomain,
                      senderID,
                      receiverDomain,
                      receiverID);
              throw new RuntimeException(StrUtil.format(
                      "failed to query sdpMsg seq (senderDomain: {}, senderID: {}, receiverDomain: {}, receiverID: {})",
                      senderDomain,
                      senderID,
                      receiverDomain,
                      receiverID
              ), e);
          }
      }
  
      @Override
      public void setProtocol(String protocolAddress, String protocolType) {
          getBBCLogger().info("[setProtocol] start to set protocol {}-{}", protocolAddress, protocolType);
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNull(this.bbcContext.getAuthMessageContract())) {
              throw new RuntimeException("empty am contract in bbc context");
          }
  
          // 2. set protocol to am
          try {
              ResultOuterClass.TxResponse responseInfo = invokeContract(
                      this.bbcContext.getAuthMessageContract().getContractAddress(),
                      AM_METHOD_SET_PROTOCOL,
                      Arrays.<Type>asList(
                              new Address(protocolAddress),
                              new Uint32(Integer.parseInt(protocolType))),
                      Collections.<TypeReference<?>>emptyList());
  
              if (responseInfo.getCode() == ResultOuterClass.TxStatusCode.SUCCESS) {
                  ResultOuterClass.TxResponse responseInfoCheck = queryContract(
                          this.bbcContext.getAuthMessageContract().getContractAddress(),
                          AM_METHOD_GET_PROTOCOL,
                          Arrays.<Type>asList(
                                  new Uint32(Integer.parseInt(protocolType))),
                          Collections.<TypeReference<?>>emptyList());
  
                  String protocolCheck = HexUtil.encodeHexStr(responseInfoCheck.getContractResult().getResult().toByteArray()).substring(24);
                  if (StrUtil.equals(
                          protocolCheck,
                          protocolAddress.replace("0x", ""))) {
  
                      // 3. update am contract status
                      this.bbcContext.getAuthMessageContract().setStatus(ContractStatusEnum.CONTRACT_READY);
  
                      getBBCLogger().info("[setProtocol] success to set protocol {}-{}",
                              protocolAddress, protocolType);
                      return;
                  } else {
                      getBBCLogger().error("[setProtocol] protocol info get({}) is not meet exception({})",
                              protocolCheck,
                              protocolAddress);
                      return;
                  }
              } else {
                  getBBCLogger().error("[setProtocol] set protocol({}-{}) tx failed, error msg: {}",
                          protocolAddress, protocolType,
                          responseInfo.getMessage());
                  return;
              }
          } catch (Exception e) {
              getBBCLogger().error("[setProtocol] failed to invoke am contract to set protocol {}-{}", protocolAddress, protocolType);
              throw new RuntimeException(
                      String.format(
                              "failed to set protocol (address: %s, type: %s) to AM %s",
                              protocolAddress,
                              protocolType,
                              this.bbcContext.getAuthMessageContract().getContractAddress()
                      ), e
              );
          }
      }
  
      @Override
      public void setAmContract(String contractAddress) {
          getBBCLogger().info("[setAmContract] start to set am contract {}", contractAddress);
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNull(this.bbcContext.getSdpContract())) {
              throw new RuntimeException("empty sdp contract in bbc context");
          }
  
          // 2. set am to sdp
          try {
              ResultOuterClass.TxResponse responseInfo = invokeContract(
                      this.bbcContext.getSdpContract().getContractAddress(),
                      SDP_METHOD_SET_AM_CONTRACT,
                      Arrays.asList(new Address(contractAddress)),
                      Collections.<TypeReference<?>>emptyList());
  
              if (responseInfo.getCode() == ResultOuterClass.TxStatusCode.SUCCESS) {
                  // 3. check am contract
                  ResultOuterClass.TxResponse checkResponse = queryContract(
                          this.bbcContext.getSdpContract().getContractAddress(),
                          SDP_METHOD_GET_AM_CONTRACT,
                          Arrays.<Type>asList(),
                          Collections.<TypeReference<?>>emptyList());
                  String amInfoCheck = HexUtil.encodeHexStr(checkResponse.getContractResult().getResult().toByteArray()).substring(24);
                  if (!StrUtil.equals(
                          amInfoCheck,
                          contractAddress.replace("0x", ""))) {
                      getBBCLogger().error("[setAmContract] amContract info get({}) is not meet exception({})",
                              amInfoCheck,
                              contractAddress);
                      return;
                  }
  
                  // 4. check domain
                  ResultOuterClass.TxResponse domainCheckResponse = queryContract(
                          this.bbcContext.getSdpContract().getContractAddress(),
                          SDP_METHOD_GET_LOCAL_DOMAIN,
                          Arrays.<Type>asList(),
                          Collections.<TypeReference<?>>emptyList());
                  if (!HexUtil.encodeHexStr(
                                  domainCheckResponse.getContractResult().getResult().toByteArray())
                          .matches(EMPTY_MATCHER)) {
  
                      // 5. update am contract status
                      this.bbcContext.getSdpContract().setStatus(ContractStatusEnum.CONTRACT_READY);
                  }
  
                  getBBCLogger().info("[setAmContract] success to set am Contract {}",
                          contractAddress);
                  return;
  
              } else {
                  getBBCLogger().error("[setAmContract] set amContract({}) tx failed, error msg: {}",
                          contractAddress,
                          responseInfo.getMessage());
                  return;
              }
          } catch (Exception e) {
              getBBCLogger().error("[setAmContract] failed to invoke sdp contract to set am contract {}",
                      contractAddress, e);
              throw new RuntimeException(
                      String.format(
                              "failed to set am contract (address: %s) to SDP %s",
                              contractAddress,
                              this.bbcContext.getSdpContract().getContractAddress()
                      ), e
              );
          }
      }
  
      @Override
      public void setLocalDomain(String domain) {
          getBBCLogger().info("[setLocalDomain] start to set local domain {}", domain);
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
          if (ObjectUtil.isNull(this.bbcContext.getSdpContract())) {
              throw new RuntimeException("empty sdp contract in bbc context");
          }
  
          // 2. set domain to sdp
          try {
              ResultOuterClass.TxResponse responseInfo = invokeContract(
                      this.bbcContext.getSdpContract().getContractAddress(),
                      SDP_METHOD_SET_LOCAL_DOMAIN,
                      Arrays.<Type>asList(new Utf8String(domain)),
                      Collections.<TypeReference<?>>emptyList());
  
              if (responseInfo.getCode() == ResultOuterClass.TxStatusCode.SUCCESS) {
                  // 3. check domain
                  ResultOuterClass.TxResponse checkResponse = queryContract(
                          this.bbcContext.getSdpContract().getContractAddress(),
                          SDP_METHOD_GET_LOCAL_DOMAIN,
                          Arrays.<Type>asList(),
                          Collections.<TypeReference<?>>emptyList());
                  String domainCheck = HexUtil.encodeHexStr(checkResponse.getContractResult().getResult().toByteArray());
                  if (domainCheck.matches(EMPTY_MATCHER)) {
                      getBBCLogger().error("[setLocalDomain] domain info is empty({}) after set",
                              domainCheck);
                      return;
                  }
  
                  // 4. check am contract
                  ResultOuterClass.TxResponse amCheckResponse = queryContract(
                          this.bbcContext.getSdpContract().getContractAddress(),
                          SDP_METHOD_GET_AM_CONTRACT,
                          Arrays.<Type>asList(),
                          Collections.<TypeReference<?>>emptyList());
                  String amInfoCheck = HexUtil.encodeHexStr(amCheckResponse.getContractResult().getResult().toByteArray()).substring(24);
                  if (!amInfoCheck.matches(EMPTY_MATCHER)) {
  
                      // 5. update am contract status
                      this.bbcContext.getSdpContract().setStatus(ContractStatusEnum.CONTRACT_READY);
                  }
  
                  getBBCLogger().info("[setLocalDomain] success to set local domain {}",
                          domain);
                  return;
  
              } else {
                  getBBCLogger().error("[setLocalDomain] set domain({}) tx failed, error msg: {}",
                          domain,
                          responseInfo.getMessage());
                  return;
              }
          } catch (Exception e) {
              getBBCLogger().error("[setLocalDomain] failed to invoke sdp contract to set local domain {}",
                      domain, e);
              throw new RuntimeException(
                      String.format(
                              "failed to set domain (%s) to SDP %s",
                              domain,
                              this.bbcContext.getSdpContract().getContractAddress()
                      ), e
              );
          }
      }
  
      @Override
      public List<CrossChainMessage> readCrossChainMessagesByHeight(long height) {
          getBBCLogger().info("[readCrossChainMessagesByHeight] read cross chain msg by height {}",
                  height);
  
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
  
          if (ObjectUtil.isNull(this.bbcContext.getAuthMessageContract())) {
              throw new RuntimeException("empty am contract in bbc context");
          }
  
          try {
              // 1. get BlockInfo
              ChainmakerBlock.BlockInfo blockInfo = chainClient.getBlockByHeight(height, false, rpcCallTimeout);
              ChainmakerBlock.BlockHeader blockHeader = blockInfo.getBlock().getHeader();
              String blockHash = Hex.toHexString(blockHeader.getBlockHash().toByteArray());
              // 2. get transaction List in block
              List<ChainmakerTransaction.Transaction> transactionList = blockInfo.getBlock().getTxsList();
  
              List<CrossChainMessage> messageList = new ArrayList<>();
              for (ChainmakerTransaction.Transaction transaction : transactionList) {
                  // 3. get contractEvent List in transaction
                  List<ResultOuterClass.ContractEvent> contractEventList = transaction.getResult().
                          getContractResult().getContractEventList();
                  // 4. create crosschain msg from contractEvent
                  for (ResultOuterClass.ContractEvent contractEvent : contractEventList) {
                      if (contractEvent.getTopic().replace("0x", "").equals(SEND_AUTH_MESSAGE_SIGNATURE.replace("0x", ""))
                              && StrUtil.equals(contractEvent.getContractName(), bbcContext.getAmContractName())) {
                          messageList.add(CrossChainMessage.createCrossChainMessage(
                                  CrossChainMessage.CrossChainMessageType.AUTH_MSG,
                                  blockHeader.getBlockHeight(),
                                  blockHeader.getBlockTimestamp(),
                                  HexUtil.decodeHex(StrUtil.removePrefix(blockHash.trim(), "0x")),
                                  (byte[]) FunctionReturnDecoder.decode(contractEvent.getEventData(0), SEND_AUTH_MESSAGE_EVENT.getNonIndexedParameters()).get(0).getValue(),
                                  // todo: put ledger data, for SPV or other attestations
                                  // this time we need no verify. it's ok to set it with empty bytes
                                  "".getBytes(),
                                  // todo: put proof data
                                  // this time we need no proof data. it's ok to set it with empty bytes
                                  "".getBytes(),
                                  HexUtil.decodeHex(StrUtil.removePrefix(contractEvent.getTxId().trim(), "0x"))
                          ));
                      }
                  }
              }
  
              if (!messageList.isEmpty()) {
                  getBBCLogger().info("read cross chain messages (height: {}, msg_size: {})", height, messageList.size());
                  getBBCLogger().info("read cross chain messages (height: {}, msgs: {})",
                          height,
                          messageList.stream().map(JSON::toJSONString).collect(Collectors.joining(","))
                  );
              }
  
              return messageList;
          } catch (Exception e) {
              throw new RuntimeException(
                      String.format(
                              "failed to readCrossChainMessagesByHeight (Height: %d, contractAddr: %s, topic: %s)",
                              height,
                              this.bbcContext.getAuthMessageContract().getContractAddress(),
                              SEND_AUTH_MESSAGE_SIGNATURE
                      ), e
              );
          }
      }
  
      // todo
      @Override
      public CrossChainMessageReceipt readCrossChainMessageReceipt(String txHash) {
          // 1. Obtain Chainmaker TransactionInfo according to txHash
          ChainmakerTransaction.TransactionInfo transactionInfo;
          try {
              transactionInfo = chainClient.getTxByTxId(txHash, rpcCallTimeout);
          } catch (ChainMakerCryptoSuiteException | ChainClientException e) {
              throw new RuntimeException("fail to get transaction by txId", e);
          }
  
          // 2. Construct cross-chain message receipt
          CrossChainMessageReceipt crossChainMessageReceipt = getCrossChainMessageReceipt(txHash, transactionInfo);
          getBBCLogger().info("cross chain message receipt for txHash {} : {}", txHash, JSON.toJSONString(crossChainMessageReceipt));
          return crossChainMessageReceipt;
      }
  
      private CrossChainMessageReceipt getCrossChainMessageReceipt(String txHash, ChainmakerTransaction.TransactionInfo transactionInfo) {
          CrossChainMessageReceipt crossChainMessageReceipt = new CrossChainMessageReceipt();
          if (transactionInfo == null) {
              // If the transaction is not packaged, the return receipt is empty
              crossChainMessageReceipt.setConfirmed(false);
              crossChainMessageReceipt.setSuccessful(false);
              crossChainMessageReceipt.setTxhash(txHash);
              crossChainMessageReceipt.setErrorMsg("");
              return crossChainMessageReceipt;
          }
  
          ResultOuterClass.Result transactionResult = transactionInfo.getTransaction().getResult();
          String blockHash = Hex.toHexString(transactionInfo.getBlockHash().toByteArray());
          List<ResultOuterClass.ContractEvent> contractEventList = transactionResult.getContractResult().getContractEventList();
          if (ObjectUtil.isNotEmpty(contractEventList)) {
              ResultOuterClass.ContractEvent contractEvent = null;
              for (ResultOuterClass.ContractEvent contractEventTmp : contractEventList) {
                  String topic = "0x" + contractEventTmp.getTopic();
                  if (topic.equals(EventEncoder.encode(RECEIVE_MESSAGE_EVENT))) {
                      contractEvent = contractEventTmp;
                      break;
                  }
              }
              if (contractEvent != null) {
                  EventValues eventValues = analyzeContractEvent(contractEvent, RECEIVE_MESSAGE_EVENT);
                  String senderDomain = (String) eventValues.getNonIndexedValues().get(0).getValue();
                  byte[] senderID = (byte[]) eventValues.getNonIndexedValues().get(1).getValue();
                  String receiverID = (String) eventValues.getNonIndexedValues().get(2).getValue();
                  BigInteger seqExpected = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue();
                  Boolean res = (Boolean) eventValues.getNonIndexedValues().get(4).getValue();
                  String errMsg = (String) eventValues.getNonIndexedValues().get(5).getValue();
  
                  crossChainMessageReceipt.setConfirmed(true);
                  crossChainMessageReceipt.setSuccessful(transactionResult.getCodeValue() == 0 &&
                          transactionResult.getContractResult().getCode() == 0 && res);
                  crossChainMessageReceipt.setTxhash(txHash);
                  crossChainMessageReceipt.setErrorMsg(transactionResult.getCodeValue() == 0 ?
                          StrUtil.format("SDP calls biz contract: {}", transactionResult.getContractResult().getCode() == 0 && res ?
                                  "SUCCESS" : contractEvent.getEventData(0)) :
                          transactionResult.getMessage() + "\n" + transactionResult.getContractResult().getMessage());
  
                  getBBCLogger().info(
                          "event receiveMessage from SDP contract is found in no.{} tx {} of block {} : " +
                                  "( send_domain: {}, sender: {}, receiver: {}, biz_call: {}, err_msg: {} )",
                          transactionInfo.getTxIndex(), txHash, blockHash, senderDomain, HexUtil.encodeHexStr(senderID),
                          receiverID, res.toString(), errMsg
                  );
                  return crossChainMessageReceipt;
              }
          }
  
          crossChainMessageReceipt.setConfirmed(true);
          crossChainMessageReceipt.setSuccessful(transactionResult.getCodeValue() == 0 &&
                  transactionResult.getContractResult().getCode() == 0);
          crossChainMessageReceipt.setTxhash(txHash);
          crossChainMessageReceipt.setErrorMsg(StrUtil.emptyToDefault(transactionResult.getMessage() +
                  transactionResult.getContractResult().getMessage(), ""));
  
          return crossChainMessageReceipt;
      }
  
  
      private EventValues analyzeContractEvent(ResultOuterClass.ContractEvent contractEvent, Event event) {
          Log log = new Log();
          List<String> topics = new ArrayList<>();
          topics.add("0x" + contractEvent.getTopic());
          int size = contractEvent.getEventDataCount();
          for (int i = 0; i < size; ++i) {
              if (i == size - 1) {
                  log.setData(contractEvent.getEventData(i));
                  break;
              }
              topics.add(contractEvent.getEventData(i));
          }
          log.setTopics(topics);
  
          return org.web3j.tx.Contract.staticExtractEventParameters(event, log);
      }
  
      @Override
      public CrossChainMessageReceipt relayAuthMessage(byte[] rawMessage) {
          getBBCLogger().info("[relayAuthMessage] relay AM {} to {} ",
                  HexUtil.encodeHexStr(rawMessage),
                  this.bbcContext.getAuthMessageContract().getContractAddress());
  
          // 1. check context
          if (ObjectUtil.isNull(this.bbcContext)) {
              throw new RuntimeException("empty bbc context");
          }
  
          if (ObjectUtil.isNull(this.bbcContext.getAuthMessageContract())) {
              throw new RuntimeException("empty am contract in bbc context");
          }
  
          // 2. send Transaction
          try {
              CrossChainMessageReceipt crossChainMessageReceipt = new CrossChainMessageReceipt();
  
              ResultOuterClass.TxResponse responseInfo = invokeContract(this.bbcContext.getAuthMessageContract().getContractAddress(),
                      AM_METHOD_RECV_PKG_FROM_RELAYER,
                      Arrays.<Type>asList(new DynamicBytes(rawMessage)),
                      Collections.<TypeReference<?>>emptyList());
  
              boolean txResult = responseInfo.getCode() == ResultOuterClass.TxStatusCode.SUCCESS &&
                      responseInfo.getContractResult().getCode() == 0;
              String txMsg = HexUtil.decodeHexStr(HexUtil.decodeHexStr(responseInfo.getContractResult().getMessage()));
  
              crossChainMessageReceipt.setConfirmed(txResult);
              crossChainMessageReceipt.setSuccessful(txResult);
              crossChainMessageReceipt.setTxhash(responseInfo.getTxId());
              crossChainMessageReceipt.setErrorMsg(txMsg);
  
              getBBCLogger().error("[relayAuthMessage] relay am [{}] to amContract {} by tx {} with msg [{}], result: {}",
                      HexUtil.encodeHexStr(rawMessage),
                      this.bbcContext.getAuthMessageContract().getContractAddress(),
                      responseInfo.getTxId(),
                      txMsg,
                      txResult);
              return crossChainMessageReceipt;
          } catch (Exception e) {
              getBBCLogger().error("[relayAuthMessage] failed to relay am {} to amContract {} with exception",
                      HexUtil.encodeHexStr(rawMessage),
                      this.bbcContext.getAuthMessageContract().getContractAddress(),
                      e);
              throw new RuntimeException(
                      StrUtil.format("failed to relay AM {} to {}",
                              HexUtil.encodeHexStr(rawMessage),
                              this.bbcContext.getAuthMessageContract().getContractAddress()
                      ), e
              );
          }
      }
  
      // ================================================================================================
      // ================================================================================================
      // 合约操作工具方法
      // ================================================================================================
      // ================================================================================================
  
      /**
       * 合约部署
       *
       * @param contractBytecode
       * @param contractName     合约名称不是合约地址
       * @return
       */
      public ContractOuterClass.Contract setupContract(String contractBytecode, String contractName) {
          // 1. Create contract construction parameter (RLP encoding)
          Function function = new Function("", Collections.singletonList(new Address(clientAddress)),
                  Collections.emptyList());
          String methodDataStr = FunctionEncoder.encode(function);
          Map<String, byte[]> paramMap = new HashMap<>();
          paramMap.put(CONTRACT_ARGS_EVM_PARAM, methodDataStr.substring(10).getBytes());
  
          // 2. create payload
          Request.Payload payload = null;
          try {
              payload = chainClient.createContractCreatePayload(
                      Utils.calcContractName(contractName),
                      "1",
                      contractBytecode.getBytes(),
                      ContractOuterClass.RuntimeType.EVM,
                      paramMap);
          } catch (ChainMakerCryptoSuiteException e) {
              throw new RuntimeException("fail to create" + contractName + "payload", e);
          }
  
          // 3. create payload with endorsement
          User[] adminUserArray = adminUserList.toArray(new User[adminUserList.size()]);
          Request.EndorsementEntry[] endorsementEntries = null;
          try {
              endorsementEntries = SdkUtils.getEndorsers(payload, adminUserArray);
          } catch (ChainMakerCryptoSuiteException | UtilsException e) {
              throw new RuntimeException("fail to create" + contractName + "payload with endorsement", e);
          }
  
          // 4. send request
          ResultOuterClass.TxResponse responseInfo = null;
          try {
              responseInfo = chainClient.sendContractManageRequest(
                      payload, endorsementEntries, rpcCallTimeout, syncResultTimeout);
          } catch (ChainMakerCryptoSuiteException | ChainClientException e) {
              throw new RuntimeException("failed to send request for " + contractName, e);
          }
  
          if (responseInfo.getCode() != ResultOuterClass.TxStatusCode.SUCCESS) {
              throw new RuntimeException("failed to deploy contract" + contractName);
          }
  
          // 5. get Contract
          ContractOuterClass.Contract contract;
          try {
              contract = ContractOuterClass.Contract.newBuilder()
                      .mergeFrom(responseInfo.getContractResult().getResult().toByteArray()).build();
          } catch (InvalidProtocolBufferException e) {
              throw new RuntimeException("failed to get contract from deploy contract response for " + contractName, e);
          }
  
          return contract;
      }
  
      /**
       * 合约调用交易，交易会出块
       *
       * @param contractName 合约地址
       * @param methodName
       * @param inputParams
       * @param outputParams
       * @return
       */
      public ResultOuterClass.TxResponse invokeContract(String contractName,
                                                        String methodName,
                                                        List<Type> inputParams,
                                                        List<TypeReference<?>> outputParams) {
  
          Map<String, byte[]> params = new HashMap<>();
          Function function = new Function(methodName, inputParams, outputParams);
          String methodDataStr = FunctionEncoder.encode(function);
          String method = methodDataStr.substring(0, 10);
          params.put(CONTRACT_ARGS_EVM_PARAM, methodDataStr.getBytes());
  
          ResultOuterClass.TxResponse responseInfo = null;
          try {
              return chainClient.invokeContract(contractName,
                      method, null, params, rpcCallTimeout, syncResultTimeout);
  
          } catch (ChainMakerCryptoSuiteException | ChainClientException | RuntimeException e) {
              throw new RuntimeException(
                      StrUtil.format(
                              "failed to invoke contract {} with method {}",
                              contractName,
                              methodName
                      ), e
              );
          }
      }
  
      /**
       * 合约查询交易，交易不会出块
       *
       * @param contractName
       * @param methodName
       * @param inputParams
       * @param outputParams
       * @return
       */
      public ResultOuterClass.TxResponse queryContract(String contractName,
                                                       String methodName,
                                                       List<Type> inputParams,
                                                       List<TypeReference<?>> outputParams) {
  
          Map<String, byte[]> params = new HashMap<>();
          Function function = new Function(methodName, inputParams, outputParams);
          String methodDataStr = FunctionEncoder.encode(function);
          String method = methodDataStr.substring(0, 10);
          params.put(CONTRACT_ARGS_EVM_PARAM, methodDataStr.getBytes());
  
          try {
              return chainClient.queryContract(contractName,
                      method, null, params, rpcCallTimeout);
          } catch (ChainMakerCryptoSuiteException | ChainClientException | RuntimeException e) {
              throw new RuntimeException(
                      StrUtil.format(
                              "failed to query contract {} with method {}",
                              contractName,
                              methodName
                      ), e
              );
          }
      }
  }
  