From 0ced0c64bfa3d1d2a5fd99ee5f4f38b7ac42927a Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 5 Mar 2024 16:57:11 +0800 Subject: [PATCH 1/9] update js and python dependencies,remove some useless code in testing script --- package-lock.json | 44 +++++++++++++++++++++++++++++++------ package.json | 9 +++----- requirements.txt | 3 +-- tests/utils.py | 55 +---------------------------------------------- 4 files changed, 42 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee495f6d..112f4cf7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "commander": "^3.0.1", - "nunjucks": "^3.2.3", + "es5-ext": "^0.10.64", + "nunjucks": "^3.2.4", "rlp": "^2.2.7", "web3": "^1.9.0" } @@ -998,13 +999,14 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -1040,6 +1042,25 @@ "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmmirror.com/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", @@ -1128,6 +1149,15 @@ "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.11.6.tgz", "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -1995,9 +2025,9 @@ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" }, "node_modules/nunjucks": { - "version": "3.2.3", - "resolved": "https://registry.npmmirror.com/nunjucks/-/nunjucks-3.2.3.tgz", - "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", "dependencies": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", diff --git a/package.json b/package.json index af214101..417600ce 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,15 @@ "description": "", "main": "index.js", "scripts": { - "testrpc": "ganache-cli --mnemonic 'clock radar mass judge dismiss just intact mind resemble fringe diary casino' --gasLimit 13000000 -e 100000", - "test:ci": "scripts/run-test.sh", - "generate-test": "node generate-system.js --mock true && node generate-validatorset.js --mock true && node generate-btclightclient.js --mock true && node generate-candidatehub.js --mock true && node generate-pledgeagent.js --mock true && node generate-slash.js --mock true", "generate-mainnet": "node generate-genesis.js --chainid 1116 --initValidatorSetBytes f90285ea944121f067b0f5135d77c29b2b329e8cb1bd96c96094f8b18cecc98d976ad253d38e4100a73d4e154726ea947f461f8a1c35edecd6816e76eb2e84eb661751ee94f8b18cecc98d976ad253d38e4100a73d4e154726ea94fd806ab93db5742944b7b50ce759e5eee5f6fe5094f8b18cecc98d976ad253d38e4100a73d4e154726ea947ef3a94ad1c443481fb3d86829355ca90477f8b594f8b18cecc98d976ad253d38e4100a73d4e154726ea9467d1ad48f91e131413bd0b04e823f3ae4f81e85394f8b18cecc98d976ad253d38e4100a73d4e154726ea943fb42cab4416024dc1b4c9e21b9acd0dfcef35f694f8b18cecc98d976ad253d38e4100a73d4e154726ea943511e3b8ac7336b99517d324145e9b5bb33e08a494f8b18cecc98d976ad253d38e4100a73d4e154726ea94729f39a54304fcc6ec279684c71491a385d7b9ae94f8b18cecc98d976ad253d38e4100a73d4e154726ea94f44a785fd9f23f0abd443541386e71356ce619dc94f8b18cecc98d976ad253d38e4100a73d4e154726ea942efd3cf0733421aec3e4202480d0a90bd157514994f8b18cecc98d976ad253d38e4100a73d4e154726ea94613b0f519ada008cb99b6130e89122ba416bf15994f8b18cecc98d976ad253d38e4100a73d4e154726ea94c0925eeb800ff6ba4695ded61562a10102152b5f94f8b18cecc98d976ad253d38e4100a73d4e154726ea9419e3c7d7e69f273f3f91c060bb438a007f6fc33c94f8b18cecc98d976ad253d38e4100a73d4e154726ea94e127f110d172a0c4c6209fe045dd71781e8fe9d494f8b18cecc98d976ad253d38e4100a73d4e154726ea94f778dc4a199a440dbe9f16d1e13e185bb179b3b794f8b18cecc98d976ad253d38e4100a73d4e154726 --initMembersBytes f86994548e6acce441866674e04ab84587af2d394034c094bb06d463bc143eecc4a0cfa35e0346d5690fa9f694e2fe60f349c6e1a85caad1d22200c289da40dc1294b198db68258f06e79d415a0998be7f9b38ea722694dd173b85f306128f1b10d7d7219059c28c6d6c09", - "generate-testnet": "node generate-genesis.js --chainid 1115 --initValidatorSetBytes f8d7ea9401bca3615d24d3c638836691517b2b9b49b054b1943ae030dc3717c66f63d6e8f1d1508a5c941ff46dea94a458499604a85e90225a14946f36368ae24df16d94de442f5ba55687a24f04419424e0dc2593cc9f4cea945e00c0d5c4c10d4c805aba878d51129a89d513e094cb089be171e256acdaac1ebbeb32ffba0dd438eeea941cd652bc64af3f09b490daae27f46e53726ce230940a53b7e0ffd97357e444b85f4d683c1d8e22879aea94da37ccecbb2d7c83ae27ee2bebfe8ebce162c60094d82c24274ebbfe438788d684dc6034c3c67664a4 --initMembersBytes f83f9491fb7d8a73d2752830ea189737ea0e007f999b949448bfbc530e7c54c332b0fae07312fba7078b878994de60b7d0e6b758ca5dd8c61d377a2c5f1af51ec1", - "generate-testnet2": "node generate-genesis.js --chainid 1114 --initValidatorSetBytes f8d7ea9401bca3615d24d3c638836691517b2b9b49b054b19401bca3615d24d3c638836691517b2b9b49b054b1ea94a458499604a85e90225a14946f36368ae24df16d94a458499604a85e90225a14946f36368ae24df16dea945e00c0d5c4c10d4c805aba878d51129a89d513e0945e00c0d5c4c10d4c805aba878d51129a89d513e0ea941cd652bc64af3f09b490daae27f46e53726ce230941cd652bc64af3f09b490daae27f46e53726ce230ea94da37ccecbb2d7c83ae27ee2bebfe8ebce162c60094da37ccecbb2d7c83ae27ee2bebfe8ebce162c600 --initMembersBytes f83f9491fb7d8a73d2752830ea189737ea0e007f999b949448bfbc530e7c54c332b0fae07312fba7078b878994de60b7d0e6b758ca5dd8c61d377a2c5f1af51ec1" + "generate-testnet": "node generate-genesis.js --chainid 1115 --initValidatorSetBytes f8d7ea9401bca3615d24d3c638836691517b2b9b49b054b1943ae030dc3717c66f63d6e8f1d1508a5c941ff46dea94a458499604a85e90225a14946f36368ae24df16d94de442f5ba55687a24f04419424e0dc2593cc9f4cea945e00c0d5c4c10d4c805aba878d51129a89d513e094cb089be171e256acdaac1ebbeb32ffba0dd438eeea941cd652bc64af3f09b490daae27f46e53726ce230940a53b7e0ffd97357e444b85f4d683c1d8e22879aea94da37ccecbb2d7c83ae27ee2bebfe8ebce162c60094d82c24274ebbfe438788d684dc6034c3c67664a4 --initMembersBytes f83f9491fb7d8a73d2752830ea189737ea0e007f999b949448bfbc530e7c54c332b0fae07312fba7078b878994de60b7d0e6b758ca5dd8c61d377a2c5f1af51ec1" }, "author": "", "license": "MIT", "dependencies": { "commander": "^3.0.1", - "nunjucks": "^3.2.3", + "es5-ext": "^0.10.64", + "nunjucks": "^3.2.4", "rlp": "^2.2.7", "web3": "^1.9.0" } diff --git a/requirements.txt b/requirements.txt index 63530408..4394b16f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ eth-brownie==1.19.3 -PyYAML==5.4.1 -ecdsa==0.17.0 \ No newline at end of file +PyYAML==5.4.1 \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index a6ebcefb..541ce663 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,14 +1,11 @@ import random -import pathlib import codecs import hashlib -import ecdsa -import yaml from web3 import Web3 from brownie.network.transaction import TransactionReceipt from brownie.network.account import LocalAccount -from brownie import accounts, chain, web3, history +from brownie import chain, web3, history from eth_account import Account from eth_abi import encode @@ -33,56 +30,6 @@ def expect_event_not_emitted(tx_receipt: TransactionReceipt, event_name): assert event_name not in tx_receipt.events -def get_mnemonic() -> str: - current_path = pathlib.Path(__file__) - config_file_path = current_path.parent.parent / "brownie-config.yaml" - with open(config_file_path, "r") as f: - data = yaml.load(f.read(), Loader=yaml.CLoader) - return data['networks']['development']['cmd_settings']['mnemonic'] - - -def get_private_key_by_idx(idx): - account = accounts.from_mnemonic(get_mnemonic(), offset=idx) - return account.private_key - - -def pk2public_key(pk, compressed=False): - if pk.startswith('0x'): - private_key_bytes = Web3.toBytes(hexstr=pk) - else: - private_key_bytes = codecs.decode(pk, 'hex') - - # Generating a public key in bytes using SECP256k1 & ecdsa library - public_key_raw = ecdsa.SigningKey.from_string(private_key_bytes, curve=ecdsa.SECP256k1).verifying_key - public_key_bytes = public_key_raw.to_string() - - # Hex encoding the public key from bytes - public_key_hex = codecs.encode(public_key_bytes, 'hex') - - # Bitcoin uncompressed public key begins with bytes 0x04 so we have to add the bytes at the start - public_key = (b'04' + public_key_hex).decode('utf-8') - if not compressed: - return '0x' + public_key - - # Checking if the last byte is odd or even - if ord(bytearray.fromhex(public_key[-2:])) % 2 == 0: - public_key_compressed = '02' - else: - public_key_compressed = '03' - # Add bytes 0x02 to the X of the key if even or 0x03 if odd - public_key_compressed += public_key[2:66] - return '0x' + public_key_compressed - - -def get_public_key_by_idx(idx: int, compressed=False): - return pk2public_key(get_private_key_by_idx(idx), compressed) - - -def get_public_key_by_address(address, compressed=False): - idx = [account.address for account in accounts].index(address) - return get_public_key_by_idx(idx, compressed) - - def public_key2PKHash(public_key): if public_key.startswith('0x'): public_key_bytes = Web3.toBytes(hexstr=public_key) From eaf4a64f2ce047120f1f9e461c21f6b13a4d3ee1 Mon Sep 17 00:00:00 2001 From: zfliex Date: Mon, 4 Mar 2024 20:13:30 +0800 Subject: [PATCH 2/9] btc stake --- contracts/BtcLightClient.sol | 2 +- contracts/BtcLightClient.template | 2 +- contracts/CandidateHub.sol | 22 +- contracts/CandidateHub.template | 22 +- contracts/PledgeAgent.sol | 396 ++++++++- contracts/PledgeAgent.template | 396 ++++++++- contracts/interface/ICandidateHub.sol | 1 + contracts/interface/ILightClient.sol | 2 + contracts/interface/IPledgeAgent.sol | 4 +- contracts/lib/BitcoinHelper.sol | 904 ++++++++++++++++++++ contracts/lib/SafeCast.sol | 1136 +++++++++++++++++++++++++ contracts/lib/TypedMemView.sol | 816 ++++++++++++++++++ generate-pledgeagent.js | 4 +- 13 files changed, 3619 insertions(+), 88 deletions(-) create mode 100644 contracts/lib/BitcoinHelper.sol create mode 100644 contracts/lib/SafeCast.sol create mode 100644 contracts/lib/TypedMemView.sol diff --git a/contracts/BtcLightClient.sol b/contracts/BtcLightClient.sol index a346b220..ae5f952e 100644 --- a/contracts/BtcLightClient.sol +++ b/contracts/BtcLightClient.sol @@ -291,7 +291,7 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ /// @param nodes Part of the Merkle tree from the tx to the root in LE form (called Merkle proof) /// @param index of the tx in Merkle tree /// @return True if the provided tx is confirmed on Bitcoin - function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) external view returns (bool) { + function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) { bytes32 blockHash = height2HashMap[blockHeight]; if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) { return false; diff --git a/contracts/BtcLightClient.template b/contracts/BtcLightClient.template index 447a87bc..9a8a0165 100644 --- a/contracts/BtcLightClient.template +++ b/contracts/BtcLightClient.template @@ -291,7 +291,7 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ /// @param nodes Part of the Merkle tree from the tx to the root in LE form (called Merkle proof) /// @param index of the tx in Merkle tree /// @return True if the provided tx is confirmed on Bitcoin - function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) external view returns (bool) { + function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) { bytes32 blockHash = height2HashMap[blockHeight]; if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) { return false; diff --git a/contracts/CandidateHub.sol b/contracts/CandidateHub.sol index 33e79ef6..22d4a3ec 100644 --- a/contracts/CandidateHub.sol +++ b/contracts/CandidateHub.sol @@ -92,7 +92,7 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { roundInterval = INIT_ROUND_INTERVAL; validatorCount = INIT_VALIDATOR_COUNT; maxCommissionChange = MAX_COMMISSION_CHANGE; - roundTag = 7; + roundTag = block.timestamp / INIT_ROUND_INTERVAL; alreadyInit = true; } @@ -109,6 +109,13 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { return status == (status & ACTIVE_STATUS); } + /// Whether the input address is operator address of a validator candidate + /// @param operateAddr Operator address of validator candidate + /// @return true/false + function isCandidateByOperate(address operateAddr) external override view returns (bool) { + return operateMap[operateAddr] != 0; + } + /// Jail a validator for some rounds and slash some amount of deposits /// @param operateAddress The operator address of the validator /// @param round The number of rounds to jail @@ -196,8 +203,8 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { // calculate the hybrid score for all valid candidates and // choose top ones to form the validator set of the new round - (uint256[] memory scores, uint256 totalPower, uint256 totalCoin) = - IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore(candidates, powers); + (uint256[] memory scores) = + IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore(candidates, powers, roundTag); address[] memory validatorList = getValidators(candidates, scores, validatorCount); // prepare arguments, and notify ValidatorSet contract @@ -225,7 +232,7 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { ISlashIndicator(SLASH_CONTRACT_ADDR).clean(); // notify PledgeAgent contract - IPledgeAgent(PLEDGE_AGENT_ADDR).setNewRound(validatorList, totalPower, totalCoin, roundTag); + IPledgeAgent(PLEDGE_AGENT_ADDR).setNewRound(validatorList, roundTag); // update validator jail status for (uint256 i = 0; i < candidateSize; i++) { @@ -478,13 +485,6 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { return opAddrs; } - /// Whether the input address is operator address of a validator candidate - /// @param operateAddr Operator address of validator candidate - /// @return true/false - function isCandidateByOperate(address operateAddr) external view returns (bool) { - return operateMap[operateAddr] != 0; - } - /// Whether the input address is consensus address a validator candidate /// @param consensusAddr Consensus address of validator candidate /// @return true/false diff --git a/contracts/CandidateHub.template b/contracts/CandidateHub.template index 42e6e146..d02ac772 100644 --- a/contracts/CandidateHub.template +++ b/contracts/CandidateHub.template @@ -94,7 +94,7 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { roundInterval = INIT_ROUND_INTERVAL; validatorCount = INIT_VALIDATOR_COUNT; maxCommissionChange = MAX_COMMISSION_CHANGE; - roundTag = 7; + roundTag = block.timestamp / INIT_ROUND_INTERVAL; alreadyInit = true; } {% if mock %} @@ -115,6 +115,13 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { return status == (status & ACTIVE_STATUS); } + /// Whether the input address is operator address of a validator candidate + /// @param operateAddr Operator address of validator candidate + /// @return true/false + function isCandidateByOperate(address operateAddr) external override view returns (bool) { + return operateMap[operateAddr] != 0; + } + /// Jail a validator for some rounds and slash some amount of deposits /// @param operateAddress The operator address of the validator /// @param round The number of rounds to jail @@ -208,8 +215,8 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { // calculate the hybrid score for all valid candidates and // choose top ones to form the validator set of the new round - (uint256[] memory scores, uint256 totalPower, uint256 totalCoin) = - IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore(candidates, powers); + (uint256[] memory scores) = + IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore(candidates, powers, roundTag); address[] memory validatorList = getValidators(candidates, scores, validatorCount); // prepare arguments, and notify ValidatorSet contract @@ -237,7 +244,7 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { ISlashIndicator(SLASH_CONTRACT_ADDR).clean(); // notify PledgeAgent contract - IPledgeAgent(PLEDGE_AGENT_ADDR).setNewRound(validatorList, totalPower, totalCoin, roundTag); + IPledgeAgent(PLEDGE_AGENT_ADDR).setNewRound(validatorList, roundTag); // update validator jail status for (uint256 i = 0; i < candidateSize; i++) { @@ -490,13 +497,6 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { return opAddrs; } - /// Whether the input address is operator address of a validator candidate - /// @param operateAddr Operator address of validator candidate - /// @return true/false - function isCandidateByOperate(address operateAddr) external view returns (bool) { - return operateMap[operateAddr] != 0; - } - /// Whether the input address is consensus address a validator candidate /// @param consensusAddr Consensus address of validator candidate /// @return true/false diff --git a/contracts/PledgeAgent.sol b/contracts/PledgeAgent.sol index ca8d80a6..3f83de16 100644 --- a/contracts/PledgeAgent.sol +++ b/contracts/PledgeAgent.sol @@ -5,7 +5,9 @@ import "./interface/IPledgeAgent.sol"; import "./interface/IParamSubscriber.sol"; import "./interface/ICandidateHub.sol"; import "./interface/ISystemReward.sol"; +import "./interface/ILightClient.sol"; import "./lib/Address.sol"; +import "./lib/BitcoinHelper.sol"; import "./lib/BytesToTypes.sol"; import "./lib/Memory.sol"; import "./System.sol"; @@ -20,9 +22,22 @@ import "./System.sol"; /// which are eligible for claiming rewards in the acting round contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { + using BitcoinHelper for *; + using TypedMemView for *; + uint256 public constant INIT_REQUIRED_COIN_DEPOSIT = 1e18; uint256 public constant INIT_HASH_POWER_FACTOR = 20000; uint256 public constant POWER_BLOCK_FACTOR = 1e18; + uint32 public constant INIT_BTC_CONFIRM_BLOCK = 3; + uint256 public constant INIT_MIN_BTC_LOCK_ROUND = 7; + uint256 public constant ROUND_INTERVAL = 86400; + uint256 public constant INIT_MIN_BTC_VALUE = 1e6; + uint256 public constant INIT_BTC_FACTOR = 5e4; + uint256 public constant BTC_STAKE_MAGIC = 0x5341542b; + uint256 public constant CHAINID = 1116; + uint256 public constant FEE_FACTOR = 1e18; + int256 public constant CLAIM_ROUND_LIMIT = 500; + uint256 public constant BTC_UNIT_CONVERSION = 1e10; uint256 public requiredCoinDeposit; @@ -60,6 +75,43 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { // debtDepositMap keeps delegator's amount of CORE which should be deducted when claiming rewards in every round mapping(uint256 => mapping(address => uint256)) public debtDepositMap; + // HARDFORK V-1.0.7 + // btcReceiptMap keeps all BTC staking receipts on Core + mapping(bytes32 => BtcReceipt) public btcReceiptMap; + + // round2expireInfoMap keeps the amount of expired BTC staking value for each round + mapping(uint256 => BtcExpireInfo) round2expireInfoMap; + + // staking weight of each BTC vs. CORE + uint256 public btcFactor; + + // minimum rounds to stake for a BTC staking transaction + uint256 public minBtcLockRound; + + // the number of blocks to mark a BTC staking transaction as confirmed + uint32 public btcConfirmBlock; + + // minimum value to stake for a BTC staking transaction + uint256 public minBtcValue; + + // HARDFORK V-1.0.7 + struct BtcReceipt { + address agent; + address delegator; + uint256 value; + uint256 endRound; + uint256 rewardIndex; + address payable feeReceiver; + uint256 fee; + } + + // HARDFORK V-1.0.7 + struct BtcExpireInfo { + address[] agentAddrList; + mapping(address => uint256) agent2valueMap; + mapping(address => uint256) agentExsitMap; + } + struct CoinDelegator { uint256 deposit; uint256 newDeposit; @@ -87,17 +139,22 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { Reward[] rewardSet; uint256 power; uint256 coin; + uint256 btc; + uint256 totalBtc; } struct RoundState { uint256 power; uint256 coin; uint256 powerFactor; + uint256 btc; + uint256 btcFactor; } /*********************** events **************************/ event paramChange(string key, bytes value); event delegatedCoin(address indexed agent, address indexed delegator, uint256 amount, uint256 totalAmount); + event delegatedBtc(bytes32 indexed txid, address indexed agent, address indexed delegator, bytes script, uint32 blockHeight, uint256 outputIndex); event undelegatedCoin(address indexed agent, address indexed delegator, uint256 amount); event transferredCoin( address indexed sourceAgent, @@ -106,8 +163,19 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 amount, uint256 totalAmount ); - event roundReward(address indexed agent, uint256 coinReward, uint256 powerReward); + event transferredBtc( + bytes32 indexed txid, + address sourceAgent, + address targetAgent, + address delegator, + uint256 amount, + uint256 totalAmount + ); + event btcPledgeExpired(bytes32 indexed txid, address indexed delegator); + event roundReward(address indexed agent, uint256 coinReward, uint256 powerReward, uint256 btcReward); event claimedReward(address indexed delegator, address indexed operator, uint256 amount, bool success); + event transferredBtcFee(bytes32 indexed txid, address payable feeReceiver, uint256 fee); + event failedTransferBtcFee(bytes32 indexed txid, address payable feeReceiver, uint256 fee); /// The validator candidate is inactive, it is expected to be active /// @param candidate Address of the validator candidate @@ -121,7 +189,11 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { function init() external onlyNotInit { requiredCoinDeposit = INIT_REQUIRED_COIN_DEPOSIT; powerFactor = INIT_HASH_POWER_FACTOR; - roundTag = 1; + btcFactor = INIT_BTC_FACTOR; + minBtcLockRound = INIT_MIN_BTC_LOCK_ROUND; + btcConfirmBlock = INIT_BTC_CONFIRM_BLOCK; + minBtcValue = INIT_MIN_BTC_VALUE; + roundTag = block.timestamp / ROUND_INTERVAL; alreadyInit = true; } @@ -156,63 +228,90 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { r.remainReward = rewardList[i]; uint256 coinReward = rewardList[i] * a.coin * rs.power / roundScore; uint256 powerReward = rewardList[i] * a.power * rs.coin / 10000 * rs.powerFactor / roundScore; - emit roundReward(agentList[i], coinReward, powerReward); + uint256 btcReward = rewardList[i] * a.btc * rs.btcFactor * rs.power / roundScore; + emit roundReward(agentList[i], coinReward, powerReward, btcReward); } } /// Calculate hybrid score for all candidates /// @param candidates List of candidate operator addresses /// @param powers List of power value in this round + /// @param round The new round tag /// @return scores List of hybrid scores of all validator candidates in this round - /// @return totalPower Total power delegate in this round - /// @return totalCoin Total coin delegate in this round - function getHybridScore(address[] calldata candidates, uint256[] calldata powers - ) external override onlyCandidate - returns (uint256[] memory scores, uint256 totalPower, uint256 totalCoin) { + function getHybridScore( + address[] calldata candidates, + uint256[] calldata powers, + uint256 round + ) external override onlyCandidate returns (uint256[] memory scores) { uint256 candidateSize = candidates.length; require(candidateSize == powers.length, "the length of candidates and powers should be equal"); - totalPower = 1; - totalCoin = 1; + // HARDFORK V-1.0.7 + // the expired BTC staking values will be removed before calculating hybrid score for validators + for (uint256 r = roundTag + 1; r <= round; ++r) { + BtcExpireInfo storage expireInfo = round2expireInfoMap[r]; + uint256 j = expireInfo.agentAddrList.length; + while (j > 0) { + j--; + address agent = expireInfo.agentAddrList[j]; + agentsMap[agent].totalBtc -= expireInfo.agent2valueMap[agent]; + expireInfo.agentAddrList.pop(); + delete expireInfo.agent2valueMap[agent]; + delete expireInfo.agentExsitMap[agent]; + } + delete round2expireInfoMap[r]; + } + + uint256 totalPower = 1; + uint256 totalCoin = 1; + uint256 totalBtc; // setup `power` and `coin` values for every candidate + // cost gas approximate candidateSize*20000 for (uint256 i = 0; i < candidateSize; ++i) { Agent storage a = agentsMap[candidates[i]]; // in order to improve accuracy, the calculation of power is based on 10^18 a.power = powers[i] * POWER_BLOCK_FACTOR; + a.btc = a.totalBtc; a.coin = a.totalDeposit; totalPower += a.power; totalCoin += a.coin; + totalBtc += a.btc; } - + + uint256 bf = (btcFactor == 0 ? INIT_BTC_FACTOR : btcFactor) * BTC_UNIT_CONVERSION; + uint256 pf = powerFactor; + // calc hybrid score + // HARDFORK V-1.0.7 BTC staking is added when calculating the hybrid score scores = new uint256[](candidateSize); for (uint256 i = 0; i < candidateSize; ++i) { Agent storage a = agentsMap[candidates[i]]; - scores[i] = a.power * totalCoin * powerFactor / 10000 + a.coin * totalPower; + scores[i] = a.power * (totalCoin + totalBtc * bf) * pf / 10000 + (a.coin + a.btc * bf) * totalPower; } - return (scores, totalPower, totalCoin); + + RoundState storage rs = stateMap[round]; + rs.power = totalPower; + rs.coin = totalCoin; + rs.powerFactor = pf; + rs.btc = totalBtc; + rs.btcFactor = bf; } /// Start new round, this is called by the CandidateHub contract /// @param validators List of elected validators in this round - /// @param totalPower Total power delegate in this round - /// @param totalCoin Total coin delegate in this round /// @param round The new round tag - function setNewRound(address[] calldata validators, uint256 totalPower, - uint256 totalCoin, uint256 round) external override onlyCandidate { - RoundState memory rs; - rs.power = totalPower; - rs.coin = totalCoin; - rs.powerFactor = powerFactor; - stateMap[round] = rs; - - roundTag = round; + function setNewRound(address[] calldata validators, uint256 round) external override onlyCandidate { + RoundState storage rs = stateMap[round]; uint256 validatorSize = validators.length; for (uint256 i = 0; i < validatorSize; ++i) { Agent storage a = agentsMap[validators[i]]; - uint256 score = a.power * rs.coin * powerFactor / 10000 + a.coin * rs.power; - a.rewardSet.push(Reward(0, 0, score, a.coin, round)); + // HARDFORK V-1.0.7 BTC staking is added when calculating the hybrid score + uint256 btcScore = a.btc * rs.btcFactor; + uint256 score = a.power * (rs.coin + rs.btc * rs.btcFactor) * rs.powerFactor / 10000 + (a.coin + btcScore) * rs.power; + a.rewardSet.push(Reward(0, 0, score, a.coin + btcScore, round)); } + + roundTag = round; } /// Distribute rewards for delegated hash power on one validator candidate @@ -234,14 +333,15 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { return; } RoundState storage rs = stateMap[roundTag]; - uint256 reward = rs.coin * POWER_BLOCK_FACTOR * rs.powerFactor / 10000 * r.totalReward / r.score; + uint256 reward = (rs.coin + rs.btc * rs.btcFactor) * POWER_BLOCK_FACTOR * rs.powerFactor / 10000 * r.totalReward / r.score; uint256 minerSize = miners.length; uint256 powerReward = reward * minerSize; uint256 undelegateCoinReward; - if (a.coin > r.coin) { + uint256 btcScore = a.btc * rs.btcFactor; + if (a.coin + btcScore > r.coin) { // undelegatedCoin = a.coin - r.coin - undelegateCoinReward = r.totalReward * (a.coin - r.coin) * rs.power / r.score; + undelegateCoinReward = r.totalReward * (a.coin + btcScore - r.coin) * rs.power / r.score; } uint256 remainReward = r.remainReward; require(remainReward >= powerReward + undelegateCoinReward, "there is not enough reward"); @@ -328,7 +428,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @return (Amount claimed, Are all rewards claimed) function claimReward(address[] calldata agentList) external returns (uint256, bool) { // limit round count to control gas usage - int256 roundLimit = 500; + int256 roundLimit = CLAIM_ROUND_LIMIT; uint256 reward; uint256 rewardSum = rewardMap[msg.sender]; if (rewardSum != 0) { @@ -363,6 +463,130 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { } return (rewardSum, roundLimit >= 0); } + + // HARDFORK V-1.0.7 + // User workflow to delegate BTC to Core blockchain + // 1. A user creates a bitcoin transaction, locks up certain amount ot Bitcoin in one of the transaction output for certain period. + // The transaction should also have an op_return output which contains the staking information, such as the validator and reward addresses. + // 2. Transmit the transaction to Core blockchain by calling the below method `delegateBtc`. + // 3. The user can claim rewards using the reward address set in step 1 during the staking period. + // 4. The user can spend the timelocked UTXO using the redeem script when the lock expires. + // The redeem script should start with a time lock. such as: + // OP_CLTV OP_DROP OP_CHECKSIG + // OP_CLTV OP_DROP OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + // OP_CLTV OP_DROP M ... N OP_CHECKMULTISIG + /// delegate BTC to Core network + /// @param btcTx the BTC transaction data + /// @param blockHeight block height of the transaction + /// @param nodes part of the Merkle tree from the tx to the root in LE form (called Merkle proof) + /// @param index index of the tx in Merkle tree + /// @param script the corresponding redeem script of the locked up output + function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external { + require(script[0] == bytes1(uint8(0x04)) && script[5] == bytes1(uint8(0xb1)), "not a valid redeem script"); + + bytes32 txid = btcTx.calculateTxId(); + require(ILightClient(LIGHT_CLIENT_ADDR). + checkTxProof(txid, blockHeight, (btcConfirmBlock == 0 ? INIT_BTC_CONFIRM_BLOCK : btcConfirmBlock), nodes, index), "btc tx not confirmed"); + + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value == 0, "btc tx confirmed"); + + uint32 lockTime = parseLockTime(script); + br.endRound = lockTime / ROUND_INTERVAL; + require(br.endRound > roundTag + (minBtcLockRound == 0 ? INIT_MIN_BTC_LOCK_ROUND : minBtcLockRound), "insufficient lock round"); + + (,,bytes29 voutView,) = btcTx.extractTx(); + bytes29 payload; + uint256 outputIndex; + (br.value, payload, outputIndex) = voutView.parseToScriptValueAndData(script); + require(br.value > (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); + + uint256 fee; + (br.delegator, br.agent, fee) = parseAndCheckPayload(payload); + if (!ICandidateHub(CANDIDATE_HUB_ADDR).isCandidateByOperate(br.agent)) { + revert InactiveAgent(br.agent); + } + + emit delegatedBtc(txid, br.agent, br.delegator, script, blockHeight, outputIndex); + + if (fee != 0) { + br.fee = fee; + br.feeReceiver = payable(msg.sender); + } + + Agent storage a = agentsMap[br.agent]; + br.rewardIndex = a.rewardSet.length; + addExpire(br); + a.totalBtc += br.value; + } + + // HARDFORK V-1.0.7 + /// transfer staked BTC to a different validator + /// @param txid id of the BTC staking transaction + /// @param targetAgent the new validator address to stake to + function transferBtc(bytes32 txid, address targetAgent) public { + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value != 0, "btc tx not found"); + require(br.delegator == msg.sender, "not the delegator of this btc receipt"); + address agent = br.agent; + require(agent != targetAgent, "can not transfer to the same validator"); + require(br.endRound > roundTag + 1, "insufficient locking rounds"); + + if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(targetAgent)) { + revert InactiveAgent(targetAgent); + } + + (uint256 reward,) = collectBtcReward(txid, 0x7FFFFFFF); + + Agent storage a = agentsMap[agent]; + a.totalBtc -= br.value; + round2expireInfoMap[br.endRound].agent2valueMap[agent] -= br.value; + + Reward storage r = a.rewardSet[a.rewardSet.length - 1]; + if (r.round == roundTag && br.rewardIndex < a.rewardSet.length) { + r.coin -= br.value * stateMap[roundTag].btcFactor; + } + + Agent storage ta = agentsMap[targetAgent]; + br.agent = targetAgent; + br.rewardIndex = ta.rewardSet.length; + addExpire(br); + ta.totalBtc += br.value; + + if (reward != 0) { + distributeReward(payable(msg.sender), reward); + } + + emit transferredBtc(txid, agent, targetAgent, msg.sender, br.value, ta.totalBtc); + } + + // HARDFORK V-1.0.7 + /// claim BTC staking rewards + /// @param txidList the list of BTC staking transaction id to claim rewards + /// @return rewardSum amount of reward claimed + function claimBtcReward(bytes32[] calldata txidList) public returns (uint256 rewardSum) { + int256 claimLimit = CLAIM_ROUND_LIMIT; + uint256 len = txidList.length; + for(uint256 i = 0; i < len && claimLimit != 0; i++) { + bytes32 txid = txidList[i]; + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value != 0, "btc tx not found"); + address delegator = br.delegator; + require(delegator == msg.sender, "not the delegator of this btc receipt"); + + uint256 reward; + (reward, claimLimit) = collectBtcReward(txid, claimLimit); + rewardSum += reward; + if (br.value == 0) { + emit btcPledgeExpired(txid, delegator); + } + } + + if (rewardSum != 0) { + distributeReward(payable(msg.sender), rewardSum); + } + return rewardSum; + } /*********************** Internal methods ***************************/ function distributeReward(address payable delegator, uint256 reward) internal { @@ -557,6 +781,85 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { return rewardAmount; } + function parseAndCheckPayload(bytes29 payload) internal pure returns (address delegator, address agent, uint256 fee) { + require(payload.len() >= 48, "payload length is too small"); + require(payload.indexUint(0, 4) == BTC_STAKE_MAGIC, "wrong magic"); + require(payload.indexUint(4, 1) == 1, "wrong version"); + require(payload.indexUint(5, 2) == CHAINID, "wrong chain id"); + delegator= payload.indexAddress(7); + agent = payload.indexAddress(27); + fee = payload.indexUint(47, 1) * FEE_FACTOR; + } + + function parseLockTime(bytes memory script) internal pure returns (uint32) { + uint256 t; + assembly { + let loc := add(script, 0x21) + t := mload(loc) + } + return uint32(t.reverseUint256() & 0xFFFFFFFF); + } + + function addExpire(BtcReceipt storage br) internal { + BtcExpireInfo storage expireInfo = round2expireInfoMap[br.endRound]; + if (expireInfo.agentExsitMap[br.agent] == 0) { + expireInfo.agentAddrList.push(br.agent); + expireInfo.agentExsitMap[br.agent] = 1; + } + expireInfo.agent2valueMap[br.agent] += br.value; + } + + function collectBtcReward(bytes32 txid, int256 claimLimit) internal returns (uint256, int256) { + uint256 curRound = roundTag; + BtcReceipt storage br = btcReceiptMap[txid]; + uint256 reward = 0; + Agent storage a = agentsMap[br.agent]; + uint256 rewardIndex = br.rewardIndex; + uint256 rewardLength = a.rewardSet.length; + while (rewardIndex < rewardLength && claimLimit != 0) { + Reward storage r = a.rewardSet[rewardIndex]; + uint256 rRound = r.round; + if (rRound == curRound || br.endRound <= rRound) { + break; + } + uint256 deposit = br.value * stateMap[rRound].btcFactor; + reward += collectCoinReward(r, deposit); + if (r.coin == 0) { + delete a.rewardSet[rewardIndex]; + } + rewardIndex += 1; + claimLimit -= 1; + } + + uint256 fee = br.fee; + uint256 feeReward; + if (fee != 0) { + if (fee <= reward) { + feeReward = fee; + } else { + feeReward = reward; + } + + if (feeReward != 0) { + br.fee -= feeReward; + bool success = br.feeReceiver.send(feeReward); + if (success) { + reward -= feeReward; + emit transferredBtcFee(txid, br.feeReceiver, feeReward); + } else { + emit failedTransferBtcFee(txid, br.feeReceiver, feeReward); + } + } + } + + if (br.endRound <= (rewardIndex == rewardLength ? curRound : a.rewardSet[rewardIndex].round)) { + delete btcReceiptMap[txid]; + } else { + br.rewardIndex = rewardIndex; + } + return (reward, claimLimit); + } + /*********************** Governance ********************************/ /// Update parameters through governance vote /// @param key The name of the parameter @@ -577,6 +880,30 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert OutOfBounds(key, newHashPowerFactor, 1, type(uint256).max); } powerFactor = newHashPowerFactor; + } else if (Memory.compareStrings(key, "btcFactor")) { + uint256 newBtcFactor = BytesToTypes.bytesToUint256(32, value); + if (newBtcFactor == 0) { + revert OutOfBounds(key, newBtcFactor, 1, type(uint256).max); + } + btcFactor = newBtcFactor; + } else if (Memory.compareStrings(key, "minBtcLockRound")) { + uint256 newMinBtcLockRound = BytesToTypes.bytesToUint256(32, value); + if (newMinBtcLockRound == 0) { + revert OutOfBounds(key, newMinBtcLockRound, 1, type(uint256).max); + } + minBtcLockRound = newMinBtcLockRound; + } else if (Memory.compareStrings(key, "btcConfirmBlock")) { + uint256 newBtcConfirmBlock = BytesToTypes.bytesToUint256(32, value); + if (newBtcConfirmBlock == 0) { + revert OutOfBounds(key, newBtcConfirmBlock, 1, type(uint256).max); + } + btcConfirmBlock = uint32(newBtcConfirmBlock); + } else if (Memory.compareStrings(key, "minBtcValue")) { + uint256 newMinBtcValue = BytesToTypes.bytesToUint256(32, value); + if (newMinBtcValue == 0) { + revert OutOfBounds(key, newMinBtcValue, 1e4, type(uint256).max); + } + minBtcValue = newMinBtcValue; } else { require(false, "unknown param"); } @@ -602,4 +929,13 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { require(index < a.rewardSet.length, "out of up bound"); return a.rewardSet[index]; } + + /// Get expire information of a validator by round and agent + /// @param round The end round of the btc lock + /// @param agent The operator address of validator + /// @return expireValue The expire value of the agent in the round + function getExpireValue(uint256 round, address agent) external view returns (uint256){ + BtcExpireInfo storage expireInfo = round2expireInfoMap[round]; + return expireInfo.agent2valueMap[agent]; + } } \ No newline at end of file diff --git a/contracts/PledgeAgent.template b/contracts/PledgeAgent.template index c5892f42..93ab5f5d 100644 --- a/contracts/PledgeAgent.template +++ b/contracts/PledgeAgent.template @@ -5,7 +5,9 @@ import "./interface/IPledgeAgent.sol"; import "./interface/IParamSubscriber.sol"; import "./interface/ICandidateHub.sol"; import "./interface/ISystemReward.sol"; +import "./interface/ILightClient.sol"; import "./lib/Address.sol"; +import "./lib/BitcoinHelper.sol"; import "./lib/BytesToTypes.sol"; import "./lib/Memory.sol"; import "./System.sol"; @@ -20,9 +22,22 @@ import "./System.sol"; /// which are eligible for claiming rewards in the acting round contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { + using BitcoinHelper for *; + using TypedMemView for *; + uint256 public constant INIT_REQUIRED_COIN_DEPOSIT = 1e18; uint256 public constant INIT_HASH_POWER_FACTOR = 20000; uint256 public constant POWER_BLOCK_FACTOR = {% if mock %}1{% else %}1e18{% endif %}; + uint32 public constant INIT_BTC_CONFIRM_BLOCK = 3; + uint256 public constant INIT_MIN_BTC_LOCK_ROUND = 7; + uint256 public constant ROUND_INTERVAL = {{initRoundInterval}}; + uint256 public constant INIT_MIN_BTC_VALUE = 1e6; + uint256 public constant INIT_BTC_FACTOR = 5e4; + uint256 public constant BTC_STAKE_MAGIC = 0x5341542b; + uint256 public constant CHAINID = {{chainid}}; + uint256 public constant FEE_FACTOR = 1e18; + int256 public constant CLAIM_ROUND_LIMIT = 500; + uint256 public constant BTC_UNIT_CONVERSION = 1e10; uint256 public requiredCoinDeposit; @@ -60,6 +75,43 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { // debtDepositMap keeps delegator's amount of CORE which should be deducted when claiming rewards in every round mapping(uint256 => mapping(address => uint256)) public debtDepositMap; + // HARDFORK V-1.0.7 + // btcReceiptMap keeps all BTC staking receipts on Core + mapping(bytes32 => BtcReceipt) public btcReceiptMap; + + // round2expireInfoMap keeps the amount of expired BTC staking value for each round + mapping(uint256 => BtcExpireInfo) round2expireInfoMap; + + // staking weight of each BTC vs. CORE + uint256 public btcFactor; + + // minimum rounds to stake for a BTC staking transaction + uint256 public minBtcLockRound; + + // the number of blocks to mark a BTC staking transaction as confirmed + uint32 public btcConfirmBlock; + + // minimum value to stake for a BTC staking transaction + uint256 public minBtcValue; + + // HARDFORK V-1.0.7 + struct BtcReceipt { + address agent; + address delegator; + uint256 value; + uint256 endRound; + uint256 rewardIndex; + address payable feeReceiver; + uint256 fee; + } + + // HARDFORK V-1.0.7 + struct BtcExpireInfo { + address[] agentAddrList; + mapping(address => uint256) agent2valueMap; + mapping(address => uint256) agentExsitMap; + } + struct CoinDelegator { uint256 deposit; uint256 newDeposit; @@ -87,17 +139,22 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { Reward[] rewardSet; uint256 power; uint256 coin; + uint256 btc; + uint256 totalBtc; } struct RoundState { uint256 power; uint256 coin; uint256 powerFactor; + uint256 btc; + uint256 btcFactor; } /*********************** events **************************/ event paramChange(string key, bytes value); event delegatedCoin(address indexed agent, address indexed delegator, uint256 amount, uint256 totalAmount); + event delegatedBtc(bytes32 indexed txid, address indexed agent, address indexed delegator, bytes script, uint32 blockHeight, uint256 outputIndex); event undelegatedCoin(address indexed agent, address indexed delegator, uint256 amount); event transferredCoin( address indexed sourceAgent, @@ -106,8 +163,19 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 amount, uint256 totalAmount ); - event roundReward(address indexed agent, uint256 coinReward, uint256 powerReward); + event transferredBtc( + bytes32 indexed txid, + address sourceAgent, + address targetAgent, + address delegator, + uint256 amount, + uint256 totalAmount + ); + event btcPledgeExpired(bytes32 indexed txid, address indexed delegator); + event roundReward(address indexed agent, uint256 coinReward, uint256 powerReward, uint256 btcReward); event claimedReward(address indexed delegator, address indexed operator, uint256 amount, bool success); + event transferredBtcFee(bytes32 indexed txid, address payable feeReceiver, uint256 fee); + event failedTransferBtcFee(bytes32 indexed txid, address payable feeReceiver, uint256 fee); /// The validator candidate is inactive, it is expected to be active /// @param candidate Address of the validator candidate @@ -121,7 +189,11 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { function init() external onlyNotInit { requiredCoinDeposit = INIT_REQUIRED_COIN_DEPOSIT; powerFactor = INIT_HASH_POWER_FACTOR; - roundTag = 1; + btcFactor = INIT_BTC_FACTOR; + minBtcLockRound = INIT_MIN_BTC_LOCK_ROUND; + btcConfirmBlock = INIT_BTC_CONFIRM_BLOCK; + minBtcValue = INIT_MIN_BTC_VALUE; + roundTag = block.timestamp / ROUND_INTERVAL; alreadyInit = true; } @@ -156,63 +228,90 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { r.remainReward = rewardList[i]; uint256 coinReward = rewardList[i] * a.coin * rs.power / roundScore; uint256 powerReward = rewardList[i] * a.power * rs.coin / 10000 * rs.powerFactor / roundScore; - emit roundReward(agentList[i], coinReward, powerReward); + uint256 btcReward = rewardList[i] * a.btc * rs.btcFactor * rs.power / roundScore; + emit roundReward(agentList[i], coinReward, powerReward, btcReward); } } /// Calculate hybrid score for all candidates /// @param candidates List of candidate operator addresses /// @param powers List of power value in this round + /// @param round The new round tag /// @return scores List of hybrid scores of all validator candidates in this round - /// @return totalPower Total power delegate in this round - /// @return totalCoin Total coin delegate in this round - function getHybridScore(address[] calldata candidates, uint256[] calldata powers - ) external override onlyCandidate - returns (uint256[] memory scores, uint256 totalPower, uint256 totalCoin) { + function getHybridScore( + address[] calldata candidates, + uint256[] calldata powers, + uint256 round + ) external override onlyCandidate returns (uint256[] memory scores) { uint256 candidateSize = candidates.length; require(candidateSize == powers.length, "the length of candidates and powers should be equal"); - totalPower = 1; - totalCoin = 1; + // HARDFORK V-1.0.7 + // the expired BTC staking values will be removed before calculating hybrid score for validators + for (uint256 r = roundTag + 1; r <= round; ++r) { + BtcExpireInfo storage expireInfo = round2expireInfoMap[r]; + uint256 j = expireInfo.agentAddrList.length; + while (j > 0) { + j--; + address agent = expireInfo.agentAddrList[j]; + agentsMap[agent].totalBtc -= expireInfo.agent2valueMap[agent]; + expireInfo.agentAddrList.pop(); + delete expireInfo.agent2valueMap[agent]; + delete expireInfo.agentExsitMap[agent]; + } + delete round2expireInfoMap[r]; + } + + uint256 totalPower = 1; + uint256 totalCoin = 1; + uint256 totalBtc; // setup `power` and `coin` values for every candidate + // cost gas approximate candidateSize*20000 for (uint256 i = 0; i < candidateSize; ++i) { Agent storage a = agentsMap[candidates[i]]; // in order to improve accuracy, the calculation of power is based on 10^18 a.power = powers[i] * POWER_BLOCK_FACTOR; + a.btc = a.totalBtc; a.coin = a.totalDeposit; totalPower += a.power; totalCoin += a.coin; + totalBtc += a.btc; } - + + uint256 bf = (btcFactor == 0 ? INIT_BTC_FACTOR : btcFactor) * BTC_UNIT_CONVERSION; + uint256 pf = powerFactor; + // calc hybrid score + // HARDFORK V-1.0.7 BTC staking is added when calculating the hybrid score scores = new uint256[](candidateSize); for (uint256 i = 0; i < candidateSize; ++i) { Agent storage a = agentsMap[candidates[i]]; - scores[i] = a.power * totalCoin * powerFactor / 10000 + a.coin * totalPower; + scores[i] = a.power * (totalCoin + totalBtc * bf) * pf / 10000 + (a.coin + a.btc * bf) * totalPower; } - return (scores, totalPower, totalCoin); + + RoundState storage rs = stateMap[round]; + rs.power = totalPower; + rs.coin = totalCoin; + rs.powerFactor = pf; + rs.btc = totalBtc; + rs.btcFactor = bf; } /// Start new round, this is called by the CandidateHub contract /// @param validators List of elected validators in this round - /// @param totalPower Total power delegate in this round - /// @param totalCoin Total coin delegate in this round /// @param round The new round tag - function setNewRound(address[] calldata validators, uint256 totalPower, - uint256 totalCoin, uint256 round) external override onlyCandidate { - RoundState memory rs; - rs.power = totalPower; - rs.coin = totalCoin; - rs.powerFactor = powerFactor; - stateMap[round] = rs; - - roundTag = round; + function setNewRound(address[] calldata validators, uint256 round) external override onlyCandidate { + RoundState storage rs = stateMap[round]; uint256 validatorSize = validators.length; for (uint256 i = 0; i < validatorSize; ++i) { Agent storage a = agentsMap[validators[i]]; - uint256 score = a.power * rs.coin * powerFactor / 10000 + a.coin * rs.power; - a.rewardSet.push(Reward(0, 0, score, a.coin, round)); + // HARDFORK V-1.0.7 BTC staking is added when calculating the hybrid score + uint256 btcScore = a.btc * rs.btcFactor; + uint256 score = a.power * (rs.coin + rs.btc * rs.btcFactor) * rs.powerFactor / 10000 + (a.coin + btcScore) * rs.power; + a.rewardSet.push(Reward(0, 0, score, a.coin + btcScore, round)); } + + roundTag = round; } /// Distribute rewards for delegated hash power on one validator candidate @@ -234,14 +333,15 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { return; } RoundState storage rs = stateMap[roundTag]; - uint256 reward = rs.coin * POWER_BLOCK_FACTOR * rs.powerFactor / 10000 * r.totalReward / r.score; + uint256 reward = (rs.coin + rs.btc * rs.btcFactor) * POWER_BLOCK_FACTOR * rs.powerFactor / 10000 * r.totalReward / r.score; uint256 minerSize = miners.length; uint256 powerReward = reward * minerSize; uint256 undelegateCoinReward; - if (a.coin > r.coin) { + uint256 btcScore = a.btc * rs.btcFactor; + if (a.coin + btcScore > r.coin) { // undelegatedCoin = a.coin - r.coin - undelegateCoinReward = r.totalReward * (a.coin - r.coin) * rs.power / r.score; + undelegateCoinReward = r.totalReward * (a.coin + btcScore - r.coin) * rs.power / r.score; } uint256 remainReward = r.remainReward; require(remainReward >= powerReward + undelegateCoinReward, "there is not enough reward"); @@ -328,7 +428,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @return (Amount claimed, Are all rewards claimed) function claimReward(address[] calldata agentList) external returns (uint256, bool) { // limit round count to control gas usage - int256 roundLimit = 500; + int256 roundLimit = CLAIM_ROUND_LIMIT; uint256 reward; uint256 rewardSum = rewardMap[msg.sender]; if (rewardSum != 0) { @@ -363,6 +463,130 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { } return (rewardSum, roundLimit >= 0); } + + // HARDFORK V-1.0.7 + // User workflow to delegate BTC to Core blockchain + // 1. A user creates a bitcoin transaction, locks up certain amount ot Bitcoin in one of the transaction output for certain period. + // The transaction should also have an op_return output which contains the staking information, such as the validator and reward addresses. + // 2. Transmit the transaction to Core blockchain by calling the below method `delegateBtc`. + // 3. The user can claim rewards using the reward address set in step 1 during the staking period. + // 4. The user can spend the timelocked UTXO using the redeem script when the lock expires. + // The redeem script should start with a time lock. such as: + // OP_CLTV OP_DROP OP_CHECKSIG + // OP_CLTV OP_DROP OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + // OP_CLTV OP_DROP M ... N OP_CHECKMULTISIG + /// delegate BTC to Core network + /// @param btcTx the BTC transaction data + /// @param blockHeight block height of the transaction + /// @param nodes part of the Merkle tree from the tx to the root in LE form (called Merkle proof) + /// @param index index of the tx in Merkle tree + /// @param script the corresponding redeem script of the locked up output + function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external { + require(script[0] == bytes1(uint8(0x04)) && script[5] == bytes1(uint8(0xb1)), "not a valid redeem script"); + + bytes32 txid = btcTx.calculateTxId(); + require(ILightClient(LIGHT_CLIENT_ADDR). + checkTxProof(txid, blockHeight, (btcConfirmBlock == 0 ? INIT_BTC_CONFIRM_BLOCK : btcConfirmBlock), nodes, index), "btc tx not confirmed"); + + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value == 0, "btc tx confirmed"); + + uint32 lockTime = parseLockTime(script); + br.endRound = lockTime / ROUND_INTERVAL; + require(br.endRound > roundTag + (minBtcLockRound == 0 ? INIT_MIN_BTC_LOCK_ROUND : minBtcLockRound), "insufficient lock round"); + + (,,bytes29 voutView,) = btcTx.extractTx(); + bytes29 payload; + uint256 outputIndex; + (br.value, payload, outputIndex) = voutView.parseToScriptValueAndData(script); + require(br.value > (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); + + uint256 fee; + (br.delegator, br.agent, fee) = parseAndCheckPayload(payload); + if (!ICandidateHub(CANDIDATE_HUB_ADDR).isCandidateByOperate(br.agent)) { + revert InactiveAgent(br.agent); + } + + emit delegatedBtc(txid, br.agent, br.delegator, script, blockHeight, outputIndex); + + if (fee != 0) { + br.fee = fee; + br.feeReceiver = payable(msg.sender); + } + + Agent storage a = agentsMap[br.agent]; + br.rewardIndex = a.rewardSet.length; + addExpire(br); + a.totalBtc += br.value; + } + + // HARDFORK V-1.0.7 + /// transfer staked BTC to a different validator + /// @param txid id of the BTC staking transaction + /// @param targetAgent the new validator address to stake to + function transferBtc(bytes32 txid, address targetAgent) public { + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value != 0, "btc tx not found"); + require(br.delegator == msg.sender, "not the delegator of this btc receipt"); + address agent = br.agent; + require(agent != targetAgent, "can not transfer to the same validator"); + require(br.endRound > roundTag + 1, "insufficient locking rounds"); + + if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(targetAgent)) { + revert InactiveAgent(targetAgent); + } + + (uint256 reward,) = collectBtcReward(txid, 0x7FFFFFFF); + + Agent storage a = agentsMap[agent]; + a.totalBtc -= br.value; + round2expireInfoMap[br.endRound].agent2valueMap[agent] -= br.value; + + Reward storage r = a.rewardSet[a.rewardSet.length - 1]; + if (r.round == roundTag && br.rewardIndex < a.rewardSet.length) { + r.coin -= br.value * stateMap[roundTag].btcFactor; + } + + Agent storage ta = agentsMap[targetAgent]; + br.agent = targetAgent; + br.rewardIndex = ta.rewardSet.length; + addExpire(br); + ta.totalBtc += br.value; + + if (reward != 0) { + distributeReward(payable(msg.sender), reward); + } + + emit transferredBtc(txid, agent, targetAgent, msg.sender, br.value, ta.totalBtc); + } + + // HARDFORK V-1.0.7 + /// claim BTC staking rewards + /// @param txidList the list of BTC staking transaction id to claim rewards + /// @return rewardSum amount of reward claimed + function claimBtcReward(bytes32[] calldata txidList) public returns (uint256 rewardSum) { + int256 claimLimit = CLAIM_ROUND_LIMIT; + uint256 len = txidList.length; + for(uint256 i = 0; i < len && claimLimit != 0; i++) { + bytes32 txid = txidList[i]; + BtcReceipt storage br = btcReceiptMap[txid]; + require(br.value != 0, "btc tx not found"); + address delegator = br.delegator; + require(delegator == msg.sender, "not the delegator of this btc receipt"); + + uint256 reward; + (reward, claimLimit) = collectBtcReward(txid, claimLimit); + rewardSum += reward; + if (br.value == 0) { + emit btcPledgeExpired(txid, delegator); + } + } + + if (rewardSum != 0) { + distributeReward(payable(msg.sender), rewardSum); + } + return rewardSum; + } /*********************** Internal methods ***************************/ function distributeReward(address payable delegator, uint256 reward) internal { @@ -557,6 +781,85 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { return rewardAmount; } + function parseAndCheckPayload(bytes29 payload) internal pure returns (address delegator, address agent, uint256 fee) { + require(payload.len() >= 48, "payload length is too small"); + require(payload.indexUint(0, 4) == BTC_STAKE_MAGIC, "wrong magic"); + require(payload.indexUint(4, 1) == 1, "wrong version"); + require(payload.indexUint(5, 2) == CHAINID, "wrong chain id"); + delegator= payload.indexAddress(7); + agent = payload.indexAddress(27); + fee = payload.indexUint(47, 1) * FEE_FACTOR; + } + + function parseLockTime(bytes memory script) internal pure returns (uint32) { + uint256 t; + assembly { + let loc := add(script, 0x21) + t := mload(loc) + } + return uint32(t.reverseUint256() & 0xFFFFFFFF); + } + + function addExpire(BtcReceipt storage br) internal { + BtcExpireInfo storage expireInfo = round2expireInfoMap[br.endRound]; + if (expireInfo.agentExsitMap[br.agent] == 0) { + expireInfo.agentAddrList.push(br.agent); + expireInfo.agentExsitMap[br.agent] = 1; + } + expireInfo.agent2valueMap[br.agent] += br.value; + } + + function collectBtcReward(bytes32 txid, int256 claimLimit) internal returns (uint256, int256) { + uint256 curRound = roundTag; + BtcReceipt storage br = btcReceiptMap[txid]; + uint256 reward = 0; + Agent storage a = agentsMap[br.agent]; + uint256 rewardIndex = br.rewardIndex; + uint256 rewardLength = a.rewardSet.length; + while (rewardIndex < rewardLength && claimLimit != 0) { + Reward storage r = a.rewardSet[rewardIndex]; + uint256 rRound = r.round; + if (rRound == curRound || br.endRound <= rRound) { + break; + } + uint256 deposit = br.value * stateMap[rRound].btcFactor; + reward += collectCoinReward(r, deposit); + if (r.coin == 0) { + delete a.rewardSet[rewardIndex]; + } + rewardIndex += 1; + claimLimit -= 1; + } + + uint256 fee = br.fee; + uint256 feeReward; + if (fee != 0) { + if (fee <= reward) { + feeReward = fee; + } else { + feeReward = reward; + } + + if (feeReward != 0) { + br.fee -= feeReward; + bool success = br.feeReceiver.send(feeReward); + if (success) { + reward -= feeReward; + emit transferredBtcFee(txid, br.feeReceiver, feeReward); + } else { + emit failedTransferBtcFee(txid, br.feeReceiver, feeReward); + } + } + } + + if (br.endRound <= (rewardIndex == rewardLength ? curRound : a.rewardSet[rewardIndex].round)) { + delete btcReceiptMap[txid]; + } else { + br.rewardIndex = rewardIndex; + } + return (reward, claimLimit); + } + /*********************** Governance ********************************/ /// Update parameters through governance vote /// @param key The name of the parameter @@ -577,6 +880,30 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert OutOfBounds(key, newHashPowerFactor, 1, type(uint256).max); } powerFactor = newHashPowerFactor; + } else if (Memory.compareStrings(key, "btcFactor")) { + uint256 newBtcFactor = BytesToTypes.bytesToUint256(32, value); + if (newBtcFactor == 0) { + revert OutOfBounds(key, newBtcFactor, 1, type(uint256).max); + } + btcFactor = newBtcFactor; + } else if (Memory.compareStrings(key, "minBtcLockRound")) { + uint256 newMinBtcLockRound = BytesToTypes.bytesToUint256(32, value); + if (newMinBtcLockRound == 0) { + revert OutOfBounds(key, newMinBtcLockRound, 1, type(uint256).max); + } + minBtcLockRound = newMinBtcLockRound; + } else if (Memory.compareStrings(key, "btcConfirmBlock")) { + uint256 newBtcConfirmBlock = BytesToTypes.bytesToUint256(32, value); + if (newBtcConfirmBlock == 0) { + revert OutOfBounds(key, newBtcConfirmBlock, 1, type(uint256).max); + } + btcConfirmBlock = uint32(newBtcConfirmBlock); + } else if (Memory.compareStrings(key, "minBtcValue")) { + uint256 newMinBtcValue = BytesToTypes.bytesToUint256(32, value); + if (newMinBtcValue == 0) { + revert OutOfBounds(key, newMinBtcValue, 1e4, type(uint256).max); + } + minBtcValue = newMinBtcValue; } else { require(false, "unknown param"); } @@ -602,4 +929,13 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { require(index < a.rewardSet.length, "out of up bound"); return a.rewardSet[index]; } + + /// Get expire information of a validator by round and agent + /// @param round The end round of the btc lock + /// @param agent The operator address of validator + /// @return expireValue The expire value of the agent in the round + function getExpireValue(uint256 round, address agent) external view returns (uint256){ + BtcExpireInfo storage expireInfo = round2expireInfoMap[round]; + return expireInfo.agent2valueMap[agent]; + } } \ No newline at end of file diff --git a/contracts/interface/ICandidateHub.sol b/contracts/interface/ICandidateHub.sol index 5e320cd3..c455e682 100644 --- a/contracts/interface/ICandidateHub.sol +++ b/contracts/interface/ICandidateHub.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.4; interface ICandidateHub { function canDelegate(address agent) external view returns(bool); + function isCandidateByOperate(address agent) external view returns(bool); function jailValidator(address operateAddress, uint256 round, uint256 fine) external; function getRoundTag() external view returns(uint256); } diff --git a/contracts/interface/ILightClient.sol b/contracts/interface/ILightClient.sol index 8907dc18..d3291ca4 100644 --- a/contracts/interface/ILightClient.sol +++ b/contracts/interface/ILightClient.sol @@ -7,4 +7,6 @@ interface ILightClient { function getRoundCandidates(uint256 roundTimeTag) external view returns (address[] memory candidates); function getRoundMiners(uint256 roundTimeTag, address candidate) external view returns (address[] memory miners); + + function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) external view returns (bool); } \ No newline at end of file diff --git a/contracts/interface/IPledgeAgent.sol b/contracts/interface/IPledgeAgent.sol index bc1dd82b..c421b331 100644 --- a/contracts/interface/IPledgeAgent.sol +++ b/contracts/interface/IPledgeAgent.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.4; interface IPledgeAgent { function addRoundReward(address[] calldata agentList, uint256[] calldata rewardList) payable external; - function getHybridScore(address[] calldata candidates, uint256[] calldata powers) external returns(uint256[] memory, uint256, uint256); - function setNewRound(address[] calldata validatorList, uint256 totalPower, uint256 totalCoin, uint256 round) external; + function getHybridScore(address[] calldata candidates, uint256[] calldata powers, uint256 round) external returns(uint256[] memory); + function setNewRound(address[] calldata validatorList, uint256 round) external; function distributePowerReward(address candidate, address[] calldata miners) external; function onFelony(address agent) external; } diff --git a/contracts/lib/BitcoinHelper.sol b/contracts/lib/BitcoinHelper.sol new file mode 100644 index 00000000..75b874e6 --- /dev/null +++ b/contracts/lib/BitcoinHelper.sol @@ -0,0 +1,904 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.4; + +import "./TypedMemView.sol"; +import "./SafeCast.sol"; + +enum ScriptTypes { + P2PK, // 32 bytes + P2PKH, // 20 bytes + P2SH, // 20 bytes + P2WPKH, // 20 bytes + P2WSH, // 32 bytes + P2TR // 32 bytes +} + +library BitcoinHelper { + + using SafeCast for uint96; + using SafeCast for uint256; + + using TypedMemView for bytes; + using TypedMemView for bytes29; + + // The target at minimum Difficulty. Also the target of the genesis block + uint256 internal constant DIFF1_TARGET = 0xffff0000000000000000000000000000000000000000000000000000; + + uint256 internal constant RETARGET_PERIOD = 2 * 7 * 24 * 60 * 60; // 2 weeks in seconds + uint256 internal constant RETARGET_PERIOD_BLOCKS = 2016; // 2 weeks in blocks + + enum BTCTypes { + Unknown, // 0x0 + CompactInt, // 0x1 + ScriptSig, // 0x2 - with length prefix + Outpoint, // 0x3 + TxIn, // 0x4 + IntermediateTxIns, // 0x5 - used in vin parsing + Vin, // 0x6 + ScriptPubkey, // 0x7 - with length prefix + PKH, // 0x8 - the 20-byte payload digest + WPKH, // 0x9 - the 20-byte payload digest + WSH, // 0xa - the 32-byte payload digest + SH, // 0xb - the 20-byte payload digest + OpReturnPayload, // 0xc + TxOut, // 0xd + IntermediateTxOuts, // 0xe - used in vout parsing + Vout, // 0xf + Header, // 0x10 + HeaderArray, // 0x11 + MerkleNode, // 0x12 + MerkleStep, // 0x13 + MerkleArray // 0x14 + } + + /// @notice requires `memView` to be of a specified type + /// @dev passes if it is the correct type, errors if not + /// @param memView a 29-byte view with a 5-byte type + /// @param t the expected type (e.g. BTCTypes.Outpoint, BTCTypes.TxIn, etc) + modifier typeAssert(bytes29 memView, BTCTypes t) { + memView.assertType(uint40(t)); + _; + } + + // Revert with an error message re: non-minimal VarInts + function revertNonMinimal(bytes29 ref) private pure returns (string memory) { + (, uint256 g) = TypedMemView.encodeHex(ref.indexUint(0, ref.len().toUint8())); + string memory err = string( + abi.encodePacked( + "Non-minimal var int. Got 0x", + uint144(g) + ) + ); + revert(err); + } + + /// @notice reads a compact int from the view at the specified index + /// @param memView a 29-byte view with a 5-byte type + /// @param _index the index + /// @return number returns the compact int at the specified index + function indexCompactInt(bytes29 memView, uint256 _index) internal pure returns (uint64 number) { + uint256 flag = memView.indexUint(_index, 1); + if (flag <= 0xfc) { + return flag.toUint64(); + } else if (flag == 0xfd) { + number = memView.indexLEUint(_index + 1, 2).toUint64(); + if (compactIntLength(number) != 3) {revertNonMinimal(memView.slice(_index, 3, 0));} + } else if (flag == 0xfe) { + number = memView.indexLEUint(_index + 1, 4).toUint64(); + if (compactIntLength(number) != 5) {revertNonMinimal(memView.slice(_index, 5, 0));} + } else if (flag == 0xff) { + number = memView.indexLEUint(_index + 1, 8).toUint64(); + if (compactIntLength(number) != 9) {revertNonMinimal(memView.slice(_index, 9, 0));} + } + } + + /// @notice gives the total length (in bytes) of a CompactInt-encoded number + /// @param number the number as uint64 + /// @return the compact integer length as uint8 + function compactIntLength(uint64 number) private pure returns (uint8) { + if (number <= 0xfc) { + return 1; + } else if (number <= 0xffff) { + return 3; + } else if (number <= 0xffffffff) { + return 5; + } else { + return 9; + } + } + + /// @notice extracts the LE txid from an outpoint + /// @param _outpoint the outpoint + /// @return the LE txid + function txidLE(bytes29 _outpoint) internal pure typeAssert(_outpoint, BTCTypes.Outpoint) returns (bytes32) { + return _outpoint.index(0, 32); + } + + /// @notice Calculates the required transaction Id from the transaction details + /// @dev Calculates the hash of transaction details two consecutive times + /// @param _tx The Bitcoin transaction + /// @return Transaction Id of the transaction (in LE form) + function calculateTxId(bytes memory _tx) internal pure returns (bytes32) { + bytes32 inputHash1 = sha256(_tx); + bytes32 inputHash2 = sha256(abi.encodePacked(inputHash1)); + return inputHash2; + } + + /// @notice Reverts a Bytes32 input + /// @param _input Bytes32 input that we want to revert + /// @return Reverted bytes32 + function reverseBytes32(bytes32 _input) private pure returns (bytes32) { + bytes memory temp; + bytes32 result; + for (uint i = 0; i < 32; i++) { + temp = abi.encodePacked(temp, _input[31-i]); + } + assembly { + result := mload(add(temp, 32)) + } + return result; + } + + /// @notice Parses outpoint info from an input + /// @dev Reverts if vin is null + /// @param _vin The vin of a Bitcoin transaction + /// @param _index Index of the input that we are looking at + /// @return _txId Output tx id + /// @return _outputIndex Output tx index + function extractOutpoint( + bytes memory _vin, + uint _index + ) internal pure returns (bytes32, uint) { + bytes29 vin = tryAsVin(_vin.ref(uint40(BTCTypes.Unknown))); + require(!vin.isNull(), "BitcoinHelper: vin is null"); + return extractOutpoint(vin, _index); + } + + /// @notice Parses outpoint info from an input + /// @dev Reverts if vin is null + /// @param _vinView The vin of a Bitcoin transaction + /// @param _index Index of the input that we are looking at + /// @return _txId Output tx id + /// @return _outputIndex Output tx index + function extractOutpoint( + bytes29 _vinView, + uint _index + ) internal pure typeAssert(_vinView, BTCTypes.Vin) returns (bytes32 _txId, uint _outputIndex) { + bytes29 input = indexVin(_vinView, _index); + bytes29 _outpoint = outpoint(input); + _txId = txidLE(_outpoint); + _outputIndex = outpointIdx(_outpoint); + } + + /// @notice extracts the index as an integer from the outpoint + /// @param _outpoint the outpoint + /// @return the index + function outpointIdx(bytes29 _outpoint) internal pure typeAssert(_outpoint, BTCTypes.Outpoint) returns (uint32) { + return _outpoint.indexLEUint(32, 4).toUint32(); + } + + /// @notice extracts the outpoint from an input + /// @param _input the input + /// @return the outpoint as a typed memory + function outpoint(bytes29 _input) internal pure typeAssert(_input, BTCTypes.TxIn) returns (bytes29) { + return _input.slice(0, 36, uint40(BTCTypes.Outpoint)); + } + + /// @notice extracts the script sig from an input + /// @param _input the input + /// @return the script sig as a typed memory + function scriptSig(bytes29 _input) internal pure typeAssert(_input, BTCTypes.TxIn) returns (bytes29) { + uint64 scriptLength = indexCompactInt(_input, 36); + return _input.slice(36, compactIntLength(scriptLength) + scriptLength, uint40(BTCTypes.ScriptSig)); + } + + /// @notice determines the length of the first input in an array of inputs + /// @param _inputs the vin without its length prefix + /// @return the input length + function inputLength(bytes29 _inputs) private pure typeAssert(_inputs, BTCTypes.IntermediateTxIns) returns (uint256) { + uint64 scriptLength = indexCompactInt(_inputs, 36); + return uint256(compactIntLength(scriptLength)) + uint256(scriptLength) + 36 + 4; + } + + /// @notice extracts the input at a specified index + /// @param _vin the vin + /// @param _index the index of the desired input + /// @return the desired input + function indexVin(bytes29 _vin, uint256 _index) internal pure typeAssert(_vin, BTCTypes.Vin) returns (bytes29) { + uint256 _nIns = uint256(indexCompactInt(_vin, 0)); + uint256 _viewLen = _vin.len(); + require(_index < _nIns, "Vin read overrun"); + + uint256 _offset = uint256(compactIntLength(uint64(_nIns))); + bytes29 _remaining; + for (uint256 _i = 0; _i < _index; _i += 1) { + _remaining = _vin.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns)); + _offset += inputLength(_remaining); + } + + _remaining = _vin.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns)); + uint256 _len = inputLength(_remaining); + return _vin.slice(_offset, _len, uint40(BTCTypes.TxIn)); + } + + /// @notice extracts the value from an output + /// @param _output the output + /// @return the value + function value(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (uint64) { + return _output.indexLEUint(0, 8).toUint64(); + } + + /// @notice Finds the value of a specific output + /// @dev Reverts if vout is null + /// @param _vout The vout of a Bitcoin transaction + /// @param _index Index of output + /// @return _value Value of the specified output + function parseOutputValue(bytes memory _vout, uint _index) internal pure returns (uint64) { + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return parseOutputValue(voutView, _index); + } + + /// @notice Finds the value of a specific output + /// @dev Reverts if vout is null + /// @param _voutView The vout of a Bitcoin transaction + /// @param _index Index of output + /// @return _value Value of the specified output + function parseOutputValue(bytes29 _voutView, uint _index) internal pure typeAssert(_voutView, BTCTypes.Vout) returns (uint64 _value) { + bytes29 output; + output = indexVout(_voutView, _index); + _value = value(output); + } + + /// @notice Finds total outputs value + /// @dev Reverts if vout is null + /// @param _vout The vout of a Bitcoin transaction + /// @return _totalValue Total vout value + function parseOutputsTotalValue(bytes memory _vout) internal pure returns (uint64) { + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return parseOutputsTotalValue(_vout); + } + + /// @notice Finds total outputs value + /// @dev Reverts if vout is null + /// @param _voutView The vout of a Bitcoin transaction + /// @return _totalValue Total vout value + function parseOutputsTotalValue(bytes29 _voutView) internal pure typeAssert(_voutView, BTCTypes.Vout) returns (uint64 _totalValue) { + bytes29 output; + // Finds total number of outputs + uint _numberOfOutputs = uint256(indexCompactInt(_voutView, 0)); + for (uint index = 0; index < _numberOfOutputs; index++) { + output = indexVout(_voutView, index); + _totalValue = _totalValue + value(output); + } + } + + /// @notice Parses the BTC amount that has been sent to + /// a specific script in a specific output + /// @param _vout The vout of a Bitcoin transaction + /// @param _voutIndex Index of the output that we are looking at + /// @param _script Desired recipient script + /// @param _scriptType Type of the script (e.g. P2PK) + /// @return bitcoinAmount Amount of BTC have been sent to the _script + function parseValueFromSpecificOutputHavingScript( + bytes memory _vout, + uint _voutIndex, + bytes memory _script, + ScriptTypes _scriptType + ) internal pure returns (uint64) { + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return parseValueFromSpecificOutputHavingScript(voutView, _voutIndex, _script, _scriptType); + } + + /// @notice Parses the BTC amount that has been sent to + /// a specific script in a specific output + /// @param _voutView The vout of a Bitcoin transaction + /// @param _voutIndex Index of the output that we are looking at + /// @param _script Desired recipient script + /// @param _scriptType Type of the script (e.g. P2PK) + /// @return bitcoinAmount Amount of BTC have been sent to the _script + function parseValueFromSpecificOutputHavingScript( + bytes29 _voutView, + uint _voutIndex, + bytes memory _script, + ScriptTypes _scriptType + ) internal pure typeAssert(_voutView, BTCTypes.Vout) returns (uint64 bitcoinAmount) { + bytes29 output = indexVout(_voutView, _voutIndex); + bytes29 _scriptPubkey = scriptPubkey(output); + + if (_scriptType == ScriptTypes.P2TR) { + // note: first two bytes are OP_1 and Pushdata Bytelength. + // note: script hash length is 32. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.index(2, 32))) ? value(output) : 0; + } else if (_scriptType == ScriptTypes.P2PK) { + // note: first byte is Pushdata Bytelength. + // note: public key length is 32. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.index(1, 32))) ? value(output) : 0; + } else if (_scriptType == ScriptTypes.P2PKH) { + // note: first three bytes are OP_DUP, OP_HASH160, Pushdata Bytelength. + // note: public key hash length is 20. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(3))) ? value(output) : 0; + } else if (_scriptType == ScriptTypes.P2SH) { + // note: first two bytes are OP_HASH160, Pushdata Bytelength + // note: script hash length is 20. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(2))) ? value(output) : 0; + } else if (_scriptType == ScriptTypes.P2WPKH) { + // note: first two bytes are OP_0, Pushdata Bytelength + // note: segwit public key hash length is 20. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(2))) ? value(output) : 0; + } else if (_scriptType == ScriptTypes.P2WSH) { + // note: first two bytes are OP_0, Pushdata Bytelength + // note: segwit script hash length is 32. + bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.index(2, 32))) ? value(output) : 0; + } + + } + + /// @notice Parses the BTC amount of a transaction + /// @dev Finds the BTC amount that has been sent to the locking script + /// Returns zero if no matching locking scrip is found + /// @param _vout The vout of a Bitcoin transaction + /// @param _lockingScript Desired locking script + /// @return bitcoinAmount Amount of BTC have been sent to the _lockingScript + function parseValueHavingLockingScript( + bytes memory _vout, + bytes memory _lockingScript + ) internal view returns (uint64) { + // Checks that vout is not null + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return parseValueHavingLockingScript(voutView, _lockingScript); + } + + /// @notice Parses the BTC amount of a transaction + /// @dev Finds the BTC amount that has been sent to the locking script + /// Returns zero if no matching locking scrip is found + /// @param _voutView The vout of a Bitcoin transaction + /// @param _lockingScript Desired locking script + /// @return bitcoinAmount Amount of BTC have been sent to the _lockingScript + function parseValueHavingLockingScript( + bytes29 _voutView, + bytes memory _lockingScript + ) internal view returns (uint64 bitcoinAmount) { + bytes29 output; + bytes29 _scriptPubkey; + + // Finds total number of outputs + uint _numberOfOutputs = uint256(indexCompactInt(_voutView, 0)); + + for (uint index = 0; index < _numberOfOutputs; index++) { + output = indexVout(_voutView, index); + _scriptPubkey = scriptPubkey(output); + + if ( + keccak256(abi.encodePacked(_scriptPubkey.clone())) == keccak256(abi.encodePacked(_lockingScript)) + ) { + bitcoinAmount = value(output); + // Stops searching after finding the desired locking script + break; + } + } + } + + /// @notice Parses the BTC amount and the op_return of a transaction + /// @dev Finds the BTC amount that has been sent to the locking script + /// Assumes that payload size is less than 76 bytes + /// @param _vout The vout of a Bitcoin transaction + /// @param _lockingScript Desired locking script + /// @return bitcoinAmount Amount of BTC have been sent to the _lockingScript + /// @return arbitraryData Opreturn data of the transaction + function parseValueAndDataHavingLockingScript( + bytes memory _vout, + bytes memory _lockingScript + ) internal view returns (uint64, bytes memory) { + // Checks that vout is not null + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return parseValueAndDataHavingLockingScript(voutView, _lockingScript); + } + + /// @notice Parses the BTC amount and the op_return of a transaction + /// @dev Finds the BTC amount that has been sent to the locking script + /// Assumes that payload size is less than 80 bytes + /// @param _voutView The vout of a Bitcoin transaction + /// @param _lockingScript Desired locking script + /// @return bitcoinAmount Amount of BTC have been sent to the _lockingScript + /// @return arbitraryData Opreturn data of the transaction + function parseValueAndDataHavingLockingScript( + bytes29 _voutView, + bytes memory _lockingScript + ) internal view typeAssert(_voutView, BTCTypes.Vout) returns (uint64 bitcoinAmount, bytes memory arbitraryData) { + bytes29 output; + bytes29 _scriptPubkey; + bytes29 _scriptPubkeyWithLength; + bytes29 _arbitraryData; + + // Finds total number of outputs + uint _numberOfOutputs = uint256(indexCompactInt(_voutView, 0)); + + for (uint index = 0; index < _numberOfOutputs; index++) { + output = indexVout(_voutView, index); + _scriptPubkey = scriptPubkey(output); + _scriptPubkeyWithLength = scriptPubkeyWithLength(output); + _arbitraryData = opReturnPayload(_scriptPubkeyWithLength); + + // Checks whether the output is an arbitarary data or not + if(_arbitraryData == TypedMemView.NULL) { + // Output is not an arbitrary data + if ( + keccak256(abi.encodePacked(_scriptPubkey.clone())) == keccak256(abi.encodePacked(_lockingScript)) + ) { + bitcoinAmount = value(output); + } + } else { + // Returns the whole bytes array + arbitraryData = _arbitraryData.clone(); + } + } + } + + /// @notice Parses the BTC amount and the op_return of a transaction + /// @dev Finds the BTC amount that payload size is less than 80 bytes + /// @param _voutView The vout of a Bitcoin transaction + /// @return bitcoinAmount Amount of BTC + /// @return arbitraryData Opreturn data of the transaction + function parseToScriptValueAndData( + bytes29 _voutView, + bytes memory _script + ) internal pure typeAssert(_voutView, BTCTypes.Vout) returns (uint64 bitcoinAmount, bytes29 arbitraryData, uint256 outputIndex) { + bytes29 _outputView; + bytes29 _scriptPubkeyView; + bytes29 _scriptPubkeyWithLength; + bytes29 _arbitraryData; + + // Finds total number of outputs + uint _numberOfOutputs = uint256(indexCompactInt(_voutView, 0)); + + for (uint index = 0; index < _numberOfOutputs; index++) { + _outputView = indexVout(_voutView, index); + _scriptPubkeyView = scriptPubkey(_outputView); + _scriptPubkeyWithLength = scriptPubkeyWithLength(_outputView); + _arbitraryData = opReturnPayload(_scriptPubkeyWithLength); + + // Checks whether the output is an arbitarary data or not + if(_arbitraryData == TypedMemView.NULL) { + // Output is not an arbitrary data + if ( + (_scriptPubkeyView.len() == 23 && + _scriptPubkeyView.indexUint(0, 1) == 0xa9 && + _scriptPubkeyView.indexUint(1, 1) == 0x14 && + _scriptPubkeyView.indexUint(22, 1) == 0x87 && + bytes20(_scriptPubkeyView.indexAddress(2)) == ripemd160(abi.encode(sha256(_script)))) || + (_scriptPubkeyView.len() == 34 && + _scriptPubkeyView.indexUint(0, 1) == 0 && + _scriptPubkeyView.indexUint(1, 1) == 32 && + _scriptPubkeyView.index(2, 32) == sha256(_script)) + ) { + bitcoinAmount = value(_outputView); + outputIndex = index; + } + } else { + // Returns the whole bytes array + arbitraryData = _arbitraryData; + } + } + } + + /// @notice extracts the scriptPubkey from an output + /// @param _output the output + /// @return the scriptPubkey + function scriptPubkey(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (bytes29) { + uint64 scriptLength = indexCompactInt(_output, 8); + return _output.slice(8 + compactIntLength(scriptLength), scriptLength, uint40(BTCTypes.ScriptPubkey)); + } + + /// @notice extracts the scriptPubkey from an output + /// @param _output the output + /// @return the scriptPubkey + function scriptPubkeyWithLength(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (bytes29) { + uint64 scriptLength = indexCompactInt(_output, 8); + return _output.slice(8, compactIntLength(scriptLength) + scriptLength, uint40(BTCTypes.ScriptPubkey)); + } + + /// @notice Parses locking script from an output + /// @dev Reverts if vout is null + /// @param _vout The vout of a Bitcoin transaction + /// @param _index Index of the output that we are looking at + /// @return _lockingScript Parsed locking script + function getLockingScript( + bytes memory _vout, + uint _index + ) internal view returns (bytes memory) { + bytes29 vout = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!vout.isNull(), "BitcoinHelper: vout is null"); + return getLockingScript(vout, _index); + } + + /// @notice Parses locking script from an output + /// @dev Reverts if vout is null + /// @param _voutView The vout of a Bitcoin transaction + /// @param _index Index of the output that we are looking at + /// @return _lockingScript Parsed locking script + function getLockingScript( + bytes29 _voutView, + uint _index + ) internal view returns (bytes memory _lockingScript) { + bytes29 output = indexVout(_voutView, _index); + bytes29 _lockingScriptBytes29 = scriptPubkey(output); + _lockingScript = _lockingScriptBytes29.clone(); + } + + /// @notice Returns number of outputs in a vout + /// @param _vout The vout of a Bitcoin transaction + function numberOfOutputs(bytes memory _vout) internal pure returns (uint) { + bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown))); + require(!voutView.isNull(), "BitcoinHelper: vout is null"); + return numberOfOutputs(voutView); + } + + /// @notice Returns number of outputs in a vout + /// @param _voutView The vout of a Bitcoin transaction + function numberOfOutputs(bytes29 _voutView) internal pure typeAssert(_voutView, BTCTypes.Vout) returns (uint _numberOfOutputs) { + _numberOfOutputs = uint256(indexCompactInt(_voutView, 0)); + } + + /// @notice determines the length of the first output in an array of outputs + /// @param _outputs the vout without its length prefix + /// @return the output length + function outputLength(bytes29 _outputs) private pure typeAssert(_outputs, BTCTypes.IntermediateTxOuts) returns (uint256) { + uint64 scriptLength = indexCompactInt(_outputs, 8); + return uint256(compactIntLength(scriptLength)) + uint256(scriptLength) + 8; + } + + /// @notice extracts the output at a specified index + /// @param _vout the vout + /// @param _index the index of the desired output + /// @return the desired output + function indexVout(bytes29 _vout, uint256 _index) internal pure typeAssert(_vout, BTCTypes.Vout) returns (bytes29) { + uint256 _nOuts = uint256(indexCompactInt(_vout, 0)); + uint256 _viewLen = _vout.len(); + require(_index < _nOuts, "Vout read overrun"); + + uint256 _offset = uint256(compactIntLength(uint64(_nOuts))); + bytes29 _remaining; + for (uint256 _i = 0; _i < _index; _i += 1) { + _remaining = _vout.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts)); + _offset += outputLength(_remaining); + } + + _remaining = _vout.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts)); + uint256 _len = outputLength(_remaining); + return _vout.slice(_offset, _len, uint40(BTCTypes.TxOut)); + } + + /// @notice extracts the Op Return Payload + /// @dev structure of the input is: 1 byte op return + 2 bytes indicating the length of payload + max length for op return payload is 80 bytes + /// @param _skp the scriptPubkey + /// @return the Op Return Payload (or null if not a valid Op Return output) + function opReturnPayload(bytes29 _skp) internal pure typeAssert(_skp, BTCTypes.ScriptPubkey) returns (bytes29) { + uint64 _bodyLength = indexCompactInt(_skp, 0); + if (_skp.indexUint(1, 1) == 0x6a) { + if (_skp.indexUint(2, 1) == 0x4c) { + uint64 _payloadLen = _skp.indexUint(3, 1).toUint64(); + require(_payloadLen == _bodyLength - 3 && + _bodyLength <= 83 && _bodyLength >= 79, "BitcoinHelper: invalid opreturn"); + return _skp.slice(4, _payloadLen, uint40(BTCTypes.OpReturnPayload)); + } else { + uint64 _payloadLen = _skp.indexUint(2, 1).toUint64(); + require(_payloadLen == _bodyLength - 2 && + _bodyLength <= 77 && _bodyLength >= 4, "BitcoinHelper: invalid opreturn"); + return _skp.slice(3, _payloadLen, uint40(BTCTypes.OpReturnPayload)); + } + } + return TypedMemView.nullView(); + } + + /// @notice verifies the vin and converts to a typed memory + /// @dev will return null in error cases + /// @param _vin the vin + /// @return the typed vin (or null if error) + function tryAsVin(bytes29 _vin) internal pure typeAssert(_vin, BTCTypes.Unknown) returns (bytes29) { + if (getVinLength(_vin) != _vin.len()) { + return TypedMemView.nullView(); + } + return _vin.castTo(uint40(BTCTypes.Vin)); + } + + + /// @notice verifies the vout and converts to a typed memory + /// @dev will return null in error cases + /// @param _vout the vout + /// @return the typed vout (or null if error) + function tryAsVout(bytes29 _vout) internal pure typeAssert(_vout, BTCTypes.Unknown) returns (bytes29) { + if (getVoutLength(_vout) != _vout.len()) { + return TypedMemView.nullView(); + } + return _vout.castTo(uint40(BTCTypes.Vout)); + } + + /// @notice verifies the header and converts to a typed memory + /// @dev will return null in error cases + /// @param _header the header + /// @return the typed header (or null if error) + function tryAsHeader(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Unknown) returns (bytes29) { + if (_header.len() != 80) { + return TypedMemView.nullView(); + } + return _header.castTo(uint40(BTCTypes.Header)); + } + + + /// @notice Index a header array. + /// @dev Errors on overruns + /// @param _arr The header array + /// @param index The 0-indexed location of the header to get + /// @return the typed header at `index` + function indexHeaderArray(bytes29 _arr, uint256 index) internal pure typeAssert(_arr, BTCTypes.HeaderArray) returns (bytes29) { + uint256 _start = index * 80; + return _arr.slice(_start, 80, uint40(BTCTypes.Header)); + } + + + /// @notice verifies the header array and converts to a typed memory + /// @dev will return null in error cases + /// @param _arr the header array + /// @return the typed header array (or null if error) + function tryAsHeaderArray(bytes29 _arr) internal pure typeAssert(_arr, BTCTypes.Unknown) returns (bytes29) { + if (_arr.len() % 80 != 0) { + return TypedMemView.nullView(); + } + return _arr.castTo(uint40(BTCTypes.HeaderArray)); + } + + /// @notice verifies the merkle array and converts to a typed memory + /// @dev will return null in error cases + /// @param _arr the merkle array + /// @return the typed merkle array (or null if error) + function tryAsMerkleArray(bytes29 _arr) internal pure typeAssert(_arr, BTCTypes.Unknown) returns (bytes29) { + if (_arr.len() % 32 != 0) { + return TypedMemView.nullView(); + } + return _arr.castTo(uint40(BTCTypes.MerkleArray)); + } + + /// @notice extracts the merkle root from the header + /// @param _header the header + /// @return the merkle root + function merkleRoot(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (bytes32) { + return _header.index(36, 32); + } + + /// @notice extracts the target from the header + /// @param _header the header + /// @return the target + function target(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint256) { + uint256 _mantissa = _header.indexLEUint(72, 3); + require(_header.indexUint(75, 1) > 2, "ViewBTC: invalid target difficulty"); + uint256 _exponent = _header.indexUint(75, 1) - 3; + return _mantissa * (256 ** _exponent); + } + + /// @notice calculates the difficulty from a target + /// @param _target the target + /// @return the difficulty + function toDiff(uint256 _target) private pure returns (uint256) { + return DIFF1_TARGET / (_target); + } + + /// @notice extracts the difficulty from the header + /// @param _header the header + /// @return the difficulty + function diff(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint256) { + return toDiff(target(_header)); + } + + /// @notice extracts the timestamp from the header + /// @param _header the header + /// @return the timestamp + function time(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint32) { + return uint32(_header.indexLEUint(68, 4)); + } + + /// @notice extracts the parent hash from the header + /// @param _header the header + /// @return the parent hash + function parent(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (bytes32) { + return _header.index(4, 32); + } + + /// @notice Checks validity of header chain + /// @dev Compares current header parent to previous header's digest + /// @param _header The raw bytes header + /// @param _prevHeaderDigest The previous header's digest + /// @return true if the connect is valid, false otherwise + function checkParent(bytes29 _header, bytes32 _prevHeaderDigest) internal pure typeAssert(_header, BTCTypes.Header) returns (bool) { + return parent(_header) == _prevHeaderDigest; + } + + /// @notice Validates a tx inclusion in the block + /// @dev `index` is not a reliable indicator of location within a block + /// @param _txid The txid (LE) + /// @param _merkleRoot The merkle root + /// @param _intermediateNodes The proof's intermediate nodes (digests between leaf and root) + /// @param _index The leaf's index in the tree (0-indexed) + /// @return true if fully valid, false otherwise + function prove( + bytes32 _txid, + bytes32 _merkleRoot, + bytes29 _intermediateNodes, + uint _index + ) internal view typeAssert(_intermediateNodes, BTCTypes.MerkleArray) returns (bool) { + // Shortcut the empty-block case + if ( + _txid == _merkleRoot && + _index == 0 && + _intermediateNodes.len() == 0 + ) { + return true; + } + + return checkMerkle(_txid, _intermediateNodes, _merkleRoot, _index); + } + + /// @notice verifies a merkle proof + /// @dev leaf, proof, and root are in LE format + /// @param _leaf the leaf + /// @param _proof the proof nodes + /// @param _root the merkle root + /// @param _index the index + /// @return true if valid, false if otherwise + function checkMerkle( + bytes32 _leaf, + bytes29 _proof, + bytes32 _root, + uint256 _index + ) private view typeAssert(_proof, BTCTypes.MerkleArray) returns (bool) { + require(_root != bytes32(0), "BitcoinHelper: zero root"); + + uint256 nodes = _proof.len() / 32; + if (nodes == 0) { + return _leaf == _root; + } + + uint256 _idx = _index; + bytes32 _current = _leaf; + + for (uint i = 0; i < nodes; i++) { + bytes32 _next = _proof.index(i * 32, 32); + if (_idx % 2 == 1) { + _current = merkleStep(_next, _current); + } else { + _current = merkleStep(_current, _next); + } + _idx >>= 1; + } + + return _current == _root; + } + + /// @notice Concatenates and hashes two inputs for merkle proving + /// @dev Not recommended to call directly. + /// @param _a The first hash + /// @param _b The second hash + /// @return digest The double-sha256 of the concatenated hashes + function merkleStep(bytes32 _a, bytes32 _b) private view returns (bytes32 digest) { + assembly { + // solium-disable-previous-line security/no-inline-assembly + let ptr := mload(0x40) + mstore(ptr, _a) + mstore(add(ptr, 0x20), _b) + pop(staticcall(gas(), 2, ptr, 0x40, ptr, 0x20)) // sha256 #1 + pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha256 #2 + digest := mload(ptr) + } + } + + /// @notice performs the bitcoin difficulty retarget + /// @dev implements the Bitcoin algorithm precisely + /// @param _previousTarget the target of the previous period + /// @param _firstTimestamp the timestamp of the first block in the difficulty period + /// @param _secondTimestamp the timestamp of the last block in the difficulty period + /// @return the new period's target threshold + function retargetAlgorithm( + uint256 _previousTarget, + uint256 _firstTimestamp, + uint256 _secondTimestamp + ) internal pure returns (uint256) { + uint256 _elapsedTime = _secondTimestamp - _firstTimestamp; + + // Normalize ratio to factor of 4 if very long or very short + if (_elapsedTime < RETARGET_PERIOD / 4) { + _elapsedTime = RETARGET_PERIOD / 4; + } + if (_elapsedTime > RETARGET_PERIOD * 4) { + _elapsedTime = RETARGET_PERIOD * 4; + } + + /* + NB: high targets e.g. ffff0020 can cause overflows here + so we divide it by 256**2, then multiply by 256**2 later + we know the target is evenly divisible by 256**2, so this isn't an issue + */ + uint256 _adjusted = _previousTarget / 65536 * _elapsedTime; + return _adjusted / RETARGET_PERIOD * 65536; + } + + /// @notice returns size of vin + /// @param _vinView the vin + /// @return the size of vin + function getVinLength(bytes29 _vinView) internal pure returns (uint256) { + if (_vinView.len() == 0) { + return 0; + } + uint64 _nIns = indexCompactInt(_vinView, 0); + uint256 _viewLen = _vinView.len(); + if (_nIns == 0) { + return 0; + } + + uint256 _offset = uint256(compactIntLength(_nIns)); + for (uint256 i = 0; i < _nIns; i++) { + if (_offset >= _viewLen) { + // We've reached the end, but are still trying to read more + return 0; + } + bytes29 _remaining = _vinView.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns)); + _offset += inputLength(_remaining); + } + return _offset; + } + + /// @notice returns size of vout + /// @param _voutView the vout + /// @return the size of vout + function getVoutLength(bytes29 _voutView) internal pure returns (uint256) { + if (_voutView.len() == 0) { + return 0; + } + uint64 _nOuts = indexCompactInt(_voutView, 0); + + uint256 _viewLen = _voutView.len(); + if (_nOuts == 0) { + return 0; + } + + uint256 _offset = uint256(compactIntLength(_nOuts)); + for (uint256 i = 0; i < _nOuts; i++) { + if (_offset >= _viewLen) { + // We've reached the end, but are still trying to read more + return 0; + } + bytes29 _remaining = _voutView.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts)); + _offset += outputLength(_remaining); + } + return _offset; + } + + /// @notice extracts tx details from the given tx bytes + /// @param _tx the transaction bytes + /// @return _version parsed tx version + /// @return _vinView parsed tx vin + /// @return _voutView parsed tx vout + /// @return _lockTime parsed tx lock time + function extractTx(bytes memory _tx) internal pure returns (uint32 _version, bytes29 _vinView, bytes29 _voutView, uint32 _lockTime) { + bytes29 _txView = _tx.ref(uint40(BTCTypes.Unknown)); + + _version = _txView.indexLEUint(0, 4).toUint32(); + uint256 _offset = 4; + + bytes29 _remaining = _txView.postfix(_txView.len() - _offset, uint40(BTCTypes.Unknown)); + uint256 _vinLen = getVinLength(_remaining); + _vinView = _txView.slice(_offset, _vinLen, uint40(BTCTypes.Vin)); + _offset += _vinLen; + + _remaining = _txView.postfix(_txView.len() - _offset, uint40(BTCTypes.Unknown)); + uint256 _voutLen = getVoutLength(_remaining); + _voutView = _txView.slice(_offset, _voutLen, uint40(BTCTypes.Vout)); + _offset += _voutLen; + + _lockTime = _txView.indexLEUint(_offset, 4).toUint32(); + require(_offset + 4 == _txView.len(), "BitcoinHelper: invalid tx"); + } +} \ No newline at end of file diff --git a/contracts/lib/SafeCast.sol b/contracts/lib/SafeCast.sol new file mode 100644 index 00000000..435a5f94 --- /dev/null +++ b/contracts/lib/SafeCast.sol @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toUint248(uint256 value) internal pure returns (uint248) { + require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toUint240(uint256 value) internal pure returns (uint240) { + require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toUint232(uint256 value) internal pure returns (uint232) { + require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.2._ + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toUint216(uint256 value) internal pure returns (uint216) { + require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toUint208(uint256 value) internal pure returns (uint208) { + require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toUint200(uint256 value) internal pure returns (uint200) { + require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toUint192(uint256 value) internal pure returns (uint192) { + require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toUint184(uint256 value) internal pure returns (uint184) { + require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toUint176(uint256 value) internal pure returns (uint176) { + require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toUint168(uint256 value) internal pure returns (uint168) { + require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toUint160(uint256 value) internal pure returns (uint160) { + require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toUint152(uint256 value) internal pure returns (uint152) { + require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toUint144(uint256 value) internal pure returns (uint144) { + require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toUint136(uint256 value) internal pure returns (uint136) { + require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v2.5._ + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toUint120(uint256 value) internal pure returns (uint120) { + require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toUint112(uint256 value) internal pure returns (uint112) { + require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toUint104(uint256 value) internal pure returns (uint104) { + require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.2._ + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toUint88(uint256 value) internal pure returns (uint88) { + require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toUint80(uint256 value) internal pure returns (uint80) { + require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toUint72(uint256 value) internal pure returns (uint72) { + require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v2.5._ + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toUint56(uint256 value) internal pure returns (uint56) { + require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toUint48(uint256 value) internal pure returns (uint48) { + require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toUint40(uint256 value) internal pure returns (uint40) { + require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v2.5._ + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toUint24(uint256 value) internal pure returns (uint24) { + require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v2.5._ + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v2.5._ + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + * + * _Available since v3.0._ + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.7._ + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.7._ + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + * + * _Available since v3.0._ + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} diff --git a/contracts/lib/TypedMemView.sol b/contracts/lib/TypedMemView.sol new file mode 100644 index 00000000..8aa8df6f --- /dev/null +++ b/contracts/lib/TypedMemView.sol @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +/** @author Summa (https://summa.one) */ + +/* + Original version: https://github.com/summa-tx/memview-sol/blob/main/contracts/TypedMemView.sol + We made few changes to the original version: + 1. Use solidity version 8 compiler + 2. Remove SafeMath library + 3. Add unchecked in line 522 +*/ + +library TypedMemView { + + // Why does this exist? + // the solidity `bytes memory` type has a few weaknesses. + // 1. You can't index ranges effectively + // 2. You can't slice without copying + // 3. The underlying data may represent any type + // 4. Solidity never deallocates memory, and memory costs grow + // superlinearly + + // By using a memory view instead of a `bytes memory` we get the following + // advantages: + // 1. Slices are done on the stack, by manipulating the pointer + // 2. We can index arbitrary ranges and quickly convert them to stack types + // 3. We can insert type info into the pointer, and typecheck at runtime + + // This makes `TypedMemView` a useful tool for efficient zero-copy + // algorithms. + + // Why bytes29? + // We want to avoid confusion between views, digests, and other common + // types so we chose a large and uncommonly used odd number of bytes + // + // Note that while bytes are left-aligned in a word, integers and addresses + // are right-aligned. This means when working in assembly we have to + // account for the 3 unused bytes on the righthand side + // + // First 5 bytes are a type flag. + // - ff_ffff_fffe is reserved for unknown type. + // - ff_ffff_ffff is reserved for invalid types/errors. + // next 12 are memory address + // next 12 are len + // bottom 3 bytes are empty + + // Assumptions: + // - non-modification of memory. + // - No Solidity updates + // - - wrt free mem point + // - - wrt bytes representation in memory + // - - wrt memory addressing in general + + // Usage: + // - create type constants + // - use `assertType` for runtime type assertions + // - - unfortunately we can't do this at compile time yet :( + // - recommended: implement modifiers that perform type checking + // - - e.g. + // - - `uint40 constant MY_TYPE = 3;` + // - - ` modifer onlyMyType(bytes29 myView) { myView.assertType(MY_TYPE); }` + // - instantiate a typed view from a bytearray using `ref` + // - use `index` to inspect the contents of the view + // - use `slice` to create smaller views into the same memory + // - - `slice` can increase the offset + // - - `slice can decrease the length` + // - - must specify the output type of `slice` + // - - `slice` will return a null view if you try to overrun + // - - make sure to explicitly check for this with `notNull` or `assertType` + // - use `equal` for typed comparisons. + + + // The null view + bytes29 internal constant NULL = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + uint256 constant LOW_12_MASK = 0xffffffffffffffffffffffff; + uint8 constant TWELVE_BYTES = 96; + + /** + * @notice Returns the encoded hex character that represents the lower 4 bits of the argument. + * @param _b The byte + * @return char - The encoded hex character + */ + function nibbleHex(uint8 _b) internal pure returns (uint8 char) { + // This can probably be done more efficiently, but it's only in error + // paths, so we don't really care :) + uint8 _nibble = _b | 0xf0; // set top 4, keep bottom 4 + if (_nibble == 0xf0) {return 0x30;} // 0 + if (_nibble == 0xf1) {return 0x31;} // 1 + if (_nibble == 0xf2) {return 0x32;} // 2 + if (_nibble == 0xf3) {return 0x33;} // 3 + if (_nibble == 0xf4) {return 0x34;} // 4 + if (_nibble == 0xf5) {return 0x35;} // 5 + if (_nibble == 0xf6) {return 0x36;} // 6 + if (_nibble == 0xf7) {return 0x37;} // 7 + if (_nibble == 0xf8) {return 0x38;} // 8 + if (_nibble == 0xf9) {return 0x39;} // 9 + if (_nibble == 0xfa) {return 0x61;} // a + if (_nibble == 0xfb) {return 0x62;} // b + if (_nibble == 0xfc) {return 0x63;} // c + if (_nibble == 0xfd) {return 0x64;} // d + if (_nibble == 0xfe) {return 0x65;} // e + if (_nibble == 0xff) {return 0x66;} // f + } + + /** + * @notice Returns a uint16 containing the hex-encoded byte. + * `the first 8 bits of encoded is the nibbleHex of top 4 bits of _b` + * `the second 8 bits of encoded is the nibbleHex of lower 4 bits of _b` + * @param _b The byte + * @return encoded - The hex-encoded byte + */ + function byteHex(uint8 _b) internal pure returns (uint16 encoded) { + encoded |= nibbleHex(_b >> 4); // top 4 bits + encoded <<= 8; + encoded |= nibbleHex(_b); // lower 4 bits + } + + /** + * @notice Encodes the uint256 to hex. `first` contains the encoded top 16 bytes. + * `second` contains the encoded lower 16 bytes. + * + * @param _b The 32 bytes as uint256 + * @return first - The top 16 bytes + * @return second - The bottom 16 bytes + */ + function encodeHex(uint256 _b) internal pure returns (uint256 first, uint256 second) { + for (uint8 i = 31; i > 15; i -= 1) { + uint8 _byte = uint8(_b >> (i * 8)); + first |= byteHex(_byte); + if (i != 16) { + first <<= 16; + } + } + + unchecked { + // abusing underflow here =_= + for (uint8 i = 15; i < 255 ; i -= 1) { + uint8 _byte = uint8(_b >> (i * 8)); + second |= byteHex(_byte); + if (i != 0) { + second <<= 16; + } + } + } + + } + + /** + * @notice Changes the endianness of a uint256. + * @dev https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + * @param _b The unsigned integer to reverse + * @return v - The reversed value + */ + function reverseUint256(uint256 _b) internal pure returns (uint256 v) { + v = _b; + + // swap bytes + v = ((v >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) | + ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); + // swap 2-byte long pairs + v = ((v >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) | + ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); + // swap 4-byte long pairs + v = ((v >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) | + ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); + // swap 8-byte long pairs + v = ((v >> 64) & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) | + ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); + // swap 16-byte long pairs + v = (v >> 128) | (v << 128); + } + + /** + * @notice Create a mask with the highest `_len` bits set. + * @param _len The length + * @return mask - The mask + */ + function leftMask(uint8 _len) private pure returns (uint256 mask) { + assembly { + // solium-disable-previous-line security/no-inline-assembly + mask := sar( + sub(_len, 1), + 0x8000000000000000000000000000000000000000000000000000000000000000 + ) + } + } + + /** + * @notice Return the null view. + * @return bytes29 - The null view + */ + function nullView() internal pure returns (bytes29) { + return NULL; + } + + /** + * @notice Check if the view is null. + * @return bool - True if the view is null + */ + function isNull(bytes29 memView) internal pure returns (bool) { + return memView == NULL; + } + + /** + * @notice Check if the view is not null. + * @return bool - True if the view is not null + */ + function notNull(bytes29 memView) internal pure returns (bool) { + return !isNull(memView); + } + + /** + * @notice Check if the view is of a valid type and points to a valid location + * in memory. + * @dev We perform this check by examining solidity's unallocated memory + * pointer and ensuring that the view's upper bound is less than that. + * @param memView The view + * @return ret - True if the view is valid + */ + function isValid(bytes29 memView) internal pure returns (bool ret) { + if (typeOf(memView) == 0xffffffffff) {return false;} + uint256 _end = end(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + ret := not(gt(_end, mload(0x40))) + } + } + + /** + * @notice Require that a typed memory view be valid. + * @dev Returns the view for easy chaining. + * @param memView The view + * @return bytes29 - The validated view + */ + function assertValid(bytes29 memView) internal pure returns (bytes29) { + require(isValid(memView), "Validity assertion failed"); + return memView; + } + + /** + * @notice Return true if the memview is of the expected type. Otherwise false. + * @param memView The view + * @param _expected The expected type + * @return bool - True if the memview is of the expected type + */ + function isType(bytes29 memView, uint40 _expected) internal pure returns (bool) { + return typeOf(memView) == _expected; + } + + /** + * @notice Require that a typed memory view has a specific type. + * @dev Returns the view for easy chaining. + * @param memView The view + * @param _expected The expected type + * @return bytes29 - The view with validated type + */ + function assertType(bytes29 memView, uint40 _expected) internal pure returns (bytes29) { + if (!isType(memView, _expected)) { + (, uint256 g) = encodeHex(uint256(typeOf(memView))); + (, uint256 e) = encodeHex(uint256(_expected)); + string memory err = string( + abi.encodePacked( + "Type assertion failed. Got 0x", + uint80(g), + ". Expected 0x", + uint80(e) + ) + ); + revert(err); + } + return memView; + } + + /** + * @notice Return an identical view with a different type. + * @param memView The view + * @param _newType The new type + * @return newView - The new view with the specified type + */ + function castTo(bytes29 memView, uint40 _newType) internal pure returns (bytes29 newView) { + // then | in the new type + assembly { + // solium-disable-previous-line security/no-inline-assembly + // shift off the top 5 bytes + newView := or(newView, shr(40, shl(40, memView))) + newView := or(newView, shl(216, _newType)) + } + } + + /** + * @notice Unsafe raw pointer construction. This should generally not be called + * directly. Prefer `ref` wherever possible. + * @dev Unsafe raw pointer construction. This should generally not be called + * directly. Prefer `ref` wherever possible. + * @param _type The type + * @param _loc The memory address + * @param _len The length + * @return newView - The new view with the specified type, location and length + */ + function unsafeBuildUnchecked(uint256 _type, uint256 _loc, uint256 _len) private pure returns (bytes29 newView) { + assembly { + // solium-disable-previous-line security/no-inline-assembly + newView := shl(96, or(newView, _type)) // insert type + newView := shl(96, or(newView, _loc)) // insert loc + newView := shl(24, or(newView, _len)) // empty bottom 3 bytes + } + } + + /** + * @notice Instantiate a new memory view. This should generally not be called + * directly. Prefer `ref` wherever possible. + * @dev Instantiate a new memory view. This should generally not be called + * directly. Prefer `ref` wherever possible. + * @param _type The type + * @param _loc The memory address + * @param _len The length + * @return newView - The new view with the specified type, location and length + */ + function build(uint256 _type, uint256 _loc, uint256 _len) internal pure returns (bytes29 newView) { + uint256 _end = _loc + _len; + assembly { + // solium-disable-previous-line security/no-inline-assembly + if gt(_end, mload(0x40)) { + _end := 0 + } + } + if (_end == 0) { + return NULL; + } + newView = unsafeBuildUnchecked(_type, _loc, _len); + } + + /** + * @notice Instantiate a memory view from a byte array. + * @dev Note that due to Solidity memory representation, it is not possible to + * implement a deref, as the `bytes` type stores its len in memory. + * @param arr The byte array + * @param newType The type + * @return bytes29 - The memory view + */ + function ref(bytes memory arr, uint40 newType) internal pure returns (bytes29) { + uint256 _len = arr.length; + + uint256 _loc; + assembly { + // solium-disable-previous-line security/no-inline-assembly + _loc := add(arr, 0x20) // our view is of the data, not the struct + } + + return build(newType, _loc, _len); + } + + /** + * @notice Return the associated type information. + * @param memView The memory view + * @return _type - The type associated with the view + */ + function typeOf(bytes29 memView) internal pure returns (uint40 _type) { + assembly { + // solium-disable-previous-line security/no-inline-assembly + // 216 == 256 - 40 + _type := shr(216, memView) // shift out lower (12 + 12 + 3) bytes + } + } + + /** + * @notice Optimized type comparison. Checks that the 5-byte type flag is equal. + * @param left The first view + * @param right The second view + * @return bool - True if the 5-byte type flag is equal + */ + function sameType(bytes29 left, bytes29 right) internal pure returns (bool) { + // XOR the inputs to check their difference + return (left ^ right) >> (2 * TWELVE_BYTES) == 0; + } + + /** + * @notice Return the memory address of the underlying bytes. + * @param memView The view + * @return _loc - The memory address + */ + function loc(bytes29 memView) internal pure returns (uint96 _loc) { + uint256 _mask = LOW_12_MASK; // assembly can't use globals + assembly { + // solium-disable-previous-line security/no-inline-assembly + // 120 bits = 12 bytes (the encoded loc) + 3 bytes (empty low space) + _loc := and(shr(120, memView), _mask) + } + } + + /** + * @notice The number of memory words this memory view occupies, rounded up. + * @param memView The view + * @return uint256 - The number of memory words + */ + function words(bytes29 memView) internal pure returns (uint256) { + return (uint256(len(memView)) + 32) / 32; + } + + /** + * @notice The in-memory footprint of a fresh copy of the view. + * @param memView The view + * @return uint256 - The in-memory footprint of a fresh copy of the view. + */ + function footprint(bytes29 memView) internal pure returns (uint256) { + return words(memView) * 32; + } + + /** + * @notice The number of bytes of the view. + * @param memView The view + * @return _len - The length of the view + */ + function len(bytes29 memView) internal pure returns (uint96 _len) { + uint256 _mask = LOW_12_MASK; // assembly can't use globals + assembly { + // solium-disable-previous-line security/no-inline-assembly + _len := and(shr(24, memView), _mask) + } + } + + /** + * @notice Returns the endpoint of `memView`. + * @param memView The view + * @return uint256 - The endpoint of `memView` + */ + function end(bytes29 memView) internal pure returns (uint256) { + return loc(memView) + len(memView); + } + + /** + * @notice Safe slicing without memory modification. + * @param memView The view + * @param _index The start index + * @param _len The length + * @param newType The new type + * @return bytes29 - The new view + */ + function slice(bytes29 memView, uint256 _index, uint256 _len, uint40 newType) internal pure returns (bytes29) { + uint256 _loc = loc(memView); + + // Ensure it doesn't overrun the view + if (_loc + _index + _len > end(memView)) { + return NULL; + } + + _loc = _loc + _index; + return build(newType, _loc, _len); + } + + /** + * @notice Shortcut to `slice`. Gets a view representing the first `_len` bytes. + * @param memView The view + * @param _len The length + * @param newType The new type + * @return bytes29 - The new view + */ + function prefix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) { + return slice(memView, 0, _len, newType); + } + + /** + * @notice Shortcut to `slice`. Gets a view representing the last `_len` bytes. + * @param memView The view + * @param _len The length + * @param newType The new type + * @return bytes29 - The new view + */ + function postfix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) { + return slice(memView, uint256(len(memView)) - _len, _len, newType); + } + + /** + * @notice Construct an error message for an indexing overrun. + * @param _loc The memory address + * @param _len The length + * @param _index The index + * @param _slice The slice where the overrun occurred + * @return err - The err + */ + function indexErrOverrun( + uint256 _loc, + uint256 _len, + uint256 _index, + uint256 _slice + ) internal pure returns (string memory err) { + (, uint256 a) = encodeHex(_loc); + (, uint256 b) = encodeHex(_len); + (, uint256 c) = encodeHex(_index); + (, uint256 d) = encodeHex(_slice); + err = string( + abi.encodePacked( + "TypedMemView/index - Overran the view. Slice is at 0x", + uint48(a), + " with length 0x", + uint48(b), + ". Attempted to index at offset 0x", + uint48(c), + " with length 0x", + uint48(d), + "." + ) + ); + } + + /** + * @notice Load up to 32 bytes from the view onto the stack. + * @dev Returns a bytes32 with only the `_bytes` highest bytes set. + * This can be immediately cast to a smaller fixed-length byte array. + * To automatically cast to an integer, use `indexUint`. + * @param memView The view + * @param _index The index + * @param _bytes The bytes length + * @return result - The 32 byte result + */ + function index(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (bytes32 result) { + if (_bytes == 0) {return bytes32(0);} + if (_index + _bytes > len(memView)) { + revert(indexErrOverrun(loc(memView), len(memView), _index, uint256(_bytes))); + } + require(_bytes <= 32, "TypedMemView/index - Attempted to index more than 32 bytes"); + + unchecked { + uint8 bitLength = _bytes * 8; + uint256 _loc = loc(memView); + uint256 _mask = leftMask(bitLength); + assembly { + // solium-disable-previous-line security/no-inline-assembly + result := and(mload(add(_loc, _index)), _mask) + } + } + + } + + /** + * @notice Parse an unsigned integer from the view at `_index`. + * @dev Requires that the view has >= `_bytes` bytes following that index. + * @param memView The view + * @param _index The index + * @param _bytes The bytes length + * @return result - The unsigned integer + */ + function indexUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) { + return uint256(index(memView, _index, _bytes)) >> ((32 - _bytes) * 8); + } + + /** + * @notice Parse an unsigned integer from LE bytes. + * @param memView The view + * @param _index The index + * @param _bytes The bytes length + * @return result - The unsigned integer + */ + function indexLEUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) { + return reverseUint256(uint256(index(memView, _index, _bytes))); + } + + /** + * @notice Parse an address from the view at `_index`. Requires that the view have >= 20 bytes + * following that index. + * @param memView The view + * @param _index The index + * @return address - The address + */ + function indexAddress(bytes29 memView, uint256 _index) internal pure returns (address) { + return address(uint160(indexUint(memView, _index, 20))); + } + + /** + * @notice Return the keccak256 hash of the underlying memory + * @param memView The view + * @return digest - The keccak256 hash of the underlying memory + */ + function keccak(bytes29 memView) internal pure returns (bytes32 digest) { + uint256 _loc = loc(memView); + uint256 _len = len(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + digest := keccak256(_loc, _len) + } + } + + /** + * @notice Return the sha2 digest of the underlying memory. + * @dev We explicitly deallocate memory afterwards. + * @param memView The view + * @return digest - The sha2 hash of the underlying memory + */ + function sha2(bytes29 memView) internal view returns (bytes32 digest) { + uint256 _loc = loc(memView); + uint256 _len = len(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + let ptr := mload(0x40) + pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1 + digest := mload(ptr) + } + } + + /** + * @notice Implements bitcoin's hash160 (rmd160(sha2())) + * @param memView The pre-image + * @return digest - the Digest + */ + function hash160(bytes29 memView) internal view returns (bytes20 digest) { + uint256 _loc = loc(memView); + uint256 _len = len(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + let ptr := mload(0x40) + pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 + pop(staticcall(gas(), 3, ptr, 0x20, ptr, 0x20)) // rmd160 + digest := mload(add(ptr, 0xc)) // return value is 0-prefixed. + } + } + + /** + * @notice Implements bitcoin's hash256 (double sha2) + * @param memView A view of the preimage + * @return digest - the Digest + */ + function hash256(bytes29 memView) internal view returns (bytes32 digest) { + uint256 _loc = loc(memView); + uint256 _len = len(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + let ptr := mload(0x40) + pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1 + pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha2 #2 + digest := mload(ptr) + } + } + + /** + * @notice Return true if the underlying memory is equal. Else false. + * @param left The first view + * @param right The second view + * @return bool - True if the underlying memory is equal + */ + function untypedEqual(bytes29 left, bytes29 right) internal pure returns (bool) { + return (loc(left) == loc(right) && len(left) == len(right)) || keccak(left) == keccak(right); + } + + /** + * @notice Return false if the underlying memory is equal. Else true. + * @param left The first view + * @param right The second view + * @return bool - False if the underlying memory is equal + */ + function untypedNotEqual(bytes29 left, bytes29 right) internal pure returns (bool) { + return !untypedEqual(left, right); + } + + /** + * @notice Compares type equality. + * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. + * @param left The first view + * @param right The second view + * @return bool - True if the types are the same + */ + function equal(bytes29 left, bytes29 right) internal pure returns (bool) { + return left == right || (typeOf(left) == typeOf(right) && keccak(left) == keccak(right)); + } + + /** + * @notice Compares type inequality. + * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. + * @param left The first view + * @param right The second view + * @return bool - True if the types are not the same + */ + function notEqual(bytes29 left, bytes29 right) internal pure returns (bool) { + return !equal(left, right); + } + + /** + * @notice Copy the view to a location, return an unsafe memory reference + * @dev Super Dangerous direct memory access. + * + * This reference can be overwritten if anything else modifies memory (!!!). + * As such it MUST be consumed IMMEDIATELY. + * This function is private to prevent unsafe usage by callers. + * @param memView The view + * @param _newLoc The new location + * @return written - the unsafe memory reference + */ + function unsafeCopyTo(bytes29 memView, uint256 _newLoc) private view returns (bytes29 written) { + require(notNull(memView), "TypedMemView/copyTo - Null pointer deref"); + require(isValid(memView), "TypedMemView/copyTo - Invalid pointer deref"); + uint256 _len = len(memView); + uint256 _oldLoc = loc(memView); + + uint256 ptr; + assembly { + // solium-disable-previous-line security/no-inline-assembly + ptr := mload(0x40) + // revert if we're writing in occupied memory + if gt(ptr, _newLoc) { + revert(0x60, 0x20) // empty revert message + } + + // use the identity precompile to copy + // guaranteed not to fail, so pop the success + pop(staticcall(gas(), 4, _oldLoc, _len, _newLoc, _len)) + } + + written = unsafeBuildUnchecked(typeOf(memView), _newLoc, _len); + } + + /** + * @notice Copies the referenced memory to a new loc in memory, returning a `bytes` pointing to + * the new memory + * @dev Shortcuts if the pointers are identical, otherwise compares type and digest. + * @param memView The view + * @return ret - The view pointing to the new memory + */ + function clone(bytes29 memView) internal view returns (bytes memory ret) { + uint256 ptr; + uint256 _len = len(memView); + assembly { + // solium-disable-previous-line security/no-inline-assembly + ptr := mload(0x40) // load unused memory pointer + ret := ptr + } + unsafeCopyTo(memView, ptr + 0x20); + assembly { + // solium-disable-previous-line security/no-inline-assembly + mstore(0x40, add(add(ptr, _len), 0x20)) // write new unused pointer + mstore(ptr, _len) // write len of new array (in bytes) + } + } + + /** + * @notice Join the views in memory, return an unsafe reference to the memory. + * @dev Super Dangerous direct memory access. + * + * This reference can be overwritten if anything else modifies memory (!!!). + * As such it MUST be consumed IMMEDIATELY. + * This function is private to prevent unsafe usage by callers. + * @param memViews The views + * @return unsafeView - The conjoined view pointing to the new memory + */ + function unsafeJoin(bytes29[] memory memViews, uint256 _location) private view returns (bytes29 unsafeView) { + assembly { + // solium-disable-previous-line security/no-inline-assembly + let ptr := mload(0x40) + // revert if we're writing in occupied memory + if gt(ptr, _location) { + revert(0x60, 0x20) // empty revert message + } + } + + uint256 _offset = 0; + for (uint256 i = 0; i < memViews.length; i ++) { + bytes29 memView = memViews[i]; + unsafeCopyTo(memView, _location + _offset); + _offset += len(memView); + } + unsafeView = unsafeBuildUnchecked(0, _location, _offset); + } + + /** + * @notice Produce the keccak256 digest of the concatenated contents of multiple views. + * @param memViews The views + * @return bytes32 - The keccak256 digest + */ + function joinKeccak(bytes29[] memory memViews) internal view returns (bytes32) { + uint256 ptr; + assembly { + // solium-disable-previous-line security/no-inline-assembly + ptr := mload(0x40) // load unused memory pointer + } + return keccak(unsafeJoin(memViews, ptr)); + } + + /** + * @notice Produce the sha256 digest of the concatenated contents of multiple views. + * @param memViews The views + * @return bytes32 - The sha256 digest + */ + function joinSha2(bytes29[] memory memViews) internal view returns (bytes32) { + uint256 ptr; + assembly { + // solium-disable-previous-line security/no-inline-assembly + ptr := mload(0x40) // load unused memory pointer + } + return sha2(unsafeJoin(memViews, ptr)); + } + + /** + * @notice copies all views, joins them into a new bytearray. + * @param memViews The views + * @return ret - The new byte array + */ + function join(bytes29[] memory memViews) internal view returns (bytes memory ret) { + uint256 ptr; + assembly { + // solium-disable-previous-line security/no-inline-assembly + ptr := mload(0x40) // load unused memory pointer + } + + bytes29 _newView = unsafeJoin(memViews, ptr + 0x20); + uint256 _written = len(_newView); + uint256 _footprint = footprint(_newView); + + assembly { + // solium-disable-previous-line security/no-inline-assembly + // store the legnth + mstore(ptr, _written) + // new pointer is old + 0x20 + the footprint of the body + mstore(0x40, add(add(ptr, _footprint), 0x20)) + ret := ptr + } + } +} \ No newline at end of file diff --git a/generate-pledgeagent.js b/generate-pledgeagent.js index 89d2cfc5..6c74edd7 100644 --- a/generate-pledgeagent.js +++ b/generate-pledgeagent.js @@ -20,13 +20,13 @@ program.option( program.option("--mock ", "if use mock", false); - +program.option("-c, --chainid ", "chain id", "1116") program.parse(process.argv); const data = { initRoundInterval: init_cycle.roundInterval, - initValidatorCount: init_cycle.validatorCount, + chainid: program.chainid, mock: program.mock, }; From 469151df51d7f4e8f0eedae80437d8d682985544 Mon Sep 17 00:00:00 2001 From: kevin9936 Date: Wed, 24 Jan 2024 14:50:42 +0800 Subject: [PATCH 3/9] add testcase for btc stake; modify brownie version --- brownie-config.yaml | 1 + contracts/BtcLightClient.template | 9 + contracts/PledgeAgent.template | 8 +- contracts/mock/BtcLightClientMock.sol | 4 + contracts/mock/CandidateHubMock.sol | 7 +- contracts/mock/CandidateHubUnitMock.sol | 7 +- contracts/mock/PledgeAgentMock.sol | 40 +- generate-test-contracts.sh | 2 +- requirements.txt | 2 +- tests/btc_block_data.py | 22 +- tests/common.py | 9 +- tests/conftest.py | 4 + tests/test_delegate_btc.py | 1786 +++++++++++++++++++++++ tests/test_pledge_agent.py | 8 +- tests/utils.py | 74 +- 15 files changed, 1962 insertions(+), 21 deletions(-) create mode 100644 tests/test_delegate_btc.py diff --git a/brownie-config.yaml b/brownie-config.yaml index ab1d2117..79fa7852 100644 --- a/brownie-config.yaml +++ b/brownie-config.yaml @@ -16,6 +16,7 @@ networks: mnemonic: clock radar mass judge dismiss just intact mind resemble fringe diary casino evm_version: berlin port: 8546 + unlimited_contract_size: true compiler: solc: diff --git a/contracts/BtcLightClient.template b/contracts/BtcLightClient.template index 9a8a0165..1c3a3bc9 100644 --- a/contracts/BtcLightClient.template +++ b/contracts/BtcLightClient.template @@ -293,6 +293,11 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ /// @return True if the provided tx is confirmed on Bitcoin function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) { bytes32 blockHash = height2HashMap[blockHeight]; + {% if mock %} + if (checkResult == true){ + return checkResult; + } + {% endif %} if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) { return false; } @@ -677,4 +682,8 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ function getRoundCandidates(uint256 roundTimeTag) external override view returns (address[] memory candidates) { return roundPowerMap[roundTimeTag].candidates; } + {% if mock %} + bool public checkResult; + {% endif %} + } diff --git a/contracts/PledgeAgent.template b/contracts/PledgeAgent.template index 93ab5f5d..0bd8aecf 100644 --- a/contracts/PledgeAgent.template +++ b/contracts/PledgeAgent.template @@ -27,7 +27,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 public constant INIT_REQUIRED_COIN_DEPOSIT = 1e18; uint256 public constant INIT_HASH_POWER_FACTOR = 20000; - uint256 public constant POWER_BLOCK_FACTOR = {% if mock %}1{% else %}1e18{% endif %}; + uint256 public {% if mock %}{% else %}constant{% endif %} POWER_BLOCK_FACTOR = {% if mock %}1{% else %}1e18{% endif %}; uint32 public constant INIT_BTC_CONFIRM_BLOCK = 3; uint256 public constant INIT_MIN_BTC_LOCK_ROUND = 7; uint256 public constant ROUND_INTERVAL = {{initRoundInterval}}; @@ -35,9 +35,9 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 public constant INIT_BTC_FACTOR = 5e4; uint256 public constant BTC_STAKE_MAGIC = 0x5341542b; uint256 public constant CHAINID = {{chainid}}; - uint256 public constant FEE_FACTOR = 1e18; - int256 public constant CLAIM_ROUND_LIMIT = 500; - uint256 public constant BTC_UNIT_CONVERSION = 1e10; + uint256 public constant FEE_FACTOR = {% if mock %}100{% else %}1e18{% endif %}; + int256 public {% if mock %}{% else %}constant{% endif %} CLAIM_ROUND_LIMIT = 500; + uint256 public {% if mock %}{% else %}constant{% endif %} BTC_UNIT_CONVERSION = 1e10; uint256 public requiredCoinDeposit; diff --git a/contracts/mock/BtcLightClientMock.sol b/contracts/mock/BtcLightClientMock.sol index 4ec83514..d64b0d9e 100644 --- a/contracts/mock/BtcLightClientMock.sol +++ b/contracts/mock/BtcLightClientMock.sol @@ -30,6 +30,10 @@ contract BtcLightClientMock is BtcLightClient { roundPowerMap[roundTimeTag].candidates.push(candidates[i]); } } + function setCheckResult(bool value) public { + checkResult = value; + } + function setMiners(uint roundTimeTag, address candidate, address[] memory rewardAddrs) public { RoundPower storage r = roundPowerMap[roundTimeTag]; diff --git a/contracts/mock/CandidateHubMock.sol b/contracts/mock/CandidateHubMock.sol index 44b675e9..54e71161 100644 --- a/contracts/mock/CandidateHubMock.sol +++ b/contracts/mock/CandidateHubMock.sol @@ -73,10 +73,11 @@ contract CandidateHubMock is CandidateHub { return candidateSet[operateMap[k] - 1]; } - function getScoreMock(address[] memory candidates, uint256[] memory powers) external { - (scores, totalPower, totalCoin) = IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore( + function getScoreMock(address[] memory candidates, uint256[] memory powers, uint256 round) external { + scores = IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore( candidates, - powers + powers, + round ); } diff --git a/contracts/mock/CandidateHubUnitMock.sol b/contracts/mock/CandidateHubUnitMock.sol index c71de01e..d5dbade8 100644 --- a/contracts/mock/CandidateHubUnitMock.sol +++ b/contracts/mock/CandidateHubUnitMock.sol @@ -34,10 +34,11 @@ contract CandidateHubUnitMock is CandidateHub { return candidateSet[operateMap[k] - 1]; } - function getScoreMock(address[] memory candidates, uint256[] memory powers) external { - (scores, totalPower, totalCoin) = IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore( + function getScoreMock(address[] memory candidates, uint256[] memory powers, uint256 round) external { + scores = IPledgeAgent(PLEDGE_AGENT_ADDR).getHybridScore( candidates, - powers + powers, + round ); } diff --git a/contracts/mock/PledgeAgentMock.sol b/contracts/mock/PledgeAgentMock.sol index 1e713f0f..91aa9048 100644 --- a/contracts/mock/PledgeAgentMock.sol +++ b/contracts/mock/PledgeAgentMock.sol @@ -7,10 +7,15 @@ contract PledgeAgentMock is PledgeAgent { function developmentInit() external { requiredCoinDeposit = requiredCoinDeposit / 1e16; + btcFactor = 2; + BTC_UNIT_CONVERSION = BTC_UNIT_CONVERSION / 1e9 / 2; + minBtcLockRound = 3; + minBtcValue = INIT_MIN_BTC_VALUE / 1000; + roundTag = 1; } - function setRoundState(uint256 power, uint256 coin) external { - stateMap[roundTag] = RoundState(power + 1, coin + 1, powerFactor); + function setRoundState(uint256 power, uint256 coin, uint256 btc) external { + stateMap[roundTag] = RoundState(power + 1, coin + 1, powerFactor, btc, btcFactor); } function setAgentRound(address agent, uint256 power, uint256 coin) external { @@ -40,19 +45,48 @@ contract PledgeAgentMock is PledgeAgent { return agentsMap[agent].rewardSet.length; } + function getAgentAddrList(uint256 index) external view returns (address[] memory) { + BtcExpireInfo storage expireInfo = round2expireInfoMap[index]; + uint256 length = expireInfo.agentAddrList.length; + address[] memory agentAddresses = new address[](length); + for (uint256 i = 0; i < length; i++) { + agentAddresses[i] = expireInfo.agentAddrList[i]; + } + return agentAddresses; + } + + + function getDebtDepositMap(uint256 rRound, address delegator) external view returns (uint) { uint256 debt = debtDepositMap[rRound][delegator]; return debt; } - function setPowerFactor(uint newPowerFactor) external { + function setPowerFactor(uint newPowerFactor) external { powerFactor = newPowerFactor; } + function setBtcFactor(uint newBtcFactor) external { + btcFactor = newBtcFactor; + } + function collectCoinRewardMock(address agent, address delegator, int256 roundLimit) external { Agent storage a = agentsMap[agent]; CoinDelegator storage d = a.cDelegatorMap[delegator]; rewardAmountM = collectCoinReward(a, d, roundLimit); } + + function setRoundTag(uint value) external { + roundTag = value; + } + + function setClaimRoundLimit(int value) external { + CLAIM_ROUND_LIMIT = value; + } + + function setPowerBlockFactor(uint value) external { + POWER_BLOCK_FACTOR = value; + } + } diff --git a/generate-test-contracts.sh b/generate-test-contracts.sh index 809451e1..c515222f 100755 --- a/generate-test-contracts.sh +++ b/generate-test-contracts.sh @@ -3,4 +3,4 @@ node generate-candidatehub.js --mock true node generate-pledgeagent.js --mock true node generate-validatorset.js --mock true node generate-slash.js -c 1112 -node generate-btclightclient.js --initConsensusStateBytes 000040209acaa5d26d392ace656c2428c991b0a3d3d773845a1300000000000000000000aa8e225b1f3ea6c4b7afd5aa1cecf691a8beaa7fa1e579ce240e4a62b5ac8ecc2141d9618b8c0b170d5c05bb --initChainHeight 717696 \ No newline at end of file +node generate-btclightclient.js --initConsensusStateBytes 000040209acaa5d26d392ace656c2428c991b0a3d3d773845a1300000000000000000000aa8e225b1f3ea6c4b7afd5aa1cecf691a8beaa7fa1e579ce240e4a62b5ac8ecc2141d9618b8c0b170d5c05bb --initChainHeight 717696 --mock true \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 63530408..5ad08ff4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -eth-brownie==1.19.3 +eth-brownie==1.19.4 PyYAML==5.4.1 ecdsa==0.17.0 \ No newline at end of file diff --git a/tests/btc_block_data.py b/tests/btc_block_data.py index 15663f96..a7f16e4a 100644 --- a/tests/btc_block_data.py +++ b/tests/btc_block_data.py @@ -2015,4 +2015,24 @@ '0x040000206b7459a11d516ec1672d4eca5037c99b0db218ff8d880b000000000000000000e455823eaddade58c8b836e160ad0ea7d719aa86ccac69ccff425756bac69524041cea618b8c0b17713e3a0c', '0x040080207e218cac1e9688176f85d5cc19c71d9f9168f434e14b0000000000000000000058f545cca4efacc511386a71dd0120304219820b0dc6f22511b73fb2619f08c45423ea618b8c0b1789c96daa', '0x00004020f66da9bada13173c287efbe411c25cbd22cdf5cad3d5020000000000000000000c3641e8429fb303b8b4d9d47f450cc79ab1edf146becbbf131abdc0c7c99de29a23ea6180900a17d2022766' -] \ No newline at end of file +] + + + + + + +output_script_pub_key = { + "script_public_key": "a9149ca26c6aa5a614836d041193ab7df1b6d650791387", + "script_public_key_hash": "a914c0958c8d9357598c5f7a6eea8a807d81683f9bb687", + "witness_script_public_key_hash": "0020aee9137b4958e35085907caaa2d5a9e659b0b1037e06f04280e2e98520f7f16a", + "witness_script_public_key": "00204fe5871daeae16742a2f56b616d7db1335f1a13637ddc4daa53cbd6b6ad397f7" + +} +delegate_btc_block_data = { + "btc_tx_block_data": "0200000001f57820c2694b1d88e85dbe0e23c4c9bab63af2907fd4f277761e2317c0717956020000006a473044022020509b1e3d63a3e2d893bb5a1f0b873886db420727010e7e7f024394c929885b022027953577efb6deb377e6b3df98fa58f36c05c8d08c46538c7c6a4dbde172cb1e01210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12feffffff", + "witness_btc_tx_block_data": "0200000001ac5d10fc2c7fde4aa105a740e0ae00dafa66a87f472d0395e71c4d70c4d698ba020000006b4830450221009b0f6b1f2cdb0125f166245064d18f026dc77777a657b83d6f56c79101c269b902206c84550b64755ec2eba1893e81b22a57350b003aa5a3a8915ac7c2eb905a1b7501210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12feffffff" +} + + + diff --git a/tests/common.py b/tests/common.py index 2b74ac2c..8b156ca7 100644 --- a/tests/common.py +++ b/tests/common.py @@ -33,7 +33,7 @@ def get_candidate(operator=None): idx = CandidateHubMock[0].operateMap(operator) if idx == 0: return None - return CandidateHubMock[0].candidateSet(idx-1).dict() + return CandidateHubMock[0].candidateSet(idx - 1).dict() def turn_round(miners: list = None, tx_fee=100, round_count=1): @@ -68,3 +68,10 @@ def register_relayer(relayer_address=None): RelayerHubMock[0].register({'from': relayer_address, 'value': RelayerHubMock[0].requiredDeposit()}) +def get_current_round(): + return CandidateHubMock[0].roundTag() + + +def set_last_round_tag(rount_tag): + CandidateHubMock[0].setRoundTag(rount_tag) + PledgeAgentMock[0].setRoundTag(rount_tag) diff --git a/tests/conftest.py b/tests/conftest.py index 14f4be66..9ab94a1c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -84,6 +84,9 @@ def validator_set(accounts): @pytest.fixture(scope="module") def pledge_agent(accounts): + accounts[0].deploy(BitcoinHelper) + accounts[0].deploy(TypedMemView) + accounts[0].deploy(SafeCast) c = accounts[0].deploy(PledgeAgentMock) c.init() if is_development: @@ -140,6 +143,7 @@ def set_system_contract_address( foundation.updateContractAddr(*args) system_reward.init() + candidate_hub.setControlRoundTimeTag(True) # used for distribute reward # accounts[-2].transfer(validator_set.address, Web3.toWei(100000, 'ether')) diff --git a/tests/test_delegate_btc.py b/tests/test_delegate_btc.py new file mode 100644 index 00000000..e5b42091 --- /dev/null +++ b/tests/test_delegate_btc.py @@ -0,0 +1,1786 @@ +import time + +import brownie +import pytest +from web3 import Web3 +from brownie import accounts, PledgeAgentProxy, DelegateReentry, UndelegateReentry, ClaimRewardReentry +from .btc_block_data import * +from .common import register_candidate, turn_round, get_current_round, set_last_round_tag +from .utils import * +from hashlib import sha256 + +MIN_INIT_DELEGATE_VALUE = 0 +BLOCK_REWARD = 0 +ROUND_INTERVAL = 86400 +BTC_VALUE = 2000 +btcFactor = 0 +MIN_BTC_LOCK_ROUND = 0 +BTC_AMOUNT = 0 +ONE_ETHER = Web3.toWei(1, 'ether') +ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' +TX_FEE = 100 +public_key = "0223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" +lock_time = 1736956800 +chain_id = 1116 +lock_script_type = 'hash' +FEE = 1 + + +@pytest.fixture(scope="module", autouse=True) +def deposit_for_reward(validator_set): + accounts[-2].transfer(validator_set.address, Web3.toWei(100000, 'ether')) + + +@pytest.fixture(scope="module", autouse=True) +def set_block_reward(validator_set, candidate_hub, btc_light_client, pledge_agent): + global BLOCK_REWARD, btcFactor, MIN_BTC_LOCK_ROUND, FEE, BTC_AMOUNT + FEE = 1 * pledge_agent.FEE_FACTOR() + block_reward = validator_set.blockReward() + block_reward_incentive_percent = validator_set.blockRewardIncentivePercent() + total_block_reward = block_reward + TX_FEE + BLOCK_REWARD = total_block_reward * ((100 - block_reward_incentive_percent) / 100) + btcFactor = pledge_agent.btcFactor() * pledge_agent.BTC_UNIT_CONVERSION() + BTC_AMOUNT = BTC_VALUE * btcFactor + MIN_BTC_LOCK_ROUND = pledge_agent.minBtcLockRound() + candidate_hub.setControlRoundTimeTag(True) + btc_light_client.setCheckResult(True) + + +@pytest.fixture() +def set_candidate(): + operators = [] + consensuses = [] + for operator in accounts[5:8]: + operators.append(operator) + consensuses.append(register_candidate(operator=operator)) + return operators, consensuses + + +@pytest.fixture() +def delegate_btc_valid_tx(): + operator = accounts[5] + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx, tx_id = get_btc_tx(BTC_VALUE, chain_id, operator, accounts[0], lock_script_type, lock_script) + tx_id_list = [tx_id] + return lock_script, btc_tx, tx_id_list + + +def test_delegate_btc_with_lock_time_in_tx(pledge_agent, set_candidate): + operators, consensuses = set_candidate + btc_amount = BTC_VALUE + lock_script = "0480db8767b17551210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e122103000871fc99dfcbb5a811c5e23c077683b07ab2bbbfff775ce30a809a6d41214152ae" + btc_tx = ( + "020000000188f5ba21514a0c32cbf90baab2b48feeeb0f200bfe7388730d80bf7f78ad27cd020000006a473044022066314a4e78bda5f9cb448d867ef3e8ef0678f7e0865f188e5cb362f5b40aed5c02203df085a6f742129a78729e8ca710a3065eb13cc01cb175457f947cbb6f3f89c701210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff02d007" + "00000000000017a914f8f68b9543eaf5a9306090fde09ac765e1412e4587" + "0000000000000000366a345341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a00180db8767" + "00000000") + tx_id = get_transaction_txid(btc_tx) + tx = pledge_agent.delegateBtc(btc_tx, 1, [], 0, lock_script, {"from": accounts[1]}) + expect_event(tx, 'delegatedBtc', { + 'txid': tx_id, + 'script': '0x' + lock_script, + 'blockHeight': 1, + 'outputIndex': 0 + + }) + turn_round() + agent_map = pledge_agent.agentsMap(operators[0]) + assert pledge_agent.btcReceiptMap(tx_id)['value'] == BTC_VALUE + assert agent_map['totalBtc'] == btc_amount + turn_round(consensuses) + expect_query(pledge_agent.btcReceiptMap(tx_id), { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': btc_amount, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[1], + 'fee': FEE + }) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward([tx_id]) + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE + assert tracker1.delta() == FEE + + +def test_delegate_btc_with_lock_script_in_tx(pledge_agent, set_candidate): + btc_amount = BTC_VALUE * 3 // 2 + lock_script = "0480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + operators, consensuses = set_candidate + btc_tx = ( + "0200000001dd94cb72979c528593cb1188f4e3bf43a52f5570edab981e3d303ff24166afe5000000006b483045022100f2f069e37929cdfafffa79dcc1cf478504875fbe2a41704a96aee88ec604c0e502207259c56c67de8de6bb8c15e9d14b6ad16acd86d6a834fbb0531fd27bee7e5e3301210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff03b80b00" + "000000000017a914c0958c8d9357598c5f7a6eea8a807d81683f9bb687" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a0010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + "3cd20000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + tx_id = get_transaction_txid(btc_tx) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {"from": accounts[2]}) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'txid': tx_id, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'outputIndex': 0 + }) + agent_map = pledge_agent.agentsMap(operators[0]) + expect_query(pledge_agent.btcReceiptMap(tx_id), { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': btc_amount, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[2], + 'fee': FEE + }) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[2]) + pledge_agent.claimBtcReward([tx_id]) + assert agent_map['totalBtc'] == btc_amount + assert pledge_agent.btcReceiptMap(tx_id)['value'] == btc_amount + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE + assert tracker1.delta() == FEE + + +def test_delegate_btc_success_public_key(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = get_lock_script(lock_time, public_key, 'key') + btc_tx, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], 'key') + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'outputIndex': 0, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'txid': tx_id, + }) + agent_map = pledge_agent.agentsMap(operators[0]) + expect_query(pledge_agent.btcReceiptMap(tx_id), { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': BTC_VALUE, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[0], + 'fee': FEE + }) + turn_round(consensuses) + tracker = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward([tx_id]) + assert agent_map['totalBtc'] == BTC_VALUE + assert "claimedReward" in tx.events + assert tracker.delta() == BLOCK_REWARD // 2 + + +def test_delegate_btc_success_public_hash(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'outputIndex': 0, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'txid': tx_id_list[0], + }) + + agent_map = pledge_agent.agentsMap(operators[0]) + expect_query(pledge_agent.btcReceiptMap(tx_id_list[0]), { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': BTC_VALUE, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[0], + 'fee': FEE + }) + turn_round(consensuses) + tracker = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward(tx_id_list) + assert agent_map['totalBtc'] == BTC_VALUE + assert "claimedReward" in tx.events + assert tracker.delta() == BLOCK_REWARD // 2 + + +def test_delegate_btc_success_multi_sig_script(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = "0480db8767b17551210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e122103000871fc99dfcbb5a811c5e23c077683b07ab2bbbfff775ce30a809a6d41214152ae" + btc_tx = ( + "020000000188f5ba21514a0c32cbf90baab2b48feeeb0f200bfe7388730d80bf7f78ad27cd020000006a473044022066314a4e78bda5f9cb448d867ef3e8ef0678f7e0865f188e5cb362f5b40aed5c02203df085a6f742129a78729e8ca710a3065eb13cc01cb175457f947cbb6f3f89c701210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff02d007" + "00000000000017a914f8f68b9543eaf5a9306090fde09ac765e1412e4587" + "0000000000000000366a345341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a00180db8767" + "00000000") + tx_id = get_transaction_txid(btc_tx) + pledge_agent.delegateBtc(btc_tx, 1, [], 0, lock_script, {"from": accounts[1]}) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward([tx_id]) + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE + + +def test_delegate_btc_with_witness_transaction_hash_script(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = '0480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac' + btc_tx = ( + "0200000001ac5d10fc2c7fde4aa105a740e0ae00dafa66a87f472d0395e71c4d70c4d698ba020000006b4830450221009b0f6b1f2cdb0125f166245064d18f026dc77777a657b83d6f56c79101c269b902206c84550b64755ec2eba1893e81b22a57350b003aa5a3a8915ac7c2eb905a1b7501210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff03140500" + "0000000000220020aee9137b4958e35085907caaa2d5a9e659b0b1037e06f04280e2e98520f7f16a" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a0010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + "bcc00000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + btc_amount = 1300 + tx_id = get_transaction_txid(btc_tx) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'outputIndex': 0, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'txid': tx_id, + }) + agent_map = pledge_agent.agentsMap(operators[0]) + btc_delegator = pledge_agent.btcReceiptMap(tx_id) + expect_query(btc_delegator, { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': btc_amount, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[0], + 'fee': FEE + }) + turn_round(consensuses) + tracker = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward([tx_id]) + assert "claimedReward" in tx.events + assert agent_map['totalBtc'] == btc_amount + assert tracker.delta() == BLOCK_REWARD // 2 + + +def test_delegate_btc_with_witness_transaction_key_script(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = get_lock_script(lock_time, public_key, 'key') + btc_tx = ( + "02000000015f6488617362efed9f022b8aa0ddb048607640232a118e684dea38a2141c45c9020000006b483045022100b2ecc85951154d98a6134293bc1a1e294cb6df98f8c3dd78da8da9b88ffc4ba002205c919bfa76bbe5e0e102f85bb46db797bd046ae21a437ed7886e1c47eda228de01210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff03780500" + "00000000002200204fe5871daeae16742a2f56b616d7db1335f1a13637ddc4daa53cbd6b6ad397f7" + "0000000000000000366a345341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a00180db8767" + "faaf0000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + btc_amount = 1400 + tx_id = get_transaction_txid(btc_tx) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'outputIndex': 0, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'txid': tx_id, + }) + btc_delegator = pledge_agent.btcReceiptMap(tx_id) + expect_query(btc_delegator, { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': btc_amount, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[0], + 'fee': FEE + }) + + turn_round(consensuses) + tracker = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward([tx_id]) + assert "claimedReward" in tx.events + assert tracker.delta() == BLOCK_REWARD // 2 + + +def test_invalid_lock_script(pledge_agent, delegate_btc_valid_tx): + _, btc_tx, tx_id_list = delegate_btc_valid_tx + lock_script = "0380db8767b175210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12ac" + with brownie.reverts("not a valid redeem script"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + lock_script = "0480db8767b275210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12ac" + with brownie.reverts("not a valid redeem script"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_insufficient_lock_round_revert(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + with brownie.reverts("insufficient lock round"): + pledge_agent.delegateBtc(btc_tx, end_round, [], 0, lock_script) + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND) + with brownie.reverts("insufficient lock round"): + pledge_agent.delegateBtc(btc_tx, end_round, [], 0, lock_script) + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + tx = pledge_agent.delegateBtc(btc_tx, end_round, [], 0, lock_script) + assert "delegatedBtc" in tx.events + + +def test_revert_on_duplicate_btc_tx_delegate(pledge_agent, set_candidate, delegate_btc_valid_tx): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + with brownie.reverts("btc tx confirmed"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_unconfirmed_btc_tx_delegate(pledge_agent, btc_light_client, set_candidate, delegate_btc_valid_tx): + btc_light_client.setCheckResult(False) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + with brownie.reverts("btc tx not confirmed"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_insufficient_btc_amount_delegate(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + btc_amount = 999 + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx, tx_id = get_btc_tx(btc_amount, chain_id, operators[0], accounts[0]) + with brownie.reverts("staked value does not meet requirement"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + btc_tx, tx_id = get_btc_tx(btc_amount + 1, chain_id, operators[0], accounts[0]) + with brownie.reverts("staked value does not meet requirement"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + btc_tx, tx_id = get_btc_tx(btc_amount + 2, chain_id, operators[0], accounts[0]) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + assert "delegatedBtc" in tx.events + + +def test_revert_on_unequal_chain_id(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx, tx_id = get_btc_tx(BTC_VALUE, chain_id - 1, operators[0], accounts[0], lock_script_type) + with brownie.reverts("wrong chain id"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_delegate_inactive_agent(pledge_agent, set_candidate): + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx, _ = get_btc_tx(BTC_VALUE, chain_id, accounts[1], accounts[0], lock_script_type) + error_msg = encode_args_with_signature('InactiveAgent(address)', [accounts[1].address]) + with brownie.reverts(f"typed error: {error_msg}"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +@pytest.mark.parametrize("output_view", [ + pytest.param("a6149ca26c6aa5a614836d041193ab7df1b6d650791387", id="OP_HASH160 error"), + pytest.param("a9139ca26c6aa5a614836d041193ab7df1b6d650791387", id="OP_PUSHBYTES_20 error"), + pytest.param("a9149ca26c6aa5a614836d041193ab7df1b6d650791287", id="ScriptPubKey error"), + pytest.param("a9149ca26c6aa5a614836d041193ab7df1b6d650791386", id="OP_EQUAL error"), + pytest.param("a9142d0a37f671e76a72f6dc30669ffaefa6120b798887", id="output error") +]) +def test_revert_on_invalid_btc_tx_output(pledge_agent, set_candidate, delegate_btc_valid_tx, output_view): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + btc_tx = delegate_btc_block_data['btc_tx_block_data'] + ( + f"03b80b00" + f"000000000017{output_view}" + f"0000000000000000356a335341542b04589fb29aac15b9a4b7f17c3385939b007540f4d7911ef01e76f1aad50144a32680f16aa97a10f8af950180db8767" + f"89130000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + with brownie.reverts("staked value does not meet requirement"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +@pytest.mark.parametrize("output_view", [ + pytest.param("0120aee9137b4958e35085907caaa2d5a9e659b0b1037e06f04280e2e98520f7f16a", id="OP_0 error"), + pytest.param("0021aee9137b4958e35085907caaa2d5a9e659b0b1037e06f04280e2e98520f7f16a", id="OP_PUSHBYTES_32 error"), + pytest.param("0020aee9137b4958e35085907caaa2d5a9e659b0b1036e06f04280e2e98520f7f16c", id="ScriptPubKey error") +]) +def test_revert_on_invalid_witness_btc_tx_output(pledge_agent, set_candidate, delegate_btc_valid_tx, output_view): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + btc_tx = delegate_btc_block_data[ + 'witness_btc_tx_block_data'] + ( + f"03b80b00000000000022{output_view}" + f"0000000000000000356a335341542b04589fb29aac15b9a4b7f17c3385939b007540f4d7911ef01e76f1aad50144a32680f16aa97a10f8af950180db8767" + "8e440100000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + with brownie.reverts("staked value does not meet requirement"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_insufficient_payload_length(pledge_agent, set_candidate, delegate_btc_valid_tx): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + btc_tx = delegate_btc_block_data[ + 'witness_btc_tx_block_data'] + ( + f"03b80b00" + f"0000000000220020aee9137b4958e35085907caaa2d5a9e659b0b1037e06f04280e2e98520f7f16a" + f"00000000000000002c6a2a045cccf7e1dab7d90a0a91f8b1f6a693bf0bb3a979a09fb29aac15b9a4b7f17c3385939b007540f4d791" + "8e440100000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + with brownie.reverts("payload length is too small"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_invalid_magic_value(pledge_agent, set_candidate, delegate_btc_valid_tx): + lock_script, _, _ = delegate_btc_valid_tx + btc_tx = delegate_btc_block_data[ + 'btc_tx_block_data'] + ( + "03d00700000000000017a914c0958c8d9357598c5f7a6eea8a807d81683f9bb687" + "0000000000000000526a4c4f5341542c04589fb29aac15b9a4b7f17c3385939b007540f4d7911ef01e76f1aad50144a32680f16aa97a10f8af95010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88aca443" + "0000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + with brownie.reverts("wrong magic"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_claim_rewards_with_max_delegate_btc(pledge_agent, set_candidate): + operators, consensuses = [], [] + pledge_agent.setClaimRoundLimit(10) + for operator in accounts[10:22]: + operators.append(operator) + consensuses.append(register_candidate(operator=operator)) + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + tx_id_list = [] + for index, operator in enumerate(operators): + btc_tx, tx_id = get_btc_tx(BTC_VALUE + index, chain_id, operator, accounts[0], lock_script_type, lock_script) + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + tx_id_list.append(tx_id) + turn_round() + turn_round(consensuses, round_count=3) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list) + start_index = 0 + for i in range(3): + del tx_id_list[0] + start_index += 1 + assert tracker0.delta() == BLOCK_REWARD // 2 * 10 - FEE * 4 + assert tracker1.delta() == FEE * 4 + last_voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + expect_query(last_voucher, {'agent': operators[start_index], 'rewardIndex': 1, 'value': BTC_VALUE + start_index}) + pledge_agent.claimBtcReward(tx_id_list) + for i in range(3): + del tx_id_list[0] + start_index += 1 + last_voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + expect_query(last_voucher, {'agent': operators[start_index], 'rewardIndex': 2, 'value': BTC_VALUE + start_index}) + pledge_agent.claimBtcReward(tx_id_list) + for i in range(4): + del tx_id_list[0] + start_index += 1 + last_voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + expect_query(last_voucher, {'agent': operators[start_index], 'rewardIndex': 0, 'value': BTC_VALUE + start_index}) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == BLOCK_REWARD // 2 * 26 - FEE * 8 + assert tracker1.delta() == FEE * 8 + + +def test_claim_rewards_with_max_claim_limit(pledge_agent, delegate_btc_valid_tx): + operators, consensuses = [], [] + pledge_agent.setClaimRoundLimit(2) + for operator in accounts[5:9]: + operators.append(operator) + consensuses.append(register_candidate(operator=operator)) + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE + 1, chain_id, operators[1], accounts[0], lock_script_type, + lock_script) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[1]}) + tx_id_list.append(tx_id1) + turn_round() + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + assert pledge_agent.btcReceiptMap(tx_id_list[0])['rewardIndex'] == 2 + assert pledge_agent.btcReceiptMap(tx_id1)['rewardIndex'] == 0 + turn_round(consensuses, round_count=1) + pledge_agent.setClaimRoundLimit(4) + pledge_agent.claimBtcReward(tx_id_list) + turn_round(consensuses, round_count=1) + assert tracker0.delta() == BLOCK_REWARD * 3 - FEE * 2 + assert pledge_agent.btcReceiptMap(tx_id_list[0])['agent'] == ZERO_ADDRESS + assert pledge_agent.btcReceiptMap(tx_id1)['agent'] == ZERO_ADDRESS + + +def test_collect_porter_fee_success(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + tx_id = tx_id_list[0] + round_tag = get_current_round() + pledge_agent.delegateBtc(btc_tx, round_tag, [], 0, lock_script, {'from': accounts[1]}) + btc_delegator = pledge_agent.btcReceiptMap(tx_id) + expect_query(btc_delegator, { + 'feeReceiver': accounts[1], + 'fee': FEE + }) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward(tx_id_list) + expect_event(tx, "transferredBtcFee", { + "txid": tx_id_list[0], + "feeReceiver": accounts[1], + "fee": FEE + }) + assert pledge_agent.btcReceiptMap(tx_id)['fee'] == 0 + tx = pledge_agent.claimBtcReward(tx_id_list) + assert 'transferredBtcFee' not in tx.events + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE + assert tracker1.delta() == FEE + + +def test_deduct_multiple_porter_fees_when_claiming_rewards(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + round_tag = get_current_round() + tx_id0 = tx_id_list[0] + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type) + tx_id_list.append(tx_id1) + pledge_agent.delegateBtc(delegate_btc_tx0, round_tag, [], 0, lock_script, {'from': accounts[1]}) + pledge_agent.delegateBtc(delegate_btc_tx1, round_tag, [], 0, lock_script, {'from': accounts[2]}) + btc_delegator0 = pledge_agent.btcReceiptMap(tx_id0) + btc_delegator1 = pledge_agent.btcReceiptMap(tx_id1) + expect_query(btc_delegator0, { + 'feeReceiver': accounts[1], + 'fee': FEE + }) + expect_query(btc_delegator1, { + 'feeReceiver': accounts[2], + 'fee': FEE + }) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + tx = pledge_agent.claimBtcReward(tx_id_list) + assert "claimedReward" in tx.events + expect_event(tx, "transferredBtcFee", { + "txid": tx_id0, + "feeReceiver": accounts[1], + "fee": FEE + }, idx=0) + expect_event(tx, "transferredBtcFee", { + "txid": tx_id1, + "feeReceiver": accounts[2], + "fee": FEE + }, idx=1) + assert pledge_agent.btcReceiptMap(tx_id0)['fee'] == pledge_agent.btcReceiptMap(tx_id1)['fee'] == 0 + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE * 2 + assert tracker1.delta() == FEE + assert tracker2.delta() == FEE + + +def test_claim_rewards_and_deduct_porter_fees_after_transfer(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + assert pledge_agent.btcReceiptMap(tx_id_list[0])["endRound"] == end_round + pledge_agent.transferBtc(tx_id_list[0], operators[1]) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker0.delta() == 0 + assert tracker1.delta() == total_reward - total_reward // 2 + assert pledge_agent.btcReceiptMap(tx_id_list[0])["fee"] == FEE + turn_round(consensuses) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == total_reward - FEE + assert tracker1.delta() == total_reward + FEE + + +def test_transfer_with_nonexistent_stake_certificate(pledge_agent, set_candidate, delegate_btc_valid_tx): + tx_id = '0x8a2d192b0d0276fee31689693269e14aa9c78982c0d29ddf417a3064fd623892' + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + turn_round() + with brownie.reverts("btc tx not found"): + pledge_agent.transferBtc(tx_id, operators[1]) + + +def test_transfer_btc_to_current_validator(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + with brownie.reverts("can not transfer to the same validator"): + pledge_agent.transferBtc(tx_id_list[0], operators[0]) + + +def test_transfer_btc_to_validator_with_lock_period_ending(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round(consensuses, round_count=3) + with brownie.reverts("insufficient locking rounds"): + pledge_agent.transferBtc(tx_id_list[0], operators[1]) + turn_round(consensuses, round_count=1) + assert pledge_agent.btcReceiptMap(tx_id_list[0])['agent'] == operators[0] + with brownie.reverts("insufficient locking rounds"): + pledge_agent.transferBtc(tx_id_list[0], operators[1]) + pledge_agent.claimBtcReward(tx_id_list) + # after the lockout period expires, the recorded data will be reset to zero. + assert pledge_agent.btcReceiptMap(tx_id_list[0])['agent'] == ZERO_ADDRESS + + +def test_transfer_to_non_validator_target(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round(consensuses) + error_msg = encode_args_with_signature("InactiveAgent(address)", [accounts[2].address]) + with brownie.reverts(f"typed error: {error_msg}"): + pledge_agent.transferBtc(tx_id_list[0], accounts[2]) + assert pledge_agent.btcReceiptMap(tx_id_list[0])['value'] == BTC_VALUE + + +def test_transfer_btc_between_different_validators(pledge_agent, candidate_hub, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + tx_id = tx_id_list[0] + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + pledge_agent.transferBtc(tx_id, operators[1]) + pledge_agent.transferBtc(tx_id, operators[2]) + pledge_agent.transferBtc(tx_id, operators[0]) + pledge_agent.transferBtc(tx_id, operators[1]) + addr_list = pledge_agent.getAgentAddrList(end_round) + for index, addr in enumerate(addr_list): + assert addr == operators[index] + assert len(addr_list) == 3 + assert pledge_agent.getReward(operators[0], 0)[3] == 0 + turn_round(consensuses, round_count=2) + tx = pledge_agent.claimBtcReward(tx_id_list) + expect_event(tx, "claimedReward", { + "amount": BLOCK_REWARD // 2 - FEE, + "success": True + }) + assert pledge_agent.btcReceiptMap(tx_id)['agent'] == operators[1] + turn_round(consensuses) + tx = pledge_agent.claimBtcReward(tx_id_list) + expect_event(tx, "claimedReward", { + "amount": BLOCK_REWARD // 2, + }) + + +def test_claim_rewards_with_insufficient_porter_funds(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + core_fee = 136 + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + round_tag = get_current_round() + delegate_btc_tx0, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, + core_fee=core_fee) + pledge_agent.delegateBtc(delegate_btc_tx0, round_tag, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward([tx_id]) + voucher = pledge_agent.btcReceiptMap(tx_id) + expect_event(tx, "transferredBtcFee", { + "txid": tx_id, + "feeReceiver": accounts[1], + "fee": BLOCK_REWARD // 2 + }) + remain_fee = FEE * core_fee - BLOCK_REWARD // 2 + assert voucher['fee'] == remain_fee + assert "claimedReward" not in tx.events + assert tracker0.delta() == 0 + assert tracker1.delta() == BLOCK_REWARD // 2 + turn_round(consensuses) + pledge_agent.claimBtcReward([tx_id]) + assert tracker0.delta() == BLOCK_REWARD // 2 - remain_fee + assert tracker1.delta() == remain_fee + + +def test_deduct_porter_fees_for_multi_round_rewards_successfully(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + core_fee = 255 + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + round_tag = get_current_round() + delegate_btc_tx0, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, core_fee=255) + pledge_agent.delegateBtc(delegate_btc_tx0, round_tag, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward([tx_id]) + expect_event(tx, "transferredBtcFee", { + "txid": tx_id, + "feeReceiver": accounts[1], + "fee": FEE * core_fee + }) + voucher = pledge_agent.btcReceiptMap(tx_id) + assert voucher['fee'] == 0 + assert "claimedReward" in tx.events + assert tracker0.delta() == BLOCK_REWARD - FEE * core_fee + assert tracker1.delta() == FEE * core_fee + + +def test_duplicate_transfer_success(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + tx = pledge_agent.transferBtc(tx_id_list[0], operators[1]) + assert 'transferredBtc' in tx.events + tx = pledge_agent.transferBtc(tx_id_list[0], operators[2]) + assert 'transferredBtc' in tx.events + tx = pledge_agent.transferBtc(tx_id_list[0], operators[0]) + assert 'transferredBtc' in tx.events + addr_list = pledge_agent.getAgentAddrList(end_round) + assert len(addr_list) == 3 + tx = pledge_agent.claimBtcReward(tx_id_list) + assert 'claimedReward' not in tx.events + + +def test_claim_rewards_success_with_max_stake_certificates(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + pledge_agent.setClaimRoundLimit(10) + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + tx_id = tx_id_list[0] + turn_round() + for i in range(11): + before_transfer_agent = pledge_agent.btcReceiptMap(tx_id)['agent'] + assert pledge_agent.btcReceiptMap(tx_id)['agent'] == before_transfer_agent + if i % 2 == 0: + pledge_agent.transferBtc(tx_id, operators[1]) + agent = operators[1] + else: + pledge_agent.transferBtc(tx_id, operators[2]) + agent = operators[2] + assert pledge_agent.btcReceiptMap(tx_id)['agent'] == agent + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward(tx_id_list) + assert 'claimedReward' not in tx.events + assert tracker0.delta() == 0 + + +def test_revert_on_invalid_btc_transaction(pledge_agent, set_candidate): + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx = (delegate_btc_block_data['btc_tx_block_data'] + + "03a9149ca26c6aa5a614836d041193ab7df1b6d650791387" + "00000000000000002c6a2a045c96c42c56fdb78294f96b0cfa33c92be" + "d7d75f96a9fb29aac15b9a4b7f17c3385939b007540f4d7914e930100000000001976a914574fdd26858c28ede5225a809f747c" + "01fcc1f92a88ac00000000") + with brownie.reverts("BitcoinHelper: invalid tx"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_claim_btc_staking_rewards_success(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + round_tag = get_current_round() + pledge_agent.delegateBtc(btc_tx, round_tag, [], 0, lock_script) + turn_round() + turn_round(consensuses) + tracker = get_tracker(accounts[0]) + tx = pledge_agent.claimBtcReward(tx_id_list) + expect_event(tx, "claimedReward", { + "amount": BLOCK_REWARD // 2 - FEE, + "success": True + }) + assert tracker.delta() == BLOCK_REWARD // 2 + + +@pytest.mark.parametrize("internal", [ + pytest.param(0, id="same round"), + pytest.param(1, id="adjacent rounds"), + pytest.param(2, id="spanning multiple rounds"), +]) +def test_claim_multi_round_btc_staking_rewards(pledge_agent, set_candidate, internal): + operators, consensuses = set_candidate + tx_id_list = [] + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id0 = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + tx_id_list.append(tx_id0) + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[0], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script) + tx_id_list.append(tx_id1) + turn_round() + turn_round(consensuses, round_count=internal) + tracker = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker.delta() == BLOCK_REWARD * internal + + +def test_distribute_rewards_to_multiple_addresses(pledge_agent, set_candidate): + operators, consensuses = set_candidate + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id0 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[1], lock_script_type) + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[0], lock_script_type) + tx_id_list0, tx_id_list1 = [], [] + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script) + tx_id_list0.append(tx_id0) + tx_id_list1.append(tx_id1) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list1) + pledge_agent.claimBtcReward(tx_id_list0, {"from": accounts[1]}) + assert tracker0.delta() == BLOCK_REWARD // 4 + FEE + assert tracker1.delta() == BLOCK_REWARD // 2 - BLOCK_REWARD // 4 - FEE + + +def test_claim_rewards_for_multiple_coin_staking(pledge_agent, set_candidate): + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id0 = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[1], lock_script_type) + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[0], lock_script_type) + delegate_amount = 50000 + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + pledge_agent.delegateCoin(operators[1], {"value": delegate_amount // 2}) + tx_id_list0, tx_id_list1 = [], [] + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[2]}) + tx_id_list0.append(tx_id0) + tx_id_list1.append(tx_id1) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list1) + pledge_agent.claimBtcReward(tx_id_list0, {"from": accounts[1]}) + actual_reward0 = total_reward * (BTC_VALUE * btcFactor) // (delegate_amount // 2 + BTC_VALUE * btcFactor) + actual_reward1 = total_reward * (BTC_VALUE * btcFactor) // (delegate_amount + BTC_VALUE * btcFactor) + assert tracker0.delta() == actual_reward0 - FEE + assert tracker1.delta() == actual_reward1 - FEE + + +def test_unable_to_claim_rewards_after_end_round(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + round_tag = end_round - MIN_BTC_LOCK_ROUND - 1 + set_last_round_tag(round_tag) + operators, consensuses = set_candidate + lock_script, delegate_btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx, 0, [], 0, lock_script) + turn_round() + turn_round(consensuses, round_count=3) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == BLOCK_REWARD * 1.5 + voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + assert voucher['delegator'] == ZERO_ADDRESS + turn_round(consensuses) + # Unable to claim, as the expired data is removed from the btcReceiptList, resulting in the message 'not the delegator of this btc receipt' + with brownie.reverts("btc tx not found"): + pledge_agent.claimBtcReward(tx_id_list) + + +def test_single_validator_multiple_stakes(pledge_agent, set_candidate): + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + lock_time1 = lock_time + ROUND_INTERVAL * 2 + lock_script0 = get_lock_script(lock_time, public_key, lock_script_type) + lock_script1 = get_lock_script(lock_time1, public_key, lock_script_type) + end_round = lock_time // ROUND_INTERVAL + round_tag = end_round - MIN_BTC_LOCK_ROUND - 1 + set_last_round_tag(round_tag) + btc_amount0 = 3000 * btcFactor + btc_amount1 = 2500 * btcFactor + delegate_amount = 45000 + delegate_btc_tx0, tx_id0 = get_btc_tx(btc_amount0 // btcFactor, chain_id, operators[0], accounts[0], + lock_script_type) + delegate_btc_tx1 = (delegate_btc_block_data['witness_btc_tx_block_data'] + + ("03c40900" + "000000000022002043c1a535b8941dbb2945ab932abb99995ffc7a5c3ea680b121ef9ca99b7dee45" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a0010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + "bcc00000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000")) + tx_id_list = [] + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script0) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script1) + tx_id_list.append(tx_id0) + tx_id_list.append(get_transaction_txid(delegate_btc_tx1)) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + turn_round() + turn_round(consensuses, round_count=6) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + total_btc_amount = btc_amount0 + btc_amount1 + reward0 = total_reward * total_btc_amount // (total_btc_amount + delegate_amount) + reward1 = total_reward * btc_amount1 // (btc_amount1 + delegate_amount) + actual_reward = reward0 * 3 + reward1 * 2 + assert tracker0.delta() == actual_reward + + +def test_no_rewards_generated_at_end_of_round(pledge_agent, delegate_btc_valid_tx, set_candidate): + operators, consensuses = set_candidate + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + end_round = lock_time // ROUND_INTERVAL + round_tag = end_round - MIN_BTC_LOCK_ROUND - 1 + set_last_round_tag(round_tag) + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + turn_round(consensuses, round_count=2) + assert pledge_agent.getAgentAddrList(end_round)[0] == operators[0] + # endRound:20103 + # at the end of round 20102, the expired BTC staking will be deducted from the validator upon transitioning to round 20103. + turn_round(consensuses, round_count=1) + assert len(pledge_agent.getAgentAddrList(end_round)) == 0 + tx = pledge_agent.claimBtcReward(tx_id_list) + assert "claimedReward" in tx.events + turn_round(consensuses, round_count=1) + assert "claimedReward" in tx.events + + +def test_multiple_users_staking_to_same_validator(pledge_agent, set_candidate): + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + lock_time1 = lock_time + ROUND_INTERVAL * 2 + lock_script0 = get_lock_script(lock_time, public_key, lock_script_type) + lock_script1 = get_lock_script(lock_time1, public_key, lock_script_type) + end_round = lock_time // ROUND_INTERVAL + round_tag = end_round - MIN_BTC_LOCK_ROUND - 1 + set_last_round_tag(round_tag) + btc_amount0 = 3000 * btcFactor + btc_amount1 = 2500 * btcFactor + delegate_amount = 45000 + delegate_btc_tx0, tx_id0 = get_btc_tx(btc_amount0 // btcFactor, chain_id, operators[0], accounts[1], + lock_script_type) + delegate_btc_tx1 = delegate_btc_block_data[ + 'witness_btc_tx_block_data'] + ("03c40900" + "000000000022002043c1a535b8941dbb2945ab932abb99995ffc7a5c3ea680b121ef9ca99b7dee45" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a0010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + "722f0000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + delegate_btc_tx2, tx_id2 = get_btc_tx(btc_amount0 // btcFactor, chain_id, operators[0], accounts[0], + lock_script_type) + tx_id_list0, tx_id_list1 = [], [] + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script0) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script1) + pledge_agent.delegateBtc(delegate_btc_tx2, 0, [], 0, lock_script0) + tx_id_list0.append(get_transaction_txid(delegate_btc_tx1)) + tx_id_list0.append(tx_id2) + tx_id_list1.append(tx_id0) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + turn_round() + turn_round(consensuses, round_count=5) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list0) + pledge_agent.claimBtcReward(tx_id_list1, {"from": accounts[1]}) + total_btc_amount = btc_amount0 * 2 + btc_amount1 + reward0 = (total_reward * btc_amount0 // (total_btc_amount + delegate_amount) * 3) + ( + total_reward * btc_amount1 // (total_btc_amount + delegate_amount) * 3) + total_reward * ( + btc_amount1) // (btc_amount1 + delegate_amount) * 2 + reward1 = total_reward * btc_amount0 // (total_btc_amount + delegate_amount) * 3 + assert tracker0.delta() == reward0 + FEE + assert tracker1.delta() == reward1 - FEE + + +def test_claim_rewards_for_multiple_stakes_to_different_validators(pledge_agent, set_candidate, delegate_btc_valid_tx): + total_reward = BLOCK_REWARD // 2 + delegate_amount = 30000 + operators, consensuses = set_candidate + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[0], lock_script_type) + tx_id_list.append(tx_id1) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + pledge_agent.delegateCoin(operators[1], {"value": delegate_amount, "from": accounts[2]}) + turn_round() + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + actual_reward0 = total_reward * BTC_VALUE * btcFactor // (delegate_amount + BTC_VALUE * btcFactor) + assert tracker0.delta() == actual_reward0 * 4 + + +@pytest.mark.parametrize("btc_factor", [ + pytest.param(1, id="btc_factor is 1"), + pytest.param(1000, id="btc_factor is 1000"), + pytest.param(10000, id="btc_factor is 10000"), + pytest.param(100000, id="btc_factor is 100000"), + pytest.param(1000000, id="btc_factor is 1000000"), +]) +def test_claim_rewards_after_modifying_btc_factor(pledge_agent, set_candidate, delegate_btc_valid_tx, btc_factor): + pledge_agent.setBtcFactor(btc_factor) + btc_factor = btc_factor * pledge_agent.BTC_UNIT_CONVERSION() + total_reward = BLOCK_REWARD // 2 + delegate_amount = 3000000 + operators, consensuses = set_candidate + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + actual_reward0 = total_reward * BTC_VALUE * btc_factor // (delegate_amount + BTC_VALUE * btc_factor) + assert tracker0.delta() == actual_reward0 + + +def test_claim_rewards_after_staking_every_other_round(pledge_agent, set_candidate, delegate_btc_valid_tx): + total_reward = BLOCK_REWARD // 2 + delegate_amount = 30000 + operators, consensuses = set_candidate + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[2]}) + turn_round() + btc_amount = 2500 + total_btc_amount = BTC_VALUE * btcFactor + delegate_btc_tx1, tx_id1 = get_btc_tx(btc_amount, chain_id, operators[0], accounts[0], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script) + tx_id_list.append(tx_id1) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + actual_reward0 = total_reward * BTC_VALUE * btcFactor // (delegate_amount + total_btc_amount) + assert tracker0.delta() == actual_reward0 + turn_round(consensuses) + pledge_agent.claimBtcReward(tx_id_list) + total_btc_amount += btc_amount * btcFactor + actual_reward0 = total_reward * total_btc_amount // (delegate_amount + total_btc_amount) + assert tracker0.delta() == actual_reward0 + + +def test_transfer_btc_success(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + assert voucher["endRound"] == end_round + tx = pledge_agent.transferBtc(tx_id_list[0], operators[1]) + pledge_agent.transferCoin(operators[0], operators[2], {'from': accounts[1]}) + expect_event(tx, 'transferredBtc', { + 'txid': tx_id_list[0], + 'sourceAgent': operators[0], + 'targetAgent': operators[1], + 'delegator': accounts[0], + 'amount': BTC_VALUE, + 'totalAmount': BTC_VALUE + }) + turn_round(consensuses, round_count=4) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker0.delta() == total_reward * 2 + assert tracker1.delta() == total_reward * 3 + total_reward - (total_reward // 2) + + +def test_transfer_btc_when_no_rewards_in_current_round(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + pledge_agent.transferBtc(tx_id_list[0], operators[1]) + voucher0 = pledge_agent.btcReceiptMap(tx_id_list[0]) + assert voucher0["endRound"] == end_round + turn_round(consensuses, round_count=1) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker0.delta() == 0 + assert tracker1.delta() == total_reward - total_reward // 2 + voucher0 = pledge_agent.btcReceiptMap(tx_id_list[0]) + assert voucher0["endRound"] == end_round + assert pledge_agent.agentsMap(operators[0])["btc"] == pledge_agent.agentsMap(operators[0])["totalBtc"] == 0 + assert pledge_agent.agentsMap(operators[1])["btc"] == pledge_agent.agentsMap(operators[1])["totalBtc"] == BTC_VALUE + + +def test_revert_on_max_fee_exceeded(pledge_agent, set_candidate): + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + # fee range 1-255 + delegate_btc_tx0, _ = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, lock_script, + core_fee=256) + with brownie.reverts("BitcoinHelper: invalid tx"): + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + + +def test_claim_rewards_with_fee_deduction_success(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {"from": accounts[1]}) + turn_round() + turn_round(consensuses, round_count=2) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward(tx_id_list) + expect_event(tx, 'transferredBtcFee', { + 'txid': tx_id_list[0], + 'fee': FEE, + 'feeReceiver': accounts[1] + }) + assert tracker1.delta() == FEE + + +@pytest.mark.parametrize("fee", [ + pytest.param(0, id="fee is 0"), + pytest.param(1, id="fee is 1"), + pytest.param(10, id="fee is 10"), + pytest.param(50, id="fee is 50"), + pytest.param(254, id="fee is 254"), + pytest.param(255, id="fee is 255") +]) +def test_claim_rewards_with_different_fees_success(pledge_agent, set_candidate, fee): + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id0 = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, + core_fee=fee) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {"from": accounts[1]}) + turn_round() + turn_round(consensuses, round_count=2) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward([tx_id0]) + if fee > 0: + expect_event(tx, 'transferredBtcFee', { + 'txid': tx_id0, + 'fee': fee * pledge_agent.FEE_FACTOR(), + 'feeReceiver': accounts[1] + }) + else: + assert 'transferredBtcFee' not in tx.events + assert tracker1.delta() == fee * pledge_agent.FEE_FACTOR() + + +def test_insufficient_rewards_to_pay_porter_fee(pledge_agent, set_candidate): + end_round = lock_time // ROUND_INTERVAL + fee = 100 + actual_fee = fee * pledge_agent.FEE_FACTOR() + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, core_fee=fee) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {"from": accounts[1]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + turn_round(consensuses, round_count=1) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward([tx_id]) + received_fee = BLOCK_REWARD // 4 + expect_event(tx, 'transferredBtcFee', { + 'txid': tx_id, + 'fee': received_fee, + 'feeReceiver': accounts[1] + }) + voucher = pledge_agent.btcReceiptMap(tx_id) + assert voucher['fee'] == actual_fee - received_fee + assert voucher['feeReceiver'] == accounts[1] + assert tracker1.delta() == received_fee + turn_round(consensuses, round_count=1) + pledge_agent.claimBtcReward([tx_id]) + assert tracker1.delta() == actual_fee - received_fee + assert pledge_agent.btcReceiptMap(tx_id)['fee'] == 0 + assert tracker0.delta() == BLOCK_REWARD // 4 - (actual_fee - received_fee) + + +@pytest.mark.parametrize("fee", [ + pytest.param(0, id="fee is 0"), + pytest.param(1, id="fee is 1"), + pytest.param(10, id="fee is 10"), + pytest.param(50, id="fee is 50"), + pytest.param(254, id="fee is 254"), + pytest.param(255, id="fee is 255") +]) +def test_claim_rewards_with_different_fees_after_transfer_success(pledge_agent, set_candidate, fee): + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + delegate_btc_tx0, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, core_fee=fee) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {"from": accounts[1]}) + turn_round() + pledge_agent.transferBtc(tx_id, operators[1]) + turn_round(consensuses, round_count=3) + tracker1 = get_tracker(accounts[1]) + tx = pledge_agent.claimBtcReward([tx_id]) + if fee > 0: + expect_event(tx, 'transferredBtcFee', { + 'txid': tx_id, + 'fee': fee * pledge_agent.FEE_FACTOR(), + 'feeReceiver': accounts[1] + }) + else: + assert 'transferredBtcFee' not in tx.events + assert tracker1.delta() == fee * pledge_agent.FEE_FACTOR() + + +def test_multiple_btc_receipts_to_single_address(pledge_agent, set_candidate, delegate_btc_valid_tx): + delegate_amount = 20000 + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + round_tag = get_current_round() + tx_id0 = get_transaction_txid(delegate_btc_tx0) + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[0], lock_script_type) + tx_id_list.append(tx_id1) + pledge_agent.delegateBtc(delegate_btc_tx0, round_tag, [], 0, lock_script, {'from': accounts[1]}) + pledge_agent.delegateBtc(delegate_btc_tx1, round_tag, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[2], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + pledge_agent.transferBtc(tx_id0, operators[2]) + pledge_agent.transferBtc(tx_id1, operators[2]) + turn_round(consensuses) + tx = pledge_agent.claimBtcReward(tx_id_list) + assert 'claimedReward' not in tx.events + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + actual_reward = total_reward * (BTC_VALUE * 2 * btcFactor) // (BTC_VALUE * 2 * btcFactor + delegate_amount) + assert tracker0.delta() == actual_reward * 2 - FEE * 2 + + +def test_multiple_reward_transfers_in_multiple_rounds(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = [], [] + for operator in accounts[10:22]: + operators.append(operator) + consensuses.append(register_candidate(operator=operator)) + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + tx_id_list = [] + for index, operator in enumerate(operators): + btc_tx, tx_id = get_btc_tx(BTC_VALUE + index, chain_id, operator, accounts[0], lock_script_type, + lock_script) + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + tx_id_list.append(tx_id) + turn_round() + for index, operator in enumerate(operators): + before_agent = operators[index] + + transfer_voucher = pledge_agent.btcReceiptMap(tx_id_list[index]) + expect_query(transfer_voucher, + {'agent': before_agent, + 'delegator': accounts[0], + 'value': BTC_VALUE + index, + 'endRound': end_round, + 'rewardIndex': 0, + 'feeReceiver': accounts[1], + 'fee': FEE}) + tx = pledge_agent.transferBtc(tx_id_list[index], operators[index - 1]) + before_amount = BTC_VALUE + index + target_agent_amount = 0 + if index == 0: + target_agent_amount = operators.index(operators[index - 1]) + BTC_VALUE + expect_event(tx, "transferredBtc", { + "txid": tx_id_list[index], + "sourceAgent": operators[index], + "targetAgent": operators[index - 1], + "amount": BTC_VALUE + index, + "totalAmount": before_amount + target_agent_amount, + }) + latest_voucher = pledge_agent.btcReceiptMap(tx_id_list[index]) + expect_query(latest_voucher, + { + 'agent': operators[index - 1], + 'delegator': accounts[0], + 'value': BTC_VALUE + index, + 'endRound': end_round, + 'rewardIndex': 1, + 'feeReceiver': accounts[1], + 'fee': FEE} + ) + + pledge_agent.setClaimRoundLimit(3) + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == BLOCK_REWARD // 2 * 3 - FEE * 3 + voucher = pledge_agent.btcReceiptMap(tx_id_list[0]) + assert voucher['agent'] == operators[-1] + pledge_agent.setClaimRoundLimit(7) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == BLOCK_REWARD // 2 * 7 - FEE * 7 + turn_round(consensuses, round_count=2) + pledge_agent.setClaimRoundLimit(21) + pledge_agent.claimBtcReward(tx_id_list) + """ + when a validator only has rewards for one round, but I attempt to claim rewards every other round, + even for rounds without rewards, it still counts towards the claim limit. + """ + assert tracker0.delta() == BLOCK_REWARD // 2 * 11 - FEE + assert pledge_agent.btcReceiptMap(tx_id_list[0])['agent'] == ZERO_ADDRESS + pledge_agent.claimBtcReward(tx_id_list[-2:]) + assert tracker0.delta() == BLOCK_REWARD // 2 * 3 - FEE + + +def test_claim_limit_exhausted(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + tx_id_list = [] + for index, operator in enumerate(operators): + btc_tx, tx_id = get_btc_tx(BTC_VALUE, chain_id, operator, accounts[0], lock_script_type, + lock_script) + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + tx_id_list.append(tx_id) + turn_round() + pledge_agent.setClaimRoundLimit(10) + delegate_amount = 20000 + pledge_agent.delegateCoin(operators[1], {"value": delegate_amount, "from": accounts[1]}) + pledge_agent.delegateCoin(operators[2], {"value": delegate_amount, "from": accounts[1]}) + turn_round(consensuses, round_count=2) + pledge_agent.claimBtcReward(tx_id_list) + turn_round(consensuses, round_count=2) + pledge_agent.setClaimRoundLimit(4) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + """ + If the validator has other stakes and has generated rewards, + then the claim limit count will not be affected when claiming BTC rewards. + """ + assert tracker0.delta() == BLOCK_REWARD // 2 + BLOCK_REWARD // 4 * 2 + assert pledge_agent.btcReceiptMap(tx_id_list[2])['agent'] == ZERO_ADDRESS + + +def test_claiming_reward_by_non_owner_of_this_btc_receipt_reverts(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + turn_round(consensuses, round_count=1) + with brownie.reverts("not the delegator of this btc receipt"): + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[1]}) + + +def test_claiming_reward_with_unconfirmed_txid_reverts(pledge_agent, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + turn_round(consensuses, round_count=1) + get_tracker(accounts[0]) + error_tx_id = '0x8a2d192b0d0276fee31689693269e14aa9c78982c0d29ddf417a3064fd623892' + with brownie.reverts("btc tx not found"): + pledge_agent.claimBtcReward([error_tx_id]) + error_tx_id = '0x0' + with brownie.reverts("btc tx not found"): + pledge_agent.claimBtcReward([error_tx_id]) + + +def test_revert_on_incorrect_version(pledge_agent, set_candidate): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script = get_lock_script(lock_time, public_key, lock_script_type) + btc_tx, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type, version=2) + with brownie.reverts("wrong version"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + + +def test_revert_on_transaction_without_op_return(pledge_agent): + lock_script = "0480db8767b17551210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e122103000871fc99dfcbb5a811c5e23c077683b07ab2bbbfff775ce30a809a6d41214152ae" + btc_tx = ( + "020000000102ae7f498ec542f8b2a70d3a5750058337a042b55b4130587a5271568921dc70020000006b483045022100f78b1eaacb6f10100015eca4618edea515d06d1a4ec432b2b669f4cbeed0dd1c02206c9137982f46c1129de1069b83987b1ad907314231077ac992a8e8990c92c8d401210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12ffffffff" + "02bc34" + "000000000000220020f55d9bd2487756dd81b84946aab690e0e2e9b17c681a81c2d1ce22006395292b9b69" + "0000000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000") + with brownie.reverts("payload length is too small"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {"from": accounts[2]}) + + +def test_modify_btc_factor_after_delegating_btc(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + btc_factor = 4 + pledge_agent.setBtcFactor(btc_factor) + btc_factor = btc_factor * pledge_agent.BTC_UNIT_CONVERSION() + delegate_btc_tx1, tx_id = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script) + tx_id_list.append(tx_id) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list) + assert tracker0.delta() == total_reward // 2 + turn_round(consensuses) + pledge_agent.claimBtcReward(tx_id_list) + reward = total_reward * (BTC_VALUE * btc_factor * 2) // (BTC_VALUE * btc_factor * 2 + delegate_amount) + assert tracker0.delta() == reward + + +def test_stake_multiple_currencies_and_claim_rewards(pledge_agent, candidate_hub, btc_light_client, set_candidate, + delegate_btc_valid_tx): + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + pledge_agent.setPowerBlockFactor(100000) + turn_round() + delegate_amount = 60000 + round_tag = candidate_hub.roundTag() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + pledge_agent.delegateCoin(operators[0], {'value': delegate_amount, 'from': accounts[1]}) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + turn_round(consensuses, tx_fee=TX_FEE) + pledge_agent.claimReward([], {'from': accounts[2]}) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker2.delta() == total_reward // 3 * 2 + remain_reward = total_reward - total_reward // 3 * 2 + total_coin = delegate_amount + BTC_VALUE * btcFactor + btc_value = BTC_VALUE * btcFactor + assert tracker0.delta() == remain_reward * btc_value // total_coin + assert tracker1.delta() == remain_reward - remain_reward * btc_value // total_coin + + +def test_claiming_btc_reward_with_multiple_power(pledge_agent, candidate_hub, btc_light_client, set_candidate, + delegate_btc_valid_tx): + operators, consensuses = set_candidate + delegate_amount = 40000 + pledge_agent.setPowerBlockFactor(100000) + turn_round() + round_tag = candidate_hub.roundTag() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + btc_light_client.setMiners(round_tag + 1, operators[1], [accounts[3]]) + pledge_agent.delegateCoin(operators[0], {'value': delegate_amount, 'from': accounts[1]}) + pledge_agent.delegateCoin(operators[1], {'value': delegate_amount, 'from': accounts[1]}) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + tracker3 = get_tracker(accounts[3]) + turn_round(consensuses, tx_fee=TX_FEE) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + pledge_agent.claimReward([], {'from': accounts[2]}) + pledge_agent.claimReward([], {'from': accounts[3]}) + total_reward = BLOCK_REWARD // 2 + score = (delegate_amount * 2 + BTC_VALUE * btcFactor) * 3 + power_score = score // 3 * 2 // 2 + power_reward0 = total_reward * power_score // (power_score + BTC_VALUE * btcFactor + delegate_amount) + power_reward1 = total_reward * power_score // (power_score + delegate_amount) + remain_reward0 = total_reward - power_reward0 + remain_reward1 = total_reward - power_reward1 + reward0 = remain_reward0 * (BTC_VALUE * btcFactor) // (BTC_VALUE * btcFactor + delegate_amount) + reward1 = remain_reward1 + remain_reward0 - reward0 + assert tracker0.delta() == reward0 + assert tracker1.delta() == reward1 + assert tracker2.delta() == power_reward0 + assert tracker3.delta() == power_reward1 + + +def test_claim_coin_reward_correctly_with_existing_btc_staking(pledge_agent, candidate_hub, set_candidate, + delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[1]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + turn_round(consensuses, round_count=5) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker1.delta() == total_reward // 2 * 3 + BLOCK_REWARD + + +@pytest.mark.parametrize("transfer_type", ['all', 'part']) +def test_coin_transfer_with_power_and_btc_staking(pledge_agent, btc_light_client, set_candidate, delegate_btc_valid_tx, + transfer_type): + pledge_agent.setPowerBlockFactor(100000) + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 80000 + total_delegate = delegate_amount + BTC_AMOUNT + transfer_amount = delegate_amount // 2 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + round_tag = get_current_round() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + if transfer_type == 'all': + transfer_amount = delegate_amount + tx = pledge_agent.transferCoin(operators[0], operators[2], transfer_amount, {'from': accounts[1]}) + expect_event(tx, 'transferredCoin', { + 'amount': transfer_amount + }) + total_reward = total_reward * 1 // 3 + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker0.delta() == total_reward * BTC_AMOUNT // total_delegate - FEE + assert tracker1.delta() == total_reward - total_reward * BTC_AMOUNT // total_delegate + + +@pytest.mark.parametrize("undelegate_type", ['all', 'part']) +def test_undelegate_with_power_and_btc_staking(pledge_agent, set_candidate, btc_light_client, delegate_btc_valid_tx, + undelegate_type): + pledge_agent.setPowerBlockFactor(100000) + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + delegate_amount = 20000 + total_delegate = delegate_amount + BTC_AMOUNT + undelegate_amount = delegate_amount + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + round_tag = get_current_round() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + total_score = total_delegate * 3 + if undelegate_type == 'part': + undelegate_amount = 7000 + pledge_agent.undelegateCoin(operators[0], undelegate_amount, {'from': accounts[1]}) + tx = turn_round(consensuses) + deduction_reward = total_reward * undelegate_amount // total_score + power_reward = total_reward * 2 // 3 + expect_event(tx, "receiveDeposit", { + 'amount': power_reward + }, idx=0) + expect_event(tx, "receiveDeposit", { + 'amount': deduction_reward + }, idx=1) + reward1 = total_reward * (delegate_amount - undelegate_amount) // total_score + reward0 = total_reward - power_reward - reward1 - deduction_reward + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + assert tracker1.delta() == reward1 + assert tracker0.delta() == reward0 - FEE + assert deduction_reward + reward0 + reward1 + power_reward == total_reward + + +def test_claiming_btc_reward_with_power_and_btc_staking(pledge_agent, set_candidate, btc_light_client, + delegate_btc_valid_tx): + pledge_agent.setPowerBlockFactor(100000) + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + round_tag = get_current_round() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + turn_round() + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + assert tracker0.delta() == total_reward // 3 - FEE + + +def test_operations_with_coin_power_and_btc_staking(pledge_agent, set_candidate, btc_light_client, + delegate_btc_valid_tx): + pledge_agent.setPowerBlockFactor(100000) + delegate_amount = 20000 + total_delegate = delegate_amount + BTC_AMOUNT + end_round = lock_time // ROUND_INTERVAL + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + round_tag = get_current_round() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + btc_light_client.setMiners(round_tag + 2, operators[0], [accounts[2]]) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + undelegate_amount = 7000 + transfer_amount = delegate_amount // 2 + pledge_agent.transferCoin(operators[0], operators[2], transfer_amount, {'from': accounts[1]}) + pledge_agent.undelegateCoin(operators[2], undelegate_amount, {'from': accounts[1]}) + pledge_agent.transferBtc(tx_id_list[0], operators[1]) + turn_round(consensuses) + total_score = total_delegate * 3 + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + deduction_reward = (total_reward * undelegate_amount // total_score) + (total_reward * BTC_AMOUNT // total_score) + power_reward = total_reward * 2 // 3 + reward1 = total_reward - power_reward - deduction_reward + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + pledge_agent.claimReward([], {'from': accounts[2]}) + total_delegate -= undelegate_amount + assert tracker0.delta() == 0 + assert tracker1.delta() == reward1 + assert tracker2.delta() == total_reward * 2 // 3 + total_score = total_delegate * 3 + turn_round(consensuses) + # the total score of all power. + power = total_score * 2 // 3 + power_reward = total_reward * power // (power + delegate_amount - transfer_amount) + coin_reward = total_reward - power_reward + total_reward + pledge_agent.claimReward(operators, {'from': accounts[1]}) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward([], {'from': accounts[2]}) + assert tracker0.delta() == total_reward - FEE + assert tracker1.delta() == coin_reward + assert tracker2.delta() == power_reward + FEE + + +def test_transfer_btc_reverts_for_non_delegator(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + with brownie.reverts("not the delegator of this btc receipt"): + pledge_agent.transferBtc(tx_id_list[0], operators[1], {'from': accounts[1]}) + with brownie.reverts("not the delegator of this btc receipt"): + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[1]}) + + +def test_claiming_historical_rewards_with_btc_transfer(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + tx = pledge_agent.transferBtc(tx_id_list[0], operators[1], {'from': accounts[0]}) + expect_event(tx, "claimedReward", { + 'amount': BLOCK_REWARD // 4 * 2 - FEE + }) + assert tracker0.delta() == BLOCK_REWARD // 4 * 2 + + +def test_transfer_btc_to_existing_btc_staker(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + delegate_amount = 20000 + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[1], accounts[1], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": delegate_amount, "from": accounts[1]}) + turn_round() + pledge_agent.transferBtc(tx_id_list[0], operators[1], {'from': accounts[0]}) + agent_map0 = pledge_agent.agentsMap(operators[0]) + agent_map1 = pledge_agent.agentsMap(operators[1]) + assert agent_map0['totalBtc'] == 0 + assert agent_map1['totalBtc'] == BTC_VALUE * 2 + turn_round(consensuses, round_count=2) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward([tx_id_list[0]], {'from': accounts[0]}) + pledge_agent.claimBtcReward([tx_id1], {'from': accounts[1]}) + total_reward = BLOCK_REWARD // 2 + assert tracker0.delta() == total_reward // 2 - FEE + assert tracker1.delta() == total_reward + total_reward - total_reward // 2 - FEE + + +def test_transfer_btc_from_multiple_btc_stakings(pledge_agent, set_candidate, delegate_btc_valid_tx): + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, delegate_btc_tx0, tx_id_list = delegate_btc_valid_tx + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[0], accounts[0], lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx0, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[2]}) + tx_id_list.append(tx_id1) + turn_round() + pledge_agent.transferBtc(tx_id_list[0], operators[1], {'from': accounts[0]}) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + total_reward = BLOCK_REWARD // 2 + assert tracker0.delta() == total_reward - total_reward // 2 - FEE + + +def test_multiple_btc_stakings_in_vout(pledge_agent, set_candidate): + btc_amount = 53820 + lock_script = "0480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + operators, consensuses = set_candidate + btc_tx = ( + "0200000001dd94cb72979c528593cb1188f4e3bf43a52f5570edab981e3d303ff24166afe5000000006b483045022100f2f069e37929cdfafffa79dcc1cf478504875fbe2a41704a96aee88ec604c0e502207259c56c67de8de6bb8c15e9d14b6ad16acd86d6a834fbb0531fd27bee7e5e3301210223dd766d6e38eaf9c044dcb18d8221fe8c9a5763ca331e93fadc8f55949b8e12" + "feffffff03b80b00" + "000000000017a914c0958c8d9357598c5f7a6eea8a807d81683f9bb687" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a0010480db8767b17576a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac" + "3cd200000000000017a914c0958c8d9357598c5f7a6eea8a807d81683f9bb68700000000") + tx_id = get_transaction_txid(btc_tx) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {"from": accounts[2]}) + turn_round() + expect_event(tx, 'delegatedBtc', { + 'txid': tx_id, + 'script': '0x' + lock_script, + 'blockHeight': 0, + 'outputIndex': 2 + }) + agent_map = pledge_agent.agentsMap(operators[0]) + expect_query(pledge_agent.btcReceiptMap(tx_id), { + 'agent': operators[0], + 'delegator': accounts[0], + 'value': btc_amount, + 'endRound': lock_time // ROUND_INTERVAL, + 'rewardIndex': 0, + 'feeReceiver': accounts[2], + 'fee': FEE + }) + turn_round(consensuses) + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[2]) + pledge_agent.claimBtcReward([tx_id]) + assert agent_map['totalBtc'] == btc_amount + assert pledge_agent.btcReceiptMap(tx_id)['value'] == btc_amount + assert tracker0.delta() == BLOCK_REWARD // 2 - FEE + assert tracker1.delta() == FEE diff --git a/tests/test_pledge_agent.py b/tests/test_pledge_agent.py index 8be2b222..fae849e5 100644 --- a/tests/test_pledge_agent.py +++ b/tests/test_pledge_agent.py @@ -231,6 +231,7 @@ def test_add_round_reward_success_with_normal_agent(pledge_agent, validator_set) powers = [2, 5] total_coin = 1e7 total_power = 10 + btc_coin = 10 expect_coin_rewards = [0, 0] expect_power_rewards = [0, 0] @@ -243,7 +244,7 @@ def test_add_round_reward_success_with_normal_agent(pledge_agent, validator_set) __candidate_register(agents[0]) __candidate_register(agents[1]) - pledge_agent.setRoundState(total_power, total_coin) + pledge_agent.setRoundState(total_power, total_coin, btc_coin) pledge_agent.setAgentValidator(agents[0], powers[0], coins[0]) pledge_agent.setAgentValidator(agents[1], powers[1], coins[1]) tx = validator_set.addRoundRewardMock(agents, rewards) @@ -260,10 +261,11 @@ def test_add_round_reward_success_with_no_agent(pledge_agent, validator_set): agents = accounts[1:4] rewards = (1e7, 1e8, 1e8) total_coin = 1e7 + btc_coin = 1e7 total_power = 10 __candidate_register(agents[0]) __candidate_register(agents[1]) - pledge_agent.setRoundState(total_power, total_coin) + pledge_agent.setRoundState(total_power, total_coin, btc_coin) tx = validator_set.addRoundRewardMock(agents, rewards) expect_event_not_emitted(tx, "roundReward") @@ -294,7 +296,7 @@ def test_get_score_success(candidate_hub, validator_set): powers = [0, 0, 0, 3, 5] total_coin = required_coin_deposit * 5 + 1 + 10 total_power = POWER_BLOCK_FACTOR * (3 + 5) + 1 - candidate_hub.getScoreMock(agents, powers) + candidate_hub.getScoreMock(agents, powers,0) scores = candidate_hub.getScores() assert len(scores) == 5 for i in range(5): diff --git a/tests/utils.py b/tests/utils.py index a6ebcefb..55de9e92 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -11,6 +11,9 @@ from brownie import accounts, chain, web3, history from eth_account import Account from eth_abi import encode +from hashlib import sha256 + +from tests.btc_block_data import * def random_address(): @@ -26,7 +29,15 @@ def expect_event(tx_receipt: TransactionReceipt, event_name, event_value: dict = # fetch event by idx event = tx_receipt.events[event_name][idx] for k, v in event_value.items(): - assert event[k] == v + assert event[k] == v, f'{k} error {event[k]}!={v}' + + +def get_transaction_txid(btc_tx): + try: + tx_id = '0x' + sha256(sha256(bytes.fromhex(btc_tx)).digest()).digest().hex() + except Exception: + tx_id = '0x00' + return tx_id def expect_event_not_emitted(tx_receipt: TransactionReceipt, event_name): @@ -140,3 +151,64 @@ def expect_query(query_data, expect: dict): for k, v in expect.items(): ex = query_data[k] assert ex == v, f'k:{k} {ex} != {v}' + + +def get_transaction_op_return_data(chain_id, agent_address, delegate_address, lock_data, + core_fee=1, version=1): + if lock_data is not None and len(str(lock_data)) == 10: + hex_result = hex(lock_data)[2:] + lock_time_hex = reverse_by_bytes(hex_result) + op_return_data = lock_time_hex + op_push_data = '6a' + else: + op_return_data = lock_data + op_push_data = '6a4c' + flag_hex = ''.join(format(ord(c), 'x') for c in 'SAT+').zfill(8) + version_hex = format(int(version), 'x').zfill(2) + chain_id_hex = format(int(chain_id), 'x').zfill(4) + delegate_address_hex = str(delegate_address)[2:].lower().zfill(40) + agent_address_hex = str(agent_address)[2:].lower().zfill(40) + core_fee_hex = format(core_fee, 'x').zfill(2) + data_hex = flag_hex + version_hex + chain_id_hex + delegate_address_hex + agent_address_hex + core_fee_hex + op_return_data + op_push_bytes = hex(len(data_hex) // 2).replace('0x', '') + return op_push_data + op_push_bytes + data_hex + + +def get_lock_script(lock_time, public_key, scrip_type='hash'): + lock_scrip = "0x" + hex_result = hex(lock_time)[2:] + lock_time_hex = reverse_by_bytes(hex_result) + if scrip_type == 'hash': + public = public_key2PKHash(public_key) + lock_scrip = "04" + lock_time_hex + "b17576a914" + public[2:] + "88ac" + elif scrip_type == 'key': + public = public_key + lock_scrip = "04" + lock_time_hex + "b17521" + public + "ac" + elif scrip_type == 'multi_sig': + pass + return lock_scrip + + +def reverse_by_bytes(value: str) -> str: + if len(value) % 2 > 0: + value = '0' + value + return "".join(reversed([value[i: i + 2] for i in range(0, len(value), 2)])) + + +def get_btc_tx(value, chain_id, validator, delegator, script_type='hash', lock_data=1736956800, core_fee=1, version=1): + hex_result = hex(value)[2:] + btc_coin = reverse_by_bytes(hex_result).ljust(16, '0') + op_return = get_transaction_op_return_data(chain_id, validator, delegator, lock_data, core_fee, version) + if script_type == 'key': + script_pub_key = output_script_pub_key["script_public_key"] + else: + script_pub_key = output_script_pub_key["script_public_key_hash"] + script_pub_key_length = hex(len(script_pub_key) // 2).replace('0x', '') + op_return_length = hex(len(op_return) // 2).replace('0x', '') + btc_tx = delegate_btc_block_data[ + 'btc_tx_block_data'] + (f'03{btc_coin}{script_pub_key_length}{script_pub_key}' + f'0000000000000000{op_return_length}{op_return}' + f'4e930100000000001976a914574fdd26858c28ede5225a809f747c01fcc1f92a88ac00000000') + + tx_id = get_transaction_txid(btc_tx) + return btc_tx, tx_id From b582f0d4b94ce9f6baa91283ccda6274a95952de Mon Sep 17 00:00:00 2001 From: zfliex Date: Fri, 22 Mar 2024 11:54:14 +0800 Subject: [PATCH 4/9] fix: audit modification opinion --- contracts/BtcLightClient.sol | 3 +++ contracts/PledgeAgent.sol | 30 +++++++++++++++++++--------- contracts/PledgeAgent.template | 30 +++++++++++++++++++--------- contracts/interface/IPledgeAgent.sol | 9 +++++++++ contracts/lib/TypedMemView.sol | 2 +- 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/contracts/BtcLightClient.sol b/contracts/BtcLightClient.sol index ae5f952e..0bae05df 100644 --- a/contracts/BtcLightClient.sol +++ b/contracts/BtcLightClient.sol @@ -293,6 +293,7 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ /// @return True if the provided tx is confirmed on Bitcoin function checkTxProof(bytes32 txid, uint32 blockHeight, uint32 confirmBlock, bytes32[] calldata nodes, uint256 index) public view override returns (bool) { bytes32 blockHash = height2HashMap[blockHeight]; + if (blockHeight + confirmBlock > getChainTipHeight() || txid == bytes32(0) || blockHash == bytes32(0)) { return false; } @@ -677,4 +678,6 @@ contract BtcLightClient is ILightClient, System, IParamSubscriber{ function getRoundCandidates(uint256 roundTimeTag) external override view returns (address[] memory candidates) { return roundPowerMap[roundTimeTag].candidates; } + + } diff --git a/contracts/PledgeAgent.sol b/contracts/PledgeAgent.sol index 3f83de16..ff7ff3d6 100644 --- a/contracts/PledgeAgent.sol +++ b/contracts/PledgeAgent.sol @@ -38,6 +38,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 public constant FEE_FACTOR = 1e18; int256 public constant CLAIM_ROUND_LIMIT = 500; uint256 public constant BTC_UNIT_CONVERSION = 1e10; + uint256 public constant INIT_DELEGATE_BTC_GAS_PRICE = 1e12; uint256 public requiredCoinDeposit; @@ -94,6 +95,8 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { // minimum value to stake for a BTC staking transaction uint256 public minBtcValue; + uint256 public delegateBtcGasPrice; + // HARDFORK V-1.0.7 struct BtcReceipt { address agent; @@ -376,7 +379,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /*********************** External methods ***************************/ /// Delegate coin to a validator /// @param agent The operator address of validator - function delegateCoin(address agent) external payable { + function delegateCoin(address agent) external payable override { if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(agent)) { revert InactiveAgent(agent); } @@ -386,14 +389,14 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Undelegate coin from a validator /// @param agent The operator address of validator - function undelegateCoin(address agent) external { + function undelegateCoin(address agent) external override { undelegateCoin(agent, 0); } /// Undelegate coin from a validator /// @param agent The operator address of validator /// @param amount The amount of CORE to undelegate - function undelegateCoin(address agent, uint256 amount) public { + function undelegateCoin(address agent, uint256 amount) public override { (uint256 deposit, ) = undelegateCoin(agent, msg.sender, amount, false); Address.sendValue(payable(msg.sender), deposit); emit undelegatedCoin(agent, msg.sender, deposit); @@ -402,7 +405,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Transfer coin stake to a new validator /// @param sourceAgent The validator to transfer coin stake from /// @param targetAgent The validator to transfer coin stake to - function transferCoin(address sourceAgent, address targetAgent) external { + function transferCoin(address sourceAgent, address targetAgent) external override { transferCoin(sourceAgent, targetAgent, 0); } @@ -410,7 +413,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @param sourceAgent The validator to transfer coin stake from /// @param targetAgent The validator to transfer coin stake to /// @param amount The amount of CORE to transfer - function transferCoin(address sourceAgent, address targetAgent, uint256 amount) public { + function transferCoin(address sourceAgent, address targetAgent, uint256 amount) public override { if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(targetAgent)) { revert InactiveAgent(targetAgent); } @@ -426,7 +429,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Claim reward for delegator /// @param agentList The list of validators to claim rewards on, it can be empty /// @return (Amount claimed, Are all rewards claimed) - function claimReward(address[] calldata agentList) external returns (uint256, bool) { + function claimReward(address[] calldata agentList) external override returns (uint256, bool) { // limit round count to control gas usage int256 roundLimit = CLAIM_ROUND_LIMIT; uint256 reward; @@ -481,7 +484,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @param nodes part of the Merkle tree from the tx to the root in LE form (called Merkle proof) /// @param index index of the tx in Merkle tree /// @param script the corresponding redeem script of the locked up output - function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external { + function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external override { require(script[0] == bytes1(uint8(0x04)) && script[5] == bytes1(uint8(0xb1)), "not a valid redeem script"); bytes32 txid = btcTx.calculateTxId(); @@ -507,6 +510,9 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert InactiveAgent(br.agent); } + require(IRelayerHub(RELAYER_HUB_ADDR).isRelayer(msg.sender) || msg.sender == br.delegator, "only delegator or relayer can submit the BTC transaction"); + require(tx.gasprice <= (delegateBtcGasPrice == 0 ? INIT_DELEGATE_BTC_GAS_PRICE : delegateBtcGasPrice), "gas price is too high"); + emit delegatedBtc(txid, br.agent, br.delegator, script, blockHeight, outputIndex); if (fee != 0) { @@ -524,7 +530,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// transfer staked BTC to a different validator /// @param txid id of the BTC staking transaction /// @param targetAgent the new validator address to stake to - function transferBtc(bytes32 txid, address targetAgent) public { + function transferBtc(bytes32 txid, address targetAgent) external override { BtcReceipt storage br = btcReceiptMap[txid]; require(br.value != 0, "btc tx not found"); require(br.delegator == msg.sender, "not the delegator of this btc receipt"); @@ -564,7 +570,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// claim BTC staking rewards /// @param txidList the list of BTC staking transaction id to claim rewards /// @return rewardSum amount of reward claimed - function claimBtcReward(bytes32[] calldata txidList) public returns (uint256 rewardSum) { + function claimBtcReward(bytes32[] calldata txidList) external override returns (uint256 rewardSum) { int256 claimLimit = CLAIM_ROUND_LIMIT; uint256 len = txidList.length; for(uint256 i = 0; i < len && claimLimit != 0; i++) { @@ -904,6 +910,12 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert OutOfBounds(key, newMinBtcValue, 1e4, type(uint256).max); } minBtcValue = newMinBtcValue; + } else if (Memory.compareStrings(key,"delegateBtcGasPrice")) { + uint256 newDelegateBtcGasPrice = BytesToTypes.bytesToUint256(32, value); + if (newDelegateBtcGasPrice < 1e9) { + revert OutOfBounds(key, newDelegateBtcGasPrice, 1e9, type(uint256).max); + } + delegateBtcGasPrice = newDelegateBtcGasPrice; } else { require(false, "unknown param"); } diff --git a/contracts/PledgeAgent.template b/contracts/PledgeAgent.template index 0bd8aecf..27f7df55 100644 --- a/contracts/PledgeAgent.template +++ b/contracts/PledgeAgent.template @@ -38,6 +38,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { uint256 public constant FEE_FACTOR = {% if mock %}100{% else %}1e18{% endif %}; int256 public {% if mock %}{% else %}constant{% endif %} CLAIM_ROUND_LIMIT = 500; uint256 public {% if mock %}{% else %}constant{% endif %} BTC_UNIT_CONVERSION = 1e10; + uint256 public constant INIT_DELEGATE_BTC_GAS_PRICE = 1e12; uint256 public requiredCoinDeposit; @@ -94,6 +95,8 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { // minimum value to stake for a BTC staking transaction uint256 public minBtcValue; + uint256 public delegateBtcGasPrice; + // HARDFORK V-1.0.7 struct BtcReceipt { address agent; @@ -376,7 +379,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /*********************** External methods ***************************/ /// Delegate coin to a validator /// @param agent The operator address of validator - function delegateCoin(address agent) external payable { + function delegateCoin(address agent) external payable override { if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(agent)) { revert InactiveAgent(agent); } @@ -386,14 +389,14 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Undelegate coin from a validator /// @param agent The operator address of validator - function undelegateCoin(address agent) external { + function undelegateCoin(address agent) external override { undelegateCoin(agent, 0); } /// Undelegate coin from a validator /// @param agent The operator address of validator /// @param amount The amount of CORE to undelegate - function undelegateCoin(address agent, uint256 amount) public { + function undelegateCoin(address agent, uint256 amount) public override { (uint256 deposit, ) = undelegateCoin(agent, msg.sender, amount, false); Address.sendValue(payable(msg.sender), deposit); emit undelegatedCoin(agent, msg.sender, deposit); @@ -402,7 +405,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Transfer coin stake to a new validator /// @param sourceAgent The validator to transfer coin stake from /// @param targetAgent The validator to transfer coin stake to - function transferCoin(address sourceAgent, address targetAgent) external { + function transferCoin(address sourceAgent, address targetAgent) external override { transferCoin(sourceAgent, targetAgent, 0); } @@ -410,7 +413,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @param sourceAgent The validator to transfer coin stake from /// @param targetAgent The validator to transfer coin stake to /// @param amount The amount of CORE to transfer - function transferCoin(address sourceAgent, address targetAgent, uint256 amount) public { + function transferCoin(address sourceAgent, address targetAgent, uint256 amount) public override { if (!ICandidateHub(CANDIDATE_HUB_ADDR).canDelegate(targetAgent)) { revert InactiveAgent(targetAgent); } @@ -426,7 +429,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// Claim reward for delegator /// @param agentList The list of validators to claim rewards on, it can be empty /// @return (Amount claimed, Are all rewards claimed) - function claimReward(address[] calldata agentList) external returns (uint256, bool) { + function claimReward(address[] calldata agentList) external override returns (uint256, bool) { // limit round count to control gas usage int256 roundLimit = CLAIM_ROUND_LIMIT; uint256 reward; @@ -481,7 +484,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// @param nodes part of the Merkle tree from the tx to the root in LE form (called Merkle proof) /// @param index index of the tx in Merkle tree /// @param script the corresponding redeem script of the locked up output - function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external { + function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external override { require(script[0] == bytes1(uint8(0x04)) && script[5] == bytes1(uint8(0xb1)), "not a valid redeem script"); bytes32 txid = btcTx.calculateTxId(); @@ -507,6 +510,9 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert InactiveAgent(br.agent); } + require(IRelayerHub(RELAYER_HUB_ADDR).isRelayer(msg.sender) || msg.sender == br.delegator, "only delegator or relayer can submit the BTC transaction"); + require(tx.gasprice <= (delegateBtcGasPrice == 0 ? INIT_DELEGATE_BTC_GAS_PRICE : delegateBtcGasPrice), "gas price is too high"); + emit delegatedBtc(txid, br.agent, br.delegator, script, blockHeight, outputIndex); if (fee != 0) { @@ -524,7 +530,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// transfer staked BTC to a different validator /// @param txid id of the BTC staking transaction /// @param targetAgent the new validator address to stake to - function transferBtc(bytes32 txid, address targetAgent) public { + function transferBtc(bytes32 txid, address targetAgent) external override { BtcReceipt storage br = btcReceiptMap[txid]; require(br.value != 0, "btc tx not found"); require(br.delegator == msg.sender, "not the delegator of this btc receipt"); @@ -564,7 +570,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { /// claim BTC staking rewards /// @param txidList the list of BTC staking transaction id to claim rewards /// @return rewardSum amount of reward claimed - function claimBtcReward(bytes32[] calldata txidList) public returns (uint256 rewardSum) { + function claimBtcReward(bytes32[] calldata txidList) external override returns (uint256 rewardSum) { int256 claimLimit = CLAIM_ROUND_LIMIT; uint256 len = txidList.length; for(uint256 i = 0; i < len && claimLimit != 0; i++) { @@ -904,6 +910,12 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { revert OutOfBounds(key, newMinBtcValue, 1e4, type(uint256).max); } minBtcValue = newMinBtcValue; + } else if (Memory.compareStrings(key,"delegateBtcGasPrice")) { + uint256 newDelegateBtcGasPrice = BytesToTypes.bytesToUint256(32, value); + if (newDelegateBtcGasPrice < 1e9) { + revert OutOfBounds(key, newDelegateBtcGasPrice, 1e9, type(uint256).max); + } + delegateBtcGasPrice = newDelegateBtcGasPrice; } else { require(false, "unknown param"); } diff --git a/contracts/interface/IPledgeAgent.sol b/contracts/interface/IPledgeAgent.sol index c421b331..a6ad7398 100644 --- a/contracts/interface/IPledgeAgent.sol +++ b/contracts/interface/IPledgeAgent.sol @@ -7,4 +7,13 @@ interface IPledgeAgent { function setNewRound(address[] calldata validatorList, uint256 round) external; function distributePowerReward(address candidate, address[] calldata miners) external; function onFelony(address agent) external; + function delegateCoin(address agent) external payable; + function undelegateCoin(address agent) external; + function undelegateCoin(address agent, uint256 amount) external; + function transferCoin(address sourceAgent, address targetAgent) external; + function transferCoin(address sourceAgent, address targetAgent, uint256 amount) external; + function claimReward(address[] calldata agentList) external returns (uint256, bool); + function delegateBtc(bytes calldata btcTx, uint32 blockHeight, bytes32[] memory nodes, uint256 index, bytes memory script) external; + function transferBtc(bytes32 txid, address targetAgent) external; + function claimBtcReward(bytes32[] calldata txidList) external returns (uint256 rewardSum); } diff --git a/contracts/lib/TypedMemView.sol b/contracts/lib/TypedMemView.sol index 8aa8df6f..2ee5ef33 100644 --- a/contracts/lib/TypedMemView.sol +++ b/contracts/lib/TypedMemView.sol @@ -372,7 +372,7 @@ library TypedMemView { */ function sameType(bytes29 left, bytes29 right) internal pure returns (bool) { // XOR the inputs to check their difference - return (left ^ right) >> (2 * TWELVE_BYTES) == 0; + return (left ^ right) >> 216 == 0; } /** From 2432979dd6a62105287a63409dea221619c331e0 Mon Sep 17 00:00:00 2001 From: zfliex Date: Sun, 24 Mar 2024 10:32:32 +0800 Subject: [PATCH 5/9] fix: spelling errors --- contracts/PledgeAgent.sol | 10 +++++----- contracts/PledgeAgent.template | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/PledgeAgent.sol b/contracts/PledgeAgent.sol index ff7ff3d6..621df8ef 100644 --- a/contracts/PledgeAgent.sol +++ b/contracts/PledgeAgent.sol @@ -112,7 +112,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { struct BtcExpireInfo { address[] agentAddrList; mapping(address => uint256) agent2valueMap; - mapping(address => uint256) agentExsitMap; + mapping(address => uint256) agentExistMap; } struct CoinDelegator { @@ -260,7 +260,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { agentsMap[agent].totalBtc -= expireInfo.agent2valueMap[agent]; expireInfo.agentAddrList.pop(); delete expireInfo.agent2valueMap[agent]; - delete expireInfo.agentExsitMap[agent]; + delete expireInfo.agentExistMap[agent]; } delete round2expireInfoMap[r]; } @@ -502,7 +502,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { bytes29 payload; uint256 outputIndex; (br.value, payload, outputIndex) = voutView.parseToScriptValueAndData(script); - require(br.value > (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); + require(br.value >= (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); uint256 fee; (br.delegator, br.agent, fee) = parseAndCheckPayload(payload); @@ -808,9 +808,9 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { function addExpire(BtcReceipt storage br) internal { BtcExpireInfo storage expireInfo = round2expireInfoMap[br.endRound]; - if (expireInfo.agentExsitMap[br.agent] == 0) { + if (expireInfo.agentExistMap[br.agent] == 0) { expireInfo.agentAddrList.push(br.agent); - expireInfo.agentExsitMap[br.agent] = 1; + expireInfo.agentExistMap[br.agent] = 1; } expireInfo.agent2valueMap[br.agent] += br.value; } diff --git a/contracts/PledgeAgent.template b/contracts/PledgeAgent.template index 27f7df55..2da4ed3b 100644 --- a/contracts/PledgeAgent.template +++ b/contracts/PledgeAgent.template @@ -112,7 +112,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { struct BtcExpireInfo { address[] agentAddrList; mapping(address => uint256) agent2valueMap; - mapping(address => uint256) agentExsitMap; + mapping(address => uint256) agentExistMap; } struct CoinDelegator { @@ -260,7 +260,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { agentsMap[agent].totalBtc -= expireInfo.agent2valueMap[agent]; expireInfo.agentAddrList.pop(); delete expireInfo.agent2valueMap[agent]; - delete expireInfo.agentExsitMap[agent]; + delete expireInfo.agentExistMap[agent]; } delete round2expireInfoMap[r]; } @@ -502,7 +502,7 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { bytes29 payload; uint256 outputIndex; (br.value, payload, outputIndex) = voutView.parseToScriptValueAndData(script); - require(br.value > (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); + require(br.value >= (minBtcValue == 0 ? INIT_MIN_BTC_VALUE : minBtcValue), "staked value does not meet requirement"); uint256 fee; (br.delegator, br.agent, fee) = parseAndCheckPayload(payload); @@ -808,9 +808,9 @@ contract PledgeAgent is IPledgeAgent, System, IParamSubscriber { function addExpire(BtcReceipt storage br) internal { BtcExpireInfo storage expireInfo = round2expireInfoMap[br.endRound]; - if (expireInfo.agentExsitMap[br.agent] == 0) { + if (expireInfo.agentExistMap[br.agent] == 0) { expireInfo.agentAddrList.push(br.agent); - expireInfo.agentExsitMap[br.agent] = 1; + expireInfo.agentExistMap[br.agent] = 1; } expireInfo.agent2valueMap[br.agent] += br.value; } From fbb4a12b0e7d7239fff0eaf15f37edfe762e987e Mon Sep 17 00:00:00 2001 From: kevin9936 Date: Wed, 6 Mar 2024 17:07:41 +0800 Subject: [PATCH 6/9] supplement BTC stake testcases; --- contracts/CandidateHub.template | 6 + contracts/mock/CandidateHubMock.sol | 8 + contracts/mock/RelayerHubMock.sol | 4 + contracts/test/reentryPledgeAgentProxy.sol | 66 ++++- tests/common.py | 2 +- tests/test_delegate_btc.py | 323 ++++++++++++++++++++- tests/utils.py | 15 +- 7 files changed, 415 insertions(+), 9 deletions(-) diff --git a/contracts/CandidateHub.template b/contracts/CandidateHub.template index d02ac772..6b907c65 100644 --- a/contracts/CandidateHub.template +++ b/contracts/CandidateHub.template @@ -60,6 +60,7 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { uint256 public roundTag; {% if mock %} bool public controlRoundTimeTag = false; + bool public turnroundFailed = false; {% endif %} struct Candidate { @@ -168,6 +169,11 @@ contract CandidateHub is ICandidateHub, System, IParamSubscriber { /// The `turn round` workflow /// @dev this method is called by Golang consensus engine at the end of a round function turnRound() external onlyCoinbase onlyInit onlyZeroGasPrice { + {% if mock %} + if (turnroundFailed == true){ + require(false, "turnRound failed"); + } + {% endif %} // distribute rewards for the about to end round address[] memory lastCandidates = IValidatorSet(VALIDATOR_CONTRACT_ADDR).distributeReward(); diff --git a/contracts/mock/CandidateHubMock.sol b/contracts/mock/CandidateHubMock.sol index 54e71161..c4f95e01 100644 --- a/contracts/mock/CandidateHubMock.sol +++ b/contracts/mock/CandidateHubMock.sol @@ -68,6 +68,14 @@ contract CandidateHubMock is CandidateHub { function setCandidateStatus(address k, uint256 v) public { candidateSet[operateMap[k] - 1].status = v; } + + function setTurnroundFailed(bool value) public { + turnroundFailed = value; + } + function setRoundInterval(uint256 value) public { + roundInterval = value; + } + function getCandidate(address k) public view returns (Candidate memory) { return candidateSet[operateMap[k] - 1]; diff --git a/contracts/mock/RelayerHubMock.sol b/contracts/mock/RelayerHubMock.sol index ec452a8e..27a7074b 100644 --- a/contracts/mock/RelayerHubMock.sol +++ b/contracts/mock/RelayerHubMock.sol @@ -7,4 +7,8 @@ contract RelayerHubMock is RelayerHub { dues = dues / 1e16; requiredDeposit = requiredDeposit / 1e16; } + + function setRelayerRegister(address account, bool value) external { + relayersExistMap[account] = value; + } } diff --git a/contracts/test/reentryPledgeAgentProxy.sol b/contracts/test/reentryPledgeAgentProxy.sol index c1eee91e..cddb155f 100644 --- a/contracts/test/reentryPledgeAgentProxy.sol +++ b/contracts/test/reentryPledgeAgentProxy.sol @@ -1,20 +1,33 @@ pragma solidity 0.8.4; + import "./BaseProxy.sol"; interface IPledgeAgent { function claimReward(address[] calldata agentList) external returns (uint256, bool); + + function claimBtcReward(bytes32[] calldata txidList) external returns (uint256, bool); + function delegateCoin(address agent) external payable; + function undelegateCoin(address agent) external; + function undelegateCoin(address agent, uint256 amount) external; + function transferCoin(address sourceAgent, address targetAgent) external; + function transferCoin(address sourceAgent, address targetAgent, uint256 amount) external; - function requiredCoinDeposit() external view returns(uint256); + + function transferBtc(bytes32 txid, address targetAgent) external; + + function requiredCoinDeposit() external view returns (uint256); } contract ReentryPledgeAgentProxy is BaseProxy { event proxyDelegate(bool success, string data); event proxyClaim(bool success, string data); + event proxyBtcClaim(bool success, string data); event proxyUndelegate(bool success, string data); + event proxyTransferBtc(bool success, string data); event proxyUndelegatePartial(bool success, string data); constructor(address _pledgeAgent) BaseProxy(_pledgeAgent) {} @@ -45,6 +58,11 @@ contract ReentryPledgeAgentProxy is BaseProxy { function transferCoin(address sourceAgent, address targetAgent, uint256 amount) external payable { bytes memory payload = abi.encodeWithSignature("transferCoin(address,address,uint256)", sourceAgent, targetAgent, amount); (bool success, string memory _msg) = _call(payload); + } + function transferBtc(bytes32 txid, address targetAgent) external { + bytes memory payload = abi.encodeWithSignature("transferBtc(bytes32,address)", txid,targetAgent); + (bool success, string memory _msg) = _call(payload); + emit proxyTransferBtc(success,_msg); } function claimReward(address[] calldata agentList) external payable { @@ -52,6 +70,13 @@ contract ReentryPledgeAgentProxy is BaseProxy { (bool success, string memory _msg) = _call(payload); emit proxyClaim(success, _msg); } + + function claimBtcReward(bytes32[] calldata txidList) external payable { + bytes memory payload = abi.encodeWithSignature("claimBtcReward(bytes32[])", txidList); + (bool success, string memory _msg) = _call(payload); + emit proxyBtcClaim(success, _msg); + } + } contract DelegateReentry is ReentryPledgeAgentProxy { @@ -97,7 +122,7 @@ contract ClaimRewardReentry is ReentryPledgeAgentProxy { function setAgents(address[] calldata _agents) external { delete agents; agents = new address[](_agents.length); - for (uint i=0; i<_agents.length; i++) { + for (uint i = 0; i < _agents.length; i++) { agents[i] = _agents[i]; } } @@ -108,3 +133,40 @@ contract ClaimRewardReentry is ReentryPledgeAgentProxy { } } } + +contract ClaimBtcRewardReentry is ReentryPledgeAgentProxy { + bytes32[] public txidList; + + constructor(address _pledgeAgentAddress) ReentryPledgeAgentProxy(_pledgeAgentAddress) {} + + function setTxidList(bytes32[] calldata _txid) external { + delete txidList; + txidList = new bytes32[](_txid.length); + for (uint i = 0; i < _txid.length; i++) { + txidList[i] = _txid[i]; + } + } + + receive() external payable { + IPledgeAgent(impl).claimBtcReward(txidList); + } +} + + + +contract TransferBtcReentry is ReentryPledgeAgentProxy { + bytes32 public txid; + address public targetAgent; + constructor(address _pledgeAgentAddress) ReentryPledgeAgentProxy(_pledgeAgentAddress) {} + + function setTxid(bytes32 _txid) external { + txid = _txid; + } + function setTargetAgent(bytes32 _targetAgent) external { + targetAgent = targetAgent; + } + + receive() external payable { + IPledgeAgent(impl).transferBtc(txid,targetAgent); + } +} \ No newline at end of file diff --git a/tests/common.py b/tests/common.py index 8b156ca7..66537b1a 100644 --- a/tests/common.py +++ b/tests/common.py @@ -44,7 +44,7 @@ def turn_round(miners: list = None, tx_fee=100, round_count=1): for _ in range(round_count): for miner in miners: - ValidatorSetMock[0].deposit(miner, {"value": tx_fee, "from": accounts[-1]}) + ValidatorSetMock[0].deposit(miner, {"value": tx_fee, "from": accounts[-2]}) tx = CandidateHubMock[0].turnRound() chain.sleep(1) diff --git a/tests/test_delegate_btc.py b/tests/test_delegate_btc.py index e5b42091..17efd903 100644 --- a/tests/test_delegate_btc.py +++ b/tests/test_delegate_btc.py @@ -2,12 +2,12 @@ import brownie import pytest +from brownie.network import gas_price from web3 import Web3 -from brownie import accounts, PledgeAgentProxy, DelegateReentry, UndelegateReentry, ClaimRewardReentry +from brownie import accounts, TransferBtcReentry, ClaimBtcRewardReentry from .btc_block_data import * from .common import register_candidate, turn_round, get_current_round, set_last_round_tag from .utils import * -from hashlib import sha256 MIN_INIT_DELEGATE_VALUE = 0 BLOCK_REWARD = 0 @@ -27,8 +27,9 @@ @pytest.fixture(scope="module", autouse=True) -def deposit_for_reward(validator_set): +def deposit_for_reward(validator_set, gov_hub): accounts[-2].transfer(validator_set.address, Web3.toWei(100000, 'ether')) + accounts[-2].transfer(gov_hub.address, Web3.toWei(100000, 'ether')) @pytest.fixture(scope="module", autouse=True) @@ -46,6 +47,12 @@ def set_block_reward(validator_set, candidate_hub, btc_light_client, pledge_agen btc_light_client.setCheckResult(True) +@pytest.fixture(scope="module", autouse=True) +def set_relayer_register(relay_hub): + for account in accounts[:3]: + relay_hub.setRelayerRegister(account.address, True) + + @pytest.fixture() def set_candidate(): operators = [] @@ -343,8 +350,8 @@ def test_revert_on_insufficient_btc_amount_delegate(pledge_agent, set_candidate, with brownie.reverts("staked value does not meet requirement"): pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) btc_tx, tx_id = get_btc_tx(btc_amount + 1, chain_id, operators[0], accounts[0]) - with brownie.reverts("staked value does not meet requirement"): - pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + assert "delegatedBtc" in tx.events btc_tx, tx_id = get_btc_tx(btc_amount + 2, chain_id, operators[0], accounts[0]) tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) assert "delegatedBtc" in tx.events @@ -1396,6 +1403,9 @@ def test_claiming_reward_with_unconfirmed_txid_reverts(pledge_agent, set_candida error_tx_id = '0x0' with brownie.reverts("btc tx not found"): pledge_agent.claimBtcReward([error_tx_id]) + error_tx_id = '0000012345' + with brownie.reverts("btc tx not found"): + pledge_agent.claimBtcReward([error_tx_id]) def test_revert_on_incorrect_version(pledge_agent, set_candidate): @@ -1784,3 +1794,306 @@ def test_multiple_btc_stakings_in_vout(pledge_agent, set_candidate): assert pledge_agent.btcReceiptMap(tx_id)['value'] == btc_amount assert tracker0.delta() == BLOCK_REWARD // 2 - FEE assert tracker1.delta() == FEE + + +def test_claim_reward_reentry(pledge_agent, set_candidate, delegate_btc_valid_tx): + pledge_agent_proxy = ClaimBtcRewardReentry.deploy(pledge_agent.address, {'from': accounts[0]}) + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, _, _ = delegate_btc_valid_tx + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[0], pledge_agent_proxy.address, + lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": 20000, "from": accounts[1]}) + turn_round() + pledge_agent_proxy.setTxidList([tx_id1]) + turn_round(consensuses) + tracker = get_tracker(pledge_agent_proxy) + tx = pledge_agent_proxy.claimBtcReward([tx_id1]) + expect_event(tx, "proxyBtcClaim", { + "success": False + }) + assert tracker.delta() == 0 + + +def test_transfer_btc_reentry(pledge_agent, set_candidate, delegate_btc_valid_tx): + pledge_agent_proxy = TransferBtcReentry.deploy(pledge_agent.address, {'from': accounts[0]}) + end_round = lock_time // ROUND_INTERVAL + operators, consensuses = set_candidate + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 3) + lock_script, _, _ = delegate_btc_valid_tx + delegate_btc_tx1, tx_id1 = get_btc_tx(BTC_VALUE, chain_id, operators[0], pledge_agent_proxy.address, + lock_script_type) + pledge_agent.delegateBtc(delegate_btc_tx1, 0, [], 0, lock_script, {'from': accounts[2]}) + pledge_agent.delegateCoin(operators[0], {"value": 20000, "from": accounts[1]}) + turn_round() + pledge_agent_proxy.setTxid(tx_id1) + pledge_agent_proxy.setTargetAgent(operators[1].address) + turn_round(consensuses) + tracker = get_tracker(pledge_agent_proxy) + tx = pledge_agent_proxy.transferBtc(tx_id1, operators[1].address) + expect_event(tx, "proxyTransferBtc", { + "success": False + }) + assert tracker.delta() == 0 + + +def test_claiming_rewards_with_multiple_staking_types(pledge_agent, candidate_hub, set_candidate, btc_light_client, + delegate_btc_valid_tx): + total_reward = BLOCK_REWARD // 2 + operators, consensuses = set_candidate + pledge_agent.setPowerBlockFactor(100000) + turn_round() + delegate_amount = 60000 + round_tag = candidate_hub.roundTag() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + btc_light_client.setMiners(round_tag + 2, operators[1], [accounts[2]]) + pledge_agent.delegateCoin(operators[0], {'value': delegate_amount, 'from': accounts[1]}) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + turn_round() + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + pledge_agent.transferBtc(tx_id_list[0], operators[1], {'from': accounts[0]}) + turn_round(consensuses, tx_fee=TX_FEE) + pledge_agent.claimReward([], {'from': accounts[2]}) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + assert tracker2.delta() == total_reward // 3 * 2 + remain_reward = total_reward - total_reward // 3 * 2 + total_coin = delegate_amount + BTC_VALUE * btcFactor + btc_value = BTC_VALUE * btcFactor + assert tracker0.delta() == 0 + assert tracker1.delta() == remain_reward - remain_reward * btc_value // total_coin + + +def test_btc_transaction_with_witness_as_output_address(pledge_agent, set_candidate): + btc_tx = ( + "020000000001010280516aa5b5fb7bd9b7b94b14145af46f6404da96d5f56e1504e1d9d15ef6520200000017160014a808bc3c1ba547b0ba2df4abf1396f35c4d23b4ffeffffff" + "03a08601" + "00000000002200204969dea00948f43ae8f6efb45db768e41b15f4fd70d7fcf366c270c1cbca262a" + "0000000000000000536a4c505341542b01045c9fb29aac15b9a4b7f17c3385939b007540f4d791ccf7e1DAb7D90A0a91f8B1f6A693Bf0bb3a979a001041e28fd65b17576a914a808bc3c1ba547b0ba2df4abf1396f35c4d23b4f88ac" + "a4d81d" + "000000000017a9144c35996fbf4026de7c8fe79c4320c248a10e4bf28702483045022100e32dd040238c19321407b7dfbba957e5988755779030dbcc52e6ae22a2a2088402202eeb497ae61aee9eba97cc4f5d34ba814c3ad1c0bf3286edaba05f044ab4bba401210386f359aa5a42d821370bf07a5ad86c1ff2d892662699103e462ae04d082d83ac00000000") + lock_script = '041e28fd65b17576a914a808bc3c1ba547b0ba2df4abf1396f35c4d23b4f88ac' + scrip_pubkey = 'a9144c35996fbf4026de7c8fe79c4320c248a10e4bf287' + btc_tx = remove_witness_data_from_raw_tx(btc_tx, scrip_pubkey) + tx = pledge_agent.delegateBtc(btc_tx, 200, [], 22, lock_script) + assert 'delegatedBtc' in tx.events + + +def test_claiming_rewards_after_turn_round_failure(pledge_agent, candidate_hub, btc_light_client, + set_candidate, delegate_btc_valid_tx): + set_last_round_tag(get_block_info()['timestamp'] // ROUND_INTERVAL - MIN_BTC_LOCK_ROUND) + pledge_agent.setPowerBlockFactor(100000) + candidate_hub.setControlRoundTimeTag(False) + candidate_hub.setRoundInterval(ROUND_INTERVAL) + turn_round() + block_time = get_block_info()['timestamp'] + operators, consensuses = set_candidate + delegate_amount = 60000 + round_tag = candidate_hub.roundTag() - 7 + btc_light_client.setMiners(round_tag + 1, operators[0], [accounts[2]]) + pledge_agent.delegateCoin(operators[0], {'value': delegate_amount, 'from': accounts[1]}) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + chain.mine(timestamp=block_time + ROUND_INTERVAL) + turn_round() + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + tracker2 = get_tracker(accounts[2]) + candidate_hub.setTurnroundFailed(True) + chain.mine(timestamp=block_time + ROUND_INTERVAL * 2) + with brownie.reverts("turnRound failed"): + turn_round(consensuses, tx_fee=TX_FEE) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + assert tracker0.delta() == 0 + candidate_hub.setTurnroundFailed(False) + chain.mine(timestamp=block_time + ROUND_INTERVAL * 3) + turn_round(consensuses, tx_fee=TX_FEE) + pledge_agent.claimReward([], {'from': accounts[2]}) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + total_reward = BLOCK_REWARD // 2 + assert tracker2.delta() == total_reward // 3 * 2 * 2 + remain_reward = BLOCK_REWARD - total_reward // 3 * 4 + total_coin = delegate_amount + BTC_VALUE * btcFactor + btc_value = BTC_VALUE * btcFactor + assert tracker0.delta() == remain_reward * btc_value // total_coin + assert tracker1.delta() == remain_reward - remain_reward * btc_value // total_coin + + +def test_btc_stake_expiry_after_turn_round_failure(pledge_agent, candidate_hub, btc_light_client, + set_candidate, delegate_btc_valid_tx): + set_last_round_tag(get_block_info()['timestamp'] // ROUND_INTERVAL) + round = 0 + chain_time = lock_time - ROUND_INTERVAL * (MIN_BTC_LOCK_ROUND + 2) + pledge_agent.setPowerBlockFactor(100000) + candidate_hub.setControlRoundTimeTag(False) + candidate_hub.setRoundInterval(ROUND_INTERVAL) + set_last_round_tag(get_block_info()['timestamp'] // ROUND_INTERVAL) + chain.mine(timestamp=chain_time) + turn_round() + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round() + operators, consensuses = set_candidate + delegate_amount = 20000 + pledge_agent.delegateCoin(operators[0], {'value': delegate_amount, 'from': accounts[1]}) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script) + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round() + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round(consensuses, tx_fee=TX_FEE) + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round(consensuses, tx_fee=TX_FEE) + round += 1 + candidate_hub.setTurnroundFailed(True) + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + with brownie.reverts("turnRound failed"): + turn_round(consensuses, tx_fee=TX_FEE) + candidate_hub.setTurnroundFailed(False) + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round(consensuses, tx_fee=TX_FEE) + total_reward = BLOCK_REWARD // 2 + tracker0 = get_tracker(accounts[0]) + tracker1 = get_tracker(accounts[1]) + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + pledge_agent.claimReward(operators, {'from': accounts[1]}) + reward0 = total_reward // 2 * 2 + BLOCK_REWARD // 2 + assert tracker0.delta() == reward0 + assert tracker1.delta() == BLOCK_REWARD * 2 - reward0 + round += 1 + chain.mine(timestamp=chain_time + ROUND_INTERVAL * round) + turn_round(consensuses, tx_fee=TX_FEE) + with brownie.reverts("btc tx not found"): + pledge_agent.claimBtcReward(tx_id_list, {'from': accounts[0]}) + + +def test_restaking_after_btc_staking_expiry(pledge_agent, candidate_hub, set_candidate, delegate_btc_valid_tx): + operators, consensuses = set_candidate + end_round = lock_time // ROUND_INTERVAL + set_last_round_tag(end_round - MIN_BTC_LOCK_ROUND - 1) + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round() + with brownie.reverts("btc tx confirmed"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + turn_round(consensuses, round_count=4) + pledge_agent.claimBtcReward(tx_id_list) + with brownie.reverts("insufficient lock round"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + + +@pytest.mark.parametrize("key,value", [ + ("requiredCoinDeposit", 1001), + ("powerFactor", 1002), + ("btcFactor", 1003), + ("minBtcLockRound", 1004), + ("btcConfirmBlock", 1005), + ("minBtcValue", 1006), + ("delegateBtcGasPrice", 1e9) +]) +def test_update_param_success(pledge_agent, gov_hub, key, value): + hex_value = padding_left(Web3.toHex(int(value)), 64) + tx = pledge_agent.updateParam(key, hex_value, {'from': gov_hub.address}) + expect_event(tx, 'paramChange', { + 'key': key, + 'value': hex_value + }) + if key == 'requiredCoinDeposit': + assert pledge_agent.requiredCoinDeposit() == value + elif key == 'powerFactor': + assert pledge_agent.powerFactor() == value + elif key == 'btcFactor': + assert pledge_agent.btcFactor() == value + elif key == 'minBtcLockRound': + assert pledge_agent.minBtcLockRound() == value + elif key == 'btcConfirmBlock': + assert pledge_agent.btcConfirmBlock() == value + elif key == 'delegateBtcGasPrice': + assert pledge_agent.delegateBtcGasPrice() == value + else: + assert pledge_agent.minBtcValue() == value + + +@pytest.mark.parametrize("key", [ + "requiredCoinDeposit", + "powerFactor", + "btcFactor", + "minBtcLockRound", + "btcConfirmBlock", + "minBtcValue", + "delegateBtcGasPrice" +]) +def test_update_param_failed(pledge_agent, gov_hub, key): + hex_value = padding_left(Web3.toHex(0), 64) + uint256_max = 2 ** 256 - 1 + lower_bound = 1 + if key == 'minBtcValue': + lower_bound = int(1e4) + elif key == 'delegateBtcGasPrice': + lower_bound = int(1e9) + error_msg = encode_args_with_signature( + "OutOfBounds(string,uint256,uint256,uint256)", + [key, 0, lower_bound, uint256_max] + ) + with brownie.reverts(f"typed error: {error_msg}"): + pledge_agent.updateParam(key, hex_value, {'from': gov_hub.address}) + + +def test_update_param_failed_non_governance_contract(pledge_agent): + hex_value = padding_left(Web3.toHex(1000), 64) + with brownie.reverts("the msg sender must be governance contract"): + pledge_agent.updateParam('minBtcValue', hex_value, {'from': accounts[0]}) + + +def test_delegator_calling_delegate_btc(pledge_agent, delegate_btc_valid_tx, set_candidate, relay_hub): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + relay_hub.setRelayerRegister(accounts[0], False) + assert relay_hub.isRelayer(accounts[0]) is False + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[0]}) + assert 'delegatedBtc' in tx.events + + +def test_revert_for_non_delegator_or_relayer_calling_delegate_btc(pledge_agent, delegate_btc_valid_tx, relay_hub, + set_candidate): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + relay_hub.setRelayerRegister(accounts[1], False) + with brownie.reverts("only delegator or relayer can submit the BTC transaction"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[1]}) + + +def test_successful_delegate_btc_call_by_relayer(pledge_agent, delegate_btc_valid_tx, relay_hub, + set_candidate): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + with brownie.reverts("only delegator or relayer can submit the BTC transaction"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[5]}) + relay_hub.setRelayerRegister(accounts[5], True) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[5]}) + assert 'delegatedBtc' in tx.events + + +def test_revert_for_too_high_gas_price(pledge_agent, delegate_btc_valid_tx, set_candidate, relay_hub, gov_hub): + lock_script, btc_tx, tx_id_list = delegate_btc_valid_tx + delegate_gas_price = pledge_agent.INIT_DELEGATE_BTC_GAS_PRICE() + 1 + gas_price(delegate_gas_price) + with brownie.reverts("gas price is too high"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[0]}) + new_delegate_btc_gas_price = int(1e9) + hex_value = padding_left(Web3.toHex(new_delegate_btc_gas_price), 64) + pledge_agent.updateParam('delegateBtcGasPrice', hex_value, {'from': gov_hub.address}) + gas_price(new_delegate_btc_gas_price + 1) + with brownie.reverts("gas price is too high"): + pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[0]}) + gas_price(new_delegate_btc_gas_price) + tx = pledge_agent.delegateBtc(btc_tx, 0, [], 0, lock_script, {'from': accounts[0]}) + assert 'delegatedBtc' in tx.events + assert tx.gas_price == new_delegate_btc_gas_price diff --git a/tests/utils.py b/tests/utils.py index 252e3546..f08e66db 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -101,7 +101,7 @@ def expect_query(query_data, expect: dict): def get_transaction_op_return_data(chain_id, agent_address, delegate_address, lock_data, - core_fee=1, version=1): + core_fee=1, version=1): if lock_data is not None and len(str(lock_data)) == 10: hex_result = hex(lock_data)[2:] lock_time_hex = reverse_by_bytes(hex_result) @@ -159,3 +159,16 @@ def get_btc_tx(value, chain_id, validator, delegator, script_type='hash', lock_d tx_id = get_transaction_txid(btc_tx) return btc_tx, tx_id + + +def remove_witness_data_from_raw_tx(btc_tx_hex, script_pubkey) -> str: + raw_tx: str = btc_tx_hex + if raw_tx[8:12] == '0001': + raw_tx = raw_tx[:8] + raw_tx[12:] + witness_data = raw_tx[raw_tx.index(script_pubkey) + len(script_pubkey): -8] + raw_tx = raw_tx.replace(witness_data, '') + return raw_tx + + +def get_block_info(height='latest'): + return web3.eth.get_block(height) From 5efde62ef48555ec372e02206529c0c4617a7c74 Mon Sep 17 00:00:00 2001 From: zfliex Date: Tue, 26 Mar 2024 11:00:33 +0800 Subject: [PATCH 7/9] fix: same type --- contracts/lib/TypedMemView.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/lib/TypedMemView.sol b/contracts/lib/TypedMemView.sol index 2ee5ef33..8aa8df6f 100644 --- a/contracts/lib/TypedMemView.sol +++ b/contracts/lib/TypedMemView.sol @@ -372,7 +372,7 @@ library TypedMemView { */ function sameType(bytes29 left, bytes29 right) internal pure returns (bool) { // XOR the inputs to check their difference - return (left ^ right) >> 216 == 0; + return (left ^ right) >> (2 * TWELVE_BYTES) == 0; } /** From 3c48d6abf3e39f79bfe64298496f6c4ff6f630b4 Mon Sep 17 00:00:00 2001 From: zfliex Date: Thu, 28 Mar 2024 15:40:01 +0800 Subject: [PATCH 8/9] 1.1.6 mainnet genesis and abi --- abi/PledgeAgent.abi | 2 +- genesis.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/abi/PledgeAgent.abi b/abi/PledgeAgent.abi index 8e134a79..0804c657 100644 --- a/abi/PledgeAgent.abi +++ b/abi/PledgeAgent.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"InactiveAgent","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"MismatchParamLength","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"given","type":"uint256"},{"internalType":"uint256","name":"lowerBound","type":"uint256"},{"internalType":"uint256","name":"upperBound","type":"uint256"}],"name":"OutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"source","type":"address"},{"internalType":"address","name":"target","type":"address"}],"name":"SameCandidate","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"claimedReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"delegatedCoin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"key","type":"string"},{"indexed":false,"internalType":"bytes","name":"value","type":"bytes"}],"name":"paramChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":false,"internalType":"uint256","name":"coinReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"powerReward","type":"uint256"}],"name":"roundReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sourceAgent","type":"address"},{"indexed":true,"internalType":"address","name":"targetAgent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"transferredCoin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegatedCoin","type":"event"},{"inputs":[],"name":"BURN_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CANDIDATE_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FOUNDATION_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOV_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_HASH_POWER_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_REQUIRED_COIN_DEPOSIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIGHT_CLIENT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLEDGE_AGENT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POWER_BLOCK_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RELAYER_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLASH_CONTRACT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SYSTEM_REWARD_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VALIDATOR_CONTRACT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"agentList","type":"address[]"},{"internalType":"uint256[]","name":"rewardList","type":"uint256[]"}],"name":"addRoundReward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"agentsMap","outputs":[{"internalType":"uint256","name":"totalDeposit","type":"uint256"},{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"alreadyInit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"btc2ethMap","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"agentList","type":"address[]"}],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"debtDepositMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"delegateCoin","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"candidate","type":"address"},{"internalType":"address[]","name":"miners","type":"address[]"}],"name":"distributePowerReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"address","name":"delegator","type":"address"}],"name":"getDelegator","outputs":[{"components":[{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"newDeposit","type":"uint256"},{"internalType":"uint256","name":"changeRound","type":"uint256"},{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"uint256","name":"transferOutDeposit","type":"uint256"},{"internalType":"uint256","name":"transferInDeposit","type":"uint256"}],"internalType":"struct PledgeAgent.CoinDelegator","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"candidates","type":"address[]"},{"internalType":"uint256[]","name":"powers","type":"uint256[]"}],"name":"getHybridScore","outputs":[{"internalType":"uint256[]","name":"scores","type":"uint256[]"},{"internalType":"uint256","name":"totalPower","type":"uint256"},{"internalType":"uint256","name":"totalCoin","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getReward","outputs":[{"components":[{"internalType":"uint256","name":"totalReward","type":"uint256"},{"internalType":"uint256","name":"remainReward","type":"uint256"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"},{"internalType":"uint256","name":"round","type":"uint256"}],"internalType":"struct PledgeAgent.Reward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"onFelony","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"powerFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requiredCoinDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundTag","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"validators","type":"address[]"},{"internalType":"uint256","name":"totalPower","type":"uint256"},{"internalType":"uint256","name":"totalCoin","type":"uint256"},{"internalType":"uint256","name":"round","type":"uint256"}],"name":"setNewRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stateMap","outputs":[{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"},{"internalType":"uint256","name":"powerFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sourceAgent","type":"address"},{"internalType":"address","name":"targetAgent","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sourceAgent","type":"address"},{"internalType":"address","name":"targetAgent","type":"address"}],"name":"transferCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"undelegateCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegateCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"bytes","name":"value","type":"bytes"}],"name":"updateParam","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"InactiveAgent","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"MismatchParamLength","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"given","type":"uint256"},{"internalType":"uint256","name":"lowerBound","type":"uint256"},{"internalType":"uint256","name":"upperBound","type":"uint256"}],"name":"OutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"source","type":"address"},{"internalType":"address","name":"target","type":"address"}],"name":"SameCandidate","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txid","type":"bytes32"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"}],"name":"btcPledgeExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"claimedReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txid","type":"bytes32"},{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"bytes","name":"script","type":"bytes"},{"indexed":false,"internalType":"uint32","name":"blockHeight","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"outputIndex","type":"uint256"}],"name":"delegatedBtc","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"delegatedCoin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txid","type":"bytes32"},{"indexed":false,"internalType":"address payable","name":"feeReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"failedTransferBtcFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"key","type":"string"},{"indexed":false,"internalType":"bytes","name":"value","type":"bytes"}],"name":"paramChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":false,"internalType":"uint256","name":"coinReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"powerReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"btcReward","type":"uint256"}],"name":"roundReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txid","type":"bytes32"},{"indexed":false,"internalType":"address","name":"sourceAgent","type":"address"},{"indexed":false,"internalType":"address","name":"targetAgent","type":"address"},{"indexed":false,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"transferredBtc","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txid","type":"bytes32"},{"indexed":false,"internalType":"address payable","name":"feeReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"transferredBtcFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sourceAgent","type":"address"},{"indexed":true,"internalType":"address","name":"targetAgent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"transferredCoin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"agent","type":"address"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegatedCoin","type":"event"},{"inputs":[],"name":"BTC_STAKE_MAGIC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BTC_UNIT_CONVERSION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BURN_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CANDIDATE_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHAINID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLAIM_ROUND_LIMIT","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FOUNDATION_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOV_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_BTC_CONFIRM_BLOCK","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_BTC_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_DELEGATE_BTC_GAS_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_HASH_POWER_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_MIN_BTC_LOCK_ROUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_MIN_BTC_VALUE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_REQUIRED_COIN_DEPOSIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIGHT_CLIENT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLEDGE_AGENT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POWER_BLOCK_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RELAYER_HUB_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROUND_INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLASH_CONTRACT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SYSTEM_REWARD_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VALIDATOR_CONTRACT_ADDR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"agentList","type":"address[]"},{"internalType":"uint256[]","name":"rewardList","type":"uint256[]"}],"name":"addRoundReward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"agentsMap","outputs":[{"internalType":"uint256","name":"totalDeposit","type":"uint256"},{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"},{"internalType":"uint256","name":"btc","type":"uint256"},{"internalType":"uint256","name":"totalBtc","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"alreadyInit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"btc2ethMap","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"btcConfirmBlock","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"btcFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"btcReceiptMap","outputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"endRound","type":"uint256"},{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"address payable","name":"feeReceiver","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"txidList","type":"bytes32[]"}],"name":"claimBtcReward","outputs":[{"internalType":"uint256","name":"rewardSum","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"agentList","type":"address[]"}],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"debtDepositMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"btcTx","type":"bytes"},{"internalType":"uint32","name":"blockHeight","type":"uint32"},{"internalType":"bytes32[]","name":"nodes","type":"bytes32[]"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"script","type":"bytes"}],"name":"delegateBtc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegateBtcGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"delegateCoin","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"candidate","type":"address"},{"internalType":"address[]","name":"miners","type":"address[]"}],"name":"distributePowerReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"address","name":"delegator","type":"address"}],"name":"getDelegator","outputs":[{"components":[{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"newDeposit","type":"uint256"},{"internalType":"uint256","name":"changeRound","type":"uint256"},{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"uint256","name":"transferOutDeposit","type":"uint256"},{"internalType":"uint256","name":"transferInDeposit","type":"uint256"}],"internalType":"struct PledgeAgent.CoinDelegator","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"address","name":"agent","type":"address"}],"name":"getExpireValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"candidates","type":"address[]"},{"internalType":"uint256[]","name":"powers","type":"uint256[]"},{"internalType":"uint256","name":"round","type":"uint256"}],"name":"getHybridScore","outputs":[{"internalType":"uint256[]","name":"scores","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getReward","outputs":[{"components":[{"internalType":"uint256","name":"totalReward","type":"uint256"},{"internalType":"uint256","name":"remainReward","type":"uint256"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"},{"internalType":"uint256","name":"round","type":"uint256"}],"internalType":"struct PledgeAgent.Reward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minBtcLockRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBtcValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"onFelony","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"powerFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requiredCoinDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundTag","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"validators","type":"address[]"},{"internalType":"uint256","name":"round","type":"uint256"}],"name":"setNewRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stateMap","outputs":[{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"coin","type":"uint256"},{"internalType":"uint256","name":"powerFactor","type":"uint256"},{"internalType":"uint256","name":"btc","type":"uint256"},{"internalType":"uint256","name":"btcFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"txid","type":"bytes32"},{"internalType":"address","name":"targetAgent","type":"address"}],"name":"transferBtc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sourceAgent","type":"address"},{"internalType":"address","name":"targetAgent","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sourceAgent","type":"address"},{"internalType":"address","name":"targetAgent","type":"address"}],"name":"transferCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"}],"name":"undelegateCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"agent","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegateCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"bytes","name":"value","type":"bytes"}],"name":"updateParam","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/genesis.json b/genesis.json index d9726eee..315ea56c 100644 --- a/genesis.json +++ b/genesis.json @@ -31,7 +31,7 @@ }, "0x0000000000000000000000000000000000001000": { "balance": "839900000000000000000000000", - "code": "0x6080604052600436106101dc5760003560e01c8063983443df11610102578063b7ab4db511610095578063eb57e20211610064578063eb57e2021461056d578063f340fa011461058d578063f9a2bbc7146105a0578063facd743b146105b657600080fd5b8063b7ab4db514610517578063c81b16621461052c578063dc927faf14610542578063e1c7392a1461055857600080fd5b8063a78abc16116100d1578063a78abc161461048a578063ac431751146104b4578063ad3c9da6146104d4578063b11717241461050157600080fd5b8063983443df146104265780639dc092621461043c578063a5422d5c14610452578063a730c8911461047457600080fd5b806334bc99b31161017a5780636969a25c116101495780636969a25c1461036c578063783028a9146103d25780637f05b9ef146103e85780638f73c5ae1461040457600080fd5b806334bc99b31461030957806343756e5c146103205780634392b20114610336578063565c56b31461034c57600080fd5b8063239cba4a116101b6578063239cba4a1461029e57806325ee13e2146102be578063270159f7146102d45780632eae3523146102f457600080fd5b806304e9e3a4146102245780630ac168a11461026457806314c1e1f71461028857600080fd5b3661021f57341561021d5760405134815233907ff11e547d796cc64acdf758e7cee90439494fd886a19159454aa61e473fdbafef9060200160405180910390a25b005b600080fd5b34801561023057600080fd5b5061023a61100781565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561027057600080fd5b5061027a60015481565b60405190815260200161025b565b34801561029457600080fd5b5061023a61100481565b3480156102aa57600080fd5b5061021d6102b93660046130e9565b6105fb565b3480156102ca57600080fd5b5061023a61100581565b3480156102e057600080fd5b5061021d6102ef36600461311d565b610b94565b34801561030057600080fd5b5061027a600a81565b34801561031557600080fd5b5061027a62a0668081565b34801561032c57600080fd5b5061023a61100181565b34801561034257600080fd5b5061027a60045481565b34801561035857600080fd5b5061027a6103673660046130cd565b611299565b34801561037857600080fd5b5061038c610387366004613245565b611312565b6040805173ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015292909416918301919091526060820152608081019190915260a00161025b565b3480156103de57600080fd5b5061023a61100881565b3480156103f457600080fd5b5061027a6729a2241af62c000081565b34801561041057600080fd5b5061041961136e565b60405161025b9190613311565b34801561043257600080fd5b5061027a60025481565b34801561044857600080fd5b5061023a61100681565b34801561045e57600080fd5b50610467611887565b60405161025b919061337a565b34801561048057600080fd5b5061027a6125a781565b34801561049657600080fd5b506000546104a49060ff1681565b604051901515815260200161025b565b3480156104c057600080fd5b5061021d6104cf3660046131dc565b6118a6565b3480156104e057600080fd5b5061027a6104ef3660046130cd565b60056020526000908152604090205481565b34801561050d57600080fd5b5061023a61100981565b34801561052357600080fd5b50610419611b40565b34801561053857600080fd5b5061023a61100281565b34801561054e57600080fd5b5061023a61100381565b34801561056457600080fd5b5061021d611c57565b34801561057957600080fd5b5061021d6105883660046130cd565b611eed565b61021d61059b3660046130cd565b6121a7565b3480156105ac57600080fd5b5061023a61100081565b3480156105c257600080fd5b506104a46105d13660046130cd565b73ffffffffffffffffffffffffffffffffffffffff16600090815260056020526040902054151590565b33611001146106775760405162461bcd60e51b815260206004820152602560248201527f746865206d73672073656e646572206d75737420626520736c61736820636f6e60448201527f747261637400000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260056020526040902054806106a85750505050565b6106b36001826134b3565b90506000600382815481106106d857634e487b7160e01b600052603260045260246000fd5b600091825260208220600460059092020101546003549092506106fd906001906134b3565b9050806107445760006003848154811061072757634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160040181905550505050505050565b60006003848154811061076757634e487b7160e01b600052603260045260246000fd5b60009182526020918290206005909102015460405185815273ffffffffffffffffffffffffffffffffffffffff909116925082917f3b6f9ef90462b512a1293ecec018670bf7b7f1876fb727590a8a6d7643130a70910160405180910390a273ffffffffffffffffffffffffffffffffffffffff87166000908152600560205260408120556003546107fb906001906134b3565b841461095f5760038054610811906001906134b3565b8154811061082f57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016003858154811061085e57634e487b7160e01b600052603260045260246000fd5b60009182526020909120825460059092020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff9384161782556001808501548184018054841691861691909117905560028086015490840180549093169416939093179055600380840154908201556004928301549201919091556108fe90859061344a565b600560006003878154811061092357634e487b7160e01b600052603260045260246000fd5b600091825260208083206001600590930201919091015473ffffffffffffffffffffffffffffffffffffffff1683528201929092526040019020555b600380548061097e57634e487b7160e01b600052603160045260246000fd5b60008281526020812060057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054821690556002820180549091169055600381018290556004018190559155610a078385613462565b90508015610a7a5760035460005b81811015610a77578260038281548110610a3f57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254610a5f919061344a565b90915550819050610a6f8161351d565b915050610a15565b50505b6040517fa9955b4000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101889052604481018790526110059063a9955b4090606401600060405180830381600087803b158015610af157600080fd5b505af1158015610b05573d6000803e3d6000fd5b50506040517fce73711200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152611007925063ce7371129150602401600060405180830381600087803b158015610b7257600080fd5b505af1158015610b86573d6000803e3d6000fd5b505050505050505050505050565b3361100514610c0b5760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e74726163740000000000000000000000000000000000000000000000606482015260840161066e565b610cda88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525061245792505050565b84610ce45761128f565b600354600090865b81831015610d66576005600060038581548110610d1957634e487b7160e01b600052603260045260246000fd5b600091825260208083206001600590930201919091015473ffffffffffffffffffffffffffffffffffffffff16835282019290925260400181205582610d5e8161351d565b935050610cec565b8092505b81831015610e20576003805480610d9157634e487b7160e01b600052603160045260246000fd5b60008281526020812060057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180548216905560028201805490911690556003810182905560040155905582610e188161351d565b935050610d6a565b600092505b80831015611262578183106110025760036040518060a001604052808d8d87818110610e6157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610e7691906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018b8b87818110610eb157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610ec691906130cd565b73ffffffffffffffffffffffffffffffffffffffff168152602001898987818110610f0157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610f1691906130cd565b73ffffffffffffffffffffffffffffffffffffffff168152602001878787818110610f5157634e487b7160e01b600052603260045260246000fd5b602090810292909201358352506000918101829052835460018181018655948352918190208351600590930201805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617825591840151948101805495841695831695909517909455604083015160028501805491909316911617905560608101516003830155608001516004909101556111e7565b6040518060a001604052808c8c8681811061102d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061104291906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018a8a8681811061107d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061109291906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018888868181106110cd57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906110e291906130cd565b73ffffffffffffffffffffffffffffffffffffffff16815260200186868681811061111d57634e487b7160e01b600052603260045260246000fd5b90506020020135815260200160008152506003848154811061114f57634e487b7160e01b600052603260045260246000fd5b60009182526020918290208351600590920201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178255928401516001820180549184169185169190911790556040840151600282018054919093169316929092179055606082015160038201556080909101516004909101555b6111f283600161344a565b600560008b8b8781811061121657634e487b7160e01b600052603260045260246000fd5b905060200201602081019061122b91906130cd565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205561125b8361351d565b9250610e25565b6040517fedd8d7296956dd970ab4de3f2fc03be2b0ffc615d20cd4c72c6e44f928630ebf90600090a15050505b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260056020526040812054806112cd5750600092915050565b60036112da6001836134b3565b815481106112f857634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160040154915050919050565b6003818154811061132257600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015473ffffffffffffffffffffffffffffffffffffffff9384169550918316939216919085565b606033611005146113e75760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e74726163740000000000000000000000000000000000000000000000606482015260840161066e565b60035460009081908190815b8181101561148c5760006003828154811061141e57634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506000606460025483600401546114449190613476565b61144e9190613462565b905061145a818661344a565b94508082600401600082825461147091906134b3565b92505081905550505080806114849061351d565b9150506113f3565b5061100273ffffffffffffffffffffffffffffffffffffffff1663631cbe3c836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114d757600080fd5b505af11580156114eb573d6000803e3d6000fd5b50505050508067ffffffffffffffff81111561151757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611540578160200160208202803683370190505b50945060008167ffffffffffffffff81111561156c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611595578160200160208202803683370190505b509050600080805b84811015611803576000600382815481106115c857634e487b7160e01b600052603260045260246000fd5b6000918252602090912060059091020180548b5191925073ffffffffffffffffffffffffffffffffffffffff16908b908490811061161657634e487b7160e01b600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526004810154925082156117f0576002810154600382015473ffffffffffffffffffffffffffffffffffffffff90911699506103e89061167a9085613476565b6116849190613462565b9750878311156116fa5761169888846134b3565b8583815181106116b857634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508482815181106116e457634e487b7160e01b600052603260045260246000fd5b6020026020010151846116f7919061344a565b93505b60006004820181905560405173ffffffffffffffffffffffffffffffffffffffff8b16908a156108fc02908b9084818181858888f1935050505090508015611797578154604080518b81526020810187905273ffffffffffffffffffffffffffffffffffffffff8d81169316917f5f05434e85dc7eb0d20406bd66f9b9c92a6d4d710b8cffeb61176632c83974d3910160405180910390a36117ee565b8154604080518b81526020810187905273ffffffffffffffffffffffffffffffffffffffff8d81169316917fa49797d31ee4d8d18eeb937551b21f754dd96d6e3922324d5c5ba4522ebc45bc910160405180910390a35b505b50806117fb8161351d565b91505061159d565b506040517fbaa4402b0000000000000000000000000000000000000000000000000000000081526110079063baa4402b908490611846908c908890600401613324565b6000604051808303818588803b15801561185f57600080fd5b505af1158015611873573d6000803e3d6000fd5b505060006004555050505050505050505090565b604051806102c0016040528061028881526020016135b9610288913981565b60005460ff166118f85760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e69742079657400000000000000604482015260640161066e565b336110061461196f5760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e747261637400000000000000000000000000000000000000000000606482015260840161066e565b602081146119ad5783836040517fad23613c00000000000000000000000000000000000000000000000000000000815260040161066e9291906133cb565b611a2184848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601b81527f626c6f636b526577617264496e63656e7469766550657263656e740000000000602082015291506127da9050565b15611ab557604080516020601f8401819004810282018101909252828152600091611a649185858083850183828082843760009201919091525061283392505050565b90506064811115611aad57848482600060646040517f808861f900000000000000000000000000000000000000000000000000000000815260040161066e959493929190613419565b600255611afd565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d00000000000000000000000000000000000000604482015260640161066e565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a84848484604051611b3294939291906133e7565b60405180910390a150505050565b60035460609060008167ffffffffffffffff811115611b6f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611b98578160200160208202803683370190505b50905060005b82811015611c505760038181548110611bc757634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110611c1957634e487b7160e01b600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280611c488161351d565b915050611b9e565b5092915050565b60005460ff1615611caa5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e697400000000000000604482015260640161066e565b600080611cd1604051806102c0016040528061028881526020016135b96102889139612838565b9150915080611d485760405162461bcd60e51b815260206004820152602160248201527f6661696c656420746f20706172736520696e69742076616c696461746f72536560448201527f7400000000000000000000000000000000000000000000000000000000000000606482015260840161066e565b815160005b81811015611ea9576003848281518110611d7757634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825460018082018555600094855293839020825160059092020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff938416178255938301518186018054861691841691909117905560408301516002820180549095169216919091179092556060810151600383015560800151600490910155611e2890829061344a565b60056000868481518110611e4c57634e487b7160e01b600052603260045260246000fd5b60200260200101516020015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508080611ea19061351d565b915050611d4d565b50506729a2241af62c00006001908155600a600255600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690911790555050565b3361100114611f645760405162461bcd60e51b815260206004820152602560248201527f746865206d73672073656e646572206d75737420626520736c61736820636f6e60448201527f7472616374000000000000000000000000000000000000000000000000000000606482015260840161066e565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205480611f93575050565b611f9e6001826134b3565b9050600060038281548110611fc357634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201600401549050600060038381548110611ffa57634e487b7160e01b600052603260045260246000fd5b6000918252602082206004600590920201019190915560035461201f906001906134b3565b905060006003848154811061204457634e487b7160e01b600052603260045260246000fd5b60009182526020918290206005909102015460405185815273ffffffffffffffffffffffffffffffffffffffff909116925082917f8cd4e147d8af98a9e3b6724021b8bf6aed2e5dac71c38f2dce8161b82585b25d910160405180910390a2816120af575050505050565b60006120bb8385613462565b9050801561219e5760005b858110156121285781600382815481106120f057634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254612110919061344a565b909155508190506121208161351d565b9150506120c6565b50600354600061213987600161344a565b90505b8181101561128f57826003828154811061216657634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254612186919061344a565b909155508190506121968161351d565b91505061213c565b50505050505b50565b33411461221c5760405162461bcd60e51b815260206004820152602d60248201527f746865206d6573736167652073656e646572206d75737420626520746865206260448201527f6c6f636b2070726f647563657200000000000000000000000000000000000000606482015260840161066e565b60005460ff1661226e5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e69742079657400000000000000604482015260640161066e565b3a156122bc5760405162461bcd60e51b815260206004820152601460248201527f6761737072696365206973206e6f74207a65726f000000000000000000000000604482015260640161066e565b6122c962a0668043613556565b6122ee576127106125a76001546122e09190613476565b6122ea9190613462565b6001555b60015460045434919061230290839061344a565b61230c919061344a565b47106123225760015461231f908261344a565b90505b73ffffffffffffffffffffffffffffffffffffffff82166000908152600560205260409020548015612402576000600361235d6001846134b3565b8154811061237b57634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201905082600454612399919061344a565b60049081558101546123ac90849061344a565b600482015560405183815273ffffffffffffffffffffffffffffffffffffffff8516907f93a090ecc682c002995fad3c85b30c5651d7fd29b0be5da9d784a3302aedc0559060200160405180910390a250505050565b8273ffffffffffffffffffffffffffffffffffffffff167ff177e5d6c5764d79c32883ed824111d9b13f5668cf6ab1cc12dd36791dd955b48360405161244a91815260200190565b60405180910390a2505050565b83518351146124f45760405162461bcd60e51b815260206004820152604660248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e64206f7065726174654164647265737365732073686f756c6420626560648201527f20657175616c0000000000000000000000000000000000000000000000000000608482015260a40161066e565b81518351146125915760405162461bcd60e51b815260206004820152604260248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e64206665654164647265737365732073686f756c642062652065717560648201527f616c000000000000000000000000000000000000000000000000000000000000608482015260a40161066e565b805183511461262e5760405162461bcd60e51b815260206004820152604c60248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e6420636f6d6d697373696f6e54686f7573616e64746873732073686f60648201527f756c6420626520657175616c0000000000000000000000000000000000000000608482015260a40161066e565b60005b83518110156127d35760005b818110156127215784818151811061266557634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff168583815181106126a357634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561270f5760405162461bcd60e51b815260206004820152601b60248201527f6475706c696361746520636f6e73656e73757320616464726573730000000000604482015260640161066e565b806127198161351d565b91505061263d565b506103e882828151811061274557634e487b7160e01b600052603260045260246000fd5b602002602001015111156127c15760405162461bcd60e51b815260206004820152602260248201527f636f6d6d697373696f6e54686f7573616e64746873206f7574206f6620626f7560448201527f6e64000000000000000000000000000000000000000000000000000000000000606482015260840161066e565b806127cb8161351d565b915050612631565b5050505050565b6000816040516020016127ed91906132f5565b604051602081830303815290604052805190602001208360405160200161281491906132f5565b6040516020818303038152906040528051906020012014905092915050565b015190565b60606000806128766128718560408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b6129c3565b805190915060008167ffffffffffffffff8111156128a457634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561291b57816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816128c25790505b50905060005b828110156129b65760008061295c86848151811061294f57634e487b7160e01b600052603260045260246000fd5b6020026020010151612af5565b9150915080612975575091976000975095505050505050565b8184848151811061299657634e487b7160e01b600052603260045260246000fd5b6020026020010181905250505080806129ae9061351d565b915050612921565b5095901515945092505050565b60606129ce82612bdb565b6129d757600080fd5b60006129e283612c14565b905060008167ffffffffffffffff811115612a0d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612a5257816020015b6040805180820190915260008082526020820152815260200190600190039081612a2b5790505b5090506000612a648560200151612c97565b8560200151612a73919061344a565b90506000805b84811015612aea57612a8a83612d19565b9150604051806040016040528083815260200184815250848281518110612ac157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152612ad6828461344a565b925080612ae28161351d565b915050612a79565b509195945050505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a08101825260008082526020820181905291810182905260608101829052608081018290526000612b5585612e69565b905060005b612b6382612ed5565b15612bcf57612b79612b7483612ef9565b612f56565b73ffffffffffffffffffffffffffffffffffffffff166020840152612ba0612b7483612ef9565b73ffffffffffffffffffffffffffffffffffffffff16604084018190528352506103e860608301526001612b5a565b91959194509092505050565b8051600090612bec57506000919050565b6020820151805160001a9060c0821015612c0a575060009392505050565b5060019392505050565b8051600090612c2557506000919050565b600080612c358460200151612c97565b8460200151612c44919061344a565b9050600084600001518560200151612c5c919061344a565b90505b80821015612c8e57612c7082612d19565b612c7a908361344a565b915082612c868161351d565b935050612c5f565b50909392505050565b8051600090811a6080811015612cb05750600092915050565b60b8811080612ccb575060c08110801590612ccb575060f881105b15612cd95750600192915050565b60c0811015612d0d57612cee600160b86134ca565b612cfb9060ff16826134b3565b612d0690600161344a565b9392505050565b612cee600160f86134ca565b80516000908190811a6080811015612d345760019150611c50565b60b8811015612d5a57612d486080826134b3565b612d5390600161344a565b9150611c50565b60c0811015612ddb57600060b78203600186019550806020036101000a865104915060018101820193505080831015612dd55760405162461bcd60e51b815260206004820152601160248201527f6164646974696f6e206f766572666c6f77000000000000000000000000000000604482015260640161066e565b50611c50565b60f8811015612def57612d4860c0826134b3565b600060f78203600186019550806020036101000a865104915060018101820193505080831015612e615760405162461bcd60e51b815260206004820152601160248201527f6164646974696f6e206f766572666c6f77000000000000000000000000000000604482015260640161066e565b505092915050565b6040805160808101825260009181018281526060820183905281526020810191909152612e9582612bdb565b612e9e57600080fd5b6000612ead8360200151612c97565b8360200151612ebc919061344a565b6040805180820190915293845260208401525090919050565b80518051602082015160009291612eeb9161344a565b836020015110915050919050565b6040805180820190915260008082526020820152612f1682612ed5565b612f1f57600080fd5b60208201516000612f2f82612d19565b9050612f3b818361344a565b60209485015260408051808201909152908152928301525090565b8051600090601514612f6757600080fd5b612f7082612f76565b92915050565b805160009015801590612f8b57508151602110155b612f9457600080fd5b6000612fa38360200151612c97565b90508083600001511015612ff95760405162461bcd60e51b815260206004820152601a60248201527f6c656e677468206973206c657373207468616e206f6666736574000000000000604482015260640161066e565b82516000906130099083906134b3565b905060008083866020015161301e919061344a565b905080519150602083101561303a57826020036101000a820491505b50949350505050565b60008083601f840112613054578182fd5b50813567ffffffffffffffff81111561306b578182fd5b6020830191508360208260051b850101111561308657600080fd5b9250929050565b60008083601f84011261309e578182fd5b50813567ffffffffffffffff8111156130b5578182fd5b60208301915083602082850101111561308657600080fd5b6000602082840312156130de578081fd5b8135612d0681613596565b6000806000606084860312156130fd578182fd5b833561310881613596565b95602085013595506040909401359392505050565b6000806000806000806000806080898b031215613138578384fd5b883567ffffffffffffffff8082111561314f578586fd5b61315b8c838d01613043565b909a50985060208b0135915080821115613173578586fd5b61317f8c838d01613043565b909850965060408b0135915080821115613197578586fd5b6131a38c838d01613043565b909650945060608b01359150808211156131bb578384fd5b506131c88b828c01613043565b999c989b5096995094979396929594505050565b600080600080604085870312156131f1578384fd5b843567ffffffffffffffff80821115613208578586fd5b6132148883890161308d565b9096509450602087013591508082111561322c578384fd5b506132398782880161308d565b95989497509550505050565b600060208284031215613256578081fd5b5035919050565b6000815180845260208085019450808401835b838110156132a257815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613270565b509495945050505050565b81835281816020850137506000806020838501015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600082516133078184602087016134ed565b9190910192915050565b602081526000612d06602083018461325d565b604081526000613337604083018561325d565b828103602084810191909152845180835285820192820190845b8181101561336d57845183529383019391830191600101613351565b5090979650505050505050565b60208152600082518060208401526133998160408501602087016134ed565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020815260006133df6020830184866132ad565b949350505050565b6040815260006133fb6040830186886132ad565b828103602084015261340e8185876132ad565b979650505050505050565b60808152600061342d6080830187896132ad565b602083019590955250604081019290925260609091015292915050565b6000821982111561345d5761345d61356a565b500190565b60008261347157613471613580565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156134ae576134ae61356a565b500290565b6000828210156134c5576134c561356a565b500390565b600060ff821660ff8416808210156134e4576134e461356a565b90039392505050565b60005b838110156135085781810151838201526020016134f0565b83811115613517576000848401525b50505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561354f5761354f61356a565b5060010190565b60008261356557613565613580565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff811681146121a457600080fdfef90285ea944121f067b0f5135d77c29b2b329e8cb1bd96c96094f8b18cecc98d976ad253d38e4100a73d4e154726ea947f461f8a1c35edecd6816e76eb2e84eb661751ee94f8b18cecc98d976ad253d38e4100a73d4e154726ea94fd806ab93db5742944b7b50ce759e5eee5f6fe5094f8b18cecc98d976ad253d38e4100a73d4e154726ea947ef3a94ad1c443481fb3d86829355ca90477f8b594f8b18cecc98d976ad253d38e4100a73d4e154726ea9467d1ad48f91e131413bd0b04e823f3ae4f81e85394f8b18cecc98d976ad253d38e4100a73d4e154726ea943fb42cab4416024dc1b4c9e21b9acd0dfcef35f694f8b18cecc98d976ad253d38e4100a73d4e154726ea943511e3b8ac7336b99517d324145e9b5bb33e08a494f8b18cecc98d976ad253d38e4100a73d4e154726ea94729f39a54304fcc6ec279684c71491a385d7b9ae94f8b18cecc98d976ad253d38e4100a73d4e154726ea94f44a785fd9f23f0abd443541386e71356ce619dc94f8b18cecc98d976ad253d38e4100a73d4e154726ea942efd3cf0733421aec3e4202480d0a90bd157514994f8b18cecc98d976ad253d38e4100a73d4e154726ea94613b0f519ada008cb99b6130e89122ba416bf15994f8b18cecc98d976ad253d38e4100a73d4e154726ea94c0925eeb800ff6ba4695ded61562a10102152b5f94f8b18cecc98d976ad253d38e4100a73d4e154726ea9419e3c7d7e69f273f3f91c060bb438a007f6fc33c94f8b18cecc98d976ad253d38e4100a73d4e154726ea94e127f110d172a0c4c6209fe045dd71781e8fe9d494f8b18cecc98d976ad253d38e4100a73d4e154726ea94f778dc4a199a440dbe9f16d1e13e185bb179b3b794f8b18cecc98d976ad253d38e4100a73d4e154726a264697066735822122043976e7596fe854a81c18c0cc2717f664ab382e6e237b2c1fbded39cf2b219c164736f6c63430008040033" + "code": "0x6080604052600436106101dc5760003560e01c8063983443df11610102578063b7ab4db511610095578063eb57e20211610064578063eb57e2021461056d578063f340fa011461058d578063f9a2bbc7146105a0578063facd743b146105b657600080fd5b8063b7ab4db514610517578063c81b16621461052c578063dc927faf14610542578063e1c7392a1461055857600080fd5b8063a78abc16116100d1578063a78abc161461048a578063ac431751146104b4578063ad3c9da6146104d4578063b11717241461050157600080fd5b8063983443df146104265780639dc092621461043c578063a5422d5c14610452578063a730c8911461047457600080fd5b806334bc99b31161017a5780636969a25c116101495780636969a25c1461036c578063783028a9146103d25780637f05b9ef146103e85780638f73c5ae1461040457600080fd5b806334bc99b31461030957806343756e5c146103205780634392b20114610336578063565c56b31461034c57600080fd5b8063239cba4a116101b6578063239cba4a1461029e57806325ee13e2146102be578063270159f7146102d45780632eae3523146102f457600080fd5b806304e9e3a4146102245780630ac168a11461026457806314c1e1f71461028857600080fd5b3661021f57341561021d5760405134815233907ff11e547d796cc64acdf758e7cee90439494fd886a19159454aa61e473fdbafef9060200160405180910390a25b005b600080fd5b34801561023057600080fd5b5061023a61100781565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561027057600080fd5b5061027a60015481565b60405190815260200161025b565b34801561029457600080fd5b5061023a61100481565b3480156102aa57600080fd5b5061021d6102b93660046130e9565b6105fb565b3480156102ca57600080fd5b5061023a61100581565b3480156102e057600080fd5b5061021d6102ef36600461311d565b610b94565b34801561030057600080fd5b5061027a600a81565b34801561031557600080fd5b5061027a62a0668081565b34801561032c57600080fd5b5061023a61100181565b34801561034257600080fd5b5061027a60045481565b34801561035857600080fd5b5061027a6103673660046130cd565b611299565b34801561037857600080fd5b5061038c610387366004613245565b611312565b6040805173ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015292909416918301919091526060820152608081019190915260a00161025b565b3480156103de57600080fd5b5061023a61100881565b3480156103f457600080fd5b5061027a6729a2241af62c000081565b34801561041057600080fd5b5061041961136e565b60405161025b9190613311565b34801561043257600080fd5b5061027a60025481565b34801561044857600080fd5b5061023a61100681565b34801561045e57600080fd5b50610467611887565b60405161025b919061337a565b34801561048057600080fd5b5061027a6125a781565b34801561049657600080fd5b506000546104a49060ff1681565b604051901515815260200161025b565b3480156104c057600080fd5b5061021d6104cf3660046131dc565b6118a6565b3480156104e057600080fd5b5061027a6104ef3660046130cd565b60056020526000908152604090205481565b34801561050d57600080fd5b5061023a61100981565b34801561052357600080fd5b50610419611b40565b34801561053857600080fd5b5061023a61100281565b34801561054e57600080fd5b5061023a61100381565b34801561056457600080fd5b5061021d611c57565b34801561057957600080fd5b5061021d6105883660046130cd565b611eed565b61021d61059b3660046130cd565b6121a7565b3480156105ac57600080fd5b5061023a61100081565b3480156105c257600080fd5b506104a46105d13660046130cd565b73ffffffffffffffffffffffffffffffffffffffff16600090815260056020526040902054151590565b33611001146106775760405162461bcd60e51b815260206004820152602560248201527f746865206d73672073656e646572206d75737420626520736c61736820636f6e60448201527f747261637400000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260056020526040902054806106a85750505050565b6106b36001826134b3565b90506000600382815481106106d857634e487b7160e01b600052603260045260246000fd5b600091825260208220600460059092020101546003549092506106fd906001906134b3565b9050806107445760006003848154811061072757634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160040181905550505050505050565b60006003848154811061076757634e487b7160e01b600052603260045260246000fd5b60009182526020918290206005909102015460405185815273ffffffffffffffffffffffffffffffffffffffff909116925082917f3b6f9ef90462b512a1293ecec018670bf7b7f1876fb727590a8a6d7643130a70910160405180910390a273ffffffffffffffffffffffffffffffffffffffff87166000908152600560205260408120556003546107fb906001906134b3565b841461095f5760038054610811906001906134b3565b8154811061082f57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016003858154811061085e57634e487b7160e01b600052603260045260246000fd5b60009182526020909120825460059092020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff9384161782556001808501548184018054841691861691909117905560028086015490840180549093169416939093179055600380840154908201556004928301549201919091556108fe90859061344a565b600560006003878154811061092357634e487b7160e01b600052603260045260246000fd5b600091825260208083206001600590930201919091015473ffffffffffffffffffffffffffffffffffffffff1683528201929092526040019020555b600380548061097e57634e487b7160e01b600052603160045260246000fd5b60008281526020812060057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054821690556002820180549091169055600381018290556004018190559155610a078385613462565b90508015610a7a5760035460005b81811015610a77578260038281548110610a3f57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254610a5f919061344a565b90915550819050610a6f8161351d565b915050610a15565b50505b6040517fa9955b4000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260248101889052604481018790526110059063a9955b4090606401600060405180830381600087803b158015610af157600080fd5b505af1158015610b05573d6000803e3d6000fd5b50506040517fce73711200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152611007925063ce7371129150602401600060405180830381600087803b158015610b7257600080fd5b505af1158015610b86573d6000803e3d6000fd5b505050505050505050505050565b3361100514610c0b5760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e74726163740000000000000000000000000000000000000000000000606482015260840161066e565b610cda88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525050604080516020808b0282810182019093528a82529093508a92508991829185019084908082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525061245792505050565b84610ce45761128f565b600354600090865b81831015610d66576005600060038581548110610d1957634e487b7160e01b600052603260045260246000fd5b600091825260208083206001600590930201919091015473ffffffffffffffffffffffffffffffffffffffff16835282019290925260400181205582610d5e8161351d565b935050610cec565b8092505b81831015610e20576003805480610d9157634e487b7160e01b600052603160045260246000fd5b60008281526020812060057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180548216905560028201805490911690556003810182905560040155905582610e188161351d565b935050610d6a565b600092505b80831015611262578183106110025760036040518060a001604052808d8d87818110610e6157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610e7691906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018b8b87818110610eb157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610ec691906130cd565b73ffffffffffffffffffffffffffffffffffffffff168152602001898987818110610f0157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610f1691906130cd565b73ffffffffffffffffffffffffffffffffffffffff168152602001878787818110610f5157634e487b7160e01b600052603260045260246000fd5b602090810292909201358352506000918101829052835460018181018655948352918190208351600590930201805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617825591840151948101805495841695831695909517909455604083015160028501805491909316911617905560608101516003830155608001516004909101556111e7565b6040518060a001604052808c8c8681811061102d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061104291906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018a8a8681811061107d57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061109291906130cd565b73ffffffffffffffffffffffffffffffffffffffff1681526020018888868181106110cd57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906110e291906130cd565b73ffffffffffffffffffffffffffffffffffffffff16815260200186868681811061111d57634e487b7160e01b600052603260045260246000fd5b90506020020135815260200160008152506003848154811061114f57634e487b7160e01b600052603260045260246000fd5b60009182526020918290208351600590920201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178255928401516001820180549184169185169190911790556040840151600282018054919093169316929092179055606082015160038201556080909101516004909101555b6111f283600161344a565b600560008b8b8781811061121657634e487b7160e01b600052603260045260246000fd5b905060200201602081019061122b91906130cd565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000205561125b8361351d565b9250610e25565b6040517fedd8d7296956dd970ab4de3f2fc03be2b0ffc615d20cd4c72c6e44f928630ebf90600090a15050505b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260056020526040812054806112cd5750600092915050565b60036112da6001836134b3565b815481106112f857634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160040154915050919050565b6003818154811061132257600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015473ffffffffffffffffffffffffffffffffffffffff9384169550918316939216919085565b606033611005146113e75760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e74726163740000000000000000000000000000000000000000000000606482015260840161066e565b60035460009081908190815b8181101561148c5760006003828154811061141e57634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506000606460025483600401546114449190613476565b61144e9190613462565b905061145a818661344a565b94508082600401600082825461147091906134b3565b92505081905550505080806114849061351d565b9150506113f3565b5061100273ffffffffffffffffffffffffffffffffffffffff1663631cbe3c836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114d757600080fd5b505af11580156114eb573d6000803e3d6000fd5b50505050508067ffffffffffffffff81111561151757634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611540578160200160208202803683370190505b50945060008167ffffffffffffffff81111561156c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611595578160200160208202803683370190505b509050600080805b84811015611803576000600382815481106115c857634e487b7160e01b600052603260045260246000fd5b6000918252602090912060059091020180548b5191925073ffffffffffffffffffffffffffffffffffffffff16908b908490811061161657634e487b7160e01b600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526004810154925082156117f0576002810154600382015473ffffffffffffffffffffffffffffffffffffffff90911699506103e89061167a9085613476565b6116849190613462565b9750878311156116fa5761169888846134b3565b8583815181106116b857634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508482815181106116e457634e487b7160e01b600052603260045260246000fd5b6020026020010151846116f7919061344a565b93505b60006004820181905560405173ffffffffffffffffffffffffffffffffffffffff8b16908a156108fc02908b9084818181858888f1935050505090508015611797578154604080518b81526020810187905273ffffffffffffffffffffffffffffffffffffffff8d81169316917f5f05434e85dc7eb0d20406bd66f9b9c92a6d4d710b8cffeb61176632c83974d3910160405180910390a36117ee565b8154604080518b81526020810187905273ffffffffffffffffffffffffffffffffffffffff8d81169316917fa49797d31ee4d8d18eeb937551b21f754dd96d6e3922324d5c5ba4522ebc45bc910160405180910390a35b505b50806117fb8161351d565b91505061159d565b506040517fbaa4402b0000000000000000000000000000000000000000000000000000000081526110079063baa4402b908490611846908c908890600401613324565b6000604051808303818588803b15801561185f57600080fd5b505af1158015611873573d6000803e3d6000fd5b505060006004555050505050505050505090565b604051806102c0016040528061028881526020016135b9610288913981565b60005460ff166118f85760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e69742079657400000000000000604482015260640161066e565b336110061461196f5760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e747261637400000000000000000000000000000000000000000000606482015260840161066e565b602081146119ad5783836040517fad23613c00000000000000000000000000000000000000000000000000000000815260040161066e9291906133cb565b611a2184848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601b81527f626c6f636b526577617264496e63656e7469766550657263656e740000000000602082015291506127da9050565b15611ab557604080516020601f8401819004810282018101909252828152600091611a649185858083850183828082843760009201919091525061283392505050565b90506064811115611aad57848482600060646040517f808861f900000000000000000000000000000000000000000000000000000000815260040161066e959493929190613419565b600255611afd565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d00000000000000000000000000000000000000604482015260640161066e565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a84848484604051611b3294939291906133e7565b60405180910390a150505050565b60035460609060008167ffffffffffffffff811115611b6f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611b98578160200160208202803683370190505b50905060005b82811015611c505760038181548110611bc757634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110611c1957634e487b7160e01b600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280611c488161351d565b915050611b9e565b5092915050565b60005460ff1615611caa5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e697400000000000000604482015260640161066e565b600080611cd1604051806102c0016040528061028881526020016135b96102889139612838565b9150915080611d485760405162461bcd60e51b815260206004820152602160248201527f6661696c656420746f20706172736520696e69742076616c696461746f72536560448201527f7400000000000000000000000000000000000000000000000000000000000000606482015260840161066e565b815160005b81811015611ea9576003848281518110611d7757634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825460018082018555600094855293839020825160059092020180547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff938416178255938301518186018054861691841691909117905560408301516002820180549095169216919091179092556060810151600383015560800151600490910155611e2890829061344a565b60056000868481518110611e4c57634e487b7160e01b600052603260045260246000fd5b60200260200101516020015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508080611ea19061351d565b915050611d4d565b50506729a2241af62c00006001908155600a600255600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690911790555050565b3361100114611f645760405162461bcd60e51b815260206004820152602560248201527f746865206d73672073656e646572206d75737420626520736c61736820636f6e60448201527f7472616374000000000000000000000000000000000000000000000000000000606482015260840161066e565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090205480611f93575050565b611f9e6001826134b3565b9050600060038281548110611fc357634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201600401549050600060038381548110611ffa57634e487b7160e01b600052603260045260246000fd5b6000918252602082206004600590920201019190915560035461201f906001906134b3565b905060006003848154811061204457634e487b7160e01b600052603260045260246000fd5b60009182526020918290206005909102015460405185815273ffffffffffffffffffffffffffffffffffffffff909116925082917f8cd4e147d8af98a9e3b6724021b8bf6aed2e5dac71c38f2dce8161b82585b25d910160405180910390a2816120af575050505050565b60006120bb8385613462565b9050801561219e5760005b858110156121285781600382815481106120f057634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254612110919061344a565b909155508190506121208161351d565b9150506120c6565b50600354600061213987600161344a565b90505b8181101561128f57826003828154811061216657634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016004016000828254612186919061344a565b909155508190506121968161351d565b91505061213c565b50505050505b50565b33411461221c5760405162461bcd60e51b815260206004820152602d60248201527f746865206d6573736167652073656e646572206d75737420626520746865206260448201527f6c6f636b2070726f647563657200000000000000000000000000000000000000606482015260840161066e565b60005460ff1661226e5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e69742079657400000000000000604482015260640161066e565b3a156122bc5760405162461bcd60e51b815260206004820152601460248201527f6761737072696365206973206e6f74207a65726f000000000000000000000000604482015260640161066e565b6122c962a0668043613556565b6122ee576127106125a76001546122e09190613476565b6122ea9190613462565b6001555b60015460045434919061230290839061344a565b61230c919061344a565b47106123225760015461231f908261344a565b90505b73ffffffffffffffffffffffffffffffffffffffff82166000908152600560205260409020548015612402576000600361235d6001846134b3565b8154811061237b57634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201905082600454612399919061344a565b60049081558101546123ac90849061344a565b600482015560405183815273ffffffffffffffffffffffffffffffffffffffff8516907f93a090ecc682c002995fad3c85b30c5651d7fd29b0be5da9d784a3302aedc0559060200160405180910390a250505050565b8273ffffffffffffffffffffffffffffffffffffffff167ff177e5d6c5764d79c32883ed824111d9b13f5668cf6ab1cc12dd36791dd955b48360405161244a91815260200190565b60405180910390a2505050565b83518351146124f45760405162461bcd60e51b815260206004820152604660248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e64206f7065726174654164647265737365732073686f756c6420626560648201527f20657175616c0000000000000000000000000000000000000000000000000000608482015260a40161066e565b81518351146125915760405162461bcd60e51b815260206004820152604260248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e64206665654164647265737365732073686f756c642062652065717560648201527f616c000000000000000000000000000000000000000000000000000000000000608482015260a40161066e565b805183511461262e5760405162461bcd60e51b815260206004820152604c60248201527f746865206e756d62657273206f6620636f6e73656e737573416464726573736560448201527f7320616e6420636f6d6d697373696f6e54686f7573616e64746873732073686f60648201527f756c6420626520657175616c0000000000000000000000000000000000000000608482015260a40161066e565b60005b83518110156127d35760005b818110156127215784818151811061266557634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff168583815181106126a357634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561270f5760405162461bcd60e51b815260206004820152601b60248201527f6475706c696361746520636f6e73656e73757320616464726573730000000000604482015260640161066e565b806127198161351d565b91505061263d565b506103e882828151811061274557634e487b7160e01b600052603260045260246000fd5b602002602001015111156127c15760405162461bcd60e51b815260206004820152602260248201527f636f6d6d697373696f6e54686f7573616e64746873206f7574206f6620626f7560448201527f6e64000000000000000000000000000000000000000000000000000000000000606482015260840161066e565b806127cb8161351d565b915050612631565b5050505050565b6000816040516020016127ed91906132f5565b604051602081830303815290604052805190602001208360405160200161281491906132f5565b6040516020818303038152906040528051906020012014905092915050565b015190565b60606000806128766128718560408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b6129c3565b805190915060008167ffffffffffffffff8111156128a457634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561291b57816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816128c25790505b50905060005b828110156129b65760008061295c86848151811061294f57634e487b7160e01b600052603260045260246000fd5b6020026020010151612af5565b9150915080612975575091976000975095505050505050565b8184848151811061299657634e487b7160e01b600052603260045260246000fd5b6020026020010181905250505080806129ae9061351d565b915050612921565b5095901515945092505050565b60606129ce82612bdb565b6129d757600080fd5b60006129e283612c14565b905060008167ffffffffffffffff811115612a0d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612a5257816020015b6040805180820190915260008082526020820152815260200190600190039081612a2b5790505b5090506000612a648560200151612c97565b8560200151612a73919061344a565b90506000805b84811015612aea57612a8a83612d19565b9150604051806040016040528083815260200184815250848281518110612ac157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152612ad6828461344a565b925080612ae28161351d565b915050612a79565b509195945050505050565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040805160a08101825260008082526020820181905291810182905260608101829052608081018290526000612b5585612e69565b905060005b612b6382612ed5565b15612bcf57612b79612b7483612ef9565b612f56565b73ffffffffffffffffffffffffffffffffffffffff166020840152612ba0612b7483612ef9565b73ffffffffffffffffffffffffffffffffffffffff16604084018190528352506103e860608301526001612b5a565b91959194509092505050565b8051600090612bec57506000919050565b6020820151805160001a9060c0821015612c0a575060009392505050565b5060019392505050565b8051600090612c2557506000919050565b600080612c358460200151612c97565b8460200151612c44919061344a565b9050600084600001518560200151612c5c919061344a565b90505b80821015612c8e57612c7082612d19565b612c7a908361344a565b915082612c868161351d565b935050612c5f565b50909392505050565b8051600090811a6080811015612cb05750600092915050565b60b8811080612ccb575060c08110801590612ccb575060f881105b15612cd95750600192915050565b60c0811015612d0d57612cee600160b86134ca565b612cfb9060ff16826134b3565b612d0690600161344a565b9392505050565b612cee600160f86134ca565b80516000908190811a6080811015612d345760019150611c50565b60b8811015612d5a57612d486080826134b3565b612d5390600161344a565b9150611c50565b60c0811015612ddb57600060b78203600186019550806020036101000a865104915060018101820193505080831015612dd55760405162461bcd60e51b815260206004820152601160248201527f6164646974696f6e206f766572666c6f77000000000000000000000000000000604482015260640161066e565b50611c50565b60f8811015612def57612d4860c0826134b3565b600060f78203600186019550806020036101000a865104915060018101820193505080831015612e615760405162461bcd60e51b815260206004820152601160248201527f6164646974696f6e206f766572666c6f77000000000000000000000000000000604482015260640161066e565b505092915050565b6040805160808101825260009181018281526060820183905281526020810191909152612e9582612bdb565b612e9e57600080fd5b6000612ead8360200151612c97565b8360200151612ebc919061344a565b6040805180820190915293845260208401525090919050565b80518051602082015160009291612eeb9161344a565b836020015110915050919050565b6040805180820190915260008082526020820152612f1682612ed5565b612f1f57600080fd5b60208201516000612f2f82612d19565b9050612f3b818361344a565b60209485015260408051808201909152908152928301525090565b8051600090601514612f6757600080fd5b612f7082612f76565b92915050565b805160009015801590612f8b57508151602110155b612f9457600080fd5b6000612fa38360200151612c97565b90508083600001511015612ff95760405162461bcd60e51b815260206004820152601a60248201527f6c656e677468206973206c657373207468616e206f6666736574000000000000604482015260640161066e565b82516000906130099083906134b3565b905060008083866020015161301e919061344a565b905080519150602083101561303a57826020036101000a820491505b50949350505050565b60008083601f840112613054578182fd5b50813567ffffffffffffffff81111561306b578182fd5b6020830191508360208260051b850101111561308657600080fd5b9250929050565b60008083601f84011261309e578182fd5b50813567ffffffffffffffff8111156130b5578182fd5b60208301915083602082850101111561308657600080fd5b6000602082840312156130de578081fd5b8135612d0681613596565b6000806000606084860312156130fd578182fd5b833561310881613596565b95602085013595506040909401359392505050565b6000806000806000806000806080898b031215613138578384fd5b883567ffffffffffffffff8082111561314f578586fd5b61315b8c838d01613043565b909a50985060208b0135915080821115613173578586fd5b61317f8c838d01613043565b909850965060408b0135915080821115613197578586fd5b6131a38c838d01613043565b909650945060608b01359150808211156131bb578384fd5b506131c88b828c01613043565b999c989b5096995094979396929594505050565b600080600080604085870312156131f1578384fd5b843567ffffffffffffffff80821115613208578586fd5b6132148883890161308d565b9096509450602087013591508082111561322c578384fd5b506132398782880161308d565b95989497509550505050565b600060208284031215613256578081fd5b5035919050565b6000815180845260208085019450808401835b838110156132a257815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613270565b509495945050505050565b81835281816020850137506000806020838501015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600082516133078184602087016134ed565b9190910192915050565b602081526000612d06602083018461325d565b604081526000613337604083018561325d565b828103602084810191909152845180835285820192820190845b8181101561336d57845183529383019391830191600101613351565b5090979650505050505050565b60208152600082518060208401526133998160408501602087016134ed565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020815260006133df6020830184866132ad565b949350505050565b6040815260006133fb6040830186886132ad565b828103602084015261340e8185876132ad565b979650505050505050565b60808152600061342d6080830187896132ad565b602083019590955250604081019290925260609091015292915050565b6000821982111561345d5761345d61356a565b500190565b60008261347157613471613580565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156134ae576134ae61356a565b500290565b6000828210156134c5576134c561356a565b500390565b600060ff821660ff8416808210156134e4576134e461356a565b90039392505050565b60005b838110156135085781810151838201526020016134f0565b83811115613517576000848401525b50505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561354f5761354f61356a565b5060010190565b60008261356557613565613580565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff811681146121a457600080fdfef90285ea944121f067b0f5135d77c29b2b329e8cb1bd96c96094f8b18cecc98d976ad253d38e4100a73d4e154726ea947f461f8a1c35edecd6816e76eb2e84eb661751ee94f8b18cecc98d976ad253d38e4100a73d4e154726ea94fd806ab93db5742944b7b50ce759e5eee5f6fe5094f8b18cecc98d976ad253d38e4100a73d4e154726ea947ef3a94ad1c443481fb3d86829355ca90477f8b594f8b18cecc98d976ad253d38e4100a73d4e154726ea9467d1ad48f91e131413bd0b04e823f3ae4f81e85394f8b18cecc98d976ad253d38e4100a73d4e154726ea943fb42cab4416024dc1b4c9e21b9acd0dfcef35f694f8b18cecc98d976ad253d38e4100a73d4e154726ea943511e3b8ac7336b99517d324145e9b5bb33e08a494f8b18cecc98d976ad253d38e4100a73d4e154726ea94729f39a54304fcc6ec279684c71491a385d7b9ae94f8b18cecc98d976ad253d38e4100a73d4e154726ea94f44a785fd9f23f0abd443541386e71356ce619dc94f8b18cecc98d976ad253d38e4100a73d4e154726ea942efd3cf0733421aec3e4202480d0a90bd157514994f8b18cecc98d976ad253d38e4100a73d4e154726ea94613b0f519ada008cb99b6130e89122ba416bf15994f8b18cecc98d976ad253d38e4100a73d4e154726ea94c0925eeb800ff6ba4695ded61562a10102152b5f94f8b18cecc98d976ad253d38e4100a73d4e154726ea9419e3c7d7e69f273f3f91c060bb438a007f6fc33c94f8b18cecc98d976ad253d38e4100a73d4e154726ea94e127f110d172a0c4c6209fe045dd71781e8fe9d494f8b18cecc98d976ad253d38e4100a73d4e154726ea94f778dc4a199a440dbe9f16d1e13e185bb179b3b794f8b18cecc98d976ad253d38e4100a73d4e154726a264697066735822122078c85b4f63b2f8b4fd34c611107eab0fa2704e542260fa2e0cb8f5d14c50943964736f6c63430008040033" }, "0x0000000000000000000000000000000000001001": { "balance": "0x0", @@ -43,7 +43,7 @@ }, "0x0000000000000000000000000000000000001003": { "balance": "0x0", - "code": "0x608060405234801561001057600080fd5b50600436106104dd5760003560e01c80638a9c5aa111610286578063c81b16621161016b578063e1c7392a116100e3578063f016e48111610097578063f7d134071161007c578063f7d134071461099a578063f9a2bbc7146109a3578063fdd31fcd146109ac57600080fd5b8063f016e4811461098a578063f446687d1461099257600080fd5b8063e75d72c7116100c8578063e75d72c71461095c578063ea54b2aa1461096f578063edade1891461097757600080fd5b8063e1c7392a1461094b578063e287d9ac1461095357600080fd5b8063d5fe55581161013a578063dcae76ab1161011f578063dcae76ab14610925578063dd86037e1461092e578063dfb6419f1461093857600080fd5b8063d5fe555814610914578063dc927faf1461091c57600080fd5b8063c81b1662146108dd578063cab8966b146108e6578063d06305a9146108ee578063d45c44351461090157600080fd5b8063a78abc16116101fe578063ac7b3b7c116101cd578063b1171724116101b2578063b1171724146108b8578063b8a6c086146108c1578063b95c4a57146108d457600080fd5b8063ac7b3b7c146108a5578063ad2dec82146108ae57600080fd5b8063a78abc1614610869578063a8b0bb8314610876578063ac0e37a01461087f578063ac4317511461089257600080fd5b806393e9d41311610255578063951188891161023a578063951188891461082d5780639dc092621461084d578063a7206cd61461085657600080fd5b806393e9d413146107f7578063948602331461080a57600080fd5b80638a9c5aa1146107b25780638b07ac61146107bb5780638ea7e9be146107c4578063930e1b09146107d757600080fd5b80633a975612116103c75780636949b35c1161033f578063783028a9116102f35780637ba53285116102d85780637ba532851461076e57806382f8b6e914610781578063896efbf21461078a57600080fd5b8063783028a914610752578063792626be1461075b57600080fd5b8063730055aa11610324578063730055aa1461072057806374f2272d14610740578063750a012e1461074957600080fd5b80636949b35c146106f75780636daf2f5b1461071757600080fd5b806351b6ada31161039657806353a467291161037b57806353a46729146106dd57806354133307146106e65780635e9d2b1f146106ee57600080fd5b806351b6ada3146106aa57806351e13fac146106ca57600080fd5b80633a9756121461067c5780633cfc97bf1461068457806343756e5c146106975780634c86f558146106a057600080fd5b8063285d84cc1161045a5780632ca3212511610429578063352b33551161040e578063352b33551461061c578063378bc94c1461063d578063388d3a551461067357600080fd5b80632ca32125146105f35780632ed87b09146105fc57600080fd5b8063285d84cc146105a4578063288e8dc4146105c75780632a88b694146105cf5780632b861629146105de57600080fd5b80631ad5bb5c116104b15780631c643312116104965780631c6433121461057f5780631fca52781461059257806325ee13e21461059b57600080fd5b80631ad5bb5c146105405780631b20087c1461057657600080fd5b80625eeee9146104e257806304e9e3a41461050157806308f2ec061461052f57806314c1e1f714610537575b600080fd5b6104ee640826299e0081565b6040519081526020015b60405180910390f35b61050a61100781565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104f8565b6104ee601481565b61050a61100481565b61050a61054e366004613320565b60009081526011602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6104ee60085481565b61050a61058d366004613320565b6109cc565b6104ee60015481565b61050a61100581565b6104ee7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b6104ee600681565b6104ee678ac7230489e8000081565b6105f16105ec3660046133c5565b610a03565b005b6104ee60035481565b6104ee61060a3660046134e3565b60136020526000908152604090205481565b6106246110b5565b60405167ffffffffffffffff90911681526020016104f8565b61050a61064b366004613320565b60116020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6104ee61272481565b6104ee603281565b6104ee610692366004613320565b6110c6565b61050a61100181565b6104ee6201518081565b6106bd6106b836600461346e565b61118c565b6040516104f89190613561565b6104ee6106d8366004613320565b611227565b6104ee61271a81565b6104ee606481565b6104ee60065481565b6104ee6107053660046134e3565b60106020526000908152604090205481565b6104ee61274281565b61073361072e366004613320565b611390565b6040516104f891906135f3565b6104ee60045481565b6104ee60025481565b61050a61100881565b6104ee610769366004613320565b61142a565b6104ee61077c366004613320565b6114e2565b6104ee600a5481565b61079d610798366004613320565b61150e565b60405163ffffffff90911681526020016104f8565b6104ee61273881565b6104ee60055481565b61079d6107d2366004613320565b611532565b6104ee6107e53660046132df565b600c6020526000908152604090205481565b6106bd610805366004613320565b61157f565b61081d610818366004613320565b6115f8565b60405190151581526020016104f8565b61084061083b366004613499565b611615565b6040516104f891906135bb565b61050a61100681565b6104ee610864366004613320565b61172c565b60005461081d9060ff1681565b6104ee60075481565b61050a61088d366004613320565b611751565b6105f16108a0366004613405565b611775565b6104ee61276a81565b61079d620bb08081565b61050a61100981565b6108406108cf36600461346e565b611eac565b6104ee61272e81565b61050a61100281565b61079d611f34565b61079d6108fc366004613320565b611f46565b61062461090f366004613320565b611f6a565b610624611f9d565b61050a61100381565b6104ee60095481565b6106246212750081565b61050a610946366004613320565b611fab565b6105f1611fcf565b6104ee60125481565b6105f161096a3660046132df565b612154565b610733612311565b61081d610985366004613350565b61232d565b6104ee600781565b6002546104ee565b61079d6107e081565b61050a61100081565b6104ee6109ba3660046132df565b600d6020526000908152604090205481565b600b81815481106109dc57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b6040517f541d55480000000000000000000000000000000000000000000000000000000081523360048201526110049063541d55489060240160206040518083038186803b158015610a5457600080fd5b505afa158015610a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8c9190613300565b610af7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f746865206d73672073656e646572206973206e6f7420612072656c617965720060448201526064015b60405180910390fd5b60125415610b0757601254610b0e565b640826299e005b3a14610b76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6d75737420757365206c696d69746564206761737072696365000000000000006044820152606401610aee565b6000610bba83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250605091506124679050565b90506000610bc7826124e0565b60008181526011602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1615610c56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e27742073796e63206475706c69636174656420686561646572000000006044820152606401610aee565b6000806000610c6585856125c0565b92509250925080600014610ca957604051819085907f591f7e28c6b17bc54bdf2bdbf67daf8aaca92221f3577319607fba0ce38edc7290600090a350505050505050565b610cb460025461150e565b63ffffffff16610cc6846102d06136db565b63ffffffff1611610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e27742073796e6320686561646572203520646179732061676f000000006044820152606401610aee565b6000610d408760206136c3565b90506000610d8589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525092508c91506124679050565b9050610d8f613145565b6000808060808487876064600019fa610da757600080fd5b5050815160208301516040840151919250906000610dc76107e08b613717565b9050610dd78c848b8d8589612822565b60008c8152600f602090815260409091208251610dfa9391929190910190613163565b50610e076107e08b6138fe565b63ffffffff16610e2a5763ffffffff811660009081526010602052604090208b90555b60008b815260116020526040812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556005546009805491929091610e769084906136c3565b9091555050336000908152600c6020526040902054610eea57600b80546001810182556000919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790555b336000908152600c60205260408120805491610f05836138ab565b9190505550600654600860008154610f1c906138ab565b918290555010610f60576000610f30612900565b336000908152600d6020526040812080549293508392909190610f549084906136c3565b90915550506000600855505b6001548910611047576000610f7660018c6137de565b90506000610f838d611227565b90505b63ffffffff82166000908152601360205260409020548114801590610fc357508b63ffffffff1660068363ffffffff16610fc091906136c3565b10155b15610ffb5763ffffffff82166000908152601360205260409020819055610fe982613850565b9150610ff481611227565b9050610f86565b61100660025461150e565b63ffffffff168c63ffffffff161115611022576110228d612cd2565b505060028b9055600189905563ffffffff8a1660009081526013602052604090208b90555b6040805173ffffffffffffffffffffffffffffffffffffffff86811682526020820185905263ffffffff8d1692908616918e917f04d2d7fea49cb37d18896bf3ec2d974275b276f48a657185bc22fb2c7bb3323a910160405180910390a45050505050505050505050505050565b6110c36212750060046137a0565b81565b6000818152600f602052604081208054829160e09161116e91609c916110eb90613870565b80601f016020809104026020016040519081016040528092919081815260200182805461111790613870565b80156111645780601f1061113957610100808354040283529160200191611164565b820191906000526020600020905b81548152906001019060200180831161114757829003601f168201915b5050505050015190565b63ffffffff911c166000908152601060205260409020549392505050565b6000828152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845260010182529182902080548351818402810184019094528084526060939283018282801561121a57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116111ef575b5050505050905092915050565b6000818152600f60205260408120805461138a9161124c91602491906110eb90613870565b7bffffffff000000000000000000000000ffffffff00000000000000007eff000000ff000000ff000000ff000000ff000000ff000000ff000000ff0000600883811c9182167fff000000ff000000ff000000ff000000ff000000ff000000ff000000ff0000009490911b93841617601090811c7cff000000ff000000ff000000ff000000ff000000ff000000ff000000ff9092167dff000000ff000000ff000000ff000000ff000000ff000000ff000000ff009094169390931790921b91909117602081811c9283167fffffffff000000000000000000000000ffffffff0000000000000000000000009290911b91821617604090811c73ffffffff000000000000000000000000ffffffff90931677ffffffff000000000000000000000000ffffffff0000000090921691909117901b17608081811c91901b1790565b92915050565b600f60205260009081526040902080546113a990613870565b80601f01602080910402602001604051908101604052809291908181526020018280546113d590613870565b80156114225780601f106113f757610100808354040283529160200191611422565b820191906000526020600020905b81548152906001019060200180831161140557829003601f168201915b505050505081565b60006007548211611439575090565b816007541080156114575750600754611453906002613755565b8211155b1561146457505060075490565b8160075460026114749190613755565b1080156114b357506004600754600361148d9190613755565b6114979190613703565b6007546114a5906002613755565b6114af91906136c3565b8211155b156114d2578160075460036114c89190613755565b61138a91906137c7565b61138a600483613703565b919050565b6000818152600f60205260408120805460809161150691608891906110eb90613870565b901c92915050565b6000818152600f60205260408120805460e09161150691609891906110eb90613870565b6000818152600f60205260408120805461138a9160e091611559916068916110eb90613870565b901c600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1790565b6000818152600e60209081526040918290208054835181840281018401909452808452606093928301828280156115ec57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116115c1575b50505050509050919050565b6000620bb0806116078361150e565b63ffffffff16101592915050565b6060818067ffffffffffffffff81111561163f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611668578160200160208202803683370190505b506000868152600e602052604081209193505b82811015611722578160010160008787848181106116a957634e487b7160e01b600052603260045260246000fd5b90506020020160208101906116be91906132df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002054845185908390811061170757634e487b7160e01b600052603260045260246000fd5b602090810291909101015261171b816138ab565b905061167b565b5050509392505050565b6000818152600f60205260408120805461138a9161124c91604491906110eb90613870565b6000818152600f60205260408120805460609161150691607491906110eb90613870565b60005460ff166117e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610aee565b3361100614611872576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610aee565b602081146118b05783836040517fad23613c000000000000000000000000000000000000000000000000000000008152600401610aee929190613644565b61192484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f726577617264466f7253796e634865616465720000000000000000000000000060208201529150612f149050565b156119d157604080516020601f840181900481028201810190925282815260009161196791858580838501838280828437600092019190915250612f0f92505050565b905080158061197e575068056bc75e2d6310000081115b156119c957848482600168056bc75e2d631000006040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600555611e69565b611a4584848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601a81527f63616c6c6572436f6d70656e736174696f6e4d6f6c6563756c6500000000000060208201529150612f149050565b15611adb57604080516020601f8401819004810282018101909252828152600091611a8891858580838501838280828437600092019190915250612f0f92505050565b9050612710811115611ad35784848260006127106040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600455611e69565b611b4f84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600981527f726f756e6453697a65000000000000000000000000000000000000000000000060208201529150612f149050565b15611be557604080516020601f8401819004810282018101909252828152600091611b9291858580838501838280828437600092019190915250612f0f92505050565b9050600754811015611bdd576007546040517f808861f9000000000000000000000000000000000000000000000000000000008152610aee9187918791859160001990600401613692565b600655611e69565b611c5984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600981527f6d6178576569676874000000000000000000000000000000000000000000000060208201529150612f149050565b15611cf857604080516020601f8401819004810282018101909252828152600091611c9c91858580838501838280828437600092019190915250612f0f92505050565b9050801580611cac575060065481115b15611cf05784848260016006546040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600755611e69565b611d6c84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601281527f73746f7265426c6f636b4761735072696365000000000000000000000000000060208201529150612f149050565b15611e0757604080516020601f8401819004810282018101909252828152600091611daf91858580838501838280828437600092019190915250612f0f92505050565b9050633b9aca00811015611dff57848482633b9aca006000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b601255611e69565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610aee565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a84848484604051611e9e9493929190613660565b60405180910390a150505050565b6000828152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452600190810183529281902090920180548351818402810184019094528084526060939283018282801561121a57602002820191906000526020600020905b815481526020019060010190808311611f14575050505050905092915050565b6000611f4160025461150e565b905090565b6000818152600f60205260408120805460e09161150691609c91906110eb90613870565b6000818152600f602052604081208054611f919160e091611559916064916110eb90613870565b63ffffffff1692915050565b6110c360046212750061373a565b6000818152600f6020526040812080546060916115069160a091906110eb90613870565b60005460ff161561203c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610aee565b600061205f60405180608001604052806050815260200161394e605091396124e0565b60018080556002829055600382905560408051608081019091526050808252929350600092839291839161394e6020830139905060006120a46107e0620bb080613717565b63ffffffff811660009081526010602052604081208890559091506120d0838786620bb080868a612822565b6000888152600f6020908152604090912082519293506120f4929091840190613163565b5050678ac7230489e8000060055550506032600455505060646006555050601460075562015180600a55640826299e00601255600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60005460ff166121c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610aee565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d60205260409020548061224d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6e6f2072656c61796572207265776172640000000000000000000000000000006044820152606401610aee565b73ffffffffffffffffffffffffffffffffffffffff82166000818152600d602052604080822091909155517f9a99b4f0000000000000000000000000000000000000000000000000000000008152600481019190915260248101829052829061100290639a99b4f090604401602060405180830381600087803b1580156122d357600080fd5b505af11580156122e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061230b9190613338565b50505050565b60405180608001604052806050815260200161394e6050913981565b63ffffffff851660009081526013602052604081205461234b611f34565b63ffffffff1661235b87896136db565b63ffffffff16118061236b575087155b80612374575080155b1561238357600091505061245d565b6000818152600f6020526040812080546123a3916044916110eb90613870565b9050846123b5578814915061245d9050565b8860005b86811015612457576123cc6002876138ea565b6001141561240b576124048888838181106123f757634e487b7160e01b600052603260045260246000fd5b9050602002013583612f6d565b915061243e565b61243b8289898481811061242f57634e487b7160e01b600052603260045260246000fd5b90506020020135612f6d565b91505b60019590951c948061244f816138ab565b9150506123b9565b50149150505b9695505050505050565b6060600061247584846137c7565b90508067ffffffffffffffff81111561249e57634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156124c8576020820181803683370190505b50915060208581019085840101611722828285612f99565b600061138a600280846040516124f69190613545565b602060405180830381855afa158015612513573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906125369190613338565b60405160200161254891815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261258091613545565b602060405180830381855afa15801561259d573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061124c9190613338565b6000806000806125d461124c602488015190565b905060006125e1826114e2565b9050806125f5575061272e915061281b9050565b6125fe866114e2565b935083156126135750612738915061281b9050565b600061262560e061155960688b015190565b9050600061263282612ff2565b90508715806126415750808810155b15612655575061276a935061281b92505050565b61265e8461150e565b6126699060016136db565b9650600061267685611532565b90506126846107e0896138fe565b63ffffffff16156126ca578063ffffffff168363ffffffff16141580156126b0575063ffffffff811615155b156126c5575061271a945061281b9350505050565b6127da565b60006126d582612ff2565b905060006126e287611f6a565b905060006126ef886110c6565b905060006126fc82611f6a565b9050600061270a8285613803565b905061271a60046212750061373a565b67ffffffffffffffff168167ffffffffffffffff1610156127465761274360046212750061373a565b90505b6127546212750060046137a0565b67ffffffffffffffff168167ffffffffffffffff1611156127805761277d6212750060046137a0565b90505b6212750081860204600061279382613068565b90508063ffffffff168a63ffffffff16141580156127b6575063ffffffff811615155b156127d257506127249b5061281b9a5050505050505050505050565b505050505050505b6000612802837bffff0000000000000000000000000000000000000000000000000000613703565b905061280e81866136c3565b9750600096505050505050505b9250925092565b6040805160a080825260c08201909252606091602082018180368337019050509050600060408773ffffffffffffffffffffffffffffffffffffffff16901b9050600060408563ffffffff16901b60608763ffffffff16901b608089901b61288a91906136c3565b61289491906136c3565b90507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b1660208481019060708601908c015b818310156128e35780518352602092830192016128cb565b509384525060188301919091526030909101529695505050505050565b600954600b805460408051602080840282018101909252828152600094938593849383018282801561296857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161293d575b5050505050905060008151905060008167ffffffffffffffff81111561299e57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156129c7578160200160208202803683370190505b50905060005b82811015612a9a5760008482815181106129f757634e487b7160e01b600052603260045260246000fd5b602002602001015190506000612a4b600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461142a565b905080848481518110612a6e57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152612a8381886136c3565b965050508080612a92906138ab565b9150506129cd565b50600061271060045487612aae9190613755565b612ab89190613703565b9050612ac481876137c7565b95508560015b84811015612bb05760008789868481518110612af657634e487b7160e01b600052603260045260246000fd5b6020026020010151612b089190613755565b612b129190613703565b905080600d6000898581518110612b3957634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612b8a91906136c3565b90915550612b9a905081846137c7565b9250508080612ba8906138ab565b915050612aca565b5080600d600087600081518110612bd757634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612c2891906136c3565b9091555050600060098190555b84811015612cba57600c6000878381518110612c6157634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090558080612cb2906138ab565b915050612c35565b50612cc7600b60006131e7565b509695505050505050565b60005b6006811015612d0757600354821415612cec575050565b612cf582611227565b9150612d00816138ab565b9050612cd5565b506000600a54612d1683611f6a565b67ffffffffffffffff16612d2a9190613703565b90506000612d3783611fab565b90506000600761100573ffffffffffffffffffffffffffffffffffffffff166373fa03ed6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d8557600080fd5b505afa158015612d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbd9190613338565b612dc791906137c7565b905073ffffffffffffffffffffffffffffffffffffffff821615801590612ded57508083115b1561230b576000612dfd85611751565b6000858152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8816845260018101909252909120549192509080612e8c5781546001810183556000838152602090200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87161790555b5073ffffffffffffffffffffffffffffffffffffffff93841660009081526001918201602090815260408220805480850182558184528284200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169590971694909417909555918101805491820181558252929020909101929092555050565b015190565b600081604051602001612f279190613545565b6040516020818303038152906040528051906020012083604051602001612f4e9190613545565b6040516020818303038152906040528051906020012014905092915050565b600060405183815282602082015260208160408360025afa5060208160208360025afa50519392505050565b60208110612fd15782518252612fb06020836136c3565b9150612fbd6020846136c3565b9250612fca6020826137c7565b9050612f99565b915181516020939093036101000a6000190180199091169216919091179052565b600060ff601883901c1662ffffff831660038211613036576130158260036137de565b613020906008613774565b63ffffffff9182169082161c9081169250613061565b63ffffffff8116925061304a6003836137de565b613055906008613774565b63ffffffff1683901b92505b5050919050565b600080825b80156130885760011c81613080816138c6565b92505061306d565b600060036130978460076136db565b63ffffffff16901c9050600060038263ffffffff16116130e2576130bc8260036137de565b6130c7906008613774565b63ffffffff168662ffffff1663ffffffff16901b905061310c565b6130ed6003836137de565b6130f8906008613774565b63ffffffff1686901c90508062ffffff1690505b6280000081161561312d5760081c62ffffff1681613129816138c6565b9250505b60188263ffffffff16901b8117945050505050919050565b60405180608001604052806004906020820280368337509192915050565b82805461316f90613870565b90600052602060002090601f01602090048101928261319157600085556131d7565b82601f106131aa57805160ff19168380011785556131d7565b828001600101855582156131d7579182015b828111156131d75782518255916020019190600101906131bc565b506131e3929150613208565b5090565b50805460008255906000526020600020908101906132059190613208565b50565b5b808211156131e35760008155600101613209565b803573ffffffffffffffffffffffffffffffffffffffff811681146114dd57600080fd5b60008083601f840112613252578182fd5b50813567ffffffffffffffff811115613269578182fd5b6020830191508360208260051b850101111561328457600080fd5b9250929050565b60008083601f84011261329c578182fd5b50813567ffffffffffffffff8111156132b3578182fd5b60208301915083602082850101111561328457600080fd5b803563ffffffff811681146114dd57600080fd5b6000602082840312156132f0578081fd5b6132f98261321d565b9392505050565b600060208284031215613311578081fd5b815180151581146132f9578182fd5b600060208284031215613331578081fd5b5035919050565b600060208284031215613349578081fd5b5051919050565b60008060008060008060a08789031215613368578182fd5b86359550613378602088016132cb565b9450613386604088016132cb565b9350606087013567ffffffffffffffff8111156133a1578283fd5b6133ad89828a01613241565b979a9699509497949695608090950135949350505050565b600080602083850312156133d7578182fd5b823567ffffffffffffffff8111156133ed578283fd5b6133f98582860161328b565b90969095509350505050565b6000806000806040858703121561341a578384fd5b843567ffffffffffffffff80821115613431578586fd5b61343d8883890161328b565b90965094506020870135915080821115613455578384fd5b506134628782880161328b565b95989497509550505050565b60008060408385031215613480578182fd5b823591506134906020840161321d565b90509250929050565b6000806000604084860312156134ad578283fd5b83359250602084013567ffffffffffffffff8111156134ca578283fd5b6134d686828701613241565b9497909650939450505050565b6000602082840312156134f4578081fd5b6132f9826132cb565b81835281816020850137506000806020838501015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60008251613557818460208701613824565b9190910192915050565b6020808252825182820181905260009190848201906040850190845b818110156135af57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161357d565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156135af578351835292840192918401916001016135d7565b6020815260008251806020840152613612816040850160208701613824565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020815260006136586020830184866134fd565b949350505050565b6040815260006136746040830186886134fd565b82810360208401526136878185876134fd565b979650505050505050565b6080815260006136a66080830187896134fd565b602083019590955250604081019290925260609091015292915050565b600082198211156136d6576136d6613921565b500190565b600063ffffffff8083168185168083038211156136fa576136fa613921565b01949350505050565b60008261371257613712613937565b500490565b600063ffffffff8084168061372e5761372e613937565b92169190910492915050565b600067ffffffffffffffff8084168061372e5761372e613937565b600081600019048311821515161561376f5761376f613921565b500290565b600063ffffffff8083168185168183048111821515161561379757613797613921565b02949350505050565b600067ffffffffffffffff8083168185168183048111821515161561379757613797613921565b6000828210156137d9576137d9613921565b500390565b600063ffffffff838116908316818110156137fb576137fb613921565b039392505050565b600067ffffffffffffffff838116908316818110156137fb576137fb613921565b60005b8381101561383f578181015183820152602001613827565b8381111561230b5750506000910152565b600063ffffffff82168061386657613866613921565b6000190192915050565b600181811c9082168061388457607f821691505b602082108114156138a557634e487b7160e01b600052602260045260246000fd5b50919050565b60006000198214156138bf576138bf613921565b5060010190565b600063ffffffff808316818114156138e0576138e0613921565b6001019392505050565b6000826138f9576138f9613937565b500690565b600063ffffffff8084168061391557613915613937565b92169190910692915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfe0000402089138e40cd8b4832beb8013bc80b1425c8bcbe10fc280400000000000000000058a06ab0edc5653a6ab78490675a954f8d8b4d4f131728dcf965cd0022a02cdde59f8e63303808176bbe3919a26469706673582212205d4e3bb500e130d38d72905a4781b2b2c7c8c2bd1fc66fe8296947acd6a1ee5f64736f6c63430008040033" + "code": "0x608060405234801561001057600080fd5b50600436106104dd5760003560e01c80638a9c5aa111610286578063c81b16621161016b578063e1c7392a116100e3578063f016e48111610097578063f7d134071161007c578063f7d134071461099a578063f9a2bbc7146109a3578063fdd31fcd146109ac57600080fd5b8063f016e4811461098a578063f446687d1461099257600080fd5b8063e75d72c7116100c8578063e75d72c71461095c578063ea54b2aa1461096f578063edade1891461097757600080fd5b8063e1c7392a1461094b578063e287d9ac1461095357600080fd5b8063d5fe55581161013a578063dcae76ab1161011f578063dcae76ab14610925578063dd86037e1461092e578063dfb6419f1461093857600080fd5b8063d5fe555814610914578063dc927faf1461091c57600080fd5b8063c81b1662146108dd578063cab8966b146108e6578063d06305a9146108ee578063d45c44351461090157600080fd5b8063a78abc16116101fe578063ac7b3b7c116101cd578063b1171724116101b2578063b1171724146108b8578063b8a6c086146108c1578063b95c4a57146108d457600080fd5b8063ac7b3b7c146108a5578063ad2dec82146108ae57600080fd5b8063a78abc1614610869578063a8b0bb8314610876578063ac0e37a01461087f578063ac4317511461089257600080fd5b806393e9d41311610255578063951188891161023a578063951188891461082d5780639dc092621461084d578063a7206cd61461085657600080fd5b806393e9d413146107f7578063948602331461080a57600080fd5b80638a9c5aa1146107b25780638b07ac61146107bb5780638ea7e9be146107c4578063930e1b09146107d757600080fd5b80633a975612116103c75780636949b35c1161033f578063783028a9116102f35780637ba53285116102d85780637ba532851461076e57806382f8b6e914610781578063896efbf21461078a57600080fd5b8063783028a914610752578063792626be1461075b57600080fd5b8063730055aa11610324578063730055aa1461072057806374f2272d14610740578063750a012e1461074957600080fd5b80636949b35c146106f75780636daf2f5b1461071757600080fd5b806351b6ada31161039657806353a467291161037b57806353a46729146106dd57806354133307146106e65780635e9d2b1f146106ee57600080fd5b806351b6ada3146106aa57806351e13fac146106ca57600080fd5b80633a9756121461067c5780633cfc97bf1461068457806343756e5c146106975780634c86f558146106a057600080fd5b8063285d84cc1161045a5780632ca3212511610429578063352b33551161040e578063352b33551461061c578063378bc94c1461063d578063388d3a551461067357600080fd5b80632ca32125146105f35780632ed87b09146105fc57600080fd5b8063285d84cc146105a4578063288e8dc4146105c75780632a88b694146105cf5780632b861629146105de57600080fd5b80631ad5bb5c116104b15780631c643312116104965780631c6433121461057f5780631fca52781461059257806325ee13e21461059b57600080fd5b80631ad5bb5c146105405780631b20087c1461057657600080fd5b80625eeee9146104e257806304e9e3a41461050157806308f2ec061461052f57806314c1e1f714610537575b600080fd5b6104ee640826299e0081565b6040519081526020015b60405180910390f35b61050a61100781565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016104f8565b6104ee601481565b61050a61100481565b61050a61054e366004613320565b60009081526011602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6104ee60085481565b61050a61058d366004613320565b6109cc565b6104ee60015481565b61050a61100581565b6104ee7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b6104ee600681565b6104ee678ac7230489e8000081565b6105f16105ec3660046133c5565b610a03565b005b6104ee60035481565b6104ee61060a3660046134e3565b60136020526000908152604090205481565b6106246110b5565b60405167ffffffffffffffff90911681526020016104f8565b61050a61064b366004613320565b60116020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6104ee61272481565b6104ee603281565b6104ee610692366004613320565b6110c6565b61050a61100181565b6104ee6201518081565b6106bd6106b836600461346e565b61118c565b6040516104f89190613561565b6104ee6106d8366004613320565b611227565b6104ee61271a81565b6104ee606481565b6104ee60065481565b6104ee6107053660046134e3565b60106020526000908152604090205481565b6104ee61274281565b61073361072e366004613320565b611390565b6040516104f891906135f3565b6104ee60045481565b6104ee60025481565b61050a61100881565b6104ee610769366004613320565b61142a565b6104ee61077c366004613320565b6114e2565b6104ee600a5481565b61079d610798366004613320565b61150e565b60405163ffffffff90911681526020016104f8565b6104ee61273881565b6104ee60055481565b61079d6107d2366004613320565b611532565b6104ee6107e53660046132df565b600c6020526000908152604090205481565b6106bd610805366004613320565b61157f565b61081d610818366004613320565b6115f8565b60405190151581526020016104f8565b61084061083b366004613499565b611615565b6040516104f891906135bb565b61050a61100681565b6104ee610864366004613320565b61172c565b60005461081d9060ff1681565b6104ee60075481565b61050a61088d366004613320565b611751565b6105f16108a0366004613405565b611775565b6104ee61276a81565b61079d620bb08081565b61050a61100981565b6108406108cf36600461346e565b611eac565b6104ee61272e81565b61050a61100281565b61079d611f34565b61079d6108fc366004613320565b611f46565b61062461090f366004613320565b611f6a565b610624611f9d565b61050a61100381565b6104ee60095481565b6106246212750081565b61050a610946366004613320565b611fab565b6105f1611fcf565b6104ee60125481565b6105f161096a3660046132df565b612154565b610733612311565b61081d610985366004613350565b61232d565b6104ee600781565b6002546104ee565b61079d6107e081565b61050a61100081565b6104ee6109ba3660046132df565b600d6020526000908152604090205481565b600b81815481106109dc57600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b6040517f541d55480000000000000000000000000000000000000000000000000000000081523360048201526110049063541d55489060240160206040518083038186803b158015610a5457600080fd5b505afa158015610a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a8c9190613300565b610af7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f746865206d73672073656e646572206973206e6f7420612072656c617965720060448201526064015b60405180910390fd5b60125415610b0757601254610b0e565b640826299e005b3a14610b76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6d75737420757365206c696d69746564206761737072696365000000000000006044820152606401610aee565b6000610bba83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250605091506124679050565b90506000610bc7826124e0565b60008181526011602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1615610c56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e27742073796e63206475706c69636174656420686561646572000000006044820152606401610aee565b6000806000610c6585856125c0565b92509250925080600014610ca957604051819085907f591f7e28c6b17bc54bdf2bdbf67daf8aaca92221f3577319607fba0ce38edc7290600090a350505050505050565b610cb460025461150e565b63ffffffff16610cc6846102d06136db565b63ffffffff1611610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e27742073796e6320686561646572203520646179732061676f000000006044820152606401610aee565b6000610d408760206136c3565b90506000610d8589898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525092508c91506124679050565b9050610d8f613145565b6000808060808487876064600019fa610da757600080fd5b5050815160208301516040840151919250906000610dc76107e08b613717565b9050610dd78c848b8d8589612822565b60008c8152600f602090815260409091208251610dfa9391929190910190613163565b50610e076107e08b6138fe565b63ffffffff16610e2a5763ffffffff811660009081526010602052604090208b90555b60008b815260116020526040812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556005546009805491929091610e769084906136c3565b9091555050336000908152600c6020526040902054610eea57600b80546001810182556000919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790555b336000908152600c60205260408120805491610f05836138ab565b9190505550600654600860008154610f1c906138ab565b918290555010610f60576000610f30612900565b336000908152600d6020526040812080549293508392909190610f549084906136c3565b90915550506000600855505b6001548910611047576000610f7660018c6137de565b90506000610f838d611227565b90505b63ffffffff82166000908152601360205260409020548114801590610fc357508b63ffffffff1660068363ffffffff16610fc091906136c3565b10155b15610ffb5763ffffffff82166000908152601360205260409020819055610fe982613850565b9150610ff481611227565b9050610f86565b61100660025461150e565b63ffffffff168c63ffffffff161115611022576110228d612cd2565b505060028b9055600189905563ffffffff8a1660009081526013602052604090208b90555b6040805173ffffffffffffffffffffffffffffffffffffffff86811682526020820185905263ffffffff8d1692908616918e917f04d2d7fea49cb37d18896bf3ec2d974275b276f48a657185bc22fb2c7bb3323a910160405180910390a45050505050505050505050505050565b6110c36212750060046137a0565b81565b6000818152600f602052604081208054829160e09161116e91609c916110eb90613870565b80601f016020809104026020016040519081016040528092919081815260200182805461111790613870565b80156111645780601f1061113957610100808354040283529160200191611164565b820191906000526020600020905b81548152906001019060200180831161114757829003601f168201915b5050505050015190565b63ffffffff911c166000908152601060205260409020549392505050565b6000828152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845260010182529182902080548351818402810184019094528084526060939283018282801561121a57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116111ef575b5050505050905092915050565b6000818152600f60205260408120805461138a9161124c91602491906110eb90613870565b7bffffffff000000000000000000000000ffffffff00000000000000007eff000000ff000000ff000000ff000000ff000000ff000000ff000000ff0000600883811c9182167fff000000ff000000ff000000ff000000ff000000ff000000ff000000ff0000009490911b93841617601090811c7cff000000ff000000ff000000ff000000ff000000ff000000ff000000ff9092167dff000000ff000000ff000000ff000000ff000000ff000000ff000000ff009094169390931790921b91909117602081811c9283167fffffffff000000000000000000000000ffffffff0000000000000000000000009290911b91821617604090811c73ffffffff000000000000000000000000ffffffff90931677ffffffff000000000000000000000000ffffffff0000000090921691909117901b17608081811c91901b1790565b92915050565b600f60205260009081526040902080546113a990613870565b80601f01602080910402602001604051908101604052809291908181526020018280546113d590613870565b80156114225780601f106113f757610100808354040283529160200191611422565b820191906000526020600020905b81548152906001019060200180831161140557829003601f168201915b505050505081565b60006007548211611439575090565b816007541080156114575750600754611453906002613755565b8211155b1561146457505060075490565b8160075460026114749190613755565b1080156114b357506004600754600361148d9190613755565b6114979190613703565b6007546114a5906002613755565b6114af91906136c3565b8211155b156114d2578160075460036114c89190613755565b61138a91906137c7565b61138a600483613703565b919050565b6000818152600f60205260408120805460809161150691608891906110eb90613870565b901c92915050565b6000818152600f60205260408120805460e09161150691609891906110eb90613870565b6000818152600f60205260408120805461138a9160e091611559916068916110eb90613870565b901c600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1790565b6000818152600e60209081526040918290208054835181840281018401909452808452606093928301828280156115ec57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116115c1575b50505050509050919050565b6000620bb0806116078361150e565b63ffffffff16101592915050565b6060818067ffffffffffffffff81111561163f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611668578160200160208202803683370190505b506000868152600e602052604081209193505b82811015611722578160010160008787848181106116a957634e487b7160e01b600052603260045260246000fd5b90506020020160208101906116be91906132df565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002054845185908390811061170757634e487b7160e01b600052603260045260246000fd5b602090810291909101015261171b816138ab565b905061167b565b5050509392505050565b6000818152600f60205260408120805461138a9161124c91604491906110eb90613870565b6000818152600f60205260408120805460609161150691607491906110eb90613870565b60005460ff166117e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610aee565b3361100614611872576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610aee565b602081146118b05783836040517fad23613c000000000000000000000000000000000000000000000000000000008152600401610aee929190613644565b61192484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f726577617264466f7253796e634865616465720000000000000000000000000060208201529150612f149050565b156119d157604080516020601f840181900481028201810190925282815260009161196791858580838501838280828437600092019190915250612f0f92505050565b905080158061197e575068056bc75e2d6310000081115b156119c957848482600168056bc75e2d631000006040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600555611e69565b611a4584848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601a81527f63616c6c6572436f6d70656e736174696f6e4d6f6c6563756c6500000000000060208201529150612f149050565b15611adb57604080516020601f8401819004810282018101909252828152600091611a8891858580838501838280828437600092019190915250612f0f92505050565b9050612710811115611ad35784848260006127106040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600455611e69565b611b4f84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600981527f726f756e6453697a65000000000000000000000000000000000000000000000060208201529150612f149050565b15611be557604080516020601f8401819004810282018101909252828152600091611b9291858580838501838280828437600092019190915250612f0f92505050565b9050600754811015611bdd576007546040517f808861f9000000000000000000000000000000000000000000000000000000008152610aee9187918791859160001990600401613692565b600655611e69565b611c5984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600981527f6d6178576569676874000000000000000000000000000000000000000000000060208201529150612f149050565b15611cf857604080516020601f8401819004810282018101909252828152600091611c9c91858580838501838280828437600092019190915250612f0f92505050565b9050801580611cac575060065481115b15611cf05784848260016006546040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b600755611e69565b611d6c84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601281527f73746f7265426c6f636b4761735072696365000000000000000000000000000060208201529150612f149050565b15611e0757604080516020601f8401819004810282018101909252828152600091611daf91858580838501838280828437600092019190915250612f0f92505050565b9050633b9aca00811015611dff57848482633b9aca006000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610aee959493929190613692565b601255611e69565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610aee565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a84848484604051611e9e9493929190613660565b60405180910390a150505050565b6000828152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452600190810183529281902090920180548351818402810184019094528084526060939283018282801561121a57602002820191906000526020600020905b815481526020019060010190808311611f14575050505050905092915050565b6000611f4160025461150e565b905090565b6000818152600f60205260408120805460e09161150691609c91906110eb90613870565b6000818152600f602052604081208054611f919160e091611559916064916110eb90613870565b63ffffffff1692915050565b6110c360046212750061373a565b6000818152600f6020526040812080546060916115069160a091906110eb90613870565b60005460ff161561203c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610aee565b600061205f60405180608001604052806050815260200161394e605091396124e0565b60018080556002829055600382905560408051608081019091526050808252929350600092839291839161394e6020830139905060006120a46107e0620bb080613717565b63ffffffff811660009081526010602052604081208890559091506120d0838786620bb080868a612822565b6000888152600f6020908152604090912082519293506120f4929091840190613163565b5050678ac7230489e8000060055550506032600455505060646006555050601460075562015180600a55640826299e00601255600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60005460ff166121c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610aee565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600d60205260409020548061224d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6e6f2072656c61796572207265776172640000000000000000000000000000006044820152606401610aee565b73ffffffffffffffffffffffffffffffffffffffff82166000818152600d602052604080822091909155517f9a99b4f0000000000000000000000000000000000000000000000000000000008152600481019190915260248101829052829061100290639a99b4f090604401602060405180830381600087803b1580156122d357600080fd5b505af11580156122e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061230b9190613338565b50505050565b60405180608001604052806050815260200161394e6050913981565b63ffffffff851660009081526013602052604081205461234b611f34565b63ffffffff1661235b87896136db565b63ffffffff16118061236b575087155b80612374575080155b1561238357600091505061245d565b6000818152600f6020526040812080546123a3916044916110eb90613870565b9050846123b5578814915061245d9050565b8860005b86811015612457576123cc6002876138ea565b6001141561240b576124048888838181106123f757634e487b7160e01b600052603260045260246000fd5b9050602002013583612f6d565b915061243e565b61243b8289898481811061242f57634e487b7160e01b600052603260045260246000fd5b90506020020135612f6d565b91505b60019590951c948061244f816138ab565b9150506123b9565b50149150505b9695505050505050565b6060600061247584846137c7565b90508067ffffffffffffffff81111561249e57634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156124c8576020820181803683370190505b50915060208581019085840101611722828285612f99565b600061138a600280846040516124f69190613545565b602060405180830381855afa158015612513573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906125369190613338565b60405160200161254891815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261258091613545565b602060405180830381855afa15801561259d573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061124c9190613338565b6000806000806125d461124c602488015190565b905060006125e1826114e2565b9050806125f5575061272e915061281b9050565b6125fe866114e2565b935083156126135750612738915061281b9050565b600061262560e061155960688b015190565b9050600061263282612ff2565b90508715806126415750808810155b15612655575061276a935061281b92505050565b61265e8461150e565b6126699060016136db565b9650600061267685611532565b90506126846107e0896138fe565b63ffffffff16156126ca578063ffffffff168363ffffffff16141580156126b0575063ffffffff811615155b156126c5575061271a945061281b9350505050565b6127da565b60006126d582612ff2565b905060006126e287611f6a565b905060006126ef886110c6565b905060006126fc82611f6a565b9050600061270a8285613803565b905061271a60046212750061373a565b67ffffffffffffffff168167ffffffffffffffff1610156127465761274360046212750061373a565b90505b6127546212750060046137a0565b67ffffffffffffffff168167ffffffffffffffff1611156127805761277d6212750060046137a0565b90505b6212750081860204600061279382613068565b90508063ffffffff168a63ffffffff16141580156127b6575063ffffffff811615155b156127d257506127249b5061281b9a5050505050505050505050565b505050505050505b6000612802837bffff0000000000000000000000000000000000000000000000000000613703565b905061280e81866136c3565b9750600096505050505050505b9250925092565b6040805160a080825260c08201909252606091602082018180368337019050509050600060408773ffffffffffffffffffffffffffffffffffffffff16901b9050600060408563ffffffff16901b60608763ffffffff16901b608089901b61288a91906136c3565b61289491906136c3565b90507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b1660208481019060708601908c015b818310156128e35780518352602092830192016128cb565b509384525060188301919091526030909101529695505050505050565b600954600b805460408051602080840282018101909252828152600094938593849383018282801561296857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161293d575b5050505050905060008151905060008167ffffffffffffffff81111561299e57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156129c7578160200160208202803683370190505b50905060005b82811015612a9a5760008482815181106129f757634e487b7160e01b600052603260045260246000fd5b602002602001015190506000612a4b600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461142a565b905080848481518110612a6e57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152612a8381886136c3565b965050508080612a92906138ab565b9150506129cd565b50600061271060045487612aae9190613755565b612ab89190613703565b9050612ac481876137c7565b95508560015b84811015612bb05760008789868481518110612af657634e487b7160e01b600052603260045260246000fd5b6020026020010151612b089190613755565b612b129190613703565b905080600d6000898581518110612b3957634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612b8a91906136c3565b90915550612b9a905081846137c7565b9250508080612ba8906138ab565b915050612aca565b5080600d600087600081518110612bd757634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612c2891906136c3565b9091555050600060098190555b84811015612cba57600c6000878381518110612c6157634e487b7160e01b600052603260045260246000fd5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600090558080612cb2906138ab565b915050612c35565b50612cc7600b60006131e7565b509695505050505050565b60005b6006811015612d0757600354821415612cec575050565b612cf582611227565b9150612d00816138ab565b9050612cd5565b506000600a54612d1683611f6a565b67ffffffffffffffff16612d2a9190613703565b90506000612d3783611fab565b90506000600761100573ffffffffffffffffffffffffffffffffffffffff166373fa03ed6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d8557600080fd5b505afa158015612d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbd9190613338565b612dc791906137c7565b905073ffffffffffffffffffffffffffffffffffffffff821615801590612ded57508083115b1561230b576000612dfd85611751565b6000858152600e6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8816845260018101909252909120549192509080612e8c5781546001810183556000838152602090200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87161790555b5073ffffffffffffffffffffffffffffffffffffffff93841660009081526001918201602090815260408220805480850182558184528284200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169590971694909417909555918101805491820181558252929020909101929092555050565b015190565b600081604051602001612f279190613545565b6040516020818303038152906040528051906020012083604051602001612f4e9190613545565b6040516020818303038152906040528051906020012014905092915050565b600060405183815282602082015260208160408360025afa5060208160208360025afa50519392505050565b60208110612fd15782518252612fb06020836136c3565b9150612fbd6020846136c3565b9250612fca6020826137c7565b9050612f99565b915181516020939093036101000a6000190180199091169216919091179052565b600060ff601883901c1662ffffff831660038211613036576130158260036137de565b613020906008613774565b63ffffffff9182169082161c9081169250613061565b63ffffffff8116925061304a6003836137de565b613055906008613774565b63ffffffff1683901b92505b5050919050565b600080825b80156130885760011c81613080816138c6565b92505061306d565b600060036130978460076136db565b63ffffffff16901c9050600060038263ffffffff16116130e2576130bc8260036137de565b6130c7906008613774565b63ffffffff168662ffffff1663ffffffff16901b905061310c565b6130ed6003836137de565b6130f8906008613774565b63ffffffff1686901c90508062ffffff1690505b6280000081161561312d5760081c62ffffff1681613129816138c6565b9250505b60188263ffffffff16901b8117945050505050919050565b60405180608001604052806004906020820280368337509192915050565b82805461316f90613870565b90600052602060002090601f01602090048101928261319157600085556131d7565b82601f106131aa57805160ff19168380011785556131d7565b828001600101855582156131d7579182015b828111156131d75782518255916020019190600101906131bc565b506131e3929150613208565b5090565b50805460008255906000526020600020908101906132059190613208565b50565b5b808211156131e35760008155600101613209565b803573ffffffffffffffffffffffffffffffffffffffff811681146114dd57600080fd5b60008083601f840112613252578182fd5b50813567ffffffffffffffff811115613269578182fd5b6020830191508360208260051b850101111561328457600080fd5b9250929050565b60008083601f84011261329c578182fd5b50813567ffffffffffffffff8111156132b3578182fd5b60208301915083602082850101111561328457600080fd5b803563ffffffff811681146114dd57600080fd5b6000602082840312156132f0578081fd5b6132f98261321d565b9392505050565b600060208284031215613311578081fd5b815180151581146132f9578182fd5b600060208284031215613331578081fd5b5035919050565b600060208284031215613349578081fd5b5051919050565b60008060008060008060a08789031215613368578182fd5b86359550613378602088016132cb565b9450613386604088016132cb565b9350606087013567ffffffffffffffff8111156133a1578283fd5b6133ad89828a01613241565b979a9699509497949695608090950135949350505050565b600080602083850312156133d7578182fd5b823567ffffffffffffffff8111156133ed578283fd5b6133f98582860161328b565b90969095509350505050565b6000806000806040858703121561341a578384fd5b843567ffffffffffffffff80821115613431578586fd5b61343d8883890161328b565b90965094506020870135915080821115613455578384fd5b506134628782880161328b565b95989497509550505050565b60008060408385031215613480578182fd5b823591506134906020840161321d565b90509250929050565b6000806000604084860312156134ad578283fd5b83359250602084013567ffffffffffffffff8111156134ca578283fd5b6134d686828701613241565b9497909650939450505050565b6000602082840312156134f4578081fd5b6132f9826132cb565b81835281816020850137506000806020838501015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60008251613557818460208701613824565b9190910192915050565b6020808252825182820181905260009190848201906040850190845b818110156135af57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161357d565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156135af578351835292840192918401916001016135d7565b6020815260008251806020840152613612816040850160208701613824565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020815260006136586020830184866134fd565b949350505050565b6040815260006136746040830186886134fd565b82810360208401526136878185876134fd565b979650505050505050565b6080815260006136a66080830187896134fd565b602083019590955250604081019290925260609091015292915050565b600082198211156136d6576136d6613921565b500190565b600063ffffffff8083168185168083038211156136fa576136fa613921565b01949350505050565b60008261371257613712613937565b500490565b600063ffffffff8084168061372e5761372e613937565b92169190910492915050565b600067ffffffffffffffff8084168061372e5761372e613937565b600081600019048311821515161561376f5761376f613921565b500290565b600063ffffffff8083168185168183048111821515161561379757613797613921565b02949350505050565b600067ffffffffffffffff8083168185168183048111821515161561379757613797613921565b6000828210156137d9576137d9613921565b500390565b600063ffffffff838116908316818110156137fb576137fb613921565b039392505050565b600067ffffffffffffffff838116908316818110156137fb576137fb613921565b60005b8381101561383f578181015183820152602001613827565b8381111561230b5750506000910152565b600063ffffffff82168061386657613866613921565b6000190192915050565b600181811c9082168061388457607f821691505b602082108114156138a557634e487b7160e01b600052602260045260246000fd5b50919050565b60006000198214156138bf576138bf613921565b5060010190565b600063ffffffff808316818114156138e0576138e0613921565b6001019392505050565b6000826138f9576138f9613937565b500690565b600063ffffffff8084168061391557613915613937565b92169190910692915050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfe0000402089138e40cd8b4832beb8013bc80b1425c8bcbe10fc280400000000000000000058a06ab0edc5653a6ab78490675a954f8d8b4d4f131728dcf965cd0022a02cdde59f8e63303808176bbe3919a26469706673582212200def20bf436b11f1346157a7c5f75d78f4d9fbe3f9fc59c48b461873fc043e1864736f6c63430008040033" }, "0x0000000000000000000000000000000000001004": { "balance": "0x0", @@ -51,11 +51,11 @@ }, "0x0000000000000000000000000000000000001005": { "balance": "0x0", - "code": "0x60806040526004361061034a5760003560e01c806395468d26116101bb578063c81b1662116100f7578063e59dcea511610095578063eef504661161006f578063eef50466146108d1578063f04d7834146108e6578063f9a2bbc7146108f9578063fed9727e1461090f57600080fd5b8063e59dcea514610891578063e79a198f146108a7578063ee9de3f6146108bc57600080fd5b8063d6dd7c0a116100d1578063d6dd7c0a14610831578063d87cf91e14610851578063dc927faf14610866578063e1c7392a1461087c57600080fd5b8063c81b1662146107d9578063cd7ac977146107ef578063d2a36e461461081c57600080fd5b8063ae6079f211610164578063b894aac51161013e578063b894aac51461070b578063ba70d54a14610777578063c666907b1461078c578063c6a9dcc0146107ac57600080fd5b8063ae6079f2146106cb578063b1171724146106e0578063b87c6c82146106f657600080fd5b8063a78abc1611610195578063a78abc1614610671578063a9955b401461068b578063ac431751146106ab57600080fd5b806395468d26146106295780639c54a73d146106465780639dc092621461065b57600080fd5b806343756e5c1161028a57806373fa03ed11610233578063791afc0f1161020d578063791afc0f146105d457806382f8b6e9146105e95780638d09732f146105ff57806395254e601461061457600080fd5b806373fa03ed1461059357806375b10c71146105a8578063783028a9146105be57600080fd5b80634c86f558116102645780634c86f5581461055157806364f54ec7146105685780636a87d7801461057d57600080fd5b806343756e5c1461051b578063483a00e81461053157806348fdb8711461053b57600080fd5b806314bfb527116102f7578063210d6fd7116102d1578063210d6fd7146104a357806325ee13e2146104b85780632845986b146104ce57806330b5468e146104e357600080fd5b806314bfb5271461043b57806314c1e1f7146104785780631768b43b1461048e57600080fd5b80630bd5a92f116103285780630bd5a92f146103c75780630f43a6771461040f57806314843ac91461042557600080fd5b8063035f8b701461034f57806304e9e3a41461037757806306a49fce146103a5575b600080fd5b34801561035b57600080fd5b50610364600181565b6040519081526020015b60405180910390f35b34801561038357600080fd5b5061038d61100781565b6040516001600160a01b03909116815260200161036e565b3480156103b157600080fd5b506103ba61092d565b60405161036e9190613f14565b3480156103d357600080fd5b506103ff6103e2366004613bce565b6001600160a01b0316600090815260086020526040902054151590565b604051901515815260200161036e565b34801561041b57600080fd5b5061036460045481565b34801561043157600080fd5b506103646103e881565b34801561044757600080fd5b506103ff610456366004613bce565b600a546001600160a01b03909116600090815260096020526040902054101590565b34801561048457600080fd5b5061038d61100481565b34801561049a57600080fd5b50610364600881565b3480156104af57600080fd5b50610364610a24565b3480156104c457600080fd5b5061038d61100581565b3480156104da57600080fd5b50610364600281565b3480156104ef57600080fd5b506103ff6104fe366004613bce565b6001600160a01b0316600090815260076020526040902054151590565b34801561052757600080fd5b5061038d61100181565b610539610a33565b005b34801561054757600080fd5b5061036460015481565b34801561055d57600080fd5b506103646201518081565b34801561057457600080fd5b50610539610c86565b34801561058957600080fd5b5061036460025481565b34801561059f57600080fd5b50600a54610364565b3480156105b457600080fd5b50610364600a5481565b3480156105ca57600080fd5b5061038d61100881565b3480156105e057600080fd5b50610364600b81565b3480156105f557600080fd5b5061036460035481565b34801561060b57600080fd5b50610364611999565b34801561062057600080fd5b506103646119a5565b34801561063557600080fd5b5061036468056bc75e2d6310000081565b34801561065257600080fd5b506103646119b1565b34801561066757600080fd5b5061038d61100681565b34801561067d57600080fd5b506000546103ff9060ff1681565b34801561069757600080fd5b506105396106a6366004613c43565b6119bd565b3480156106b757600080fd5b506105396106c6366004613d9c565b611c8f565b3480156106d757600080fd5b50610364601081565b3480156106ec57600080fd5b5061038d61100981565b34801561070257600080fd5b506105396122ac565b34801561071757600080fd5b5061072b610726366004613e05565b6123bc565b604080516001600160a01b03998a168152978916602089015295909716948601949094526060850192909252608084015260a083015260c082015260e08101919091526101000161036e565b34801561078357600080fd5b50610364601181565b34801561079857600080fd5b506103ff6107a7366004613bce565b612420565b3480156107b857600080fd5b506103646107c7366004613bce565b60076020526000908152604090205481565b3480156107e557600080fd5b5061038d61100281565b3480156107fb57600080fd5b5061036461080a366004613bce565b60096020526000908152604090205481565b34801561082857600080fd5b50610364600a81565b34801561083d57600080fd5b5061053961084c366004613bf1565b612495565b34801561085d57600080fd5b50610539612935565b34801561087257600080fd5b5061038d61100381565b34801561088857600080fd5b50610539612a4e565b34801561089d57600080fd5b5061036460055481565b3480156108b357600080fd5b50610539612b02565b3480156108c857600080fd5b50610364600481565b3480156108dd57600080fd5b50610364601581565b6105396108f4366004613bf1565b612cef565b34801561090557600080fd5b5061038d61100081565b34801561091b57600080fd5b5061036469021e19e0c9bab240000081565b60065460609060008167ffffffffffffffff81111561095c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610985578160200160208202803683370190505b50905060005b82811015610a1d57600681815481106109b457634e487b7160e01b600052603260045260246000fd5b600091825260209091206008909102015482516001600160a01b03909116908390839081106109f357634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280610a158161413c565b91505061098b565b5092915050565b610a30600860ff614125565b81565b60005460ff16610a8a5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e6974207965740000000000000060448201526064015b60405180910390fd5b33600090815260076020526040902054610ae65760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b34610b335760405162461bcd60e51b815260206004820152601860248201527f76616c75652073686f756c64206e6f74206265207a65726f00000000000000006044820152606401610a81565b3360009081526007602052604081205490346006610b52600185614125565b81548110610b7057634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160040154610b8c91906140ed565b9050806006610b9c600185614125565b81548110610bba57634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160040181905550336001600160a01b03167f4ab0f80899f780c0521f849ed8883e85682657aa1d8979dde42d5f347b995db63483604051610c13929190918252602082015260400190565b60405180910390a26001548110610c825760006006610c33600185614125565b81548110610c5157634e487b7160e01b600052603260045260246000fd5b6000918252602082206008918202019250610c6d9060ff614125565b8260050154169050610c7f8282613285565b50505b5050565b334114610cfb5760405162461bcd60e51b815260206004820152602d60248201527f746865206d6573736167652073656e646572206d75737420626520746865206260448201527f6c6f636b2070726f6475636572000000000000000000000000000000000000006064820152608401610a81565b60005460ff16610d4d5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3a15610d9b5760405162461bcd60e51b815260206004820152601460248201527f6761737072696365206973206e6f74207a65726f0000000000000000000000006044820152606401610a81565b60006110006001600160a01b0316638f73c5ae6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dda57600080fd5b505af1158015610dee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e169190810190613c77565b805190915060005b81811015610f925760006110036001600160a01b03166351b6ada36007600a54610e489190614125565b868581518110610e6857634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b8152600401610e9f9291909182526001600160a01b0316602082015260400190565b60006040518083038186803b158015610eb757600080fd5b505afa158015610ecb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef39190810190613c77565b90506110076001600160a01b0316634fd6979e858481518110610f2657634e487b7160e01b600052603260045260246000fd5b6020026020010151836040518363ffffffff1660e01b8152600401610f4c929190613ef2565b600060405180830381600087803b158015610f6657600080fd5b505af1158015610f7a573d6000803e3d6000fd5b50505050508080610f8a9061413c565b915050610e1e565b50600060035442610fa39190614105565b9050600a54811161101c5760405162461bcd60e51b815260206004820152602d60248201527f6e6f7420616c6c6f77656420746f207475726e20726f756e642c20776169742060448201527f666f72206d6f72652074696d65000000000000000000000000000000000000006064820152608401610a81565b600a8190556006546000808267ffffffffffffffff81111561104e57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611077578160200160208202803683370190505b50905060005b8381101561114157611091601060ff614125565b600682815481106110b257634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160050154168282815181106110e557634e487b7160e01b600052603260045260246000fd5b602002602001018181525050600182828151811061111357634e487b7160e01b600052603260045260246000fd5b6020026020010151141561112f578261112b8161413c565b9350505b806111398161413c565b91505061107d565b50606060008367ffffffffffffffff81111561116d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611196578160200160208202803683370190505b5090506000805b8681101561126e5760018582815181106111c757634e487b7160e01b600052603260045260246000fd5b6020026020010151141561125c57600681815481106111f657634e487b7160e01b600052603260045260246000fd5b60009182526020909120600890910201546001600160a01b0316838361121b8161413c565b94508151811061123b57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250505b806112668161413c565b91505061119d565b506110036001600160a01b031663951188896007600a5461128f9190614125565b846040518363ffffffff1660e01b81526004016112ad92919061407f565b60006040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113019190810190613d13565b925060008060006110076001600160a01b03166322b4fe9e86886040518363ffffffff1660e01b8152600401611338929190613fab565b600060405180830381600087803b15801561135257600080fd5b505af1158015611366573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261138e9190810190613d4e565b92509250925060006113a386856004546132e4565b805190915060008167ffffffffffffffff8111156113d157634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156113fa578160200160208202803683370190505b50905060008267ffffffffffffffff81111561142657634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561144f578160200160208202803683370190505b50905060008367ffffffffffffffff81111561147b57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156114a4578160200160208202803683370190505b50905060005b848110156116c1576000600760008884815181106114d857634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020549050600060066001836115149190614125565b8154811061153257634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020190508060010160009054906101000a90046001600160a01b031686848151811061157a57634e487b7160e01b600052603260045260246000fd5b6001600160a01b039283166020918202929092010152600282015486519116908690859081106115ba57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250508a83815181106115fa57634e487b7160e01b600052603260045260246000fd5b60200260200101516000141561163e576103e884848151811061162d57634e487b7160e01b600052603260045260246000fd5b602002602001018181525050611670565b806003015484848151811061166357634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b60108f61167e600185614125565b8151811061169c57634e487b7160e01b600052603260045260246000fd5b6020026020010181815117915081815250505050806116ba9061413c565b90506114aa565b506040517f270159f70000000000000000000000000000000000000000000000000000000081526110009063270159f790611706908890879087908790600401613f27565b600060405180830381600087803b15801561172057600080fd5b505af1158015611734573d6000803e3d6000fd5b505050506110016001600160a01b031663fc4333cd6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561177557600080fd5b505af1158015611789573d6000803e3d6000fd5b5050600a546040517fca40de51000000000000000000000000000000000000000000000000000000008152611007935063ca40de5192506117d29189918c918c91600401613fd9565b600060405180830381600087803b1580156117ec57600080fd5b505af1158015611800573d6000803e3d6000fd5b5050505060005b8e8110156119085760006006828154811061183257634e487b7160e01b600052603260045260246000fd5b600091825260208083206008909202909101546001600160a01b0316808352600990915260409091205490915080158015906118705750600a548111155b156118f357611881600460ff614125565b8f84815181106118a157634e487b7160e01b600052603260045260246000fd5b6020026020010151168f84815181106118ca57634e487b7160e01b600052603260045260246000fd5b6020908102919091018101919091526001600160a01b0383166000908152600990915260408120555b505080806119009061413c565b915050611807565b5060005b8e811015611985576119736006828154811061193857634e487b7160e01b600052603260045260246000fd5b90600052602060002090600802018e838151811061196657634e487b7160e01b600052603260045260246000fd5b6020026020010151613285565b8061197d8161413c565b91505061190c565b505050505050505050505050505050505050565b610a30600460ff614125565b610a30600260ff614125565b610a30601060ff614125565b3361100014611a345760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b03831660009081526007602052604090205480611a585750505050565b60006006611a67600184614125565b81548110611a8557634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020190506000816004015490506002548110158015611abd57508360025482611aba9190614125565b10155b15611c085760058201546001600160a01b03871660009081526009602052604090205460049091179015611b2d576001600160a01b038716600090815260096020526040902054611b0f9087906140ed565b6001600160a01b038816600090815260096020526040902055611b55565b85600a54611b3b91906140ed565b6001600160a01b0388166000908152600960205260409020555b6000611b618684614125565b6004850181905560408051888152602081018390529192506001600160a01b038a16917f81cfec6c72eedd64db356101ba3b131efdc623de91bb003917d85a30dd79dc14910160405180910390a2600154811015611bc0576008821791505b611bca8483613285565b8515611c01576040516110029087156108fc029088906000818181858888f19350505050158015611bff573d6000803e3d6000fd5b505b5050611c86565b611c118361366f565b6040516110029082156108fc029083906000818181858888f19350505050158015611c40573d6000803e3d6000fd5b5060408051828152600060208201526001600160a01b038816917f81cfec6c72eedd64db356101ba3b131efdc623de91bb003917d85a30dd79dc14910160405180910390a25b5050505b505050565b60005460ff16611ce15760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3361100614611d585760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610a81565b60208114611d965783836040517fad23613c000000000000000000000000000000000000000000000000000000008152600401610a81929190614008565b611e0a84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600e81527f72657175697265644d617267696e000000000000000000000000000000000000602082015291506139a19050565b15611eca57604080516020601f8401819004810282018101909252828152600091611e4d918585808385018382808284376000920191909152506139fa92505050565b90506002548111611ec2578484826002546001611e6a91906140ed565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a8195949392919061404e565b600155612269565b611f3e84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600481527f6475657300000000000000000000000000000000000000000000000000000000602082015291506139a19050565b15611fe957604080516020601f8401819004810282018101909252828152600091611f81918585808385018382808284376000920191909152506139fa92505050565b9050801580611f9257506001548110155b15611fe157848482600180600154611faa9190614125565b6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a8195949392919061404e565b600255612269565b61205d84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600e81527f76616c696461746f72436f756e74000000000000000000000000000000000000602082015291506139a19050565b156120fe57604080516020601f84018190048102820181019092528281526000916120a0918585808385018382808284376000920191909152506139fa92505050565b90506005811115806120b35750602a8110155b156120f657848482600660296040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a8195949392919061404e565b600455612269565b61217284848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f6d6178436f6d6d697373696f6e4368616e676500000000000000000000000000602082015291506139a19050565b1561222157604080516020601f84018190048102820181019092528281526000916121b5918585808385018382808284376000920191909152506139fa92505050565b9050806122195784848260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a8195949392919061404e565b600555612269565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610a81565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a8484848460405161229e949392919061401c565b60405180910390a150505050565b60005460ff166122fe5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3360009081526007602052604090205461235a5760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b33600090815260076020526040812054906006612378600184614125565b8154811061239657634e487b7160e01b600052603260045260246000fd5b90600052602060002090600802019050600060028260050154179050611c8a8282613285565b600681815481106123cc57600080fd5b6000918252602090912060089091020180546001820154600283015460038401546004850154600586015460068701546007909701546001600160a01b039687169850948616969390951694919390929088565b6001600160a01b038116600090815260076020526040812054806124475750600092915050565b60006006612456600184614125565b8154811061247457634e487b7160e01b600052603260045260246000fd5b60009182526020909120600560089092020101546011811614949350505050565b60005460ff166124e75760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b336000908152600760205260409020546125435760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b63ffffffff81161580159061255f57506103e88163ffffffff16105b6125d15760405162461bcd60e51b815260206004820152602f60248201527f636f6d6d697373696f6e54686f7573616e647468732073686f756c6420696e2060448201527f72616e67652028302c20313030302900000000000000000000000000000000006064820152608401610a81565b6001600160a01b03831661264c5760405162461bcd60e51b8152602060048201526024808201527f636f6e73656e73757320616464726573732073686f756c64206e6f742062652060448201527f7a65726f000000000000000000000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b0382166126a25760405162461bcd60e51b815260206004820152601e60248201527f66656520616464726573732073686f756c64206e6f74206265207a65726f00006044820152606401610a81565b336000908152600760205260408120549060066126c0600184614125565b815481106126de57634e487b7160e01b600052603260045260246000fd5b9060005260206000209060080201905060008160060154600a541461270757816003015461270d565b81600701545b9050806005548563ffffffff1661272491906140ed565b1015801561274557508363ffffffff166005548261274291906140ed565b10155b6127b75760405162461bcd60e51b815260206004820152602d60248201527f636f6d6d697373696f6e54686f7573616e64746873206f7574206f662061646a60448201527f7573746d656e742072616e6765000000000000000000000000000000000000006064820152608401610a81565b8160060154600a54146127d757600a546006830155600382015460078301555b60018201546001600160a01b038781169116146128ac576001600160a01b038616600090815260086020526040902054156128545760405162461bcd60e51b815260206004820152601c60248201527f74686520636f6e73656e73757320616c726561647920657869737473000000006044820152606401610a81565b6001820180546001600160a01b0390811660009081526008602052604080822082905583547fffffffffffffffffffffffff000000000000000000000000000000000000000016928a16928317909355908152208390555b6002820180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387811691821790925563ffffffff861660038501819055604051908152909188169033907f9b4c7bb5cf782797c33b6c6ca3bbd0ed9ae9e823611ebac8726889da7ef216d39060200160405180910390a4505050505050565b60005460ff166129875760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b336000908152600760205260409020546129e35760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b33600090815260076020526040812054906006612a01600184614125565b81548110612a1f57634e487b7160e01b600052603260045260246000fd5b6000918252602082206008909102019150612a3c600260ff614125565b8260050154169050611c8a8282613285565b60005460ff1615612aa15760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610a81565b69021e19e0c9bab2400000600190815568056bc75e2d63100000600255620151806003556015600455600a600581905560079055600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169091179055565b60005460ff16612b545760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b33600090815260076020526040902054612bb05760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b33600090815260076020526040812054906006612bce600184614125565b81548110612bec57634e487b7160e01b600052603260045260246000fd5b600091825260209091206008909102016005810154909150600b811614612c555760405162461bcd60e51b815260206004820152601f60248201527f63616e64696461746520737461747573206973206e6f7420636c6561726564006044820152606401610a81565b6004810154612c638361366f565b600254811115612cc057600060025482612c7d9190614125565b9050612c8933826139ff565b6002546040516110029180156108fc02916000818181858888f19350505050158015612cb9573d6000803e3d6000fd5b5050505050565b6040516110029082156108fc029083906000818181858888f19350505050158015610c7f573d6000803e3d6000fd5b60005460ff16612d415760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b6006546103e81015612d955760405162461bcd60e51b815260206004820152601e60248201527f6d6178696d756d2063616e6469646174652073697a65207265616368656400006044820152606401610a81565b3360009081526007602052604090205415612df25760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520616c72656164792065786973747300000000000000006044820152606401610a81565b600154341015612e445760405162461bcd60e51b815260206004820152601560248201527f6465706f736974206973206e6f7420656e6f75676800000000000000000000006044820152606401610a81565b63ffffffff811615801590612e6057506103e88163ffffffff16105b612ed25760405162461bcd60e51b815260206004820152602c60248201527f636f6d6d697373696f6e54686f7573616e647468732073686f756c642062652060448201527f696e2028302c20313030302900000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b03831660009081526008602052604090205415612f385760405162461bcd60e51b815260206004820152601860248201527f636f6e73656e73757320616c72656164792065786973747300000000000000006044820152606401610a81565b6001600160a01b038316612fb35760405162461bcd60e51b8152602060048201526024808201527f636f6e73656e73757320616464726573732073686f756c64206e6f742062652060448201527f7a65726f000000000000000000000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b0382166130095760405162461bcd60e51b815260206004820152601e60248201527f66656520616464726573732073686f756c64206e6f74206265207a65726f00006044820152606401610a81565b600a5433600090815260096020526040902054106130695760405162461bcd60e51b815260206004820152600d60248201527f697420697320696e206a61696c000000000000000000000000000000000000006044820152606401610a81565b6000600190506006604051806101000160405280336001600160a01b03168152602001866001600160a01b03168152602001856001600160a01b031681526020018463ffffffff168152602001348152602001838152602001600a5481526020018463ffffffff16815250908060018154018082558091505060019003906000526020600020906008020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701555050600060068054905090508060076000336001600160a01b03166001600160a01b03168152602001908152602001600020819055508060086000876001600160a01b03166001600160a01b0316815260200190815260200160002081905550836001600160a01b0316856001600160a01b0316336001600160a01b03167fe71c4590fcb5b3e76cd2b3e68071e231bb479c3461ddccafdbbf89de64d530de863460405161327692919063ffffffff929092168252602082015260400190565b60405180910390a45050505050565b6005820154818114611c8a5760058301829055825460408051838152602081018590526001600160a01b03909216917f4b35b40ad96adb69950ee8e04201a2258550524199e2fcf2b5d3830023ff99ce910160405180910390a2505050565b825160609060008082851015613306576132ff600184614125565b905061330a565b8294505b80821015613648578651829082906000908a908490811061333b57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600089848151811061336757634e487b7160e01b600052603260045260246000fd5b602002602001015190505b82841015613592575b82841080156133b05750808a84815181106133a657634e487b7160e01b600052603260045260246000fd5b6020026020010151105b156133c7576133c0600184614125565b925061337b565b8a83815181106133e757634e487b7160e01b600052603260045260246000fd5b60200260200101518b858151811061340f57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505089838151811061344f57634e487b7160e01b600052603260045260246000fd5b60200260200101518a858151811061347757634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b82841080156134ba5750808a85815181106134af57634e487b7160e01b600052603260045260246000fd5b602002602001015110155b156134d1576134ca8460016140ed565b9350613484565b8a84815181106134f157634e487b7160e01b600052603260045260246000fd5b60200260200101518b848151811061351957634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505089848151811061355957634e487b7160e01b600052603260045260246000fd5b60200260200101518a848151811061358157634e487b7160e01b600052603260045260246000fd5b602002602001018181525050613372565b818b85815181106135b357634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b031681525050808a85815181106135f457634e487b7160e01b600052603260045260246000fd5b6020908102919091010152838981101561361a576136138160016140ed565b965061363e565b898111156136345761362d600182614125565b955061363e565b5050505050613648565b505050505061330a565b60006136548685614125565b90508015613663578088510388525b50959695505050505050565b6000600661367e600184614125565b8154811061369c57634e487b7160e01b600052603260045260246000fd5b60009182526020822060016008909202019081015481546040519294506001600160a01b03918216939116917f17b07b19259c7122b0e5b0e3a4ca4ebf0b240e93e26cb44cff9addc7dbf9e99c9190a380546001600160a01b03908116600090815260076020908152604080832083905560018501549093168252600890529081205560065482146138ec576006805461373890600190614125565b8154811061375657634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160066001846137739190614125565b8154811061379157634e487b7160e01b600052603260045260246000fd5b600091825260208220835460089092020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0393841617825560018086015481840180548416918616919091179055600280870154908401805490931694169390931790556003808501549082015560048085015490820155600580850154908201556006808501548183015560079485015491850191909155859392916138459085614125565b8154811061386357634e487b7160e01b600052603260045260246000fd5b60009182526020808320600892830201546001600160a01b0316845283019390935260409091018120929092558391600661389f600185614125565b815481106138bd57634e487b7160e01b600052603260045260246000fd5b60009182526020808320600160089093020191909101546001600160a01b031683528201929092526040019020555b600680548061390b57634e487b7160e01b600052603160045260246000fd5b60008281526020812060087fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054821690556002820180549091169055600381018290556004810182905560058101829055600681018290556007015590555050565b6000816040516020016139b49190613eb9565b60405160208183030381529060405280519060200120836040516020016139db9190613eb9565b6040516020818303038152906040528051906020012014905092915050565b015190565b80471015613a4f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a81565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613a9c576040519150601f19603f3d011682016040523d82523d6000602084013e613aa1565b606091505b5050905080611c8a5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a81565b600082601f830112613b28578081fd5b81516020613b3d613b38836140c9565b614098565b80838252828201915082860187848660051b8901011115613b5c578586fd5b855b85811015613b7a57815184529284019290840190600101613b5e565b5090979650505050505050565b60008083601f840112613b98578182fd5b50813567ffffffffffffffff811115613baf578182fd5b602083019150836020828501011115613bc757600080fd5b9250929050565b600060208284031215613bdf578081fd5b8135613bea816141a1565b9392505050565b600080600060608486031215613c05578182fd5b8335613c10816141a1565b92506020840135613c20816141a1565b9150604084013563ffffffff81168114613c38578182fd5b809150509250925092565b600080600060608486031215613c57578283fd5b8335613c62816141a1565b95602085013595506040909401359392505050565b60006020808385031215613c89578182fd5b825167ffffffffffffffff811115613c9f578283fd5b8301601f81018513613caf578283fd5b8051613cbd613b38826140c9565b80828252848201915084840188868560051b8701011115613cdc578687fd5b8694505b83851015613d07578051613cf3816141a1565b835260019490940193918501918501613ce0565b50979650505050505050565b600060208284031215613d24578081fd5b815167ffffffffffffffff811115613d3a578182fd5b613d4684828501613b18565b949350505050565b600080600060608486031215613d62578283fd5b835167ffffffffffffffff811115613d78578384fd5b613d8486828701613b18565b93505060208401519150604084015190509250925092565b60008060008060408587031215613db1578081fd5b843567ffffffffffffffff80821115613dc8578283fd5b613dd488838901613b87565b90965094506020870135915080821115613dec578283fd5b50613df987828801613b87565b95989497509550505050565b600060208284031215613e16578081fd5b5035919050565b6000815180845260208085019450808401835b83811015613e555781516001600160a01b031687529582019590820190600101613e30565b509495945050505050565b6000815180845260208085019450808401835b83811015613e5557815187529582019590820190600101613e73565b8183528181602085013750600080602083850101526020601f19601f840116840101905092915050565b60008251815b81811015613ed95760208186018101518583015201613ebf565b81811115613ee75782828501525b509190910192915050565b6001600160a01b0383168152604060208201526000613d466040830184613e1d565b602081526000613bea6020830184613e1d565b608081526000613f3a6080830187613e1d565b602083820381850152613f4d8288613e1d565b84810360408601528651808252828801935090820190845b81811015613f8a5784516001600160a01b031683529383019391830191600101613f65565b50508481036060860152613f9e8187613e60565b9998505050505050505050565b604081526000613fbe6040830185613e1d565b8281036020840152613fd08185613e60565b95945050505050565b608081526000613fec6080830187613e1d565b6020830195909552506040810192909252606090910152919050565b602081526000613d46602083018486613e8f565b604081526000614030604083018688613e8f565b8281036020840152614043818587613e8f565b979650505050505050565b608081526000614062608083018789613e8f565b602083019590955250604081019290925260609091015292915050565b828152604060208201526000613d466040830184613e1d565b604051601f8201601f1916810167ffffffffffffffff811182821017156140c1576140c161418b565b604052919050565b600067ffffffffffffffff8211156140e3576140e361418b565b5060051b60200190565b6000821982111561410057614100614175565b500190565b60008261412057634e487b7160e01b81526012600452602481fd5b500490565b60008282101561413757614137614175565b500390565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561416e5761416e614175565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146141b657600080fd5b5056fea2646970667358221220713bec2b7f32b4ae0b3c3ac70705668a28e4b3efc3d207e77d173503b1c8443964736f6c63430008040033" + "code": "0x60806040526004361061034a5760003560e01c806395468d26116101bb578063c81b1662116100f7578063e59dcea511610095578063eef504661161006f578063eef50466146108d1578063f04d7834146108e6578063f9a2bbc7146108f9578063fed9727e1461090f57600080fd5b8063e59dcea514610891578063e79a198f146108a7578063ee9de3f6146108bc57600080fd5b8063d6dd7c0a116100d1578063d6dd7c0a14610831578063d87cf91e14610851578063dc927faf14610866578063e1c7392a1461087c57600080fd5b8063c81b1662146107d9578063cd7ac977146107ef578063d2a36e461461081c57600080fd5b8063ae6079f211610164578063b894aac51161013e578063b894aac51461070b578063ba70d54a14610777578063c666907b1461078c578063c6a9dcc0146107ac57600080fd5b8063ae6079f2146106cb578063b1171724146106e0578063b87c6c82146106f657600080fd5b8063a78abc1611610195578063a78abc1614610671578063a9955b401461068b578063ac431751146106ab57600080fd5b806395468d26146106295780639c54a73d146106465780639dc092621461065b57600080fd5b806343756e5c1161028a57806373fa03ed11610233578063791afc0f1161020d578063791afc0f146105d457806382f8b6e9146105e95780638d09732f146105ff57806395254e601461061457600080fd5b806373fa03ed1461059357806375b10c71146105a8578063783028a9146105be57600080fd5b80634c86f558116102645780634c86f5581461055157806364f54ec7146105685780636a87d7801461057d57600080fd5b806343756e5c1461051b578063483a00e81461053157806348fdb8711461053b57600080fd5b806314bfb527116102f7578063210d6fd7116102d1578063210d6fd7146104a357806325ee13e2146104b85780632845986b146104ce57806330b5468e146104e357600080fd5b806314bfb5271461043b57806314c1e1f7146104785780631768b43b1461048e57600080fd5b80630bd5a92f116103285780630bd5a92f146103c75780630f43a6771461040f57806314843ac91461042557600080fd5b8063035f8b701461034f57806304e9e3a41461037757806306a49fce146103a5575b600080fd5b34801561035b57600080fd5b50610364600181565b6040519081526020015b60405180910390f35b34801561038357600080fd5b5061038d61100781565b6040516001600160a01b03909116815260200161036e565b3480156103b157600080fd5b506103ba61092d565b60405161036e9190613ec0565b3480156103d357600080fd5b506103ff6103e2366004613b6f565b6001600160a01b0316600090815260086020526040902054151590565b604051901515815260200161036e565b34801561041b57600080fd5b5061036460045481565b34801561043157600080fd5b506103646103e881565b34801561044757600080fd5b506103ff610456366004613b6f565b600a546001600160a01b03909116600090815260096020526040902054101590565b34801561048457600080fd5b5061038d61100481565b34801561049a57600080fd5b50610364600881565b3480156104af57600080fd5b50610364610a24565b3480156104c457600080fd5b5061038d61100581565b3480156104da57600080fd5b50610364600281565b3480156104ef57600080fd5b506103ff6104fe366004613b6f565b6001600160a01b0316600090815260076020526040902054151590565b34801561052757600080fd5b5061038d61100181565b610539610a33565b005b34801561054757600080fd5b5061036460015481565b34801561055d57600080fd5b506103646201518081565b34801561057457600080fd5b50610539610c86565b34801561058957600080fd5b5061036460025481565b34801561059f57600080fd5b50600a54610364565b3480156105b457600080fd5b50610364600a5481565b3480156105ca57600080fd5b5061038d61100881565b3480156105e057600080fd5b50610364600b81565b3480156105f557600080fd5b5061036460035481565b34801561060b57600080fd5b506103646119a2565b34801561062057600080fd5b506103646119ae565b34801561063557600080fd5b5061036468056bc75e2d6310000081565b34801561065257600080fd5b506103646119ba565b34801561066757600080fd5b5061038d61100681565b34801561067d57600080fd5b506000546103ff9060ff1681565b34801561069757600080fd5b506105396106a6366004613be4565b6119c6565b3480156106b757600080fd5b506105396106c6366004613d40565b611c98565b3480156106d757600080fd5b50610364601081565b3480156106ec57600080fd5b5061038d61100981565b34801561070257600080fd5b506105396122b5565b34801561071757600080fd5b5061072b610726366004613da9565b6123c5565b604080516001600160a01b03998a168152978916602089015295909716948601949094526060850192909252608084015260a083015260c082015260e08101919091526101000161036e565b34801561078357600080fd5b50610364601181565b34801561079857600080fd5b506103ff6107a7366004613b6f565b612429565b3480156107b857600080fd5b506103646107c7366004613b6f565b60076020526000908152604090205481565b3480156107e557600080fd5b5061038d61100281565b3480156107fb57600080fd5b5061036461080a366004613b6f565b60096020526000908152604090205481565b34801561082857600080fd5b50610364600a81565b34801561083d57600080fd5b5061053961084c366004613b92565b61249e565b34801561085d57600080fd5b5061053961293e565b34801561087257600080fd5b5061038d61100381565b34801561088857600080fd5b50610539612a57565b34801561089d57600080fd5b5061036460055481565b3480156108b357600080fd5b50610539612b12565b3480156108c857600080fd5b50610364600481565b3480156108dd57600080fd5b50610364601581565b6105396108f4366004613b92565b612cff565b34801561090557600080fd5b5061038d61100081565b34801561091b57600080fd5b5061036469021e19e0c9bab240000081565b60065460609060008167ffffffffffffffff81111561095c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610985578160200160208202803683370190505b50905060005b82811015610a1d57600681815481106109b457634e487b7160e01b600052603260045260246000fd5b600091825260209091206008909102015482516001600160a01b03909116908390839081106109f357634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280610a15816140e3565b91505061098b565b5092915050565b610a30600860ff6140cc565b81565b60005460ff16610a8a5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e6974207965740000000000000060448201526064015b60405180910390fd5b33600090815260076020526040902054610ae65760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b34610b335760405162461bcd60e51b815260206004820152601860248201527f76616c75652073686f756c64206e6f74206265207a65726f00000000000000006044820152606401610a81565b3360009081526007602052604081205490346006610b526001856140cc565b81548110610b7057634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160040154610b8c9190614094565b9050806006610b9c6001856140cc565b81548110610bba57634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160040181905550336001600160a01b03167f4ab0f80899f780c0521f849ed8883e85682657aa1d8979dde42d5f347b995db63483604051610c13929190918252602082015260400190565b60405180910390a26001548110610c825760006006610c336001856140cc565b81548110610c5157634e487b7160e01b600052603260045260246000fd5b6000918252602082206008918202019250610c6d9060ff6140cc565b8260050154169050610c7f8282613295565b50505b5050565b334114610cfb5760405162461bcd60e51b815260206004820152602d60248201527f746865206d6573736167652073656e646572206d75737420626520746865206260448201527f6c6f636b2070726f6475636572000000000000000000000000000000000000006064820152608401610a81565b60005460ff16610d4d5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3a15610d9b5760405162461bcd60e51b815260206004820152601460248201527f6761737072696365206973206e6f74207a65726f0000000000000000000000006044820152606401610a81565b60006110006001600160a01b0316638f73c5ae6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dda57600080fd5b505af1158015610dee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e169190810190613c18565b805190915060005b81811015610f925760006110036001600160a01b03166351b6ada36007600a54610e4891906140cc565b868581518110610e6857634e487b7160e01b600052603260045260246000fd5b60200260200101516040518363ffffffff1660e01b8152600401610e9f9291909182526001600160a01b0316602082015260400190565b60006040518083038186803b158015610eb757600080fd5b505afa158015610ecb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef39190810190613c18565b90506110076001600160a01b0316634fd6979e858481518110610f2657634e487b7160e01b600052603260045260246000fd5b6020026020010151836040518363ffffffff1660e01b8152600401610f4c929190613e96565b600060405180830381600087803b158015610f6657600080fd5b505af1158015610f7a573d6000803e3d6000fd5b50505050508080610f8a906140e3565b915050610e1e565b50600060035442610fa391906140ac565b9050600a54811161101c5760405162461bcd60e51b815260206004820152602d60248201527f6e6f7420616c6c6f77656420746f207475726e20726f756e642c20776169742060448201527f666f72206d6f72652074696d65000000000000000000000000000000000000006064820152608401610a81565b600a8190556006546000808267ffffffffffffffff81111561104e57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611077578160200160208202803683370190505b50905060005b8381101561114157611091601060ff6140cc565b600682815481106110b257634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020160050154168282815181106110e557634e487b7160e01b600052603260045260246000fd5b602002602001018181525050600182828151811061111357634e487b7160e01b600052603260045260246000fd5b6020026020010151141561112f578261112b816140e3565b9350505b80611139816140e3565b91505061107d565b50606060008367ffffffffffffffff81111561116d57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611196578160200160208202803683370190505b5090506000805b8681101561126e5760018582815181106111c757634e487b7160e01b600052603260045260246000fd5b6020026020010151141561125c57600681815481106111f657634e487b7160e01b600052603260045260246000fd5b60009182526020909120600890910201546001600160a01b0316838361121b816140e3565b94508151811061123b57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250505b80611266816140e3565b91505061119d565b506110036001600160a01b031663951188896007600a5461128f91906140cc565b846040518363ffffffff1660e01b81526004016112ad929190614026565b60006040518083038186803b1580156112c557600080fd5b505afa1580156112d9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113019190810190613cb9565b600a546040517fc62846a70000000000000000000000000000000000000000000000000000000081529194506000916110079163c62846a79161134b918791899190600401613f57565b600060405180830381600087803b15801561136557600080fd5b505af1158015611379573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113a19190810190613cb9565b905060006113b284836004546132f4565b805190915060008167ffffffffffffffff8111156113e057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611409578160200160208202803683370190505b50905060008267ffffffffffffffff81111561143557634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561145e578160200160208202803683370190505b50905060008367ffffffffffffffff81111561148a57634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156114b3578160200160208202803683370190505b50905060005b848110156116d0576000600760008884815181106114e757634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205490506000600660018361152391906140cc565b8154811061154157634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020190508060010160009054906101000a90046001600160a01b031686848151811061158957634e487b7160e01b600052603260045260246000fd5b6001600160a01b039283166020918202929092010152600282015486519116908690859081106115c957634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505088838151811061160957634e487b7160e01b600052603260045260246000fd5b60200260200101516000141561164d576103e884848151811061163c57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505061167f565b806003015484848151811061167257634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b60108d61168d6001856140cc565b815181106116ab57634e487b7160e01b600052603260045260246000fd5b6020026020010181815117915081815250505050806116c9906140e3565b90506114b9565b506040517f270159f70000000000000000000000000000000000000000000000000000000081526110009063270159f790611715908890879087908790600401613ed3565b600060405180830381600087803b15801561172f57600080fd5b505af1158015611743573d6000803e3d6000fd5b505050506110016001600160a01b031663fc4333cd6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561178457600080fd5b505af1158015611798573d6000803e3d6000fd5b5050600a546040517f773b807e000000000000000000000000000000000000000000000000000000008152611007935063773b807e92506117dd918991600401613f8d565b600060405180830381600087803b1580156117f757600080fd5b505af115801561180b573d6000803e3d6000fd5b5050505060005b8c8110156119135760006006828154811061183d57634e487b7160e01b600052603260045260246000fd5b600091825260208083206008909202909101546001600160a01b03168083526009909152604090912054909150801580159061187b5750600a548111155b156118fe5761188c600460ff6140cc565b8d84815181106118ac57634e487b7160e01b600052603260045260246000fd5b6020026020010151168d84815181106118d557634e487b7160e01b600052603260045260246000fd5b6020908102919091018101919091526001600160a01b0383166000908152600990915260408120555b5050808061190b906140e3565b915050611812565b5060005b8c8110156119905761197e6006828154811061194357634e487b7160e01b600052603260045260246000fd5b90600052602060002090600802018c838151811061197157634e487b7160e01b600052603260045260246000fd5b6020026020010151613295565b80611988816140e3565b915050611917565b50505050505050505050505050505050565b610a30600460ff6140cc565b610a30600260ff6140cc565b610a30601060ff6140cc565b3361100014611a3d5760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b03831660009081526007602052604090205480611a615750505050565b60006006611a706001846140cc565b81548110611a8e57634e487b7160e01b600052603260045260246000fd5b906000526020600020906008020190506000816004015490506002548110158015611ac657508360025482611ac391906140cc565b10155b15611c115760058201546001600160a01b03871660009081526009602052604090205460049091179015611b36576001600160a01b038716600090815260096020526040902054611b18908790614094565b6001600160a01b038816600090815260096020526040902055611b5e565b85600a54611b449190614094565b6001600160a01b0388166000908152600960205260409020555b6000611b6a86846140cc565b6004850181905560408051888152602081018390529192506001600160a01b038a16917f81cfec6c72eedd64db356101ba3b131efdc623de91bb003917d85a30dd79dc14910160405180910390a2600154811015611bc9576008821791505b611bd38483613295565b8515611c0a576040516110029087156108fc029088906000818181858888f19350505050158015611c08573d6000803e3d6000fd5b505b5050611c8f565b611c1a8361367f565b6040516110029082156108fc029083906000818181858888f19350505050158015611c49573d6000803e3d6000fd5b5060408051828152600060208201526001600160a01b038816917f81cfec6c72eedd64db356101ba3b131efdc623de91bb003917d85a30dd79dc14910160405180910390a25b5050505b505050565b60005460ff16611cea5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3361100614611d615760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610a81565b60208114611d9f5783836040517fad23613c000000000000000000000000000000000000000000000000000000008152600401610a81929190613faf565b611e1384848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600e81527f72657175697265644d617267696e000000000000000000000000000000000000602082015291506139b19050565b15611ed357604080516020601f8401819004810282018101909252828152600091611e5691858580838501838280828437600092019190915250613a0a92505050565b90506002548111611ecb578484826002546001611e739190614094565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a81959493929190613ff5565b600155612272565b611f4784848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600481527f6475657300000000000000000000000000000000000000000000000000000000602082015291506139b19050565b15611ff257604080516020601f8401819004810282018101909252828152600091611f8a91858580838501838280828437600092019190915250613a0a92505050565b9050801580611f9b57506001548110155b15611fea57848482600180600154611fb391906140cc565b6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a81959493929190613ff5565b600255612272565b61206684848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600e81527f76616c696461746f72436f756e74000000000000000000000000000000000000602082015291506139b19050565b1561210757604080516020601f84018190048102820181019092528281526000916120a991858580838501838280828437600092019190915250613a0a92505050565b90506005811115806120bc5750602a8110155b156120ff57848482600660296040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a81959493929190613ff5565b600455612272565b61217b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f6d6178436f6d6d697373696f6e4368616e676500000000000000000000000000602082015291506139b19050565b1561222a57604080516020601f84018190048102820181019092528281526000916121be91858580838501838280828437600092019190915250613a0a92505050565b9050806122225784848260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610a81959493929190613ff5565b600555612272565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610a81565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a848484846040516122a79493929190613fc3565b60405180910390a150505050565b60005460ff166123075760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b336000908152600760205260409020546123635760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b336000908152600760205260408120549060066123816001846140cc565b8154811061239f57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600802019050600060028260050154179050611c938282613295565b600681815481106123d557600080fd5b6000918252602090912060089091020180546001820154600283015460038401546004850154600586015460068701546007909701546001600160a01b039687169850948616969390951694919390929088565b6001600160a01b038116600090815260076020526040812054806124505750600092915050565b6000600661245f6001846140cc565b8154811061247d57634e487b7160e01b600052603260045260246000fd5b60009182526020909120600560089092020101546011811614949350505050565b60005460ff166124f05760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b3360009081526007602052604090205461254c5760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b63ffffffff81161580159061256857506103e88163ffffffff16105b6125da5760405162461bcd60e51b815260206004820152602f60248201527f636f6d6d697373696f6e54686f7573616e647468732073686f756c6420696e2060448201527f72616e67652028302c20313030302900000000000000000000000000000000006064820152608401610a81565b6001600160a01b0383166126555760405162461bcd60e51b8152602060048201526024808201527f636f6e73656e73757320616464726573732073686f756c64206e6f742062652060448201527f7a65726f000000000000000000000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b0382166126ab5760405162461bcd60e51b815260206004820152601e60248201527f66656520616464726573732073686f756c64206e6f74206265207a65726f00006044820152606401610a81565b336000908152600760205260408120549060066126c96001846140cc565b815481106126e757634e487b7160e01b600052603260045260246000fd5b9060005260206000209060080201905060008160060154600a5414612710578160030154612716565b81600701545b9050806005548563ffffffff1661272d9190614094565b1015801561274e57508363ffffffff166005548261274b9190614094565b10155b6127c05760405162461bcd60e51b815260206004820152602d60248201527f636f6d6d697373696f6e54686f7573616e64746873206f7574206f662061646a60448201527f7573746d656e742072616e6765000000000000000000000000000000000000006064820152608401610a81565b8160060154600a54146127e057600a546006830155600382015460078301555b60018201546001600160a01b038781169116146128b5576001600160a01b0386166000908152600860205260409020541561285d5760405162461bcd60e51b815260206004820152601c60248201527f74686520636f6e73656e73757320616c726561647920657869737473000000006044820152606401610a81565b6001820180546001600160a01b0390811660009081526008602052604080822082905583547fffffffffffffffffffffffff000000000000000000000000000000000000000016928a16928317909355908152208390555b6002820180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387811691821790925563ffffffff861660038501819055604051908152909188169033907f9b4c7bb5cf782797c33b6c6ca3bbd0ed9ae9e823611ebac8726889da7ef216d39060200160405180910390a4505050505050565b60005460ff166129905760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b336000908152600760205260409020546129ec5760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b33600090815260076020526040812054906006612a0a6001846140cc565b81548110612a2857634e487b7160e01b600052603260045260246000fd5b6000918252602082206008909102019150612a45600260ff6140cc565b8260050154169050611c938282613295565b60005460ff1615612aaa5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610a81565b69021e19e0c9bab240000060015568056bc75e2d631000006002556201518060038190556015600455600a600555612ae290426140ac565b600a55600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60005460ff16612b645760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b33600090815260076020526040902054612bc05760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520646f6573206e6f7420657869737400000000000000006044820152606401610a81565b33600090815260076020526040812054906006612bde6001846140cc565b81548110612bfc57634e487b7160e01b600052603260045260246000fd5b600091825260209091206008909102016005810154909150600b811614612c655760405162461bcd60e51b815260206004820152601f60248201527f63616e64696461746520737461747573206973206e6f7420636c6561726564006044820152606401610a81565b6004810154612c738361367f565b600254811115612cd057600060025482612c8d91906140cc565b9050612c993382613a0f565b6002546040516110029180156108fc02916000818181858888f19350505050158015612cc9573d6000803e3d6000fd5b5050505050565b6040516110029082156108fc029083906000818181858888f19350505050158015610c7f573d6000803e3d6000fd5b60005460ff16612d515760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610a81565b6006546103e81015612da55760405162461bcd60e51b815260206004820152601e60248201527f6d6178696d756d2063616e6469646174652073697a65207265616368656400006044820152606401610a81565b3360009081526007602052604090205415612e025760405162461bcd60e51b815260206004820152601860248201527f63616e64696461746520616c72656164792065786973747300000000000000006044820152606401610a81565b600154341015612e545760405162461bcd60e51b815260206004820152601560248201527f6465706f736974206973206e6f7420656e6f75676800000000000000000000006044820152606401610a81565b63ffffffff811615801590612e7057506103e88163ffffffff16105b612ee25760405162461bcd60e51b815260206004820152602c60248201527f636f6d6d697373696f6e54686f7573616e647468732073686f756c642062652060448201527f696e2028302c20313030302900000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b03831660009081526008602052604090205415612f485760405162461bcd60e51b815260206004820152601860248201527f636f6e73656e73757320616c72656164792065786973747300000000000000006044820152606401610a81565b6001600160a01b038316612fc35760405162461bcd60e51b8152602060048201526024808201527f636f6e73656e73757320616464726573732073686f756c64206e6f742062652060448201527f7a65726f000000000000000000000000000000000000000000000000000000006064820152608401610a81565b6001600160a01b0382166130195760405162461bcd60e51b815260206004820152601e60248201527f66656520616464726573732073686f756c64206e6f74206265207a65726f00006044820152606401610a81565b600a5433600090815260096020526040902054106130795760405162461bcd60e51b815260206004820152600d60248201527f697420697320696e206a61696c000000000000000000000000000000000000006044820152606401610a81565b6000600190506006604051806101000160405280336001600160a01b03168152602001866001600160a01b03168152602001856001600160a01b031681526020018463ffffffff168152602001348152602001838152602001600a5481526020018463ffffffff16815250908060018154018082558091505060019003906000526020600020906008020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701555050600060068054905090508060076000336001600160a01b03166001600160a01b03168152602001908152602001600020819055508060086000876001600160a01b03166001600160a01b0316815260200190815260200160002081905550836001600160a01b0316856001600160a01b0316336001600160a01b03167fe71c4590fcb5b3e76cd2b3e68071e231bb479c3461ddccafdbbf89de64d530de863460405161328692919063ffffffff929092168252602082015260400190565b60405180910390a45050505050565b6005820154818114611c935760058301829055825460408051838152602081018590526001600160a01b03909216917f4b35b40ad96adb69950ee8e04201a2258550524199e2fcf2b5d3830023ff99ce910160405180910390a2505050565b8251606090600080828510156133165761330f6001846140cc565b905061331a565b8294505b80821015613658578651829082906000908a908490811061334b57634e487b7160e01b600052603260045260246000fd5b60200260200101519050600089848151811061337757634e487b7160e01b600052603260045260246000fd5b602002602001015190505b828410156135a2575b82841080156133c05750808a84815181106133b657634e487b7160e01b600052603260045260246000fd5b6020026020010151105b156133d7576133d06001846140cc565b925061338b565b8a83815181106133f757634e487b7160e01b600052603260045260246000fd5b60200260200101518b858151811061341f57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505089838151811061345f57634e487b7160e01b600052603260045260246000fd5b60200260200101518a858151811061348757634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b82841080156134ca5750808a85815181106134bf57634e487b7160e01b600052603260045260246000fd5b602002602001015110155b156134e1576134da846001614094565b9350613494565b8a848151811061350157634e487b7160e01b600052603260045260246000fd5b60200260200101518b848151811061352957634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505089848151811061356957634e487b7160e01b600052603260045260246000fd5b60200260200101518a848151811061359157634e487b7160e01b600052603260045260246000fd5b602002602001018181525050613382565b818b85815181106135c357634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b031681525050808a858151811061360457634e487b7160e01b600052603260045260246000fd5b6020908102919091010152838981101561362a57613623816001614094565b965061364e565b898111156136445761363d6001826140cc565b955061364e565b5050505050613658565b505050505061331a565b600061366486856140cc565b90508015613673578088510388525b50959695505050505050565b6000600661368e6001846140cc565b815481106136ac57634e487b7160e01b600052603260045260246000fd5b60009182526020822060016008909202019081015481546040519294506001600160a01b03918216939116917f17b07b19259c7122b0e5b0e3a4ca4ebf0b240e93e26cb44cff9addc7dbf9e99c9190a380546001600160a01b03908116600090815260076020908152604080832083905560018501549093168252600890529081205560065482146138fc5760068054613748906001906140cc565b8154811061376657634e487b7160e01b600052603260045260246000fd5b9060005260206000209060080201600660018461378391906140cc565b815481106137a157634e487b7160e01b600052603260045260246000fd5b600091825260208220835460089092020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03938416178255600180860154818401805484169186169190911790556002808701549084018054909316941693909317905560038085015490820155600480850154908201556005808501549082015560068085015481830155600794850154918501919091558593929161385590856140cc565b8154811061387357634e487b7160e01b600052603260045260246000fd5b60009182526020808320600892830201546001600160a01b031684528301939093526040909101812092909255839160066138af6001856140cc565b815481106138cd57634e487b7160e01b600052603260045260246000fd5b60009182526020808320600160089093020191909101546001600160a01b031683528201929092526040019020555b600680548061391b57634e487b7160e01b600052603160045260246000fd5b60008281526020812060087fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9093019283020180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054821690556002820180549091169055600381018290556004810182905560058101829055600681018290556007015590555050565b6000816040516020016139c49190613e5d565b60405160208183030381529060405280519060200120836040516020016139eb9190613e5d565b6040516020818303038152906040528051906020012014905092915050565b015190565b80471015613a5f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a81565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613aac576040519150601f19603f3d011682016040523d82523d6000602084013e613ab1565b606091505b5050905080611c935760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a81565b60008083601f840112613b39578182fd5b50813567ffffffffffffffff811115613b50578182fd5b602083019150836020828501011115613b6857600080fd5b9250929050565b600060208284031215613b80578081fd5b8135613b8b81614148565b9392505050565b600080600060608486031215613ba6578182fd5b8335613bb181614148565b92506020840135613bc181614148565b9150604084013563ffffffff81168114613bd9578182fd5b809150509250925092565b600080600060608486031215613bf8578283fd5b8335613c0381614148565b95602085013595506040909401359392505050565b60006020808385031215613c2a578182fd5b825167ffffffffffffffff811115613c40578283fd5b8301601f81018513613c50578283fd5b8051613c63613c5e82614070565b61403f565b80828252848201915084840188868560051b8701011115613c82578687fd5b8694505b83851015613cad578051613c9981614148565b835260019490940193918501918501613c86565b50979650505050505050565b60006020808385031215613ccb578182fd5b825167ffffffffffffffff811115613ce1578283fd5b8301601f81018513613cf1578283fd5b8051613cff613c5e82614070565b80828252848201915084840188868560051b8701011115613d1e578687fd5b8694505b83851015613cad578051835260019490940193918501918501613d22565b60008060008060408587031215613d55578081fd5b843567ffffffffffffffff80821115613d6c578283fd5b613d7888838901613b28565b90965094506020870135915080821115613d90578283fd5b50613d9d87828801613b28565b95989497509550505050565b600060208284031215613dba578081fd5b5035919050565b6000815180845260208085019450808401835b83811015613df95781516001600160a01b031687529582019590820190600101613dd4565b509495945050505050565b6000815180845260208085019450808401835b83811015613df957815187529582019590820190600101613e17565b8183528181602085013750600080602083850101526020601f19601f840116840101905092915050565b60008251815b81811015613e7d5760208186018101518583015201613e63565b81811115613e8b5782828501525b509190910192915050565b6001600160a01b0383168152604060208201526000613eb86040830184613dc1565b949350505050565b602081526000613b8b6020830184613dc1565b608081526000613ee66080830187613dc1565b602083820381850152613ef98288613dc1565b84810360408601528651808252828801935090820190845b81811015613f365784516001600160a01b031683529383019391830191600101613f11565b50508481036060860152613f4a8187613e04565b9998505050505050505050565b606081526000613f6a6060830186613dc1565b8281036020840152613f7c8186613e04565b915050826040830152949350505050565b604081526000613fa06040830185613dc1565b90508260208301529392505050565b602081526000613eb8602083018486613e33565b604081526000613fd7604083018688613e33565b8281036020840152613fea818587613e33565b979650505050505050565b608081526000614009608083018789613e33565b602083019590955250604081019290925260609091015292915050565b828152604060208201526000613eb86040830184613dc1565b604051601f8201601f1916810167ffffffffffffffff8111828210171561406857614068614132565b604052919050565b600067ffffffffffffffff82111561408a5761408a614132565b5060051b60200190565b600082198211156140a7576140a761411c565b500190565b6000826140c757634e487b7160e01b81526012600452602481fd5b500490565b6000828210156140de576140de61411c565b500390565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156141155761411561411c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461415d57600080fd5b5056fea26469706673582212205f0da0dbf6ce2885a98d8592a77e6116c95b014c8015c67cd7963ffa2a8c080164736f6c63430008040033" }, "0x0000000000000000000000000000000000001007": { "balance": "0x0", - "code": "0x60806040526004361061026a5760003560e01c8063820356c511610153578063c81b1662116100cb578063dc927faf1161007f578063e3b899f311610064578063e3b899f3146106e6578063f474c8ce14610752578063f9a2bbc7146107b457600080fd5b8063dc927faf146106bb578063e1c7392a146106d157600080fd5b8063ce737112116100b0578063ce73711214610643578063d52d2a3314610663578063db03c9dd1461069b57600080fd5b8063c81b16621461060d578063ca40de511461062357600080fd5b8063a78abc1611610122578063b117172411610107578063b1171724146105a1578063baa4402b146105b7578063c35cc334146105ca57600080fd5b8063a78abc1614610557578063ac4317511461058157600080fd5b8063820356c5146104df57806383d44339146105145780639dc0926214610541578063a204ce971461046157600080fd5b806343756e5c116101e657806351916fc0116101b557806367b06a361161019a57806367b06a361461049d57806375b10c71146104b3578063783028a9146104c957600080fd5b806351916fc01461046157806365057e771461047d57600080fd5b806343756e5c146103eb57806347a15006146104015780634db8a60b146104215780634fd6979e1461044157600080fd5b806314c1e1f71161023d57806322b4fe9e1161022257806322b4fe9e1461039157806325e2c700146103c057806325ee13e2146103d557600080fd5b806314c1e1f7146103405780631c96b3191461035657600080fd5b806304e9e3a41461026f5780630a4aa4d3146102af5780630fcfd420146102d35780631003b502146102e9575b600080fd5b34801561027b57600080fd5b5061028561100781565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156102bb57600080fd5b506102c560025481565b6040519081526020016102a6565b3480156102df57600080fd5b506102c5614e2081565b3480156102f557600080fd5b50610325610304366004613182565b60066020526000908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016102a6565b34801561034c57600080fd5b5061028561100481565b34801561036257600080fd5b50610325610371366004612ec3565b600360208190526000918252604090912080549181015460049091015483565b34801561039d57600080fd5b506103b16103ac366004613004565b6107ca565b6040516102a69392919061323d565b6103d36103ce366004612ec3565b610b25565b005b3480156103e157600080fd5b5061028561100581565b3480156103f757600080fd5b5061028561100181565b34801561040d57600080fd5b506103d361041c366004612ec3565b610c7c565b34801561042d57600080fd5b506103d361043c366004612f0f565b610c8a565b34801561044d57600080fd5b506103d361045c366004612f4a565b610eb0565b34801561046d57600080fd5b506102c5670de0b6b3a764000081565b34801561048957600080fd5b506103d3610498366004612f9b565b6112b5565b3480156104a957600080fd5b506102c560015481565b3480156104bf57600080fd5b506102c560075481565b3480156104d557600080fd5b5061028561100881565b3480156104eb57600080fd5b506104ff6104fa366004612fc4565b611323565b604080519283529015156020830152016102a6565b34801561052057600080fd5b506102c561052f366004612ec3565b60046020526000908152604090205481565b34801561054d57600080fd5b5061028561100681565b34801561056357600080fd5b506000546105719060ff1681565b60405190151581526020016102a6565b34801561058d57600080fd5b506103d361059c366004613125565b6114d8565b3480156105ad57600080fd5b5061028561100981565b6103d36105c5366004613004565b6118b0565b3480156105d657600080fd5b506102856105e53660046130e5565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b34801561061957600080fd5b5061028561100281565b34801561062f57600080fd5b506103d361063e36600461306d565b611d07565b34801561064f57600080fd5b506103d361065e366004612ec3565b611f20565b34801561066f57600080fd5b506102c561067e36600461319a565b600860209081526000928352604080842090915290825290205481565b3480156106a757600080fd5b506103d36106b6366004612edd565b612081565b3480156106c757600080fd5b5061028561100381565b3480156106dd57600080fd5b506103d3612091565b3480156106f257600080fd5b50610706610701366004612edd565b61212a565b6040516102a69190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b34801561075e57600080fd5b5061077261076d366004612f9b565b6121e1565b6040516102a69190600060a082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b3480156107c057600080fd5b5061028561100081565b6060600080336110051461084b5760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e7472616374000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b858481146108c15760405162461bcd60e51b815260206004820152603360248201527f746865206c656e677468206f662063616e6469646174657320616e6420706f7760448201527f6572732073686f756c6420626520657175616c000000000000000000000000006064820152608401610842565b600192506001915060005b818110156109c3576000600360008b8b858181106108fa57634e487b7160e01b600052603260045260246000fd5b905060200201602081019061090f9190612ec3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050670de0b6b3a764000088888481811061097457634e487b7160e01b600052603260045260246000fd5b905060200201356109859190613341565b600382018190558154600483015561099d9086613309565b94508060040154846109af9190613309565b935050806109bc90613409565b90506108cc565b508067ffffffffffffffff8111156109eb57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610a14578160200160208202803683370190505b50935060005b81811015610b19576000600360008b8b85818110610a4857634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610a5d9190612ec3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050848160040154610aa99190613341565b612710600254868460030154610abf9190613341565b610ac99190613341565b610ad39190613321565b610add9190613309565b868381518110610afd57634e487b7160e01b600052603260045260246000fd5b602090810291909101015250610b1281613409565b9050610a1a565b50509450945094915050565b6040517fc666907b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526110059063c666907b9060240160206040518083038186803b158015610b8c57600080fd5b505afa158015610ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc491906130c5565b610c12576040517f74e640ee00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610842565b6000610c218233346000612302565b6040805134815260208101839052919250339173ffffffffffffffffffffffffffffffffffffffff8516917f69e36aaf9558a3c30415c0a2bc6cb4c2d592c041a0718697bf69c2e7c7e0bdac91015b60405180910390a35050565b610c878160006112b5565b50565b6040517fc666907b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526110059063c666907b9060240160206040518083038186803b158015610cf157600080fd5b505afa158015610d05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2991906130c5565b610d77576040517f74e640ee00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610842565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610dfd576040517f15e8d31800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808516600483015283166024820152604401610842565b600080610e0d8533856001612475565b915091506000610e1f85338585612302565b90503373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f037bbd0a1321bedfe51f505a5e6cede0b346e57521d957f9e76cb348b7758cb18685604051610ea0929190918252602082015260400190565b60405180910390a4505050505050565b3361100514610f275760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610842565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600360205260409020600281015480610f5d575050505050565b600060028301610f6e6001846133f2565b81548110610f8c57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502019050806000015460001480610fb55750600754816004015414155b15610fc257505050505050565b600060066000600754815260200190815260200160002090506000826002015483600001546127108460020154670de0b6b3a764000086600101546110079190613341565b6110119190613341565b61101b9190613321565b6110259190613341565b61102f9190613321565b905085600061103e8284613341565b905060008560030154886004015411156110905760028601548554600388015460048b015461106d91906133f2565b88546110799190613341565b6110839190613341565b61108d9190613321565b90505b600186015461109f8284613309565b8110156110ee5760405162461bcd60e51b815260206004820152601a60248201527f7468657265206973206e6f7420656e6f756768207265776172640000000000006044820152606401610842565b60005b848110156111945785600460008e8e8581811061111e57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906111339190612ec3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461117c9190613309565b9091555081905061118c81613409565b9150506110f1565b50600387015461120757600289016111ad60018a6133f2565b815481106111cb57634e487b7160e01b600052603260045260246000fd5b6000918252602082206005909102018181556001810182905560028101829055600381018290556004015561120083826133f2565b915061123c565b8215158061121457508115155b1561123c576112238284613309565b87600101600082825461123691906133f2565b90915550505b81156112a65761100273ffffffffffffffffffffffffffffffffffffffff1663631cbe3c836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561128c57600080fd5b505af11580156112a0573d6000803e3d6000fd5b50505050505b5050505050505050505b505050565b60006112c48333846000612475565b5090506112d13382612810565b604051818152339073ffffffffffffffffffffffffffffffffffffffff8516907f888585afd9421c43b48dc50229aa045dd1048c03602b46c83ad2aa36be798d429060200160405180910390a3505050565b3360009081526004602052604081205481906101f4908290801561135257336000908152600460205260408120555b8560005b818110156114b5576000600360008b8b8581811061138457634e487b7160e01b600052603260045260246000fd5b90506020020160208101906113999190612ec3565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160002060028101549091506113d157506114a5565b33600090815260018083016020526040909120908101541580156113f757506004810154155b156114035750506114a5565b60038101546002830154600091611419916133f2565b905061142683838a612936565b9650611432818961337e565b975061143e8787613309565b95508160010154600014801561145657506004820154155b1561149057336000908152600180850160205260408220828155908101829055600281018290556003810182905560048101829055600501555b60008812156114a1575050506114b5565b5050505b6114ae81613409565b9050611356565b5081156114c6576114c63383612c1b565b509350506000131590505b9250929050565b60005460ff1661152a5760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610842565b33611006146115a15760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610842565b602081146115df5783836040517fad23613c00000000000000000000000000000000000000000000000000000000815260040161084292919061328a565b61165384848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f7265717569726564436f696e4465706f7369740000000000000000000000000060208201529150612c759050565b1561170257604080516020601f840181900481028201810190925282815260009161169691858580838501838280828437600092019190915250612cce92505050565b9050806116fa5784848260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f90000000000000000000000000000000000000000000000000000000081526004016108429594939291906132d8565b60015561186d565b61177684848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600b81527f706f776572466163746f7200000000000000000000000000000000000000000060208201529150612c759050565b1561182557604080516020601f84018190048102820181019092528281526000916117b991858580838501838280828437600092019190915250612cce92505050565b90508061181d5784848260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040517f808861f90000000000000000000000000000000000000000000000000000000081526004016108429594939291906132d8565b60025561186d565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610842565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a848484846040516118a294939291906132a6565b60405180910390a150505050565b33611000146119275760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610842565b8281811461199d5760405162461bcd60e51b815260206004820152603660248201527f746865206c656e677468206f66206167656e744c69737420616e64207265776160448201527f72644c6973742073686f756c6420626520657175616c000000000000000000006064820152608401610842565b60075460009081526006602090815260408083208151606081018352815481526001820154938101939093526002015490820152905b82811015611cfe57600060036000898985818110611a0157634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611a169190612ec3565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002810154909150611a4e5750611cee565b60028101805460009190611a64906001906133f2565b81548110611a8257634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506000816002015490508060001415611b0857600283018054611ab6906001906133f2565b81548110611ad457634e487b7160e01b600052603260045260246000fd5b6000918252602082206005909102018181556001810182905560028101829055600381018290556004015550611cee915050565b878785818110611b2857634e487b7160e01b600052603260045260246000fd5b9050602002013560001415611b3f57505050611cee565b878785818110611b5f57634e487b7160e01b600052603260045260246000fd5b6020029190910135835550878785818110611b8a57634e487b7160e01b600052603260045260246000fd5b60200291909101356001840155508451600484015460009183918b8b89818110611bc457634e487b7160e01b600052603260045260246000fd5b90506020020135611bd59190613341565b611bdf9190613341565b611be99190613321565b90506000828760400151612710896020015188600301548e8e8c818110611c2057634e487b7160e01b600052603260045260246000fd5b90506020020135611c319190613341565b611c3b9190613341565b611c459190613321565b611c4f9190613341565b611c599190613321565b90508b8b87818110611c7b57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611c909190612ec3565b73ffffffffffffffffffffffffffffffffffffffff167f2cc22b0f7e1f407f48b96ee89f308c04b3d3605a6188f5f799730a8f7a795c958383604051611ce0929190918252602082015260400190565b60405180910390a250505050505b611cf781613409565b90506119d3565b50505050505050565b3361100514611d7e5760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610842565b611da260405180606001604052806000815260200160008152602001600081525090565b8381526020808201848152600280546040808601918252600087815260069095528420855181559251600184015551910155600783905585905b81811015611f16576000600360008a8a85818110611e0a57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611e1f9190612ec3565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600090812085516004820154919350611e5a91613341565b61271060025487602001518560030154611e749190613341565b611e7e9190613341565b611e889190613321565b611e929190613309565b6040805160a0810182526000808252602080830182815293830194855260048088015460608501908152608085018d81526002998a018054600181810183559187529490952095516005909402909501928355945192820192909255935195840195909555905160038301555192019190915550611f0f81613409565b9050611ddc565b5050505050505050565b3361100014611f975760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610842565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600360205260409020600281015480156112b057600060028301611fd86001846133f2565b81548110611ff657634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502019050600754816004015414801561201d57506003810154155b1561207b57600283016120316001846133f2565b8154811061204f57634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b50505050565b61208d82826000610c8a565b5050565b60005460ff16156120e45760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610842565b670de0b6b3a76400006001908155614e206002556007819055600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169091179055565b6121636040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff808316600090815260036020818152604080842094861684526001948501825292839020835160c0810185528154815294810154918501919091526002810154928401929092528101546060830152600481015460808301526005015460a082015292915050565b6122136040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b73ffffffffffffffffffffffffffffffffffffffff831660009081526003602052604090206002810154831061228b5760405162461bcd60e51b815260206004820152600f60248201527f6f7574206f6620757020626f756e6400000000000000000000000000000000006044820152606401610842565b8060020183815481106122ae57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505091505092915050565b60006001548310156123565760405162461bcd60e51b815260206004820152601460248201527f6465706f73697420697320746f6f20736d616c6c0000000000000000000000006044820152606401610842565b73ffffffffffffffffffffffffffffffffffffffff808616600090815260036020908152604080832093881683526001840190915281206002810154909190156123ac576123a98383637fffffff612936565b90505b858360000160008282546123c09190613309565b909155505060018201541580156123d957506004820154155b156123fe57600182018690556007546002808401919091558301546003830155612436565b6007548260020154101561241c576001820154825560075460028301555b858260010160008282546124309190613309565b90915550505b841561245657848260050160008282546124509190613309565b90915550505b8015612466576124668782612c1b565b50600101549695505050505050565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526003602090815260408083209387168352600180850190925282209081015491928392909190866124c1578096505b8061250e5760405162461bcd60e51b815260206004820152601860248201527f64656c656761746f7220646f6573206e6f7420657869737400000000000000006044820152606401610842565b8681146125c4576001548710156125675760405162461bcd60e51b815260206004820152601e60248201527f756e64656c656761746520616d6f756e7420697320746f6f20736d616c6c00006044820152606401610842565b866001546125759190613309565b8110156125c45760405162461bcd60e51b815260206004820152601d60248201527f72656d61696e696e6720616d6f756e7420697320746f6f20736d616c6c0000006044820152606401610842565b60006125d58484637fffffff612936565b9050878460000160008282546125eb91906133f2565b92505081905550600060075484600201541061260857835461260a565b825b905061261689846133f2565b9250600080856005015485101561267e5784866005015461263791906133f2565b6005870186905591508961267757600754600090815260086020908152604080832033845290915281208054849290612671908490613309565b90915550505b50816126b3565b82866005015461268e9190613309565b8510156126b357848387600501546126a69190613309565b6126b091906133f2565b90505b801561276a576126c381846133f2565b60028801549093501561276657600287018054600091906126e6906001906133f2565b8154811061270457634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506007548160040154141561275b578a15612747578187600401600082825461273c9190613309565b909155506127609050565b8181600301600082825461273c91906133f2565b600091505b5061276a565b5060005b8415801561277a57506004860154155b156127ce5773ffffffffffffffffffffffffffffffffffffffff8c166000908152600180890160205260408220828155908101829055600281018290556003810182905560048101829055600501556127e1565b8286556001860185905560075460028701555b83156127f1576127f18c85612c1b565b8a6127fc8284613309565b985098505050505050505094509492505050565b804710156128605760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610842565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146128ba576040519150601f19603f3d011682016040523d82523d6000602084013e6128bf565b606091505b50509050806112b05760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610842565b6002820154600754600091908082101561295257600060058601555b60028601546003860154818110612970576000945050505050612c14565b8161297b8783613309565b101561298e5761298b8682613309565b91505b81811015612c0b5760008860020182815481106129bb57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502019050600081600401549050848114156129e3575050612c0b565b600189015481871415612b7e5760048a0154600083815260086020908152604080832033845290915290205480821115612a4257612a2181836133f2565b60008581526008602090815260408083203384529091528120559150612a75565b600084815260086020908152604080832033845290915281208054849290612a6b9084906133f2565b9091555060009250505b8b600401548214612b5e576000612a9b86848f60040154612a9691906133f2565b612cd3565b9050856003015460001415612af9578d6002018781548110612acd57634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b61100273ffffffffffffffffffffffffffffffffffffffff1663631cbe3c826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612b4357600080fd5b505af1158015612b57573d6000803e3d6000fd5b5050505050505b8b54612b6b908390613309565b60018d01548d55600060048e0155925050505b8015612bf657612b8e8382612cd3565b612b989089613309565b9750826003015460001415612bf6578a6002018481548110612bca57634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b83612c0081613409565b94505050505061298e565b60038701555050505b9392505050565b612c258282612810565b6040805182815260016020820152339173ffffffffffffffffffffffffffffffffffffffff8516917fe33256fedbe96d2ddbd7462c2b1fc3b39e587b388060ce34d1ace27287dad8d39101610c70565b600081604051602001612c889190613204565b6040516020818303038152906040528051906020012083604051602001612caf9190613204565b6040516020818303038152906040528051906020012014905092915050565b015190565b60008183600301541015612d295760405162461bcd60e51b815260206004820152601460248201527f726577617264206973206e6f7420656e6f7567680000000000000000000000006044820152606401610842565b60008284600301541415612d495750600183015460006003850155612c14565b6004840154600090815260066020526040902054600285015485548290612d71908790613341565b612d7b9190613341565b612d859190613321565b91508185600101541015612ddb5760405162461bcd60e51b815260206004820152601a60248201527f7468657265206973206e6f7420656e6f756768207265776172640000000000006044820152606401610842565b83856003016000828254612def91906133f2565b9250508190555081856001016000828254612e0a91906133f2565b9091555050509392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114612e3b57600080fd5b919050565b60008083601f840112612e51578182fd5b50813567ffffffffffffffff811115612e68578182fd5b6020830191508360208260051b85010111156114d157600080fd5b60008083601f840112612e94578182fd5b50813567ffffffffffffffff811115612eab578182fd5b6020830191508360208285010111156114d157600080fd5b600060208284031215612ed4578081fd5b612c1482612e17565b60008060408385031215612eef578081fd5b612ef883612e17565b9150612f0660208401612e17565b90509250929050565b600080600060608486031215612f23578081fd5b612f2c84612e17565b9250612f3a60208501612e17565b9150604084013590509250925092565b600080600060408486031215612f5e578283fd5b612f6784612e17565b9250602084013567ffffffffffffffff811115612f82578283fd5b612f8e86828701612e40565b9497909650939450505050565b60008060408385031215612fad578182fd5b612fb683612e17565b946020939093013593505050565b60008060208385031215612fd6578182fd5b823567ffffffffffffffff811115612fec578283fd5b612ff885828601612e40565b90969095509350505050565b60008060008060408587031215613019578081fd5b843567ffffffffffffffff80821115613030578283fd5b61303c88838901612e40565b90965094506020870135915080821115613054578283fd5b5061306187828801612e40565b95989497509550505050565b600080600080600060808688031215613084578081fd5b853567ffffffffffffffff81111561309a578182fd5b6130a688828901612e40565b9099909850602088013597604081013597506060013595509350505050565b6000602082840312156130d6578081fd5b81518015158114612c14578182fd5b6000602082840312156130f6578081fd5b81357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168114612c14578182fd5b6000806000806040858703121561313a578384fd5b843567ffffffffffffffff80821115613151578586fd5b61315d88838901612e83565b90965094506020870135915080821115613175578384fd5b5061306187828801612e83565b600060208284031215613193578081fd5b5035919050565b600080604083850312156131ac578182fd5b82359150612f0660208401612e17565b81835281816020850137506000806020838501015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60008251815b81811015613224576020818601810151858301520161320a565b818111156132325782828501525b509190910192915050565b606080825284519082018190526000906020906080840190828801845b828110156132765781518452928401929084019060010161325a565b505050908301949094525060400152919050565b60208152600061329e6020830184866131bc565b949350505050565b6040815260006132ba6040830186886131bc565b82810360208401526132cd8185876131bc565b979650505050505050565b6080815260006132ec6080830187896131bc565b602083019590955250604081019290925260609091015292915050565b6000821982111561331c5761331c613442565b500190565b60008261333c57634e487b7160e01b81526012600452602481fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561337957613379613442565b500290565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156133b8576133b8613442565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156133ec576133ec613442565b50500390565b60008282101561340457613404613442565b500390565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561343b5761343b613442565b5060010190565b634e487b7160e01b600052601160045260246000fdfea26469706673582212208148b47643e728cb4321b06ad94c5bd501a94bc2bbb15492418525676df2f92564736f6c63430008040033" + "code": "0x6080604052600436106103815760003560e01c8063820356c5116101d1578063c35cc33411610102578063db03c9dd116100a0578063e8798c3f1161006f578063e8798c3f14610b09578063f474c8ce14610b1f578063f9a2bbc714610b81578063fc5b75a014610b9757600080fd5b8063db03c9dd14610a52578063dc927faf14610a72578063e1c7392a14610a88578063e3b899f314610a9d57600080fd5b8063c9ea28ee116100dc578063c9ea28ee146109c4578063cc79f97b146109e4578063ce737112146109fa578063d52d2a3314610a1a57600080fd5b8063c35cc3341461094b578063c62846a714610981578063c81b1662146109ae57600080fd5b8063a6d26d471161016f578063b117172411610149578063b11717241461085a578063b6fa172714610870578063baa4402b1461091b578063c35842541461092e57600080fd5b8063a6d26d47146107c9578063a78abc1614610810578063ac4317511461083a57600080fd5b806395b70888116101ab57806395b708881461078357806397687f941461079a5780639dc09262146107b3578063a204ce97146105d757600080fd5b8063820356c51461070157806383d443391461073657806392071f821461076357600080fd5b806347a15006116102b65780635eeb2cf0116102545780636d5c1ff3116102235780636d5c1ff31461069f57806375b10c71146106b5578063773b807e146106cb578063783028a9146106eb57600080fd5b80635eeb2cf0146106295780635fa5381e1461063f57806365057e771461066957806367b06a361461068957600080fd5b806351916fc01161029057806351916fc0146105d7578063584d509a146105f35780635afbc4a8146105d75780635ee99de61461061357600080fd5b806347a15006146105775780634db8a60b146105975780634fd6979e146105b757600080fd5b806325e2c700116103235780633f23503d116102fd5780633f23503d1461051d57806341c1a7061461053357806343756e5c14610548578063477d8f7f1461055e57600080fd5b806325e2c700146104dc57806325ee13e2146104f157806337d074501461050757600080fd5b80631003b5021161035f5780631003b502146103f357806314c1e1f7146104655780631c96b3191461047b57806323c9c5e2146104c457600080fd5b806304e9e3a4146103865780630a4aa4d3146103b95780630fcfd420146103dd575b600080fd5b34801561039257600080fd5b5061039c61100781565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156103c557600080fd5b506103cf60025481565b6040519081526020016103b0565b3480156103e957600080fd5b506103cf614e2081565b3480156103ff57600080fd5b5061043d61040e366004616a14565b600660205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a0016103b0565b34801561047157600080fd5b5061039c61100481565b34801561048757600080fd5b5061043d61049636600461674f565b6003602081905260009182526040909120805491810154600482015460058301546006909301549192909185565b3480156104d057600080fd5b506103cf635341542b81565b6104ef6104ea36600461674f565b610bae565b005b3480156104fd57600080fd5b5061039c61100581565b34801561051357600080fd5b506103cf600f5481565b34801561052957600080fd5b506103cf600e5481565b34801561053f57600080fd5b506103cf600781565b34801561055457600080fd5b5061039c61100181565b34801561056a57600080fd5b506103cf64e8d4a5100081565b34801561058357600080fd5b506104ef61059236600461674f565b610ce3565b3480156105a357600080fd5b506104ef6105b236600461679b565b610cf1565b3480156105c357600080fd5b506104ef6105d23660046167d6565b610eaf565b3480156105e357600080fd5b506103cf670de0b6b3a764000081565b3480156105ff57600080fd5b506104ef61060e366004616a66565b6112cc565b34801561061f57600080fd5b506103cf6101f481565b34801561063557600080fd5b506103cf61c35081565b34801561064b57600080fd5b50610654600381565b60405163ffffffff90911681526020016103b0565b34801561067557600080fd5b506104ef610684366004616827565b611acd565b34801561069557600080fd5b506103cf60015481565b3480156106ab57600080fd5b506103cf600c5481565b3480156106c157600080fd5b506103cf60075481565b3480156106d757600080fd5b506104ef6106e636600461696a565b611b2e565b3480156106f757600080fd5b5061039c61100881565b34801561070d57600080fd5b5061072161071c366004616850565b611d51565b604080519283529015156020830152016103b0565b34801561074257600080fd5b506103cf61075136600461674f565b60046020526000908152604090205481565b34801561076f57600080fd5b506103cf61077e366004616850565b611ef9565b34801561078f57600080fd5b506103cf620f424081565b3480156107a657600080fd5b506103cf6402540be40081565b3480156107bf57600080fd5b5061039c61100681565b3480156107d557600080fd5b506103cf6107e4366004616a44565b6000828152600a602090815260408083206001600160a01b038516845260010190915290205492915050565b34801561081c57600080fd5b5060005461082a9060ff1681565b60405190151581526020016103b0565b34801561084657600080fd5b506104ef610855366004616b7c565b6120bf565b34801561086657600080fd5b5061039c61100981565b34801561087c57600080fd5b506108d761088b366004616a14565b60096020526000908152604090208054600182015460028301546003840154600485015460058601546006909601546001600160a01b0395861696948616959394929391929091169087565b604080516001600160a01b03988916815296881660208801528601949094526060850192909252608084015290921660a082015260c081019190915260e0016103b0565b6104ef610929366004616890565b6129b1565b34801561093a57600080fd5b50600d546106549063ffffffff1681565b34801561095757600080fd5b5061039c6109663660046169d4565b6005602052600090815260409020546001600160a01b031681565b34801561098d57600080fd5b506109a161099c3660046168f9565b612e6c565b6040516103b09190616dda565b3480156109ba57600080fd5b5061039c61100281565b3480156109d057600080fd5b506104ef6109df366004616a44565b61338f565b3480156109f057600080fd5b506103cf61045c81565b348015610a0657600080fd5b506104ef610a1536600461674f565b61384b565b348015610a2657600080fd5b506103cf610a35366004616a44565b600860209081526000928352604080842090915290825290205481565b348015610a5e57600080fd5b506104ef610a6d366004616769565b61399f565b348015610a7e57600080fd5b5061039c61100381565b348015610a9457600080fd5b506104ef6139af565b348015610aa957600080fd5b50610abd610ab8366004616769565b613a8e565b6040516103b09190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b348015610b1557600080fd5b506103cf600b5481565b348015610b2b57600080fd5b50610b3f610b3a366004616827565b613b38565b6040516103b09190600060a082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b348015610b8d57600080fd5b5061039c61100081565b348015610ba357600080fd5b506103cf6201518081565b6040517fc666907b0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526110059063c666907b9060240160206040518083038186803b158015610c0857600080fd5b505afa158015610c1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4091906169b4565b610c86576040517f74e640ee0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b6000610c958233346000613c4c565b604080513481526020810183905291925033916001600160a01b038516917f69e36aaf9558a3c30415c0a2bc6cb4c2d592c041a0718697bf69c2e7c7e0bdac91015b60405180910390a35050565b610cee816000611acd565b50565b6040517fc666907b0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526110059063c666907b9060240160206040518083038186803b158015610d4b57600080fd5b505afa158015610d5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8391906169b4565b610dc4576040517f74e640ee0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610c7d565b816001600160a01b0316836001600160a01b03161415610e23576040517f15e8d3180000000000000000000000000000000000000000000000000000000081526001600160a01b03808516600483015283166024820152604401610c7d565b600080610e338533856001613db4565b915091506000610e4585338585613c4c565b9050336001600160a01b0316856001600160a01b0316876001600160a01b03167f037bbd0a1321bedfe51f505a5e6cede0b346e57521d957f9e76cb348b7758cb18685604051610e9f929190918252602082015260400190565b60405180910390a4505050505050565b3361100514610f265760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610c7d565b6001600160a01b0383166000908152600360205260409020600281015480610f4f575050505050565b600060028301610f606001846170de565b81548110610f7e57634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502019050806000015460001480610fa75750600754816004015414155b15610fb457505050505050565b600060066000600754815260200190815260200160002090506000826002015483600001546127108460020154670de0b6b3a764000086600401548760030154610ffe9190617022565b876001015461100d9190616f72565b6110179190617022565b6110219190617022565b61102b9190617002565b6110359190617022565b61103f9190617002565b905085600061104e8284617022565b9050600080856004015489600501546110679190617022565b90508660030154818a6004015461107e9190616f72565b11156110cd5760028701548654600389015460048c01546110a0908590616f72565b6110aa91906170de565b89546110b69190617022565b6110c09190617022565b6110ca9190617002565b91505b60018701546110dc8385616f72565b81101561112b5760405162461bcd60e51b815260206004820152601a60248201527f7468657265206973206e6f7420656e6f756768207265776172640000000000006044820152606401610c7d565b60005b858110156111b75786600460008f8f8581811061115b57634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611170919061674f565b6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461119f9190616f72565b909155508190506111af81617184565b91505061112e565b50600388015461122a5760028a016111d060018b6170de565b815481106111ee57634e487b7160e01b600052603260045260246000fd5b6000918252602082206005909102018181556001810182905560028101829055600381018290556004015561122384826170de565b925061125f565b8315158061123757508215155b1561125f576112468385616f72565b88600101600082825461125991906170de565b90915550505b82156112bc576110026001600160a01b031663631cbe3c846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156112a257600080fd5b505af11580156112b6573d6000803e3d6000fd5b50505050505b505050505050505050505b505050565b80517f040000000000000000000000000000000000000000000000000000000000000090829060009061130f57634e487b7160e01b600052603260045260246000fd5b01602001517fff00000000000000000000000000000000000000000000000000000000000000161480156113ac575080517fb100000000000000000000000000000000000000000000000000000000000000908290600590811061138357634e487b7160e01b600052603260045260246000fd5b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b6113f85760405162461bcd60e51b815260206004820152601960248201527f6e6f7420612076616c69642072656465656d20736372697074000000000000006044820152606401610c7d565b600061143987878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061413592505050565b600d549091506110039063edade189908390889063ffffffff161561146657600d5463ffffffff16611469565b60035b88886040518663ffffffff1660e01b815260040161148b959493929190616e1e565b60206040518083038186803b1580156114a357600080fd5b505afa1580156114b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db91906169b4565b6115275760405162461bcd60e51b815260206004820152601460248201527f627463207478206e6f7420636f6e6669726d65640000000000000000000000006044820152606401610c7d565b60008181526009602052604090206002810154156115875760405162461bcd60e51b815260206004820152601060248201527f62746320747820636f6e6669726d6564000000000000000000000000000000006044820152606401610c7d565b6000611592846141fb565b90506115a76201518063ffffffff8316617002565b6003830155600c54156115bc57600c546115bf565b60075b6007546115cc9190616f72565b82600301541161161e5760405162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e74206c6f636b20726f756e640000000000000000006044820152606401610c7d565b600061165f8a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061435192505050565b5092506000915081905061167962ffffff198416886144d2565b67ffffffffffffffff9092166002880155600e54909350909150156116a057600e546116a5565b620f42405b8560020154101561171e5760405162461bcd60e51b815260206004820152602660248201527f7374616b65642076616c756520646f6573206e6f74206d65657420726571756960448201527f72656d656e7400000000000000000000000000000000000000000000000000006064820152608401610c7d565b6000611729836147b3565b88546001600160a01b039283167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681178b5560018b0180549590941694909116939093179091556040517f30b5468e00000000000000000000000000000000000000000000000000000000815260048101929092529150611005906330b5468e9060240160206040518083038186803b1580156117c857600080fd5b505afa1580156117dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180091906169b4565b6118445785546040517f74e640ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610c7d565b6040517f541d55480000000000000000000000000000000000000000000000000000000081523360048201526110049063541d55489060240160206040518083038186803b15801561189557600080fd5b505afa1580156118a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cd91906169b4565b806118e4575060018601546001600160a01b031633145b6119565760405162461bcd60e51b815260206004820152603860248201527f6f6e6c792064656c656761746f72206f722072656c617965722063616e20737560448201527f626d69742074686520425443207472616e73616374696f6e00000000000000006064820152608401610c7d565b600f541561196657600f5461196d565b64e8d4a510005b3a11156119bc5760405162461bcd60e51b815260206004820152601560248201527f67617320707269636520697320746f6f206869676800000000000000000000006044820152606401610c7d565b8560010160009054906101000a90046001600160a01b03166001600160a01b03168660000160009054906101000a90046001600160a01b03166001600160a01b0316887fd9bac531f4c140de07ef9de431f5233b0d6ead3f0aa7834fcffd828c882dfdd28b8f87604051611a3293929190616e8c565b60405180910390a48015611a7457600686018190556005860180547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790555b85546001600160a01b0316600090815260036020526040902060028101546004880155611aa087614998565b8660020154816006016000828254611ab89190616f72565b90915550505050505050505050505050505050565b6000611adc8333846000613db4565b509050611ae93382614a62565b60405181815233906001600160a01b038516907f888585afd9421c43b48dc50229aa045dd1048c03602b46c83ad2aa36be798d429060200160405180910390a3505050565b3361100514611ba55760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610c7d565b60008181526006602052604081209083905b81811015611d4757600060036000888885818110611be557634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611bfa919061674f565b6001600160a01b03166001600160a01b031681526020019081526020016000209050600084600401548260050154611c329190617022565b905060008560000154828460040154611c4b9190616f72565b611c559190617022565b612710876002015488600401548960030154611c719190617022565b8960010154611c809190616f72565b8660030154611c8f9190617022565b611c999190617022565b611ca39190617002565b611cad9190616f72565b9050826002016040518060a001604052806000815260200160008152602001838152602001848660040154611ce29190616f72565b815260209081018a90528254600181810185556000948552938290208351600590920201908155908201519281019290925560408101516002830155606081015160038301556080015160049091015550611d409150829050617184565b9050611bb7565b5050506007555050565b3360009081526004602052604081205481906101f49082908015611d8057336000908152600460205260408120555b8560005b81811015611ed6576000600360008b8b85818110611db257634e487b7160e01b600052603260045260246000fd5b9050602002016020810190611dc7919061674f565b6001600160a01b0316815260208101919091526040016000206002810154909150611df25750611ec6565b3360009081526001808301602052604090912090810154158015611e1857506004810154155b15611e24575050611ec6565b60038101546002830154600091611e3a916170de565b9050611e4783838a614b7b565b9650611e53818961706a565b9750611e5f8787616f72565b955081600101546000148015611e7757506004820154155b15611eb157336000908152600180850160205260408220828155908101829055600281018290556003810182905560048101829055600501555b6000881215611ec257505050611ed6565b5050505b611ecf81617184565b9050611d84565b508115611ee757611ee73383614e53565b509350506000131590505b9250929050565b60006101f482825b8181108015611f0f57508215155b156120a5576000868683818110611f3657634e487b7160e01b600052603260045260246000fd5b9050602002013590506000600960008381526020019081526020016000209050806002015460001415611fab5760405162461bcd60e51b815260206004820152601060248201527f627463207478206e6f7420666f756e64000000000000000000000000000000006044820152606401610c7d565b60018101546001600160a01b031633811461202e5760405162461bcd60e51b815260206004820152602560248201527f6e6f74207468652064656c656761746f72206f6620746869732062746320726560448201527f63656970740000000000000000000000000000000000000000000000000000006064820152608401610c7d565b600061203a8488614ea0565b975090506120488189616f72565b975082600201546000141561208e576040516001600160a01b0383169085907ff8eb61142bc2f586caeaaef859e1f1486a659d7319ae668bf01c612009be527f90600090a35b50505050808061209d90617184565b915050611f01565b5082156120b6576120b63384614e53565b50505b92915050565b60005460ff166121115760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e7472616374206e6f7420696e697420796574000000000000006044820152606401610c7d565b33611006146121885760405162461bcd60e51b815260206004820152602a60248201527f746865206d73672073656e646572206d75737420626520676f7665726e616e6360448201527f6520636f6e7472616374000000000000000000000000000000000000000000006064820152608401610c7d565b602081146121c65783836040517fad23613c000000000000000000000000000000000000000000000000000000008152600401610c7d929190616eb7565b61223a84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f7265717569726564436f696e4465706f73697400000000000000000000000000602082015291506151eb9050565b156122cb57604080516020601f840181900481028201810190925282815260009161227d9185858083850183828082843760009201919091525061524492505050565b9050806122c35784848260016000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b60015561296e565b61233f84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600b81527f706f776572466163746f72000000000000000000000000000000000000000000602082015291506151eb9050565b156123d057604080516020601f84018190048102820181019092528281526000916123829185858083850183828082843760009201919091525061524492505050565b9050806123c85784848260016000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b60025561296e565b61244484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600981527f627463466163746f720000000000000000000000000000000000000000000000602082015291506151eb9050565b156124d557604080516020601f84018190048102820181019092528281526000916124879185858083850183828082843760009201919091525061524492505050565b9050806124cd5784848260016000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b600b5561296e565b61254984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600f81527f6d696e4274634c6f636b526f756e640000000000000000000000000000000000602082015291506151eb9050565b156125da57604080516020601f840181900481028201810190925282815260009161258c9185858083850183828082843760009201919091525061524492505050565b9050806125d25784848260016000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b600c5561296e565b61264e84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600f81527f627463436f6e6669726d426c6f636b0000000000000000000000000000000000602082015291506151eb9050565b1561271157604080516020601f84018190048102820181019092528281526000916126919185858083850183828082843760009201919091525061524492505050565b9050806126d75784848260016000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9290921691909117905561296e565b61278584848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152600b81527f6d696e42746356616c7565000000000000000000000000000000000000000000602082015291506151eb9050565b1561281757604080516020601f84018190048102820181019092528281526000916127c89185858083850183828082843760009201919091525061524492505050565b90508061280f578484826127106000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b600e5561296e565b61288b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051808201909152601381527f64656c6567617465427463476173507269636500000000000000000000000000602082015291506151eb9050565b1561292657604080516020601f84018190048102820181019092528281526000916128ce9185858083850183828082843760009201919091525061524492505050565b9050633b9aca0081101561291e57848482633b9aca006000196040517f808861f9000000000000000000000000000000000000000000000000000000008152600401610c7d959493929190616efd565b600f5561296e565b60405162461bcd60e51b815260206004820152600d60248201527f756e6b6e6f776e20706172616d000000000000000000000000000000000000006044820152606401610c7d565b7f6cdb0ac70ab7f2e2d035cca5be60d89906f2dede7648ddbd7402189c1eeed17a848484846040516129a39493929190616ecb565b60405180910390a150505050565b3361100014612a285760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610c7d565b82818114612a9e5760405162461bcd60e51b815260206004820152603660248201527f746865206c656e677468206f66206167656e744c69737420616e64207265776160448201527f72644c6973742073686f756c6420626520657175616c000000000000000000006064820152608401610c7d565b6007546000908152600660209081526040808320815160a0810183528154815260018201549381019390935260028101549183019190915260038101546060830152600401546080820152905b82811015612e6357600060036000898985818110612b1957634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612b2e919061674f565b6001600160a01b0316815260208101919091526040016000206002810154909150612b595750612e53565b60028101805460009190612b6f906001906170de565b81548110612b8d57634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506000816002015490508060001415612c1357600283018054612bc1906001906170de565b81548110612bdf57634e487b7160e01b600052603260045260246000fd5b6000918252602082206005909102018181556001810182905560028101829055600381018290556004015550612e53915050565b878785818110612c3357634e487b7160e01b600052603260045260246000fd5b9050602002013560001415612c4a57505050612e53565b878785818110612c6a57634e487b7160e01b600052603260045260246000fd5b6020029190910135835550878785818110612c9557634e487b7160e01b600052603260045260246000fd5b60200291909101356001840155508451600484015460009183918b8b89818110612ccf57634e487b7160e01b600052603260045260246000fd5b90506020020135612ce09190617022565b612cea9190617022565b612cf49190617002565b90506000828760400151612710896020015188600301548e8e8c818110612d2b57634e487b7160e01b600052603260045260246000fd5b90506020020135612d3c9190617022565b612d469190617022565b612d509190617002565b612d5a9190617022565b612d649190617002565b90506000838860000151896080015188600501548e8e8c818110612d9857634e487b7160e01b600052603260045260246000fd5b90506020020135612da99190617022565b612db39190617022565b612dbd9190617022565b612dc79190617002565b90508c8c88818110612de957634e487b7160e01b600052603260045260246000fd5b9050602002016020810190612dfe919061674f565b60408051858152602081018590529081018390526001600160a01b0391909116907f879b6ff02d0773bf19afa98f16c7e20163137d54be04280545379659c80868f59060600160405180910390a25050505050505b612e5c81617184565b9050612aeb565b50505050505050565b60603361100514612ee55760405162461bcd60e51b815260206004820152602960248201527f746865206d73672073656e646572206d7573742062652063616e64696461746560448201527f20636f6e747261637400000000000000000000000000000000000000000000006064820152608401610c7d565b84838114612f5b5760405162461bcd60e51b815260206004820152603360248201527f746865206c656e677468206f662063616e6469646174657320616e6420706f7760448201527f6572732073686f756c6420626520657175616c000000000000000000000000006064820152608401610c7d565b60006007546001612f6c9190616f72565b90505b8381116130be576000818152600a6020526040902080545b80156130905780612f978161716d565b9150506000826000018281548110612fbf57634e487b7160e01b600052603260045260246000fd5b60009182526020808320909101546001600160a01b03168083526001860182526040808420546003909352832060060180549194509192906130029084906170de565b9091555050825483908061302657634e487b7160e01b600052603160045260246000fd5b60008281526020808220830160001990810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559092019092556001600160a01b0392909216815260018401825260408082208290556002850190925290812055612f87565b6000838152600a60205260408120906130a9828261661c565b50505050806130b790617184565b9050612f6f565b506001806000805b848110156131bf576000600360008d8d858181106130f457634e487b7160e01b600052603260045260246000fd5b9050602002016020810190613109919061674f565b6001600160a01b03166001600160a01b031681526020019081526020016000209050670de0b6b3a76400008a8a8481811061315457634e487b7160e01b600052603260045260246000fd5b905060200201356131659190617022565b6003820181905560068201546005830155815460048301556131879086616f72565b94508060040154846131999190616f72565b93508060050154836131ab9190616f72565b925050806131b890617184565b90506130c6565b5060006402540be400600b546000146131da57600b546131de565b61c3505b6131e89190617022565b6002549091508567ffffffffffffffff81111561321557634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561323e578160200160208202803683370190505b50965060005b86811015613354576000600360008f8f8581811061327257634e487b7160e01b600052603260045260246000fd5b9050602002016020810190613287919061674f565b6001600160a01b03166001600160a01b031681526020019081526020016000209050868482600501546132ba9190617022565b82600401546132c99190616f72565b6132d39190617022565b612710846132e18789617022565b6132eb908a616f72565b84600301546132fa9190617022565b6133049190617022565b61330e9190617002565b6133189190616f72565b89838151811061333857634e487b7160e01b600052603260045260246000fd5b60209081029190910101525061334d81617184565b9050613244565b506000978852600660205260409097209384556001840192909255600283019590955560038201949094556004019290925595945050505050565b600082815260096020526040902060028101546133ee5760405162461bcd60e51b815260206004820152601060248201527f627463207478206e6f7420666f756e64000000000000000000000000000000006044820152606401610c7d565b60018101546001600160a01b031633146134705760405162461bcd60e51b815260206004820152602560248201527f6e6f74207468652064656c656761746f72206f6620746869732062746320726560448201527f63656970740000000000000000000000000000000000000000000000000000006064820152608401610c7d565b80546001600160a01b039081169083168114156134f55760405162461bcd60e51b815260206004820152602660248201527f63616e206e6f74207472616e7366657220746f207468652073616d652076616c60448201527f696461746f7200000000000000000000000000000000000000000000000000006064820152608401610c7d565b600754613503906001616f72565b8260030154116135555760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e74206c6f636b696e6720726f756e647300000000006044820152606401610c7d565b6040517fc666907b0000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526110059063c666907b9060240160206040518083038186803b1580156135af57600080fd5b505afa1580156135c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e791906169b4565b613628576040517f74e640ee0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610c7d565b600061363885637fffffff614ea0565b506001600160a01b03831660009081526003602052604081206002860154600682018054949550919390929061366f9084906170de565b9091555050600284015460038501546000908152600a602090815260408083206001600160a01b0388168452600101909152812080549091906136b39084906170de565b9091555050600281018054600091906136ce906001906170de565b815481106136ec57634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506007548160040154148015613718575060028201546004860154105b1561375a5760075460009081526006602052604090206004015460028601546137419190617022565b81600301600082825461375491906170de565b90915550505b6001600160a01b038616600081815260036020526040902086547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091178655600281015460048701556137ae86614998565b85600201548160060160008282546137c69190616f72565b909155505083156137db576137db3385614e53565b60028601546006820154604080516001600160a01b03808a1682528b16602082015233918101919091526060810192909252608082015288907f6c64601f7d803c3d59e77b53fc8bc002bbe820bfe708a82f10b4860d351c1bc99060a00160405180910390a25050505050505050565b33611000146138c25760405162461bcd60e51b815260206004820152602c60248201527f746865206d73672073656e646572206d7573742062652076616c696461746f7260448201527f53657420636f6e747261637400000000000000000000000000000000000000006064820152608401610c7d565b6001600160a01b0381166000908152600360205260409020600281015480156112c7576000600283016138f66001846170de565b8154811061391457634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502019050600754816004015414801561393b57506003810154155b15613999576002830161394f6001846170de565b8154811061396d57634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b50505050565b6139ab82826000610cf1565b5050565b60005460ff1615613a025760405162461bcd60e51b815260206004820152601960248201527f74686520636f6e747261637420616c726561647920696e6974000000000000006044820152606401610c7d565b670de0b6b3a7640000600155614e2060025561c350600b556007600c55600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000166003179055620f4240600e55613a5e6201518042617002565b600755600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b613ac76040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b03808316600090815260036020818152604080842094861684526001948501825292839020835160c0810185528154815294810154918501919091526002810154928401929092528101546060830152600481015460808301526005015460a082015292915050565b613b6a6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038316600090815260036020526040902060028101548310613bd55760405162461bcd60e51b815260206004820152600f60248201527f6f7574206f6620757020626f756e6400000000000000000000000000000000006044820152606401610c7d565b806002018381548110613bf857634e487b7160e01b600052603260045260246000fd5b90600052602060002090600502016040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505091505092915050565b6000600154831015613ca05760405162461bcd60e51b815260206004820152601460248201527f6465706f73697420697320746f6f20736d616c6c0000000000000000000000006044820152606401610c7d565b6001600160a01b0380861660009081526003602090815260408083209388168352600184019091528120600281015490919015613ce957613ce68383637fffffff614b7b565b90505b85836000016000828254613cfd9190616f72565b90915550506001820154158015613d1657506004820154155b15613d3b57600182018690556007546002808401919091558301546003830155613d73565b60075482600201541015613d59576001820154825560075460028301555b85826001016000828254613d6d9190616f72565b90915550505b8415613d935784826005016000828254613d8d9190616f72565b90915550505b8015613da357613da38782614e53565b50600101549150505b949350505050565b6001600160a01b038085166000908152600360209081526040808320938716835260018085019092528220908101549192839290919086613df3578096505b80613e405760405162461bcd60e51b815260206004820152601860248201527f64656c656761746f7220646f6573206e6f7420657869737400000000000000006044820152606401610c7d565b868114613ef657600154871015613e995760405162461bcd60e51b815260206004820152601e60248201527f756e64656c656761746520616d6f756e7420697320746f6f20736d616c6c00006044820152606401610c7d565b86600154613ea79190616f72565b811015613ef65760405162461bcd60e51b815260206004820152601d60248201527f72656d61696e696e6720616d6f756e7420697320746f6f20736d616c6c0000006044820152606401610c7d565b6000613f078484637fffffff614b7b565b905087846000016000828254613f1d91906170de565b925050819055506000600754846002015410613f3a578354613f3c565b825b9050613f4889846170de565b92506000808560050154851015613fb057848660050154613f6991906170de565b60058701869055915089613fa957600754600090815260086020908152604080832033845290915281208054849290613fa3908490616f72565b90915550505b5081613fe5565b828660050154613fc09190616f72565b851015613fe55784838760050154613fd89190616f72565b613fe291906170de565b90505b801561409c57613ff581846170de565b6002880154909350156140985760028701805460009190614018906001906170de565b8154811061403657634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020190506007548160040154141561408d578a15614079578187600401600082825461406e9190616f72565b909155506140929050565b8181600301600082825461406e91906170de565b600091505b5061409c565b5060005b841580156140ac57506004860154155b156140f3576001600160a01b038c16600090815260018089016020526040822082815590810182905560028101829055600381018290556004810182905560050155614106565b8286556001860185905560075460028701555b8315614116576141168c85614e53565b8a6141218284616f72565b985098505050505050505094509492505050565b6000806002836040516141489190616c2f565b602060405180830381855afa158015614165573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906141889190616a2c565b905060006002826040516020016141a191815260200190565b60408051601f19818403018152908290526141bb91616c2f565b602060405180830381855afa1580156141d8573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190613dac9190616a2c565b60218101516000906143448160008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b600882901c7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff161790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b601082901c7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff161790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b602082901c7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff1617905060408177ffffffffffffffff0000000000000000ffffffffffffffff16901b604082901c77ffffffffffffffff0000000000000000ffffffffffffffff16179050608081901b608082901c179050919050565b63ffffffff169392505050565b6000808080806143618682615249565b905061437e61437962ffffff1983166000600461526d565b6153ba565b9450600460006143b26143a383601886901c6bffffffffffffffffffffffff166170de565b62ffffff19851690600061543a565b905060006143bf82615478565b90506143d462ffffff19851684836006615560565b96506143e08184616f72565b925061441061440184601887901c6bffffffffffffffffffffffff166170de565b62ffffff19861690600061543a565b9150600061441d836155e4565b90506144368482600f5b62ffffff198916929190615560565b96506144428185616f72565b935061445961437962ffffff19871686600461526d565b9550601885901c6bffffffffffffffffffffffff16614479856004616f72565b146144c65760405162461bcd60e51b815260206004820152601960248201527f426974636f696e48656c7065723a20696e76616c6964207478000000000000006044820152606401610c7d565b50505050509193509193565b6000808084600f6144eb815b62ffffff198416906156b9565b5060008060008060006144ff8c60006157bb565b67ffffffffffffffff16905060005b818110156147a4576145208d82615902565b955061452b86615a3a565b945061453686615a8f565b935061454184615ada565b925062ffffff19808416141561478e576017601886901c6bffffffffffffffffffffffff16148015614585575061458162ffffff19861660006001615ce3565b60a9145b80156145a2575061459e62ffffff198616600180615ce3565b6014145b80156145c057506145bc62ffffff19861660166001615ce3565b6087145b80156146b45750600360028d6040516145d99190616c2f565b602060405180830381855afa1580156145f6573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906146199190616a2c565b60405160200161462b91815260200190565b60408051601f198184030181529082905261464591616c2f565b602060405180830381855afa158015614662573d6000803e3d6000fd5b50506040515160601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001690506146a062ffffff1987166002615d13565b60601b6bffffffffffffffffffffffff1916145b8061477557506022601886901c6bffffffffffffffffffffffff161480156146ec57506146ea62ffffff19861660006001615ce3565b155b8015614709575061470562ffffff198616600180615ce3565b6020145b8015614775575060028c6040516147209190616c2f565b602060405180830381855afa15801561473d573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906147609190616a2c565b61477362ffffff19871660026020615d21565b145b156147895761478386615ebe565b9a508098505b614792565b8299505b8061479c81617184565b91505061450e565b50505050505050509250925092565b600080806030601885901c6bffffffffffffffffffffffff16101561481a5760405162461bcd60e51b815260206004820152601b60248201527f7061796c6f6164206c656e67746820697320746f6f20736d616c6c00000000006044820152606401610c7d565b635341542b61483262ffffff19861660006004615ce3565b1461487f5760405162461bcd60e51b815260206004820152600b60248201527f77726f6e67206d616769630000000000000000000000000000000000000000006044820152606401610c7d565b61489262ffffff19851660046001615ce3565b6001146148e15760405162461bcd60e51b815260206004820152600d60248201527f77726f6e672076657273696f6e000000000000000000000000000000000000006044820152606401610c7d565b61045c6148f762ffffff19861660056002615ce3565b146149445760405162461bcd60e51b815260206004820152600e60248201527f77726f6e6720636861696e2069640000000000000000000000000000000000006044820152606401610c7d565b61495562ffffff1985166007615d13565b925061496862ffffff198516601b615d13565b9150670de0b6b3a764000061498662ffffff198616602f6001615ce3565b6149909190617022565b929491935050565b60038101546000908152600a6020908152604080832084546001600160a01b031684526002810190925290912054614a295781548154600181810184556000848152602080822090930180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0395861617905585549093168352600284019091526040909120555b600282015482546001600160a01b0316600090815260018301602052604081208054909190614a59908490616f72565b90915550505050565b80471015614ab25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610c7d565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614aff576040519150601f19603f3d011682016040523d82523d6000602084013e614b04565b606091505b50509050806112c75760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610c7d565b60028201546007546000919080821015614b9757600060058601555b60028601546003860154818110614bb5576000945050505050614e4c565b81614bc08783616f72565b1015614bd357614bd08682616f72565b91505b81811015614e43576000886002018281548110614c0057634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201905060008160040154905084811415614c28575050614e43565b600189015481871415614db65760048a0154600083815260086020908152604080832033845290915290205480821115614c8757614c6681836170de565b60008581526008602090815260408083203384529091528120559150614cba565b600084815260086020908152604080832033845290915281208054849290614cb09084906170de565b9091555060009250505b8b600401548214614d96576000614ce086848f60040154614cdb91906170de565b615ee3565b9050856003015460001415614d3e578d6002018781548110614d1257634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b6110026001600160a01b031663631cbe3c826040518263ffffffff1660e01b81526004016000604051808303818588803b158015614d7b57600080fd5b505af1158015614d8f573d6000803e3d6000fd5b5050505050505b8b54614da3908390616f72565b60018d01548d55600060048e0155925050505b8015614e2e57614dc68382615ee3565b614dd09089616f72565b9750826003015460001415614e2e578a6002018481548110614e0257634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b83614e3881617184565b945050505050614bd3565b60038701555050505b9392505050565b614e5d8282614a62565b604080518281526001602082015233916001600160a01b038516917fe33256fedbe96d2ddbd7462c2b1fc3b39e587b388060ce34d1ace27287dad8d39101610cd7565b600754600083815260096020908152604080832080546001600160a01b03168452600390925282206004820154600282015493948594909392859290915b8082108015614eec57508815155b15615004576000836002018381548110614f1657634e487b7160e01b600052603260045260246000fd5b9060005260206000209060050201905060008160040154905087811480614f41575080876003015411155b15614f4d575050615004565b6000818152600660205260408120600401546002890154614f6e9190617022565b9050614f7a8382615ee3565b614f849088616f72565b9650826003015460001415614fe257856002018581548110614fb657634e487b7160e01b600052603260045260246000fd5b600091825260208220600590910201818155600181018290556002810182905560038101829055600401555b614fed600186616f72565b9450614ffa60018d61706a565b9b50505050614ede565b60068501546000811561511d5785821161501f575080615022565b50845b801561511d578087600601600082825461503c91906170de565b909155505060058701546040516000916001600160a01b03169083156108fc0290849084818181858888f19350505050905080156150d05761507e82886170de565b6005890154604080516001600160a01b039092168252602082018590529198508e917ff8117d40f26539bfef76146b0b21a24097bf38ad67a5bde1b34f4428c6cd9793910160405180910390a261511b565b6005880154604080516001600160a01b039092168252602082018490528e917fcf0ff1f9dab0b610323a92006ed5666b075ab010de2b59a3c36f7ad22b5be010910160405180910390a25b505b82841461515e5784600201848154811061514757634e487b7160e01b600052603260045260246000fd5b906000526020600020906005020160040154615160565b875b8760030154116151d35760008c815260096020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054821690556002820183905560038201839055600482018390556005820180549091169055600601556151db565b600487018490555b50939a9899505050505050505050565b6000816040516020016151fe9190616c2f565b60405160208183030381529060405280519060200120836040516020016152259190616c2f565b6040516020818303038152906040528051906020012014905092915050565b015190565b81516000906020840161526464ffffffffff85168284616027565b95945050505050565b6000613dac61527d858585615d21565b60008190506008817eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff16901b600882901c7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff161790506010817dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff16901b601082901c7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff161790506020817bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16901b602082901c7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff1617905060408177ffffffffffffffff0000000000000000ffffffffffffffff16901b604082901c77ffffffffffffffff0000000000000000ffffffffffffffff16179050608081901b608082901c179050919050565b600063ffffffff8211156154365760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f32206269747300000000000000000000000000000000000000000000000000006064820152608401610c7d565b5090565b6000613dac84846154598760181c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1661547191906170de565b8585615560565b6000601882901c6bffffffffffffffffffffffff1661549957506000919050565b60006154a68360006157bb565b9050601883901c6bffffffffffffffffffffffff1667ffffffffffffffff82166154d4575060009392505050565b60006154df8361606b565b60ff16905060005b8367ffffffffffffffff168110156155575782821061550c5750600095945050505050565b600061552c61551b84866170de565b60055b62ffffff198a16919061543a565b9050615537816160cc565b6155419084616f72565b925050808061554f90617184565b9150506154e7565b50949350505050565b60008061557b8660781c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff16905061559486616121565b8461559f8784616f72565b6155a99190616f72565b11156155bc5762ffffff19915050613dac565b6155c68582616f72565b90506155da8364ffffffffff168286616027565b9695505050505050565b6000601882901c6bffffffffffffffffffffffff1661560557506000919050565b60006156128360006157bb565b9050601883901c6bffffffffffffffffffffffff1667ffffffffffffffff8216615640575060009392505050565b600061564b8361606b565b60ff16905060005b8367ffffffffffffffff16811015615557578282106156785750600095945050505050565b600061568e61568784866170de565b600e61551e565b905061569981616171565b6156a39084616f72565b92505080806156b190617184565b915050615653565b60006156c583836161bb565b6157b45760006156e46156d88560d81c90565b64ffffffffff166161de565b91505060006156f98464ffffffffff166161de565b6040517f5479706520617373657274696f6e206661696c65642e20476f7420307800000060208201527fffffffffffffffffffff0000000000000000000000000000000000000000000060b086811b8216603d8401527f2e20457870656374656420307800000000000000000000000000000000000000604784015283901b16605482015290925060009150605e015b60405160208183030381529060405290508060405162461bcd60e51b8152600401610c7d9190616f2e565b5090919050565b6000806157d062ffffff198516846001615ce3565b905060fc81116157eb576157e38161628a565b9150506120b9565b8060fd141561585157615819615814615805856001616f72565b62ffffff19871690600261526d565b61628a565b91506158248261606b565b60ff1660031461584c5761584a61584562ffffff1986168560036000615560565b61630a565b505b6158fb565b8060fe14156158a65761587a61581461586b856001616f72565b62ffffff19871690600461526d565b91506158858261606b565b60ff1660051461584c5761584a61584562ffffff1986168560056000615560565b8060ff14156158fb576158cf6158146158c0856001616f72565b62ffffff19871690600861526d565b91506158da8261606b565b60ff166009146158fb576120b661584562ffffff1986168560096000615560565b5092915050565b600082600f615910816144de565b50600061591e8660006157bb565b67ffffffffffffffff169050601886901c6bffffffffffffffffffffffff1681861061598c5760405162461bcd60e51b815260206004820152601160248201527f566f75742072656164206f76657272756e0000000000000000000000000000006044820152606401610c7d565b60006159978361606b565b60ff1690506000805b888110156159ed576159c46159b584866170de565b62ffffff198c1690600e61543a565b91506159cf82616171565b6159d99084616f72565b92506159e6600182616f72565b90506159a0565b50615a0a6159fb83856170de565b62ffffff198b1690600e61543a565b90506000615a1782616171565b9050615a2c62ffffff198b168483600d615560565b9a9950505050505050505050565b600081600d615a48816144de565b506000615a568560086157bb565b9050615a84615a648261606b565b615a6f906008616fb6565b60ff1667ffffffffffffffff83166007614427565b9350505b5050919050565b600081600d615a9d816144de565b506000615aab8560086157bb565b9050615a84600882615abc8461606b565b60ff16615ac99190616f8a565b67ffffffffffffffff166007614427565b6000816007615ae8816144de565b506000615af68560006157bb565b9050615b0a62ffffff198616600180615ce3565b606a1415615cd957615b2562ffffff19861660026001615ce3565b604c1415615c0d576000615b4561581462ffffff19881660036001615ce3565b9050615b526003836170f5565b67ffffffffffffffff168167ffffffffffffffff16148015615b7f575060538267ffffffffffffffff1611155b8015615b965750604f8267ffffffffffffffff1610155b615be25760405162461bcd60e51b815260206004820152601f60248201527f426974636f696e48656c7065723a20696e76616c6964206f7072657475726e006044820152606401610c7d565b615c04600467ffffffffffffffff8316600c5b62ffffff198a16929190615560565b94505050615a88565b6000615c2561581462ffffff19881660026001615ce3565b9050615c326002836170f5565b67ffffffffffffffff168167ffffffffffffffff16148015615c5f5750604d8267ffffffffffffffff1611155b8015615c76575060048267ffffffffffffffff1610155b615cc25760405162461bcd60e51b815260206004820152601f60248201527f426974636f696e48656c7065723a20696e76616c6964206f7072657475726e006044820152606401610c7d565b615c04600367ffffffffffffffff8316600c615bf5565b62ffffff19615a84565b6000615cf082602061711e565b615cfb906008617041565b60ff16615d09858585615d21565b901c949350505050565b6000614e4c83836014615ce3565b600060ff8216615d3357506000614e4c565b615d4b8460181c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff16615d6660ff841685616f72565b1115615dde57615dc5615d878560781c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff16615dad8660181c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff16858560ff166163a7565b60405162461bcd60e51b8152600401610c7d9190616f2e565b60208260ff161115615e585760405162461bcd60e51b815260206004820152603a60248201527f54797065644d656d566965772f696e646578202d20417474656d70746564207460448201527f6f20696e646578206d6f7265207468616e2033322062797465730000000000006064820152608401610c7d565b600882026000615e768660781c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff16905060007f800000000000000000000000000000000000000000000000000000000000000060001984011d91909501511695945050505050565b600081600d615ecc816144de565b50613dac61581462ffffff1986166000600861526d565b60008183600301541015615f395760405162461bcd60e51b815260206004820152601460248201527f726577617264206973206e6f7420656e6f7567680000000000000000000000006044820152606401610c7d565b60008284600301541415615f595750600183015460006003850155614e4c565b6004840154600090815260066020526040902054600285015485548290615f81908790617022565b615f8b9190617022565b615f959190617002565b91508185600101541015615feb5760405162461bcd60e51b815260206004820152601a60248201527f7468657265206973206e6f7420656e6f756768207265776172640000000000006044820152606401610c7d565b83856003016000828254615fff91906170de565b925050819055508185600101600082825461601a91906170de565b9091555050509392505050565b6000806160348385616f72565b9050604051811115616044575060005b806160565762ffffff19915050614e4c565b5050606092831b9190911790911b1760181b90565b600060fc8267ffffffffffffffff161161608757506001919050565b61ffff8267ffffffffffffffff16116160a257506003919050565b63ffffffff8267ffffffffffffffff16116160bf57506005919050565b506009919050565b919050565b60008160056160da816144de565b5060006160e88560246157bb565b90508067ffffffffffffffff166160fe8261606b565b60ff1661610b9190616f72565b616116906024616f72565b615a84906004616f72565b600061613b8260181c6bffffffffffffffffffffffff1690565b6161538360781c6bffffffffffffffffffffffff1690565b61615d9190616fdb565b6bffffffffffffffffffffffff1692915050565b600081600e61617f816144de565b50600061618d8560086157bb565b90508067ffffffffffffffff166161a38261606b565b60ff166161b09190616f72565b615a84906008616f72565b60008164ffffffffff166161cf8460d81c90565b64ffffffffff16149392505050565b600080601f5b600f8160ff16111561623c5760006161fd826008617041565b60ff1685901c905061620e81616415565b61ffff16841793508160ff1660101461622957601084901b93505b5061623560018261711e565b90506161e4565b50600f5b60ff8160ff1610156162845760ff600882021684901c61625f81616415565b61ffff16831792508160ff1660001461627a57601083901b92505b5060001901616240565b50915091565b600067ffffffffffffffff8211156154365760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201527f34206269747300000000000000000000000000000000000000000000000000006064820152608401610c7d565b6060600061634361633e82616330601887901c6bffffffffffffffffffffffff16616447565b62ffffff1987169190615ce3565b6161de565b6040517f4e6f6e2d6d696e696d616c2076617220696e742e20476f74203078000000000060208201527fffffffffffffffffffffffffffffffffffff0000000000000000000000000000607083901b16603b82015290925060009150604d01615789565b606060006163b4866161de565b91505060006163c2866161de565b91505060006163d0866161de565b91505060006163de866161de565b915050838383836040516020016163f89493929190616c4b565b604051602081830303815290604052945050505050949350505050565b600061642760048360ff16901c6164c0565b60ff1661ffff919091161760081b61643e826164c0565b60ff1617919050565b600060ff8211156154365760405162461bcd60e51b815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f20626974730000000000000000000000000000000000000000000000000000006064820152608401610c7d565b600060f08083179060ff821614156164db5750603092915050565b8060ff1660f114156164f05750603192915050565b8060ff1660f214156165055750603292915050565b8060ff1660f3141561651a5750603392915050565b8060ff1660f4141561652f5750603492915050565b8060ff1660f514156165445750603592915050565b8060ff1660f614156165595750603692915050565b8060ff1660f7141561656e5750603792915050565b8060ff1660f814156165835750603892915050565b8060ff1660f914156165985750603992915050565b8060ff1660fa14156165ad5750606192915050565b8060ff1660fb14156165c25750606292915050565b8060ff1660fc14156165d75750606392915050565b8060ff1660fd14156165ec5750606492915050565b8060ff1660fe14156166015750606592915050565b8060ff1660ff14156166165750606692915050565b50919050565b5080546000825590600052602060002090810190610cee91905b808211156154365760008155600101616636565b80356001600160a01b03811681146160c757600080fd5b60008083601f840112616672578182fd5b50813567ffffffffffffffff811115616689578182fd5b6020830191508360208260051b8501011115611ef257600080fd5b60008083601f8401126166b5578182fd5b50813567ffffffffffffffff8111156166cc578182fd5b602083019150836020828501011115611ef257600080fd5b600082601f8301126166f4578081fd5b813567ffffffffffffffff81111561670e5761670e6171b5565b6167216020601f19601f84011601616f41565b818152846020838601011115616735578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215616760578081fd5b614e4c8261664a565b6000806040838503121561677b578081fd5b6167848361664a565b91506167926020840161664a565b90509250929050565b6000806000606084860312156167af578081fd5b6167b88461664a565b92506167c66020850161664a565b9150604084013590509250925092565b6000806000604084860312156167ea578283fd5b6167f38461664a565b9250602084013567ffffffffffffffff81111561680e578283fd5b61681a86828701616661565b9497909650939450505050565b60008060408385031215616839578182fd5b6168428361664a565b946020939093013593505050565b60008060208385031215616862578182fd5b823567ffffffffffffffff811115616878578283fd5b61688485828601616661565b90969095509350505050565b600080600080604085870312156168a5578081fd5b843567ffffffffffffffff808211156168bc578283fd5b6168c888838901616661565b909650945060208701359150808211156168e0578283fd5b506168ed87828801616661565b95989497509550505050565b600080600080600060608688031215616910578081fd5b853567ffffffffffffffff80821115616927578283fd5b61693389838a01616661565b9097509550602088013591508082111561694b578283fd5b5061695888828901616661565b96999598509660400135949350505050565b60008060006040848603121561697e578081fd5b833567ffffffffffffffff811115616994578182fd5b6169a086828701616661565b909790965060209590950135949350505050565b6000602082840312156169c5578081fd5b81518015158114614e4c578182fd5b6000602082840312156169e5578081fd5b81357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168114614e4c578182fd5b600060208284031215616a25578081fd5b5035919050565b600060208284031215616a3d578081fd5b5051919050565b60008060408385031215616a56578182fd5b823591506167926020840161664a565b60008060008060008060a08789031215616a7e578384fd5b863567ffffffffffffffff80821115616a95578586fd5b616aa18a838b016166a4565b9098509650602091508882013563ffffffff81168114616abf578687fd5b9550604089013581811115616ad2578384fd5b8901601f81018b13616ae2578384fd5b803582811115616af457616af46171b5565b8060051b616b03858201616f41565b8083825286820191508685018f88858801011115616b1f578889fd5b8895505b84861015616b41578035835260019590950194918701918701616b23565b50985050505060608a01359450506080890135915080821115616b62578283fd5b50616b6f89828a016166e4565b9150509295509295509295565b60008060008060408587031215616b91578182fd5b843567ffffffffffffffff80821115616ba8578384fd5b616bb4888389016166a4565b90965094506020870135915080821115616bcc578384fd5b506168ed878288016166a4565b8183528181602085013750600080602083850101526020601f19601f840116840101905092915050565b60008151808452616c1b816020860160208601617141565b601f01601f19169290920160200192915050565b60008251616c41818460208701617141565b9190910192915050565b7f54797065644d656d566965772f696e646578202d204f76657272616e2074686581527f20766965772e20536c6963652069732061742030780000000000000000000000602082015260007fffffffffffff0000000000000000000000000000000000000000000000000000808760d01b1660358401527f2077697468206c656e677468203078000000000000000000000000000000000080603b850152818760d01b16604a8501527f2e20417474656d7074656420746f20696e646578206174206f6666736574203060508501527f78000000000000000000000000000000000000000000000000000000000000006070850152616d71607185018760d01b7fffffffffffff0000000000000000000000000000000000000000000000000000169052565b607784015250616da8608683018460d01b7fffffffffffff0000000000000000000000000000000000000000000000000000169052565b507f2e00000000000000000000000000000000000000000000000000000000000000608c820152608d01949350505050565b6020808252825182820181905260009190848201906040850190845b81811015616e1257835183529284019291840191600101616df6565b50909695505050505050565b600060a08201878352602063ffffffff8089168286015280881660408601525060a0606085015281865180845260c0860191508288019350845b81811015616e7457845183529383019391830191600101616e58565b50508093505050508260808301529695505050505050565b606081526000616e9f6060830186616c03565b63ffffffff9490941660208301525060400152919050565b602081526000613dac602083018486616bd9565b604081526000616edf604083018688616bd9565b8281036020840152616ef2818587616bd9565b979650505050505050565b608081526000616f11608083018789616bd9565b602083019590955250604081019290925260609091015292915050565b602081526000614e4c6020830184616c03565b604051601f8201601f1916810167ffffffffffffffff81118282101715616f6a57616f6a6171b5565b604052919050565b60008219821115616f8557616f8561719f565b500190565b600067ffffffffffffffff808316818516808303821115616fad57616fad61719f565b01949350505050565b600060ff821660ff84168060ff03821115616fd357616fd361719f565b019392505050565b60006bffffffffffffffffffffffff808316818516808303821115616fad57616fad61719f565b60008261701d57634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161561703c5761703c61719f565b500290565b600060ff821660ff84168160ff04811182151516156170625761706261719f565b029392505050565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156170a4576170a461719f565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156170d8576170d861719f565b50500390565b6000828210156170f0576170f061719f565b500390565b600067ffffffffffffffff838116908316818110156171165761711661719f565b039392505050565b600060ff821660ff8416808210156171385761713861719f565b90039392505050565b60005b8381101561715c578181015183820152602001617144565b838111156139995750506000910152565b60008161717c5761717c61719f565b506000190190565b60006000198214156171985761719861719f565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fdfea264697066735822122005680c14715a152fa42df0a5b58ef66e8b056178c9023b47d1006cdcd6bd7fa864736f6c63430008040033" }, "0x0000000000000000000000000000000000001008": { "balance": "0x0", From 98e2f59230bb67050d2037beffc78d29fd8a12f6 Mon Sep 17 00:00:00 2001 From: kevin9936 <134139194+kevin9936@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:40:43 +0800 Subject: [PATCH 9/9] modify workflow config --- .github/workflows/main.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7c6cee6e..7d409048 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -44,6 +44,14 @@ jobs: run: | npm install npm install -g ganache + + - name: modify openssl.cnf + run: | + sudo chmod -R 777 /usr/lib/ssl/openssl.cnf + sudo sed -i 's/^#openssl_conf = openssl_init/openssl_conf = openssl_init/' /usr/lib/ssl/openssl.cnf + sudo sed -i '/^\[provider_sect\]$/,/^\[/ s/^default = default_sect$/&\nlegacy = legacy_sect/' /usr/lib/ssl/openssl.cnf + sudo sed -i 's/^# activate = 1/activate = 1/' /usr/lib/ssl/openssl.cnf + sudo echo -e "\n[legacy_sect]\nactivate = 1" >> /usr/lib/ssl/openssl.cnf - name: Run tests run: |