{"_id":"@keep-network/ecdsa","_rev":"155-7a95a05e053a0d00868e8b3fdf8bb4c9","name":"@keep-network/ecdsa","dist-tags":{"goerli":"2.1.0-goerli.4","dapp-development-goerli":"2.1.0-dapp-dev-goerli.3","sepolia":"2.1.0-sepolia.1","dapp-development-sepolia":"2.1.0-dapp-dev-sepolia.0","mainnet":"2.0.1","latest":"2.0.1","development":"2.1.0-dev.19"},"versions":{"2.0.0-dev.0":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.0","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2b5dac9ebf0c132a901383750de1ece7885d592f","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.0.tgz","fileCount":104,"integrity":"sha512-3kv/S4w++Zlu08EvogUO7y7a8hgmjoosQFGzihvch1zcwEg8npsWXWiKJiGnYCDnV1f/juVZ/giL7tK7XzLXrA==","signatures":[{"sig":"MEYCIQCVAXncACmpb53NBho8+tCQIj0kkW1riqG17qO7Kh3JYQIhAJdsd26+uJ0B8qE59fad26dfxHbHbvXkBUBQSkD/8kxk","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14397329,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiMPdSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqoTxAApGsrzrzNBX3OIOCY8g5O/4e5p2T9Lzs5FzP9VzJSePptIVQE\r\nY5Eb8LtLPzEYKLOKdoIpf3fyCJ42f7IoQhdvCkm0JqaAumSRGpXzw+gL4aey\r\nijRMuM6TuVdnuEeDqEo02ILlgLngaVosUXzMdlM2X2bFfN57F24foFM6oeTq\r\niboY6/haYbk64c2STiaGkrqMpcsbcJRFiTzIrW/VQCTA6nJlM+MIkpD46Qne\r\nO4FvPHe/OfmA0K0ifW3wKJ8T1BsxrJ6xe/VwiGO9CmYb8VVrzfDGarDzRd1a\r\n4YxN62IvH4wFUP957M9m5LByFFr6unCyGBHX9wJ1IFRClMr6ZyLAw97FfA6l\r\nDi4IZdsy380Hgjm1pNrvj9ZOIQarQGaKsg7hlxPqGsri1dcsUcIVThb23qCT\r\nPjxwV851JaiHb66nFLLal/0XCtZLyQVy+Cz/BBqqet7iwZpeB/wxlV/5A7l5\r\nDnLWIe0YXqHwWbYIzAI4tmp6FKA50fM0FwvSKKwYxVh2bVRuLBJH6/F1X3hR\r\nBrVuqS6xEo7qTUJcyIgiyvWNDencQE1GjalBBgpB+HCm/6nw+52+mhQQCOi+\r\nPRmvuRy2R5JSTz5kzznar9/vGk3X8o2Fl3UbC7dkbOuRXEbZdQ5fvW3tGiTP\r\n6OfKXdHaempKLH8m7HTrRKqSQrPhQdl0NO8=\r\n=8rPi\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.0_1647376210100_0.4407523068609569","host":"s3://npm-registry-packages"}},"2.0.0-dev.1":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.1","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5c91e31aa43eddc8cd57619dc34007f4c242618c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.1.tgz","fileCount":104,"integrity":"sha512-pUIhv2asXukavuoKNs9eUgLJ0YMRUq84Eo3Q7mPpcyuSOcopMS1mb/HvODnhieOoioMRV7xyH5psKY/s7n5gUA==","signatures":[{"sig":"MEUCIQDIs1iO0uOfYuZ0iY4xDCqiW+OynaCguoU5Q0aycIJJQgIgIqOWR6n+h/WOuiPERzI4l+3z1vAp4E8VBnPC8oatqkY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14397324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiMPjgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqwuw//X0uQyJy5cVKDRVR4+dd/Qw2ZE4kJoikliD29K46/CmLnOLwI\r\nZ9g6ucXwOs7lBTw62IDvSgD2zLCtLOaLhykCm2o7Q0LnDHZ/rApuFtZP7Uat\r\nnhlMLEX51hHXXOeLLhHLa7aBiSTFtDznDPZ9AzS2eWXxNQ0SjH2tnHxblVIo\r\nJmIT3CZWmD2lcX5vdJyALKuHaUWQq6bCbUSAaI0iCVWFJAbSMWZWlq+Ta52H\r\nlIwJH6tPLc+dD1XvKhvuA5KOUtIjOMQgNQv5pPkYIzNzCRh0wE966u5ng8Z3\r\n8V8hu5Jn9C6cwnDuxkoaqKwR1l3c3bjtLzi+AT7I4RnG61L0MjYBT7E00BTH\r\necwZ68Z5fHXMz0OidKOiOHaPsrpC9E5Cn5B8vTwcKCvvb2Jha58beK7F4C+T\r\nD3enV1Tu7BXztJOMFXPJnFurnBt9SHmy+39pjV4NYRhP/mBnhLaKbwulMIEP\r\nBjmpw/+9Cf6CJzzMhtCiLJvs8VJOVw70ioTVXSa7hnDgg9lNztMXmamy0KyZ\r\npD53NbYl9B/bK2IZotqBQoet8CXU6C6e+USv6H3jkLiyl0QZZPmItNGe3cwm\r\nm3qOUBlv6RTjHf4NMjKmX33M4Kjybefp1cr1Ni63kY1oeW++ubRpO4/2U7On\r\nGEry1NUbdXhiWgg01q8su3W6U4HX08FPvTA=\r\n=BoOo\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"2.0.0-dev.0","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.1_1647376608033_0.34855518334455504","host":"s3://npm-registry-packages"}},"2.0.0-dev.2":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.2","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5dd5cdc00ae2d865472c0dacbdce263c4528e8e2","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.2.tgz","fileCount":119,"integrity":"sha512-h1dHjHATaXFIuj0p65fe9g6bDUMdb8ZQ4z9tdit4nNwHNKPtROjK8n80gPZguJzBqAQrEdCaD8t9UAGznD8ELQ==","signatures":[{"sig":"MEMCHy3KvPI7tjLypO+RypVWsKbZWyCiZ3qfvSkKIiyV8mMCIGT9SRDdDUtzjUxsi6XpOkJNWVVd3QunTPm6zI8nR2U4","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15258158,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiMzQoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrAxA/7B/5IVf0AMIR8+vCoXQA2UfkS1lsg31KCmHlpWscOxgL5oYuH\r\ngH/1L4yIOu1lP+Of+axjPFeTH6RzrDsvJqBb57YzHz112Cem1ynL8VxZcIK5\r\n5JlvC9EiVmztJMyO6YczzSJE5tuF/pFzyBCChuz1RtBhFXgKS19GzHw2jCNB\r\nOAbz690DG9EjF+35fef18odGoqfmmExgPDRzI8FHpPY11B8u+Vj/9CEWPlCi\r\n7TcK1+W6jPCUlljvggTECyHkpGN5Bfa+6KZ9y1KRlIQgTv+kZUcCcH75mM1A\r\nEgMwupxAxdMbob28BUvtCXwiMBdarv6wOeYamHkoPWsC9tm/UhgAcibNPpml\r\nxOkjzy3W/igv9D8FzSp3LKXu2cJaeKOcbbqo6ejgFPVkxrxL3MSTkhCv0wU8\r\n8GjaTw8lipyS1OZtyJiKPRd0sijaeMT0F6vzmHfe5njnToCz2maTuXBZAZJy\r\nODX2dHMgZUYIM1ta0L5HHIU2BPdWynY09Eb/FxRNjcQBu22pZI7YJrg4i8dp\r\nBS9nkzpWNqv5pLpiKIIdtlCeAEY/FDKiIzPaz4cLcx4Vuh3GqxKmAXnQTyN6\r\nWkd0k/6G+WIiHfD3QPYwe8v98JyaquIuhCimGaMeK2aS8LQlI4oay8wj9dN5\r\nmMxsv95EesBp6A2u5qUxFLcGxqIQppGOxpw=\r\n=kZYQ\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},"_npmVersion":"6.14.12","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.16.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":">2.0.0-dev <2.0.0-ropsten","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.2_1647522856203_0.5526201122893724","host":"s3://npm-registry-packages"}},"2.0.0-dev.3":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.3","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"943c35abbe8444b232b52bd1662e4512053ceea6","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.3.tgz","fileCount":119,"integrity":"sha512-kvf+3vpGLoZU6vpw6Ljek7YEf2xH2xCasQP81nGZiz4dFhXRHy/KzKtVEPxL0adtF60QFzVs9ML/sTIa47q+jQ==","signatures":[{"sig":"MEYCIQC614W+DNYofVAbmyJF/9ZUru6rmZCFSUIp48gOOgjMvwIhAK7lMRCzfbI+W2P/kKGEvV/eyMRyrEauY0xw9EFDXN+W","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15370856,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiOyXtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqPzBAAmEU6/6dQqaepdsHeQnMMixa2RShn3+EMJUJeWIkQsUpsSTED\r\nZbMBi88xUjXLMVG/8JdZfB/c2aOpI5LBjYeuPU2X8YkC1Yp0O5T++3aHrlRb\r\n6apZy22K61qFmFZbr+N7/M0OPzKKUENqz9L2YWjAlPl033l0aeBMYefT8UOm\r\nScURHGuLl7yQYbXU0Ygt3I+X/+50CvXDSZBahMxTtxHI4qu9Gr7sqDTak9BZ\r\na9pSYUVkTp7jWitoo/hLU33f09muUbESGbule6p2I4pyMvimsocoMFLn9XWK\r\not/cnCePJeliX7RROzyb6xQWgNNgOGWLlEbY+ptoORPH9cLJUDoQnM5fO899\r\n4OJEdX9KVejOuANFcP+3YEJSbapHf7pne1W/Cr/cUrlaciTq8NkJj4dT8Ztz\r\npUrkQRfuzKLk/4DHAFH9wXzODqiO1CneoI3UkRHBVULUOPaFaTGaobv68GnQ\r\n5obFZasjhtiQqttKF7oM25qOJA0bAvohc9jYGqbSgTaMlL140diqI7spQB3B\r\nN0SxIGhOO/Z1scTviJzlYofTQp7z2XWiIEVGc1lBPyf61wiB6oqvVdKYFje5\r\ng9/TflNhR81NrSmH+gLKbM8bZafI2/DjzsTw1JUpVjZxuqqgI3iVgnUrMafs\r\nlbzXOsYlNviQPnLC4EfCMW4wJ7fKKpQLnxo=\r\n=pUtW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},"_npmVersion":"6.14.12","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.16.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":">2.0.0-dev <2.0.0-ropsten","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.3_1648043500913_0.7097423174043833","host":"s3://npm-registry-packages"}},"2.0.0-dev.4":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.4","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5fe1c06079ab9449805c7816e1674fd4109b070e","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.4.tgz","fileCount":119,"integrity":"sha512-3qCzhuNt9gClQrDpdUVG5i1NVSQNOqu7Xbz7sV+r/eTEedHhfNJDrHIiVo15KH18wPUFlvVPtc+6Sio9TGriFQ==","signatures":[{"sig":"MEUCIDpr+HqMYVxsatcyif9agPJWf8SUjlaZupuZuqk47GQYAiEAoxrcqgfg1ykUBd5YoVI7CrTwR9cDCaZrFSElmZfc1Jo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15376626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiOydWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqXGA/9FWuWpJt41VwmG7TWHa5FP0V1mSnluudealvm3Utf9TMvKdU5\r\nmcR25RG933y7F8RwkbxQcvYzuqf7MqOds5BxKK7bFERmg6T91eIgGuUhxtfq\r\n8GTIUl+mIgB9D87ezLFT3Ankd+wsy2oIbXjmeXN1esCAJoAq4qwRICuL0Dkh\r\naNwJb73ww2atkXvMfrh5Bi1uJ2WambNYOp31ytvHl9GqmH3FuNfM10rdQUpw\r\nr86EzbF+mnRhpcU2zMIk7Bln5kmVNEpWxpLudMbBjOltzBYip8ZFipRVARKn\r\nGtYZOfpmgrPzWSxm11k3nTHlgY9NDi/wCHlcBtmwjvkM+2fG9QuZa4cFYA39\r\ntsXFnjkrA8KTp1dUhujA78lIpK5Y+59SIUEVC0YkewN4NwZpRgML2SapxROJ\r\n/UfHADZCxO3P1Oa/05SRkeqzNAysDlkBGiaYTkyB5Nm6HfyceMZajpAnJO2k\r\nso3Uc7fflI3fPXrUzIbnJzT3s+Z5XtrUAeNjj+gof8e7ab10Wu2fjnmEjny3\r\nlMWWQjX21UBfrygrD7uO28HYQAIijt0puxsxYfiRX7TAWIEFD8SpZckmJ6Zv\r\n8uTV801OOzb2H75fx1Zlqiu5d+ycXGhErdM74pU8Al7hM+UHulqaJk42WQ73\r\n7hF9ZPElGaBqrYLXv83wm7Qk/aNEmaIm48w=\r\n=ftV2\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},"_npmVersion":"6.14.12","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.16.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":">2.0.0-dev <2.0.0-ropsten","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.4_1648043861955_0.5599123393625887","host":"s3://npm-registry-packages"}},"2.0.0-dev.5":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.5","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1808c179c7a3e15073a2b13fe3d6bef738eca253","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.5.tgz","fileCount":106,"integrity":"sha512-L6QoTV5YlmGctB8NNaJsA/v6hOnFtqEXBSbLfjnULWGduzS13vPo1dghRzpW5SP5/9NfKZfP7ZjXFHWoCnyzDg==","signatures":[{"sig":"MEYCIQC87emI/VmdCUMxe8xeG0w5+TeiQapLZ2VEoHeIuIFRYAIhALcTPRoowZuut0x7PNJHyS70fy6dX9T6zoOGxFN2xI/D","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":13904026,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiPFw1ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqx8Q/+M/KVOz7NZBprxRfrhBmvMTKX87IgWgs0UC27eJa5ERFb5nDk\r\nqJWQ0CIuLdNnKwZL+9CEGPbivulE/ZFWKV+y3gJdErw5GXeDLKAGFCE0dg4O\r\nTeardLGYw74ii240VBibKpRzrZMnG+oJIrpYj11U+XHs33h4A1brjKJSED8Z\r\nlbL0KdFpiaOs9sl/B9HL6VO6ZJrtz/qtSSwPW9AYdRmoSc+eVVpopMyKDhCI\r\nUe03Ds0W7oQNW4M5QGlX7XDmeBl1VnHVkYeyyvQJVNtoCOxTKSOYYxoRHNj9\r\nH04tP/tHuKcfJBpWhQ3O9PRNMtSU4H6sYLZHMsvailgbEBv3uu4MsquFmJo2\r\nozDYy/FJDG8RchjFReMfW/NGoqIaVNcmELZWMb6Dmvj24it4S27ga/tdvM3M\r\nKx5vDBIQY6P5zvVTgGcOCsGgXAQMZ6RjsTm8CvYiO/LIZQqsJz5E1bvw5BH0\r\nrgjx8kMpF9MoYS+oDeBXKZ/C4DQcICuDBWXILauPWmaoPcV85VENMpe4RFWd\r\nbX2rr5g5dASzqgtqkfJ4S/YuT9HpYZxQ/L7RSNy8f20DzzTB8cNQJyMTDiby\r\nRZcp9CaJJ0zN3e7hEIhf141UGFcNO2sUVJAKlBYA666sMclzfCNfengAQe3C\r\naKinLLsq4gWGkZLfphSlyXILTGC5NxNcSZ0=\r\n=IdFc\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.5_1648122933248_0.6068462318897703","host":"s3://npm-registry-packages"}},"2.0.0-dev.6":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.6","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2a26aba0262bb91f65aadbf0d278d97f941249ab","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.6.tgz","fileCount":106,"integrity":"sha512-WvyEj53Z10HbOZdHuJu2QsMpNyApyPe2aS/i1exhWWADS7TFyIxwOf67IH6zBKr18lFFVW+vkdw3olzkmqNmOg==","signatures":[{"sig":"MEYCIQCyVyw8C4npJv4wx9lAuZL1WTxhUJSPji0blbVgO+Yn/AIhAPW0y55aY/i1Ma8gabZYf+PEaok7ABOfvoOT8G2/qzTP","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":13885805,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQwqRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr8cg/8Dug9+tc+fSt5zo2g9QsWGqWncdgIcGD/IENqrIi5sEXKd7fJ\r\nq3Fqux4CNantoOAWP0PF+wBrQ5tnYcKak1eA3va/Wz2iOrRsW2lrAFPwUOdz\r\nsB1gXM+2b7maUWbnauwPnnh+3NdM9ODFrhyJsSrglrgJ3ZcfcjbxiItX1YCH\r\niZGjq5DVv77CrP+KQld2OHdTHiuQnwhEHcb8m/PXfSianSmtda7GDNL+OTOp\r\nHAxG6ZakCWRYycRQfFaqXtVwXbAfdRAv8cYsy9yiPChQsa+a6RRbvK3MzCni\r\nCXqPOzSQ3IZnR2GdyVws5Xw5BtGWiK0CrMFFqLtYT23tSyctSG3fgfVqHLbE\r\nqYURacrq7okSqJyB0ffgdT9wuenEPiFYA4sAKP+fCtCFz7tzQEKPcFoABeka\r\nndCvZWT7xBinJvZhoNykkrfYaS+ocd9l/OC9N9jv7F9CIHX/RcKg3A2Z67Ax\r\nPWlESiL16f3Fv+cxaX84sThB8t+foeCcbOateDnoGhKa7R9k1BZM1PsT7CVO\r\ntEFd5qSHDvzXq178cv7cMhHmAEbG4qknfGbHz+PSeF/R4Lq5zYo5c4+WyG1T\r\nzcFjw8Zax11FaeodW1pMtKILzp+GdY5hF3xnuQLNmQLqhHVp9ShnxgIJ/1q1\r\nw4bYgElb2CAZCT0BOfxHPdpk2sG45Ewta7g=\r\n=jLpj\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.6_1648560785379_0.32711749351283603","host":"s3://npm-registry-packages"}},"2.0.0-dev.7":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.7","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.7","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f32947ab6032d1b290330afd6a88b0ca4e0b4f13","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.7.tgz","fileCount":106,"integrity":"sha512-N3Go7asnlHDjjdQBJsNBw7Fckh0l3tDEMDwISA0Qz1Z+GGrOhPiKmdqT0AGNpdZqiDrVXJMGisFpOZdd4eRF5g==","signatures":[{"sig":"MEYCIQDOqhPgabSaP6VvJ8ibzc+uhnBgT4pnOEc8AGq6eU7u9wIhAIQRkSMHptM9rxm6xeXr/b7N22KCx3Z4KjDV/Uqu3mGa","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":13498910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQyNhACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoVUQ//Whkz7gPxI7j+YlXArShPJ7Ybc5qaNd4KrPCeeP/g8o63eVKg\r\nCMcb/NoNl908VmuZoFoW3iQcqTPJrlYMeaq25y6bW+5m9gMb2z85bZ3DblWF\r\nYlwIr9B2gZFB9UpRAnTOnJ4LzWDDax50d90Xps3TmWmdExhRlbnMWA7SQQmR\r\nf07GRMbIhddgIEQjQJJL79Ckn0u6HjccPUDh0RBcjPYtWuloujp/E9q2Pcow\r\nHr/60Okp6JbQWlhr5HQCWnO7FnZd26OKnjXwYJNCUAq8jJQfQbpreaa/vX2m\r\nwHqrgZrEa5Q26Vbps7T0+/VEJA9LE2dwKpmpGAd74O04NrT3pDiFHnA+gy+k\r\nPIy1eMKmsZcnLgpG514oETc6NPFTECqDDGMdrRjyduGiaDlwqeT+I9Q5ZU36\r\nmhNz6DiMsEDkwlePGm1OBv+CFiJwIe98EKSSoTiE2vD6WMQCQa8teiCEg6W5\r\n2SIHNaxzd7uNJJi09nkVLaYdtyi1uCn/s98plTavBNFZjLzOqknfvB4NbcuS\r\nO4yM3TzcvWjoR7rX/Fu87jirOEbtlVaQvYdGc5NKG7WZD4rYD6lyj2D1AQIe\r\n6oQ+9h5/tXh5MdCjyr0AAC95qR3/AR311Ya9crPMRcoOij/XVoZnY1xktK8+\r\nQl7JZDxBYuxz07xWGwuJTUQUEp2sGlOArEY=\r\n=XJrr\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.7_1648567137399_0.9454782490362776","host":"s3://npm-registry-packages"}},"2.0.0-dev.8":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.8","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.8","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a938e5876424ae05c70b3248ea90617e2ce6d61b","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.8.tgz","fileCount":110,"integrity":"sha512-d0umtIQf3Vig/beQ9NeqYYkO8hpD2bX31hWMU1vCa4xzX23qy1p3BEsgX5CmFWdsNsh851G9wpR6VCIk3o2+/w==","signatures":[{"sig":"MEUCIQDf3UUZbCdEJKtTZYO2vQlW2RRQnUNJuLYmkdvM7olMNgIgHSZRJskr/AYorBA55rpHqrgEglPuXDb75qmjiuCv4Gk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14469402,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiREKrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpfBRAAgpUrDEEGrXijBKxBIVaz6uslQBpYKrhDBCQ47T3P0os25eEG\r\nbveEn3q13CsGHUa+mBZgtfGDMY915C3vX0M3pPUYoiv+FMRm30wpEnUkVOp2\r\noBoR4wixNDzYQCA3kfw8aDMaAgzj66SI8i+U1p6tgZsF75J/pjfWjTl6tUVr\r\n9QG+PzKMilno6OkSEIgwEe8e/fqzbSMWn+LXgW+ofL2LBAZKzlFeyFB6zBQh\r\nLXCdp5XC48hZ5dC/fYntDDoAVoseUteWacgDxAVgUdm3u4P8VSQaZv34rbwz\r\nIOB645pzN84bJdu2nKnTVfJOIlbx+cr0jfUVA7lUGBh0Om5ZUzwQxjnYU//R\r\nUxcP2Z+hoJXXlVMD3RXvkWNZ/YOhJKTrpBPVM1YQ2OWoKA7AsC62AzN/x2TX\r\nOh1vrD9wn8fXnzPJVZ4wLSAsQSzJWYOi4Cuc1xRVBhipPJg7E7M7GDOouNX8\r\nZiamVeGREncT1Do6NzjwXj44OjUbtFsQWAmltI5oA2jZXKrhI/tRY2PSvg4s\r\nYWyHOYEmh1ta7gmVlIvElTT9uHTdUxVJKdv2yCy6UN725Z6KJ9D/VLdZnKUp\r\nRdQoKsG9AIuCOVM+0GhNigzf3EKZKhimEI9zV/Cn64rEYKFjXZKz2gUWPPSC\r\nsDXedvBzVDNmy4cGZuina21KsEF1WX/FaN0=\r\n=yRuF\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.8_1648640683518_0.05219784161264118","host":"s3://npm-registry-packages"}},"2.0.0-dev.9":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.9","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.9","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"292a5abfe432756dce20e85f42309214db7b3328","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.9.tgz","fileCount":110,"integrity":"sha512-tJCINrCN2v9EmDi+Ert1CytbQmIq4SGkUrBbxDCSeOdTFJ1TK7BSVC0Q3vTkvKYl7PmeNAaPa/aJbnsfw2qTpA==","signatures":[{"sig":"MEQCIFl3sRIyaQI6t3oS7Q048eiJvQcUGrsvXjHtkr42tNrJAiA09JL5ibK1e83CLsjFJ0hSxWXVgXnBAepRd+sY86kPcA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14483225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRFxyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpeahAAiCOntamGyjd8aoZt06H93lSW6GIW/xfiA6mjMr3viKebV5QE\r\nTNFFWDaNhfE0kNlaqcOruk44fy+BXSnks6qNyCYF5VQGLTI4dvT+4hPAB3Rv\r\nIjYKxnjpilM/un3rwwmVwtcfGXIlQaNaEUFaVjqfP6TwXtorFD2ruMgljxWP\r\n+odlEM17VI5po9QYs2h34051DCifdzjiSSjdIJ9mbgWdw6i4DZjUUEBJJulJ\r\nd/eto0xoThKpRqwitGF8g/9sLeBDO4sKI3f5yXv8RN2Nf6JZUni/PBqDo/WB\r\ndsy/JK8a9YW7WHsojrngbLrZ7gMaeOUCwDc8ixq4G5dYeQpxIXgaOOD/72gK\r\nVLM9SKpdc12eGW9y7NKEve2J94zXhNV1xi5sIBy2JfQoCRnV2l3jrVqLnnHP\r\nIn91INPsnnsPJ1p2jazuwHdq3EZxUMbs/A5HKQ6k5YxD/gBbLjlOZc+6ZOlu\r\nqm44vYr9cKu39DD0dWZ4PVmkD5Jq3TU0Zg+BV06SnJibPQKJGT071Q63w6g6\r\nqZoT8I8gokP36rmVmf6audK3LCas8lxqpiRxK5IpBNzMULzHM9LRy3P81E8J\r\nv89RYiPZw86Qt+37METHyOOKg41+HKlDzOhSsUus5i0QiNoxjaprmFJJD94G\r\nJnqGTnR1Fwjff1Ukpxl/+zLZTodD6B8n9bA=\r\n=NdEP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.9_1648647281959_0.025098335844356612","host":"s3://npm-registry-packages"}},"2.0.0-dev.10":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.10","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.10","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"4f1a70ed97bfbb035fc5dbe29b291288fa5f8d1c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.10.tgz","fileCount":110,"integrity":"sha512-eORHxyP1rC7vb0zqYGe8ZOpV/5XDcrk009MuVAcwRpApoLjN2zq0ZgFSEqXVDcvrmUapatn/HrHsap3c2RHVtA==","signatures":[{"sig":"MEUCIFxglWzxxZp0aKnkWuy6oIKb6kUzHWo2elXyKRgZ1II3AiEA6szpcwMg9RrQR5TEqg6j1sl+HpF0TQSLjkCmQavmk7M=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14552617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRstOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpfiA//QovDCMBiBb88T0Glz9ox5pbP+bUiyY8uvs+n4nttSGF0hVIU\r\necOhu+OWYQhoREKtBU1NDhEqf5vTot82AH8/al4d/3VRetlMA2Ie1b2dD6Or\r\nZRsaOR7l4uyGB0ONRdDNXm85WJQ236Qhp1Mmvmb5O1ezAPiW5p4C+5fbPdkU\r\n9a5zghuTcbSzErrZ98fIG1KUBuK4rT5AAl5uNwkv7FADuzmj3un9rPJCKyZ6\r\nC0tpVSUdHh4+sw7yezoyn+RmdDGIduFTwxi5VXMmEi5gahw/pN4fi4PbB2tC\r\nxRZGI+0E303PyTNPyY5j0O2dTEws5+3P+0DhhRhRTCstvNZBknGsK2HsuvFO\r\n6ee5G1s6HNlb8xzJz+Qk9FqqL6fkd2JuOv36WscMZATBmBMCC9N8XREoLjkV\r\noR6bM4fTi7rAZhNupGPTc1cdIZ/ihmO0F703XCbCBjTBpSd9x64TGdP3huP+\r\n7Cc2wXtds08qgSyFAi2QuTOqYcF1oIu+olE5KOINDwCKg0jWXuk4Tj7N8nWM\r\ng/et6wN6Q0f+exVjuctAseGUdTSs1BI+OKVYUAE5y4pijEf9qA0AR4DfWIFj\r\nIctDe1qpDFrxCl2jk3hS0KEL0wt+QCcZo/x2+10Rch5XNLZd972jShu/tRlT\r\nTyX/HtVQWQwEaGwJdXbRIaw857HRskkPVqI=\r\n=36TG\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"../random-beacon","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.10_1648806734462_0.05721931696414906","host":"s3://npm-registry-packages"}},"2.0.0-dev.11":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.11","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.11","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"23bd7c2558f00908694f47a8c8b299a9671a8340","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.11.tgz","fileCount":110,"integrity":"sha512-YEHHGB0pRohhddpQXIerPszUSm/CN9F2OYfaqQb+RThCB50wRoUI5hAQA6XrB5WOBpKKMpxoHp0j4WdbJk6wTw==","signatures":[{"sig":"MEYCIQDOvXWf5X5XgIkS22wfWrk18NAZrU9eD0++EoMtNZB4qQIhAPCbXiIFgO977zLw1FajUkdvMKhzvE7dXoeZpuPqDici","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14552613,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRudxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrvPw/+JQclKp8O4n+xNEEZAwpMFxsgAlDwSAQtTVHY2pJPcubNX1tB\r\nXqKPf432haTyfQCjBYP7xi+SBkKd4bcc1iWVA9IKf4yFohujTOGnetX7n8ZD\r\nGVGmWqTDawnobKuoo8pJ+P8q2xmQwgzn59Xy5VN0Ooa4sp23bALSSGJfZOVo\r\ngDLkOP3UsYp27kb6B1py2qY4DNXJHMUDkn/2EbbOr4yqh1bghSjel2G8wNbF\r\n4PyKsMzCvOg9Ft7a3C8kulF54FKdWnsAsWWyM5mz4lgWHy8UaNtvHKPeUIRR\r\nvMPo6+Y0opYlnNmlinUSW0pEtTvsrJW/7UZh5bux2kjpOoAWBX2yuSOyxxg1\r\nI/1k/89TM8MoFmcyK3S+Fq1F8VJWRilB20QEVvBdJs2n8JPUZ6E2RdAwwtN2\r\nF6StzQeW2GVh6RPAzlCSNVy00/U87cwwdUz/WhpiDnMDPAO10ah3ZJiXIjL/\r\nDZeUOx1Q86wEvjvDQnz3/ad5MkW0bQoVf+uNGS8R+d5HBcI5lFvpZXfxtlEi\r\nJAYVWs6IIwzKhmM689DSY+n5FX7iLnBY2zXjSFyDmzDU08Uv4Xh1nhVmhQrv\r\nXdtE3mwvJQitwsA+78dgAZefvn3b2PCwHepxkgn/kRFUf7lWa5clZ59Rq4uo\r\nj6CNu8U0Ab5K2L+auP+nokUuqPPv7zklLvw=\r\n=aTVM\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.0","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-dev.0","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.11_1648813937129_0.38767531987725357","host":"s3://npm-registry-packages"}},"2.0.0-dev.12":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.12","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.12","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f25b683911996c23afc216f9a8b0de1ee4ca6ef9","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.12.tgz","fileCount":110,"integrity":"sha512-ZLugTwG6CsbSFY+VMNTCekEfjEhxWYahwUlq7toSxTH+beJLnNHpG6wCqUtbzh8m2dcvwbbzq5iBTZpBmc6grQ==","signatures":[{"sig":"MEUCIC7aJe++slKNyb+EIig2tRCApV7k0fmsuFI2P/PVG/YHAiEA9G9e3Vh6Zu3PvGQOpO4WXS+nB9AxA/q5C/KIRjOlBPU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14556288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTZmeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqA9A/5Ad4YuggyEM7gf0U4Q2rP0SpI6/6vIgrQWI2UH/zG/pjayAfG\r\n+R4cPyDF8pMKqwUBxUhoYr/EBw51Z9J6jeRQ7g4YbDZRuG6zxm/lxWWNVg15\r\n3niPQRwV/b/bLIj4bOsnIqxkvPpMFKm9Ca5RIbypXAzuyTBj+nhe0w37On4r\r\nX5HJnOePP/EDZ+GBvpYISBBiDW43AwLZTUz4Z80LWHBFwxvJbQw7ki/ba1ll\r\n51DfiIbEd9mOkTMRz7z3xIvYxJdd1ZSxna5crOe2YhOSURs7O1Dh5dCIyEE5\r\nmA9lK5vrgpsW0RjlrrkpuCI65YMZbAYn4a1mz3l0SEjKuAx3dB6Mm/ZRfPbi\r\nTQlg3HJ74NBFjxf2rMmsaYDI4y3eNU3OZzJD3gTULmSgzTm8PCTZIpxQu4Xl\r\nRtQetjdrdkr8ECVYOVhRFO/XE7t7psHEVUs21MeDpmMYkB8Wcx6H59MY/VRo\r\nPNm69gTs2BNUIHtWx1GYF2/wPvdwO69XdElKrHFUKT+S6xfB8ajhM7LoBKQh\r\nLcbexXNfgCtkUPoxHR9FHUCvOFLglQ8hDZP3AFtkloBixCim9mmvpZmA02q1\r\nByWr4yTA0N9FNgGJlnxAy3w2JfVDU6wlXh6//Ws/ccOnC3AhIZSuKayDy5Cj\r\nRD2+pGDpCWUrEiklLo7MmZdsNfcr3ReemZs=\r\n=JQD6\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.12_1649252765852_0.9377563449171149","host":"s3://npm-registry-packages"}},"2.0.0-dev.13":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.13","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.13","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"11b0a2a62d3dae9f1f82c3fba8772c11f30fd315","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.13.tgz","fileCount":110,"integrity":"sha512-/44VEKX8EfgyhyLNudlPqvMDVMQmaz+d5TISrmLO72iPIsKBT8VxaEOZjKcPoeWnsXNvlcOaiSoECm/1HY9/5Q==","signatures":[{"sig":"MEUCICTJ62m2q6j/RDBZeS6SYl37k/UXEiEEfm0+ycmxR9ZlAiEAzqttTEuGO8rBA9DKXtl+TmuqqUP5ABXxritaUDVrnYg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14556288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTbPwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo0vRAAo8IIt6/Q9iN49cDg2GMMUwwrfGQjT88ElyJpieSp+0hY3ikJ\r\nNPolhqA6GD9sRbBxWW95u7n/E095qaL/PsrjwLGgRXCCTaijG92b+VHxCkSP\r\n6ZV/AYMp83QXNtyplCFMZxwU7AwhSzCCE8OhEEYq/Qxz4OZ4OptErMvjBvSo\r\n49QX8VhADHfFcvWxfCaZiGQv4pL/YXOFDJ383ak0Dk9jprQCIznXB7SaESnL\r\nGfbQnccRNNU+q7n5lAJ/kmZJq9ItAonYaHOUOjOUwkn+v97D1r/Jynyqo0kW\r\nYDkHOj5kSVWnna0S6NsUO0GUCHqXzJYyZeuofxeH03kaxOjqBac08lv8s0l+\r\nm3DcN6v63cjnyJvp+X4sm4s1P7EYk7ocokNIs0wTOe0wyhsbAy0TLWvgv3tT\r\n7rs6FlzRWB1Gt3NJgCpF0AGVbxhV2CfzTDI/HaJy+KErzAQA0fwsYqyulbBx\r\n1V7FZvwtMX1restXa+slrHpkgQ67bXAsYqk1KOVn2Tf1b4fqz17UNLUjp0Gf\r\nkBq5JMstNOxaHy857997M69iWHzYfroDMxnIh5Si0YPMXOUdt1cubYpgFLUg\r\n0JsYURokZ7OpKAmOdcFOROGKD3EpKb+vVZQyETKtLRE18Io0Fj7EO0ZMAfVW\r\nlmDhWho/iEqaAvOQH6aWVSTcqsWei2e2Tko=\r\n=rkdR\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.13_1649259504066_0.1781074923998227","host":"s3://npm-registry-packages"}},"2.0.0-dev.14":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.14","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.14","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1eed8a6fb41cac524c777a065c4744b2b917fb8c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.14.tgz","fileCount":110,"integrity":"sha512-N/ZgqqgrxebqEgLoTKfbA+P6VhX3qCwfZ3j5D3GoMkE8AQtn96b9+15OnMN+w77z3W5fX+Z09ffjro1oLj/zJg==","signatures":[{"sig":"MEUCIAnJwupQr2tYRf7W3YbfHs9cjUGaZ58L32vHAYOpbeECAiEAuFLeoIiQaaPNeZ2RzY2alaqMja19FOqHMZVU5vMVBes=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14656268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTrQSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoB5g/9Hw7Edf+Fl1STmdhlALnCIf0LsMqhcYbgJrj+RM52NMwgsrAL\r\nnAXxUEDXnwm5r06LVARPR0ZuSYEFGpKBAF+Wr4zlSkS3RJh7oN4Fglmf/Ee5\r\n151BptVVes+lZnUt4ByYoZdBl11+O+sIfYitpM3UvssFw+NBHwBN/ymXfAvr\r\n8D7bvcrv8ioj7zT63wL9wc+VJtB6S8+2Dd8w0FDiDeKRZXm8kL0PhedwVmd5\r\nJumJ6UUrHR37zqHpteRoLWl6/a88hC0IzTzK1o9kumze2IFwFKHBJr8LyRKN\r\nMllaj7DcIGG45ThRGA4D0gyTHzZkHXrLS4WQGFB4cq75ye/Lk11Dc0obQmgk\r\n0V9Zzk7a2mFiqsFUUaPC3pvnbM8ZgrfGt2qAarBKXJNrXTOJv1MxvVZ+Jc60\r\nkGrZXRgg1H46QSx574bHlsn8zFr0QSiw2wRohvYiju9hQWbxBizMeuFU/JAE\r\nTI0l7bH5aGcuNkcsfuQEoZ5if43ftC3HvWMY1S8L75ucwkLh9LlaV2Dr0SK6\r\nDfXXZ4A78lZQD5D57nzdibxeMzYPLMLNgwxGdbSVLPhxTzqf4RIZXSLvpvpb\r\nQEpl5iSm4x1q+BIhxi8lACAhA17qtYxSavuOMaKm0XBnK5BslxwjyrTelQ1O\r\nnLFjC2nbAcxg+0Ak/gHcY+t6pI73plMKV3s=\r\n=jd0N\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.14_1649325073857_0.2440733019360446","host":"s3://npm-registry-packages"}},"2.0.0-dev.15":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.15","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.15","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1f4b8a9832d6a2bbd7d5b2717a6f23076b95e047","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.15.tgz","fileCount":109,"integrity":"sha512-Vu/z7xGuZ8ZXlfbJK3HBynlY/17FVD6YVKf81qWDKUJfUTX2SndfdFWkT3S2IMdjYxmGgAe18yF/ZPlxjxNZiw==","signatures":[{"sig":"MEQCID8BxctvBEziDROS4UiE55c1pyMac5aLJJteIu6WGqLoAiBZyi5kWOGhQmU6gNXE4SVmzJLww+H5W85P4DfAoap3AA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14620540,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTu4GACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrrog/+IcJz2yEGkPLgXS7y3GUdsC4fXo9nGQetui5HG61VoB+9wWGz\r\n3oC3RNyamH/GLLXitxY4ND+vGBWKwqRV0jWNjIKQGQMv9/SZ246vXT2NIoxN\r\n3Zu3kgjjn2v1FeawcWhd7+32JyzjYxZ16NOPIrTdkObRX287aOkByXERKKjw\r\n9aeSYlGcO5pk2Fxmzz5Qvc4/4Vw2otdzCPIbgo+stznmU0R9jraueRLJ3o7y\r\nqFKAlKe28HYml2uBqUXfMGXxKe6VfIDaAg2ecAYKP/juFSVOPDPcaZ7Bl4t1\r\nYB2BntE2BYWM+LBPFObIwd1ElfWK7Kf80jKHjpTd+wHl0cKGi3opcuDxczsJ\r\ny6SB8FdtmYSbrpqPnXYH8/W/OxpUPiEnwIaN7Z84VD8LWOd0bAL97YaMdMi4\r\ngS06KywYajYRchR/LSAwJ8l4SUE1zwXt7ckgxqS/uLSSIV58+RhHNRDXu1pm\r\nrnLp1XkBHd/+MBlwB1RfF/FipipXWEA/bGxGERHpqzyC0e6FHajij4qCX5Dw\r\n4IzOA7NazU5FpGCeq1MfRts4CCPW2nO5A78Jxk7Uq0ekuHTNUd/ZTZFk9Ktt\r\nk8vCL0pqpQUkCkeTIEoGIi6RAMkGjJoornx2Y7z1CJkpSl9sx/Dx8KhHFKNn\r\nCUtunZn18Zfzvv1z9xlvL38HMoG6WoS6jGU=\r\n=8qhR\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.15_1649339910284_0.12951916960161802","host":"s3://npm-registry-packages"}},"2.0.0-dev.16":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.16","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.16","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bc7ef40595ac31114ca5ebb9f312a80f4e90bca8","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.16.tgz","fileCount":109,"integrity":"sha512-81EcMbaEzkHAhV5Y2jnSlIE09k5bmLq8koC1SfzvaknA5RW4iRVVOPz6G2ACoWTa4tjT+ubSlV/zlpmj9j8F2A==","signatures":[{"sig":"MEUCID4kne2Pn0CaJuOXRwsc4cx1rA78BivXTx3iOL4CfWrQAiEA3KRwtl3yHBAe6etmfrMOsKuIKPzM+jlWj+OuGMa2l6A=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14622485,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiVE++ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqalw//Uki4UdRXBI15yyUSL3/tmEvMz8HY+7TEZcGiF6dJ+ywOr5L/\r\nIuoco905ZTTKFEIXhPwYEQKShLMtHd8hlajGi6xeKFCA2xLOn2ClSgKL0tO4\r\n3yhP8WObRDtD7UK7XKg7Ttkjwe9fjV3ZiVrDr7C1beihpQecjznHcWX/FTCC\r\nkyMlan2T21d/mdVyhCsZGlWmIAaYvJm2Lknjz2fjRzIWvgmLd6FkS93n95ko\r\nN2SYI82kQFEFpRmodwO+jq/BogbjIbwOMaHom55TWiIiSBgndiSOcqKm7/Tf\r\ndXLsl1jqapTEiOVxI0Mgt+ZR7ih1habn0OBFsb2B91KJ9Fb8OfID18bGHY9Y\r\nL+zUf/GPGWmnjTz/drcUAkfCftFbt2aBIFzoyFY5ZalefdRPWnmAEFsKEz8c\r\nFMcFVlYfekqlUxckaRh4aOgf7iNcU8Aksf4k6oQhWVV5f7Eh9BV5Ul/xSzg4\r\nHhUQcjZc+N3lh96g7Lm5uAxyzxSUaCEG5esMeisahqxua7urYCDmgUifOMS3\r\ncbZH4rLmB1VYrGotSfmMGpYlZb1aX+yeDHcO+VM0XgC55sM+KeHpQ4a1NsCP\r\noGV0fvchvygPsWQRv48Wh8oByJ8dkRfcjFE7G5dYaiSh4ZXSLIYKmfGG1pfR\r\nWvCNkO8+tiB3zABHr153SVUNfRQUgM6urr4=\r\n=4Oy+\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.16_1649692605908_0.3978603009468258","host":"s3://npm-registry-packages"}},"2.0.0-dev.17":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.17","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.17","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a5e3a93fba324f2b4f8459d56b224633eed1d9f8","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.17.tgz","fileCount":109,"integrity":"sha512-UtCPfUGb5LLQGX6GQ3nxbGJ101Hc6w2HOglebS3xyRYLygmxS29E77JH3BzwjmI9h12VEvPnepI6Eqmq0YctVg==","signatures":[{"sig":"MEYCIQDekyzLOdF6nyDKR5c6HIkmSblehQztSwgI5dH59GcGyAIhAPO2kY1KDsZhOXWqUF0O75+R3XB+BXHRhSHwn3TXFNd1","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14795623,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiX+lIACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpP1hAAlOtDTQiuK50S2ksyiqrBeHrdNSkq0Ua3deECJCvfkxQV01lz\r\nodccDsQYg8+7ReaMmV/kqHu6t31ID4npKl7TWP1+XiskEzTe03cMYFxsyWep\r\nWF5pZYJ6+wxbCwVUSUSDK5BIqAxYBmxgn3YOZ/+qZgAkVBMBCwOl2v2NG6Ti\r\nqK3GKZ5iCukFc4Zc8sP9/BlXtjwt7mjlMQ9VYfZQ9ZCnufKOrbgxbAf2kS4D\r\nvwxIdGxKJxpRBFymjFMeUC8PGgB6Q4QaQ1XW0klLrNy5Y24OAz6Uym/H1YZJ\r\n80X42E5re70i5ZsCq59v5hnZDJQzqhoz5pAzhntFKH7vu33mpMeBDXNxk4z1\r\nDPzywFIvS/4ymuVTzml9UpBJbIww//R5dLE+p36F0purA8uyuMrqYVWiG66z\r\nsD3WZANbnGSSy6vQZYDsfY9kuU8thquuJAqt9JQGxSMChACno8AvyOIKZ6VN\r\n28JQ6orQfh1MV3ktn4MLXe7+MLNuPBObRXy6qj1ctlGfXUI4KPuEEbDsZiJb\r\niWoPYsHqhk23hNBeijxWLKEg4S6sP0B8JKNgRVLz3bG5LDwXRKkuU+RmzaXJ\r\n4iwIbfFrcwFMoLYt3NpjY3H9GYKJaYv8tw7VeAnDwV7MwrAkF0k7p58zD59V\r\nSV5iSoMstlaD31UXKaKR9poilzea/8HJaSM=\r\n=uZFJ\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.17_1650452807900_0.6632992130029833","host":"s3://npm-registry-packages"}},"2.0.0-dev.18":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.18","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.18","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ba14def30b7fa9bce84f9036071e724952b93241","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.18.tgz","fileCount":109,"integrity":"sha512-NoXlVdJg/wMgWaESmzXXpongAdU2eMFV2NnMIrE41YJjk6VGDp4j8v68ekyhdj4ZZjQrPpc9XN8ppsGn8nH13w==","signatures":[{"sig":"MEUCIB/KGvDVYQzjOZBGS7TEM2/jSOpaKknu8hepBQpQHQl3AiEA3R7NfZWpzW+2SOaYpH9XHYUuXsx+AB6JElZG1/gMoZ4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14795235,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYX8JACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqQ0g//R2MS9IX3cAUpxE2gFII1SEHDOfUScLCsQ8UHZSwBjHNYRahp\r\nXFUfms6/u0mSlHsUy/+Y/kEECF8FmVrSp/glHmZEGQTpVpaOT9bWmLnIVmTi\r\nwr7ukA1nYmzg2ldT/qXbZLc1gEwypQ8C71KdWuysEWv49WJXTmqosLgUBPRm\r\nvWtors6P70mYRV7XyNpCNKufy8/jPRMBoJN2YDcSJv6YE0G7T4GA9SEO/Smc\r\ndqqQE4DmllkTcbsJrxYZoXE/RErWdsBcpreiPu1ycCyDSuSyqxB0cby2gmf3\r\n3TP6G+weKcNt46ATV2uTsiJuvRVBga/zg3JRtBB3HoruopxKuYm0knIBj01x\r\nUGJgAartLQj5b6FGL+nmP1uchWAq2m2bJeVlBs9t1B6nvWhmU8c9kRzO3BlT\r\nZ894sbn3yogc45rF206s94vlF6e99w/9CHI7yyERPnn1MVF8Vid/Q/LvBDha\r\n1a5LJh9Tk9kX66PBVSHEdtjqRqcAa5nDzIDLimEGOvJdiZCne2yVJse+6WHD\r\nM2Ty5G8LpexHkgE8neiivZF5j++qvQ0UYkUfHegjzcri+wKwEorpRJ9/w4R8\r\n3/qg5PzYOW9WJxjvYVziRNcyuW5j7iWPV2Y94+dG9FaplQ3j7k7/9wIrBty1\r\n4yQXY3WLZU6msIPZGKTMzXr5NSWwwdW+0ko=\r\n=dyD+\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.6","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.18_1650556681404_0.18727690642295336","host":"s3://npm-registry-packages"}},"2.0.0-dev.19":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.19","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.19","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8be5b72a60633e0c201ca1a4cc0f9e90f12d83df","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.19.tgz","fileCount":109,"integrity":"sha512-YmqxQaUHAmO5wqGlSD/h7OOHPaV/nuOsH3qXabKqJSkONW04q9+K3b7XZmpaGK83gXlQHw68CvONvrrhgaGqQA==","signatures":[{"sig":"MEUCIA2ye1yfBV804PKjLm3GqdVxqWDY725DT/qkvaZcRiFSAiEA1txkaxT3+TsMsVGWEZQspfasAsE/7y/JXxBg1J66UgE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14861364,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYopvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrw9w/7BN1HjQ9s/JW6K9DniDAJ9J+TGYeXIjrjyRzx2VBtDkOdgv3E\r\nUroPfXvsRpc29tePGaJqoClxLXzqnkNuf/DW76PmsV/9qlCFqBNNe6mUQRWp\r\nffKFBA/vnpkfWlWkV4MGM0VYdlD+WbgDZPBfsOUzanDDztexaNJoBFGzG7z0\r\nwaXfqKLLCbNy2YlPk08g+hX5dUCXR+cKYhJ709zleDhr13581ANdSl1c9scI\r\nHrSSa5JHIfwwAvqkX2QfhcE68cJJqh0VMKxZg/CKBAVweGjL81AXJTye5Phe\r\nJ/XMCCmtW19OCpK74pRxSajfOUGOFgStj5iTcAWqWzv1QKJU18KPENgRi1BX\r\ncX2OoNPBfTgo0vZQel8racNs5ySYHt7g+iJ4COpTfkCOGeIyaLIbXfnUsj4d\r\n67yC59JX9RABv0w2tcLkjT2E8GfaQJ/P4r2hbyUqmrZtuOzKx58kNgIS3ppn\r\n2Z8a4oiA6mjydyBV0TiS61kN5L1Ehs89fWsIy6cr+7bC/2OCIo+dpTPDGhhv\r\nkj5xXfMX0LPWgvhHuXYtTRi1iraDsbxQU+W2UVMMO7s6R8HLF5gtAniZDcsm\r\noasdkdF4XDoK57A/3dAFcamMpgz8xI8t88h3CS1CPtrNIuPycLCr7r23qIbQ\r\nNeI57SKd9MTU4RhsXxqx3rWAeNz0zjev3/E=\r\n=lZ27\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.19_1650625135241_0.3788700383463004","host":"s3://npm-registry-packages"}},"2.0.0-dev.20":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.20","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.20","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ce7ed86c94da510559abd81f7d1d1b811cf7e046","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.20.tgz","fileCount":109,"integrity":"sha512-91QOHNyiFmSEdJsIaL+FhiGwgrzO13u4y8Upbc+9EpYDbd5xIvdTtDTaS6WSerZq7UiF1hXKcWycwDN4hzJNrg==","signatures":[{"sig":"MEYCIQDM/ccSFNSGMlDph8B7bzv6NH7f3MNq8SmsXn+gWlA0tAIhAILRIZSKqKOHv0YXb7N3gyhXkbeMb2B5iekEKe1R31R2","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14861597,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYqpmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpttA/+LkkrDqzB6dnXVTNH4NRAweHznFRgDbqaFl3ARFzeCQ+ODtJW\r\n7fnhiSCFdZj1fD1SML1KSpkQsEBkpAzZzXiPWQ0A3F4VipKNeEk6n3C98/us\r\nE6IKJdWONITEonB9U3hbZww1PLOyrSJy22isf+2RJOn6UWW/WRzftqEOvr89\r\nWpY/nArR0MgpD3/XseThjfbomMkbn4AhaZJNTGEE/BSLharHDhqbxln+iAOA\r\niCWuQcDPOBVRhaBxNCnXadjoVPwO3dEiHX6n/Yu9VsLHx1Q9SDmFBXv0Tp3a\r\nrSAyXx6Ij1/+ed7Gv7CaPkkmffhc2RiiUjBgRVskfYr1oKM8l/Ge7m+mElsB\r\nDjlWdNYIEIa4ZPSJvJqeKETGZC5hC/lDPgu3PLrSfmE7CGyCTEK8pLpdEC5U\r\nx6YPkuTEeA0sGzNhZXvlweedisGPLmBUkxaN/OZBDweS/kY5QluNazuNoxbo\r\nLe0JdqpeEL3+r3rg/7ja7uzL7MZzVlNdjIM3ar+s5Wn9OCHx7gmeDzTy/Jj9\r\nxldEcngzndbuwvSmSH+/17xTs69px0QyyrdiMfPHeNOC+C/ALKYiU6vHP6kt\r\n8aqDBhs37OC5LGvhLEFqR5R7SW0Tif+nT6Xh+7CXCvLLMXjDnttpe+IIxMgJ\r\nalNeBo7qtVON5kBlHJu40b/8TQNtTHZs1Gg=\r\n=3I6j\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.20_1650633317987_0.4769422401222043","host":"s3://npm-registry-packages"}},"2.0.0-dev.21":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.21","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.21","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a2e9d5176a54dd339f97d12fcd91fed525e5f02f","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.21.tgz","fileCount":109,"integrity":"sha512-iDqg9Krsye19HNDMAmNPxzzOjw9y/bYaoYy/vtiC5CKI361Yk5eSsokEYvduiHT6us3WH7XC8LtdeESeXqYmQA==","signatures":[{"sig":"MEYCIQCfsaJGO81qbIm6MiLDRxAxBaZ5vQH3PWgMweXFUrX9NgIhAPJfESaOnr7TrB1MKcPl7MUorYtyLv3Fb9gO0lbA1S0m","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14859335,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYrYcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpQmA//Q9ZiYO92+KS8CH+PXBPNKPw+9MxXxH9BhiclNoBT4H3RC1Wr\r\nUBC2lLs+bwj9SdCyIyjiXY1MnD/DPQUD6heNm1zVkhXQZ8it54h0Nm2eXOlf\r\n/0UePlFmnQ1TortGeQoYcm1hzFOhfrrKPZ0Kycn+TLWf8/kKpnCrZn0vUZ+X\r\n47mNfe/LbjntvmIqzvhtrRKgHE3ufLbS2cbWWxj1DhpU7QUybXtQBlF6JR/S\r\nD+voIdEOg+++O5IjqOeh2MkMOoQErlKmp8O1t+coBuKMKMFibxz0kiuU5F6e\r\nen3Nfe8CNbtA5i8SBZBEMfyHO7w7LgRjnsxU/SCfMCy9PdUIjv4WFtaSDtlV\r\n8NHUO8vQQsl+UdyuBct1BX7GaEsYJghaKt1ZbSAt951GCT6aekWJNiOMOSx+\r\niHYpTVtttfQg6av26Z2ZrGBCmNpvEXanm8lwEeuRMbXu1SC3ogqOfKvOcJaH\r\n4pdtiBRrt8hT8BDZ5S6iZsFHUjXGOID474BGecvCEoOR6qx8mq+PHj5qP9ia\r\nASb1g+Kjr4mRJjcsGGX51kSKjiO1Hk2TyoWslT46ufEfZC+6AE3AMcFcCsAx\r\n/pd5nXVnmP90G39pthMe/Sk7WY8DNjLqWfMJQ0XdjuUZIzyxHKe9l+M2ALkz\r\nx4sj0wBb/eOJUaYapX5oxOUpAWFcFw1OBXE=\r\n=MMaQ\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.21_1650636315876_0.9185253289193949","host":"s3://npm-registry-packages"}},"2.0.0-dev.22":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.22","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.22","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ae80c98641cb0c6422d4e5e75224da073896468c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.22.tgz","fileCount":109,"integrity":"sha512-1yUVDhyAjOKLWFPcr8EZ//v1vy413iu9x71B/l9kao3sVid+Rg5g4pYuRT6O7P0XTxof9F//lIjjb1SdPIHd7w==","signatures":[{"sig":"MEUCIBoDyYsJDNy88EKq2QoYaZ88A6N+8780JNZf0ohEBuv0AiEAhI2l7j+Lmmw7k/+ZSycjGyQZeM7Hio77JJzdpyQCE40=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14859335,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYvOaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpssQ/7BLswxbDj0EWmg1AMPoW2LfQVfbOMJptt8ddiMKxC0vYA9L+N\r\n204gtV/oxhNwza+0uoE7QI3nNZ6Om1tUy+nthvdswey99GO2diOOvEPbDiaj\r\nGZLET4+w4N/9xfU/oLylxpPQuL0+NUhQplr8qJYqepTHxK5tTKc4M7+9GuHn\r\n//Y5Kuth1fV/G2NvGIMXpXiXPKOf2L5KArSCr/iVgx/dC1f0qwLLMp2/Np8B\r\nG13gacljtQBYxvWQzqYI3VSf1qiP0uEgtuJ/DUu32Y5YrlbCY656vcRBkKtQ\r\nsRuI0yh4BjJG0TI3fsygjhFuN3f0Zh2oXjIDscyvaxh7cQMuWENWrRWBDh2R\r\nixc38w8lVoj4CMRTOVFzLxhEM+Nh+p+oUWZsZHVb4pXr/3nhanWSZYi2JTch\r\nPeDMeHDhHsiDSwPORXmWXqcFbr7cmB4yp/Ceb+yoyKVgy6i7zsnF0m0TkFFk\r\nPXlpTCJRqhAjdgE7YZWUk23l8UliZop9hKnZFrO25cuFsSp1ZzKH1mes0wMp\r\n7GlrHbhdzHViXn2zgccJzXPdxPybQwNwv1Hs3nXbK+XUgxl9AEuiJ29ERNq3\r\njttIR1cM33Nu45HWqonQ6lGgGmB1sT14cGifOZhWMjw4b9fyYrsWsTGrsCa3\r\natSGU4sVL0lwuQt3ZMxFGDaGgTlTZO1gTfs=\r\n=Jpan\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.9","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.22_1650652058342_0.6497876905503928","host":"s3://npm-registry-packages"}},"2.0.0-dev.23":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.23","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.23","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"97be79eca543a4bbbea7293a9780d73fffbb6714","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.23.tgz","fileCount":114,"integrity":"sha512-4q9YkBD95C8ZEfaapgq9IfxAFveU7iz/wlRe0rYfdDro/AUSYS5cE0K6MsdfaqAPEFoyPwEzZgfsaMF49aRFdw==","signatures":[{"sig":"MEUCIE8bQp+h9AgvVz7uUulskVRQFW0tmKlcPpC2xF5wE+V4AiEAsEa+v+qCoGqZ9Eop/qIyepWO8EHXIyuKLyx6McW6HvA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14963387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZEyAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqzPg//aLtaxiDcS7Utm8l6Zy35IX/amkjFw/QMV5aqlaPMIV0BvIrg\r\nArBnlNCzB8MJFMlZalx44ajQ2tf9AUo3mxpFnWKVe3DV2JjTq8upH68zLBRp\r\ny7+wkafReOGJEKrKVNE8nnLThL3aUlx6Cy4RP5uNd+FwiA2DhtWqZb83fPiX\r\n58hyvqXz3hrB2sBxB+oHuQuceWN8PMThJ01hEUL9vMuDwxeljM6sfO79lSaY\r\nYhVvOK9BqybO/pxKheNFEDMJn0A+g0Hm+iwFBKRgaph5ioUm6MpVFUtoKEB4\r\na+Dqq8DU8F/6QKL3HqOpbjSWH2994XNWB8+4Cm+yzu2NIFqVmG5gYmE3aLLX\r\nplXyNKJcGpEPh8GI+6/42cDED6w41uDcVdRBWOLtHm6avCUbVyvYwS+BESWw\r\nxolxYqHEe8bNKxPbRsCEbko7w947wUFfMHjD2LjRuKasZ8+JLd7do44rh/WD\r\noCzmNRJ0HtlSoiPI6i1AYcfxAgcRwiV1FNqJyWGIaSzOUxe81nv/fEycSohP\r\nqEJ2/1WQSWC4dArJxpZAVKTcxAQbyJ529pG5StjdeKl3y4Ac+0JRkx/ARkkx\r\nAt+0m45cIVQEzFhkvzK6O70t0rMljTpEitkNRL9W1FgHVs6Ji0mXKAfCY9Zr\r\n71j/Om0+d45kUx11/yBFj6kL1rfm1dyKJWQ=\r\n=UBwR\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.19","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.23_1650740352022_0.6219966356699902","host":"s3://npm-registry-packages"}},"2.0.0-dev.24":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.24","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.24","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"73b0d3870449ec4fc4800b3dda8437bd1a659af1","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.24.tgz","fileCount":114,"integrity":"sha512-jD7Rh2nyMVEb2zxQAlNW6P3x/74RgGKwNgrF/cYRqzLDNJzoAoYTlB/GmUudfQwJ5/FGHfwVibELrVi1FkHwsw==","signatures":[{"sig":"MEUCIHxQmdKc05l9rZSuuD77VOrwf41mj77vqs6zQBVewbp8AiEA3H3/hB7GSjB/o18hREh0N8hqLwIGa7sX1KXb5BdAgCs=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14963429,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZm88ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqOjQ/+Iej1Ps8Dc9iOMmRm7FQAmuGSc1iOIxz9qAeeUWQ+lvz8W95E\r\niQ3i+kOZDf+5xsV2cXpMl1rb5wjfo9672sFAVt+JE1OkLiXLKrp25OFFoMO0\r\nc3X2KGqfImMDi8P9LYr0IDwFUsjxn9GuEsnM09i0sKXu6kvoh9ubczpJmNd9\r\nBxLWSIRwYpW6LNBLwVoIWw9d64B2a2QiIselTbDLGgmbTmN0jAQ7tiOBzTBz\r\ne3TrA3LoCjzPwEXm0okE2XO6ronwB42mlg8AHYEcz719TlehtAsqgvB3QE5Y\r\nL1OQU2+rI6xFaIy/HGmJ2bFIMOnzC261AyA+LVeYGd8xXOQxEjZlj1Q2n4oM\r\nXvwA//z9/8F6P2ELOBgOH3s6mRHBdOWMqMVZtWRoq7ZdJyNzXPd/1/GcWCIG\r\n2aUKKM79SXuUR/uOScQF7O1JtGhr83tCUI+SwiSj3jrZRwn58w5lKa898x1J\r\nGNMKe7UQjO60ylA/hJnMw3i0AQ35ZebrFFFAauj/zYcw/D4XWsH5/a9st+dz\r\nvyF6SdY37W0w0sgfuVypsX+8hG280g/hYiXJwl7ILxnLoqBrj652Bnuz5bRW\r\n00bJZdzpQfC/lsaAfMnX798cxNQ6WMrZ8HuHsi3ZIrJXXglTGuMIZ+oCrq9I\r\nCkwpSN6hBuUvNiYJiB8M9RZQdZnJhdQk2Q0=\r\n=jMcS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.19","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.24_1650880316561_0.336274818952907","host":"s3://npm-registry-packages"}},"2.0.0-dev.25":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.25","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.25","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c9e000f380ab8ebfce42a71b59fa007a8c0faadc","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.25.tgz","fileCount":114,"integrity":"sha512-BO2P+b97HV6DgGLx9mSOdzcE8ZPZGM+EMTOjrEtsPFUQmguQgcbExZQzNeBDIAv6JHZ2MEJdtISTTBN8N3kAgQ==","signatures":[{"sig":"MEYCIQDC5J4zzxqLdY/c694BVkOh1xfnMx4E/vHO/OtFjXXb9QIhAJ99srqZO95AK5qCa2TnBDw6c+gzk0CbGDMhr3NFrn3F","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14962449,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZnVQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpeohAAnP8TrHBGF52fYSWfa6v8R5hOtCbzz7TRvycdvRMNn/FyAtPX\r\nXA8Jbr+np5Ib6JCv9G2QdjJbY9F1YulhhCyHGQArRa5ni9oaSGfyN9GWtRW8\r\nWMDsvhnQQF785dZ9qgpjdmEeu0IURn9jfrbS/zrcsfDMRjldODIgTbmS0FwX\r\nvZTAUlZQcSgWQGV+TapeTQTBe2HSwEfzOPHZIFYkkGS56emUsPF+oOnEXkE8\r\n4Ht6ClaFjZP5p4hRPrHiPZ78K1gepZ2zDpUBA6iSpSIO8tZcH0pHsUmcJMAB\r\n/f0G/pQhg5IaLRpY8PI7BQasptDuD3YuhzwxRNQQWrowW4Ddl7vN+9mCIcZo\r\nr1sNcgcOIkLCdvRE6TN+Bq7IkI/jJvlWLjDqyX7HO39HHexdrkt8X8+ZF18q\r\nABy7sUM/yEeYhO0yeDg+ckj0t7hgJ2fdK/wzVqUOFfMwZ0qHbRcq2scxFIkG\r\nROChEomvQ9najNM7Ao0Ptx9zjSAq6Q4djrPjQXn8IDjjGfyDI1vfeB6nMLZE\r\nduyQSbDqoX4UvRNQX2GnsCNdlma+fgPrPnsooUL2h3dHw+PYCgKv4bMqj4+C\r\nLjBsr8FTtgeYMuxm7+qJXZ/P/AByxJWZbbscsF2ixkjLgxGGR9wi9hfKTA1R\r\nEZmPNvBBO9HrF6HQzwFTQRD1k7/BUhbesp8=\r\n=GoxP\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.19","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.25_1650881872676_0.4566614434351881","host":"s3://npm-registry-packages"}},"2.0.0-dev.26":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.26","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.26","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d29d2fca3af259218ad7f5f3545bcca263d64d44","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.26.tgz","fileCount":110,"integrity":"sha512-KZaQg9F7PfStvED2BhqZv51uU3AAc3jAXj+dt+MLDza+Q90u/sCQCclmIv0cLZQGToFZexplKB7Cpb+fYeaIpw==","signatures":[{"sig":"MEUCIF1CMiTTMuQKU4VoORvUvPMzVB1LKZi08KPxQm0PvwYbAiEAxwpPz97e5XI0BeXfKdvqKSCWnu87e0bRxaOgnevKRPI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14891811,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZn0uACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo1Dw//bV9XogAFqwSgnKam3af040ceeR136azCA5AQdZaI7HnECdyZ\r\nCspk7HW66OqaYeMQXBAmCIf9h0NZsSV4EHrAQgu54MFkvbo67YQ/WbfAWDtD\r\nteVL3O+c3oWye2jhVg0t25hvMsWESY71ay/R6bIgQHkmKqRG6kKxguc2lkN1\r\nrQ99YI8a5g0HKbR6dnT3CBIjXskc4vNbKTDcXC2/kTa8eqRxryfgdMiBzuoP\r\n/oDyNDCTIlee13xUdCfU4/gFbIpoOQgB6sgxgJirGFY51cfjWkg3ka8dEFUb\r\n8uWCcpteNWJok/hRQA1mnyPYmkHjSX10Ha/ExUzPtYgGJCi/smvXXwVniMOW\r\nVto0zUrd/IpKOUdOdQZcFx/NnnHt8RnDtz2lhWpIMxyF1lYkRbBjfTfjQFtb\r\nFQ2G7j2e7LNzjjFXUx/HL5viIe+RB1Dh8DzWJEKF+PD5XvKzvsP+NNHmSxIp\r\nJLIlIKHbdp1H8x2q70dNCcNb/r9aUJSSsLNf9GtoiCl0fDft2LuIseZ331uK\r\nD6ZEF4cV8HNRHR2+bIqakHiZTWgkQjFm1CMgxWyjIF9OaIQ24cv6rfqPNoOG\r\nxHb23BeaXpC6Uw5ko9oZkCCGQzInpnf9p7lb/3pkGpnegOXJmGrKcu7oZieO\r\nqFU6CDyDQsLHiTGEmby0MaIakRnrrmFvCp4=\r\n=kpTi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.20","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.26_1650883886774_0.8515728566965779","host":"s3://npm-registry-packages"}},"2.0.0-dev.27":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.27","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.27","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6d078c79722fac9f81075486f548516e5ae3c53a","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.27.tgz","fileCount":110,"integrity":"sha512-gVeX8EMvMi7rlNB73wznBomc2seKOH7aK8uvPMZfLYowRvQYupokfBtdbE5Gn/Dm5k/7GuaNT4wYIeMeoRcwUg==","signatures":[{"sig":"MEUCIHcl4cdVNeDeEGHPxSpf1xUMUHuU6lzVVezoGiQzoua2AiEAiLqMNFirSDR/jktzAaECJ1Q9T8FHL3qKBy5pylGZi08=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14909315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZvQgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoJkhAAjY3qlNPIMJ+H3JPVddD6vsvIK+uRJqOXaadG1YxohDD/41Hu\r\n+UFC/2lS4/rnhJvttL1IZ4hMHCWRsqFL6+NJ2LwsUSknNPdz5XA7TGLuoXoO\r\ntt4Uz8Dsi32drt7EuDYFS75WHwgu3bOnw5/XdIEMQF59XLkZfygThXPN2Iyn\r\npjyld+ZfSsSkFXW4dWtLq7K5BtlK+gRrsMt7srumIemVPoQiCTbXyUiSE3iX\r\ntO3zyqLmy+issTNcdlU/9qJw088G/BpbIZgH6+8NCu9G/YkMVJg2/wH68Fy0\r\noCPFk2tFWEvBxky9zdoDxSSyT66U6cvc93EWNIpJa/NxP+CRM11K2q46QpsP\r\nd5s+6qVIHN1hxPubeIJQ7oWGb3WIIQUP9mLu7hJbp38l51BuRT+K2DrVchYw\r\n6/G5RwNOLFh9m0/o03QYLxtfCLj/25HWvT7zHLwhB0xNI8W/4QLLn3zrjGWL\r\nb+dtllUv1LMxyxy3lwH5kIbZ+QvwBCKf6qd4SkmDMZSIow/7s1URWg+HAoUt\r\nr3vjROyAuK07hl17zyjNtBSf8KU+3/SzizmVg0UkMnQCO1/12pvn5P1MV7IY\r\nrOYs4uF9+lNO8OEZGeolFmCiIUcgIj6og8E3nz2wVQQNRePrixz8uaB0DlJG\r\nvuAaNKmctbxoqStM/ciIMyKr/pc66/IJJH4=\r\n=QKzH\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.21","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.27_1650914336290_0.08000850657300052","host":"s3://npm-registry-packages"}},"2.0.0-dev.28":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.28","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.28","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f2251fa9c62a237b859c41bed3427361ffa7beff","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.28.tgz","fileCount":110,"integrity":"sha512-++PcI7D6MJWuhb05eDR0AwTnfiy8d+htUBVUSLcvwJddbQeeR6aX6S75o8q8G1rrwC2iw/eSOXePKNcjE2ZoBA==","signatures":[{"sig":"MEUCIQCU+Bos+w+9U8GHXGuTpvrHnwBu+hEjt3ECIRI7FFz0TwIgBibFExOgpvVnX4h+24oa3KYjwS5w9p86LqlF6WIQ6h0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14916261,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZvZqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq0yA/+KP6fnKPLbVXVK120XUI17rVq5ew2Tjcetb+mIaI47AB2dUt7\r\ngTUnOCb/JkmDvHWignrd1nDqPn3IXZwl1ueGT4xulodwpWsClCjAx/s4kxaJ\r\nDW4vBaYc6GZSxMHKjJ3w8D0P2m4ewff9cNJNDE1Ec9qAsWF+3O/7LCMpOwxf\r\nlvAbI5YBdYDCGtFOj9EN5Vtu3t+AIOCiUkN0uXIEEzkfY/sW0WVdp4Wsh4C7\r\nF3ruQwPpVGfCO9/3WpmPA4HpA/kw6+GzIvGpkBPDI24Cvm3PZitSYvndOVFT\r\nqAmo9/o2leCwskKj73OTCok8/kdJJVhtF6dyJLO/hsWC2BPORDsTUCc3ggJa\r\n1RAehSOM8iuZf5/Jd1ws+Dhic/Zoh6GCRaSFmHLb2WYfiJ2zAu9LvmsUGx1j\r\n5oImIq9LmGhoqn+eELYyxQKGRyaYPRjp9gjVUJ9b9NdgwkErTSEUG0LPjLD7\r\nLn4ohb8gAKwe/a1NjTDLykwzfZt3CpLEB5E5+bhkRXNmBrD6a1Nb7OGZscH4\r\nsD6F2xRB06gnC+Spsso9mjai+X9Q0hiU4IWBZY8oGT5NwbIiNUbBStemOszE\r\nYr6AClOYrueaHNZ35lPgZPrpd15CvccttYi9VmyJr3eGPfZTlYscW/oF4duI\r\nvZsI6eQMpPONd6eJ2F3ixGTW6xMNZ+po/8Y=\r\n=JJ2W\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.21","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.28_1650914922329_0.6716204047710814","host":"s3://npm-registry-packages"}},"2.0.0-dev.29":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.29","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.29","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0a627707f0f1ac892604882bb42c60c6538c1151","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.29.tgz","fileCount":110,"integrity":"sha512-U8IkmE4u1mYADFFWzvjSFVIKsfxJUCNIDfFUwlXHqHGnhMUsjug21AiPtkFLAWZNbd9dWioEjGgfTAkA5JAi1g==","signatures":[{"sig":"MEUCIHRIe6frZ04zS0dV4waQkLcVC7np3pvBHXtMeySSpHliAiEAxsQ/XY2EEYf+W+IVuDjIKmotA40CUczKQwx6y/ZC+ZI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":14934639,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZ8frACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrWoQ//W6ym25O4RQURyqpkH2YdjCgfh1s0rtn0pU2UOLwKHdU9NBY6\r\nETdiRzSPUa22g7Ig8vlSfrJLSvP4zcVAYBUAyZ6xfMYxkwyy8woSXn4lCNtz\r\nUpxABqjQETDlbMqANNO5du4XFkfTC9D+TMsNzKBbzUFVteABDf7nFb7vd+B0\r\n5UZRf6nt988ASdWQDlgULOWxhEeD8ehlY0dCpdETZQgOsWuT0PtWrrXGTJ81\r\nDUiblX8H6UNvYiWsajWcIo9J7aZyinxyD9slPhUXwYK7tzYbgYMul+f9zCQy\r\n8AWJxfTbD9zm3klIBI7ZF8kKR1gaWiNuaYAqn+mzfZBIHZDhJeoD7+I2ROKD\r\nJovipe7cTEefvHcnOKF6f+cTXw38W5UjVOcHBYUFPDfXvk2zegalPwSxdZwd\r\noXdQeNh8S/f6hAJNeb0jXrsfBhmPfQ/+F2QOPLSyHJka5R/zwIaj73svvGRk\r\nIn8JyN/uEC4kh5E+N+H7GXmn6+dPAnFoLKIIVsb5yaALmTgjwrI9krAoGpJm\r\n7HRGywAcyomxMCfpVAEA3RdEhSYPHg0nu2sUHhDt1W06Bbwc/iwgQnyegStC\r\nFlLaeik8hF2bbm52tL0hjeL9ONC1Hr7iS/8tP7yOzu8+A+SgSgxMz11+K0uH\r\nPI1XI6IJqd9dIixLwZeQmw0p1lf9S953JWk=\r\n=iA3K\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.23","@keep-network/sortition-pools":"^2.0.0-pre.7","@threshold-network/solidity-contracts":">1.1.0-dev <1.1.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.29_1650968554975_0.5787609896199648","host":"s3://npm-registry-packages"}},"2.0.0-dev.30":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.30","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.30","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a13e58069b84ed6d6ee4185c2dff636745715526","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.30.tgz","fileCount":110,"integrity":"sha512-F0U93ROuk2u3NJJR9G3gwmzL3QiBJcnIkrhyvWv2yYw6JFz6cGLalo3ONP7tEO34dtAqN8i+G6IeAKC+cURMbA==","signatures":[{"sig":"MEQCIEmJvZa6COtdrA5kly6ORQv12hTOZt6VbU5h58QtZZKYAiA4+0abjvUVsItdvhvR1OCtBSGo/DI955JoVPUhmE68Pg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15141694,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiaPTqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoUhw//fIbISSbtNClhyBF1o4pafqrZmY2/GcTUTqeT+MjAsAkqaGX9\r\n9pkBM096ONYOJq+gYQ++6aV/+SayDjKqLrUw4Ws3GPnN+QcZN7Fj1rFIDC/z\r\nvx2qOd0tUhdewK+tFvRwuV+PHCwKADA1qraN1U4FmKxeJhcNRfoJuOMroqP0\r\nxTlMRGhr5e43GJmWKBcHGFv8wP2u9O+I8gjwQwJ8La+/e3duWfRr95M/oWo/\r\nOzmbpQNvu6uYFG9aHH3qdEyZDQDxzwyjJj4cMjKSyIMIVaLuVxjoNNAUbLQW\r\nGWpP8ToPTXcEJMp9DuBDeOfQQ65IMCmHe42b5Q0si1hDCnzOjEVufX9srPJH\r\nNz/1IkJmcaFAmnxn/xWZPNBdQZQhWS3abbiPdkqCOEBjqhVDvym14Ee1Gxth\r\nNrpiQPq56N/P5nDsm9RMXQG9OAIW3pbPADEJxkAQfz1Wx6qGc93EVyQYTH0y\r\nojHKyK8J7U+cAb3HvW629c6XhAvkRXPvSfBnisNOAUwUBmb22Icw7iQYaYrW\r\nCLtiIlo1DtYzen54LWceKC9dQ15D07QutYSGLOTlvocZfLB4aSInr10i+R7m\r\nHc7n9R9KbOopZzNNkC0hXcVurqlqZVNTIFg0JENLzXqSE3kNKu2fhxwVThKW\r\nFUsz8Gsz7BVX/0ah2plHqUoIL6ewfAse86k=\r\n=FWlj\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.25","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.30_1651045610107_0.9658485596327508","host":"s3://npm-registry-packages"}},"2.0.0-dev.31":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.31","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.31","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c63f75c3eac75606b6f3ec2170b5fa9c806c5dae","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.31.tgz","fileCount":110,"integrity":"sha512-XlgbCRH+fVHhxnNqOdUjHc7WPabWuGvR1Va0uoQAwtDSFNwZcyVGl2fEakNoArri0X4iFiZQe6PMjXCewAq20Q==","signatures":[{"sig":"MEUCIFKtgMRjvj13HQxc165JteYeFS2LblwfsxMzxpGaGfgaAiEA4B9gsIN57TQ5QSufItACNqUHI64MyHfInMcgQqG2zP8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15141694,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiaSeYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrfGBAAgHiDdisfou+e411cIL/svykbHhT4P6duQLr/+Bp89MUItSWM\r\nAHgFyILhNhLBYWBTNS8JUOv1VkiAeOOd/UX2BHO2Lt5W52D5oC8W4bkaX+qm\r\nhuAi7ct7Jd3t3PhISfyixOhgf6/xIasajhwYiPrkZEuSL4WD6w4FWFmK5gUR\r\nAvEpq3jPsssY8qjfaGj9Du721T2dHfbsr4HeLoKLgRB964evWVRFERz4tA5E\r\nCjjONH+PF8GOHB7VmosoaArSiUVtCEwCDpnYQOgR1WmppdvlUIjQ8UaJqxLt\r\nsuI+gYZEdC6g0tQSw+5jmzZ2QmmN5gDOCyFgylIfK7Pdepua22vtpagIUHM+\r\ndY+/OorqdrpGKKDk6YzDBB/XUwr2JeSbuh4TgJV7YC7kurOxubrj9MJ3jKky\r\nXkd1h+3+NsuPOElbL2lsxo7oKRYwYbJDyPE5syHCw5UgjOglogEWclFkorwV\r\ntlgFd8z3H+hXvEkPHQh5Or38lajd1r8+HSsYNz1U4BMQcwiKtybqmbwiQIUi\r\nImdk4cj0TScfbd1XnBaPjNZ+HYR7KziCYXC5lpyYj3PxnHX2wYu+LEnQkPlX\r\nnBY5FmMV2AZipRA33vHJFTWYLd7B0wHO3jpU9sf3yop53H60dZ3vfVBQhr2M\r\njZNPtOptd89/gFNJUc/HYmKIfHDMgZRttb4=\r\n=x9KG\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.25","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.31_1651058583751_0.802920979712932","host":"s3://npm-registry-packages"}},"2.0.0-dev.32":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.32","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.32","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"b7e435b2665e477e8db12f11a5ba50db96adc5d9","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.32.tgz","fileCount":111,"integrity":"sha512-0szqeA1TkgEW9NJHAi590lGEOIfArZ10xsbHwnIHstOJin12GPPTCYD0Um/zptEquibetZH1t5JPC7v5xL5ZLw==","signatures":[{"sig":"MEUCID2XomDK2wPU+dDsNpSLXJBkhou6XxvIa7wh2bXIQGfJAiEAkS6sOfFZ558UWDeMGgqy6BGldIKEKC6x/tC1xNcUDOM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15300530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia9ZCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqP4w//bVqP+K5u1ojgi0Xnv+HIymII/531KF74k9jqFtCIGJiEa8BP\r\nbkUcHoIdlVkHyhVBTijrFhqKZ0oPxlOVj15MCfcMu1cCW1hhGCP9w4mb9aTz\r\nLs8I9OEan93sxcSy4NiI4iio59ANn0hLQBnEMMclGaZNzUEpKX+s/excynyO\r\nHeAmMU1Ctq6mkEeB/vT9xot6kw9L4drGr02XuU/a4w800cT/YFt9r7fPZe7a\r\nxVhzzH8zX5Qvt4magr/zxunAFBM1JHkbXTihyD7mjKUf15pBjA7pjwXPcq2Z\r\nQ9tWOoJaN6lMwht48bnsYoecyuwHk7llftY4DQtUcSr2QyHagDgO5OKXcfFH\r\nkk8SrLmKJ7JI3dxvZC1scfhbjgeIgtOyNwDvhiFCY7T2ajNMnXZSFM/mnnCB\r\n1z/CjbMET/3/LWO5RSOwjVHv5BizcMJBmhUrm6T0F4ceAGZcW5flYXzDKnSG\r\nJ0KkQ+Hxv8QMtOl7e2lE0Uksmd/gKjA2ZDvnb7n2mxAd2LLLNoCJRAN4TWcZ\r\n4tPEF9sSUhFaLJ4l8iUULfrjHFSMYHBIPkibQ+eKPGkcR1Iap28KMmnOZLDZ\r\na3QrWVtC/XRfrS/Ws2oELYAEimnnzQIhplmgj+VeT3VjfCa0g3LT2racLcGn\r\nebAUJCP1y2tizpswjJlsNwYad6M/Cxd3n1I=\r\n=rEaA\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA Wallets\n\n// TODO: Add intro\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n// TODO: Describe protocol\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\nWallet Owner in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.28","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.32_1651234370068_0.4099977238003334","host":"s3://npm-registry-packages"}},"2.0.0-dev.33":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.33","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.33","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e01e1100a79a4f1b2a660d5d69a23106de0ae845","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.33.tgz","fileCount":111,"integrity":"sha512-dAlNRFyhW8r3zpGu9sNamNJZ7UFYrruxQThUtD7eucKoZVPed3K+Z49gx4PRlEt6ZVfKRNLf3I6mO4XVc56o9g==","signatures":[{"sig":"MEQCIHlcd1PAXlZLIzsO7RTiOKu1Tdg311yHCzn9loIW0vcKAiA3SgHBuZ0qdYVDJ7Gc88lD+CdsgCSbM/+jKtlDpiKTtw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15323964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia/5IACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo+2RAAh2Rx3Sij4D7+FvCPGHzjVhO8gGSiVpF+hez/RDOkFYM6n308\r\nM7n1NTYdrO9LRgKX3qKPDW4AhANkuiTZMgHGzLInEZhJum1k6c8qZSUDoq8m\r\n2bTFMpJR/WrRG2jelyULlbOWRMUr8eoDKtdxaR1GDhKKD64I3iNDb+ouIg8C\r\nZSvSzNUUw3Q+g2/BHn5dsjLGVPSBQ8Muk2CZzzwKlEPqDOg/bOBlTuFULGRK\r\nQnvEXejBvI2VLiEbi7YPcFdJyf24msxIOYXTA+ed5HFrv5IOu950t1I8KQgD\r\nX1stWn3l1CtU35pK8HzxjLYLSNcHHtBlFzqql8gaeC5XFr+oAptIhxRMMf40\r\nCsKPD/GQlKCikQ3QDmVmtar9rq0wtgvlPcH6GHu0QTNlQvhkgKeeZqIEJqRB\r\nRy5ijMAZxVYad3kJc6FPVfLvdS1PzQo03D1gF/nexNeHM2EPfIi8Uxk9U4h+\r\ny/qQYCrS39RI4jtmuXrmnM5tLRRz6C4uzSIlLxTFPIC0ebLmCcs1BQmszFK4\r\n5MF75N3yWJHb2MYm9Hmg+C1FjfyICystwrbBZpmKkniX0Pzq1+liVaX5M4he\r\nzAGCEP08FpQyfItnBgvCFcyqBiCCJar/PerxI5idmG3I2SZpm2woxZAzh3tS\r\neKOhpm/7Smv3fAoSKcJHpm5KImNO/13eZCI=\r\n=XktI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`275_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`65_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`85_000`\n\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000 * 1e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.29","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.33_1651244615865_0.4288996450580147","host":"s3://npm-registry-packages"}},"2.0.0-dev.34":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.34","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.34","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"530a8ceba5a8422b201131de8ca440f0a99b4c90","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.34.tgz","fileCount":111,"integrity":"sha512-Yf0zU0r6efqoBW6fS7f2KtJZiDjBNOPJQosRfbeyn9S69dckM9f4cubqKz5RnmD0Htr4sT5bXZW/9CQJgsB2+Q==","signatures":[{"sig":"MEQCIAp7w3fqJvR+Dfa3QrBA9kzPpuif42vT4xOLuLTwQczJAiA55hn5gupTbDMcEPz4TmfcdziyIiThzvn0Du58Qlg9Ew==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15324231,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibA76ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr82A/+MClJVcPZWvUhexRxdsqlRfDxDJTghAw6eq40eJuD1N5xBuka\r\nUooiCmnU6XnRHYY6osbl/cg4uWZxzn0A1n4yqM4+RhZOsP8Dduqx0mvjwoCv\r\npNNZsiDXUT7IyBd8VFm8Em7+dFREQtxignsZ+xHoY97U90scMVY2LuZvCS5X\r\nnR/MzcpW0XZkG85aWyewEql38gXkaZ5CszP7fEfTljGnKitQSenXMFnfeL6r\r\nXvhFY48GpbCZkxaVWZEEPIcF5a3KxRHfSMzCIDtQz6fn2fhpcbXQ2lmyh02o\r\nlUhYySR3BejfxKQmNYowE3+maG8/OK7C4ufWvKjxBucnLqV1tm+AldWEkepC\r\njjV539zUU1Jdrp8SQl6y5SSCas2FoMPPEICpHlH0uLjjk5TUYU2re7uQaSna\r\nF0b0HN+a3/HYI/sCu7I97FCjmnhXwkzJLWyTpZgxDj0qMvtcZy/02P0DgnAq\r\n0Q8N3M5rIQ2qNaHliVaajVTQ+VamWUdDZSUhqSWW4HbV3KC3iyD5oVZ54jWZ\r\nV9jhKhXu0zO56B+wQqxzAvqmDNDja6xmIDmEVOg9mw+jThisiWsclMFAnr/D\r\nOfxtweo+UkWpwNja9AOuZhoK2XeMo2J/rHg4mXZVTMSE1X+hw+dvdzkwAM/f\r\n615fH695z64mJJ6JryGz0GvG24F3CQhPYxk=\r\n=g8rt\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`275_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`65_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`85_000`\n\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"^4.4.1","@keep-network/random-beacon":"^2.0.0-dev.29","@keep-network/sortition-pools":"^2.0.0-pre.9","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.4.1-pre.2","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.34_1651248890640_0.3954783067069483","host":"s3://npm-registry-packages"}},"2.0.0-dev.35":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.35","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.35","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e8fc1ffa7e5b7a9d40abaae565ad2657b026914c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.35.tgz","fileCount":294,"integrity":"sha512-aUZU5hjWnjbWErM6fOz9crVA1JHZ5uM+4HF/Matu8KVJFHc5kxqlnrdp9P/DcxY2f/7YPnCEJSPKNufBSqd9CQ==","signatures":[{"sig":"MEUCICgFL8fAbarQqLLtsI2s+QTMKjNXZIwCX/MCbfn763QEAiEA47kNnxCbYR0Z8CRh+mMBE5jLpw7xfQF9ycqMNnFW31g=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":33098806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibvlHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo5aA/+I4qXfFQvUsQkDPCSI6U4KESeLS+/iBVlP0G6t8dU4vHsWT7t\r\n0WgJFFGIQUB4uVtC20zeZg44pOCxX2ELBaun94K2df+1Jlf/AJZ6Wu+RpTn/\r\nW1Hft7fXkM+mDTq1TI9/ylit2ELQVbJQUOR/l9WM1Y2CY+niEF7FOfcn1dhC\r\npo82+MLlMHOqvbAWRggLZ8iDujOP3a4cOA68ICqShapYimoTpmZ9kL4gHyzW\r\nkANrKhm8nv9jnv7xgMtP+H/V18yF7d8QFZemj/60tyb3fVChe61iB1Wp1ThS\r\nbayYLi55c8/Z7yIr9X2xjz57zj0zBBmMF/vVMHp5xU4htwJckAFlPr+1UrjE\r\n5Ue40erVlqJ1agDJizlT/mNIzbSTEF3iiuAPLOHzDuAY71z5wNM7mBOwqD3H\r\nOTkcTTLPGGQ9ly0iudPruys8/5GyBJZ70BqAEAzN3Lkk3qG4/3q8OXZek4Dg\r\n7za7Zl/1NoA+8KXCXen46bJX5sffsYdFWcuoTfxICLigeB0gCXRuaA9fChwR\r\nIB3XZB5cWC4l3oJYDjmdfDBc/yWlJsk+0UaOeybTHhen61K4Y0rIiqmHxbY7\r\n+uOTi1jjiKQFMB6k2My+Ra+PfzBfoOrzWP3JmnEpPAaiRoz7PJGpOP4bh6vy\r\nSNe9hhpIYDEKTmibkVxcUKJF+y9hOYLYiUQ=\r\n=qWu0\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.30","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.5.0","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.35_1651439943670_0.3046980791196692","host":"s3://npm-registry-packages"}},"2.0.0-dev.36":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.36","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.36","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c1b42692423518c6c75211206bdcd8e582f7f299","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.36.tgz","fileCount":295,"integrity":"sha512-+fuaToGoV9B5bUQ5TkBYTSndqEcI5zNCe4zqYFagQTt9bGUGqb5j9sCqe+E8Z/mHB2OLlVyq9zlW8F6n/iYOgw==","signatures":[{"sig":"MEUCIBwa6PDgIFKaRs9xMomb/yVGvP7XhMgKwnKCEb+u4SVLAiEA69cHR5rmXTlx0ceOxULTeR3U+KR+q1VBfBUH65usHSU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":33105459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJidPyeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrRuQ//T5bt8CFldsjroAgL77LtiVS6XBnXJ5dmBmHHI1gjbDOy4jVJ\r\njhUP/jFIdS80TkcYuUVCXgG2PSy8u6Ic7iRI/VJ6G/6lIJ8bXlt1Fh7vixhd\r\nXIbtDB3NxtbUs5MLaaP72Ih0AQZV5SGpExUM0zOCU+YVd66Q37ySrYme+bDs\r\nhmW4UTjh3ka8ABi1lP/aW2abeYB33eF6JYrRfsyHqgh8caSD6OmSR46zaBm4\r\nGg+vA3dXCidbk2cvBkyRJMaC6uWAlNxh1Vqyhki+CSYiWVfXHizvhPZeip8+\r\nw5cwoqtXmM1pKCV8SfdNvpaPvZ9PeOMcQhH/oLJLyB60xvU5nIHoyampQ8Kt\r\nb1KNK1gLv1hVHONuXVOAeAoU6bbf043oknjFMwrliZuWh3Q/rD52F9e+GMG1\r\nbASR/5CPcvhhSTI3VXUxKTA5pcOxwV2TlYbzCkdeolx5lD+6NHp2xW9hPkZV\r\nywEn08DewA4JbBa5DEkis3WdYQ6gAnBl8QCS3o1QeWpG2HyrpSK9R431cARs\r\n0O7aUfSuRTHoRaLSksSFVaqAvAvsszD3UCtqqTgff7RLRs1IbFWg3vRZ9jU6\r\n5IHu6A30ASE3B90IuqzWrfTbhRZtSoYQR9/tchC8cQHfULr2fMtqtoBdRCjt\r\nb3m3wIQkf6hi9lJdWjHhwzFxSoTRzc33cS4=\r\n=Ws5J\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.32","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.6","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"0.5.0","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.36_1651834013992_0.579337834020377","host":"s3://npm-registry-packages"}},"2.0.0-dev.37":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.37","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.37","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1abb1326d771b903ab733bdcf05c27dd7f20a8ea","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.37.tgz","fileCount":151,"integrity":"sha512-DvBbYAZA3PP/343rc18HCdHXypeumGYYpeW+kyHrl2H+P1v/R4pbSSirW2pepmt9pGSQeF/0XSA/WvLBbn98qg==","signatures":[{"sig":"MEYCIQDZWDYT6ZlTYx2Vr4dGlQNKdwFcornnKcHMFfT1HZ2miQIhANZ0B9L3NJaBPGUUotmOjX1XjCwhm19exOFv6L54q6Pq","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31582318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJienZLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpYkQ/6A83X4GcpRlAHZjudwMR3zoz82Gf7VrlVXYQErHWEhIufAsro\r\nYwtsvZCnl4I08HTlH80ewdyOdokxSJoHD7doy9/i9mFJHDqGGU+5LIXjdi+3\r\ncfJxm+8Ow2odLqsblMHG9m/Sntn3BEehhiJpavql2pgntHs8qzsFU6RFmzLm\r\nvvbe2dYDFr5nCdD2JSa2zJhYc69Jb2CnvGt53UVCnzJoNfuZzDZmpaDscGNj\r\npwpYW4C77UPOdojJou+RoytpfhDMQkLXWrtJpfLduRNfMm0smjh5uFBTQVpX\r\nL7BIpZOGVVuWnofxRPJBsHB1v9h7OJeGTRSqeSRABN8TI22ECQ8UH26Lo5Ah\r\ntwWqkrPP8qMVp5iwa6o64m/osPcSvq9WO2t6HDtQd8n0YL1OJogo6prcptQo\r\n5BuImXZQQeYG8rdhdJ2igZ6y5+HRG7C6xtl1dbqkIod9jyIpaKg7TMaMNkdv\r\nF0OZ7OSMdGLpAqqIIVlsy+FdFsXTl+4XI/fv7SaGCxVePyB6OaClekUBNs4m\r\nWny183yyaJZ5ICLXS1VueVLVi0zAX1ILeujkOR91djkwCHbCzinyAC9bWyRu\r\nnXhAUWgpq4umb5WrTA2n837FNY+We+0nzDgqK4vLjV5x8LdVKXyI+amXjjby\r\n1pzHYejhrfKbEK7bIBPUABWw0bOT43XFlsI=\r\n=YDPA\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.32","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.37_1652192842994_0.0485529953308097","host":"s3://npm-registry-packages"}},"2.0.0-dev.38":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.38","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.38","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"983ff50a2417163dd63db1486d29aeef09e201af","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.38.tgz","fileCount":151,"integrity":"sha512-PY+3IKU3aGJ/PYMyEZDPPc5FSxhJiEeHhjFjHFzVq9hGgT5i2R7vzzx0po2BRIqGcOJFaVjCMlhi/RcrwBtlZA==","signatures":[{"sig":"MEYCIQC5A3K/0dbAPJH2HS5RMnZ+07Wrn86ye7bPNGdIwY+GoQIhAOV9inxRJPiKIigR/wu5YKhYCzQkBQGGj75mCYxjlI6K","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31586547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJieoKzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpl+g//d5c1Cfr3OpIYKD8z4YXG8Wm6ybsI49NMTuhCZkA3TnkCW3qU\r\n9AkxzXOAcrA20gpfTgBdpe+F/2XmNmWJTrAqjYCboPLtIFnFEYgifn0AnoNr\r\nd0YKGaxo+Bkycl/pWoXTObYwcG5YSAQTNu1ncqRtxYeNeyZbR1PY04jSTVzL\r\nrppVcWxXRQfFkgubpeAAb7iFLmpuP5w8ulNr42xIPJdNbgYkd+CKYNf5kWkU\r\nq/fiOaGK/NIbQwcIOdE9nu1UHBJHVeekYq6qeIsK7nIz323SvzXv6XjiOqkF\r\nwJpZ5LifjOeY9Xk0TGvEYjoro4j/nTXVzKxnAtcKsYiHAe3yJpBxpexIYCF8\r\nHEiC5O7pgYSDy8uVdH+DAK/D+R6hPITvtU86SgkYGKEet0Rw8Gb1WYDLXq8R\r\nhTjDAUGJgj3ih/XAE7cw6YPlDbhSsyTYHQ8BDB6L1wjZ/JlIfpNVrh06u18p\r\nTJx5AXxM5OwbFAZdZGg9Hwl5txjJB8tLGa6Y+XabqTzn+dj6CcSHL2jyCO/X\r\nsUEU4wkH4NWx5Fu4/pThZIsb7gnYQSrEDVphqAK+OZ4tcf3O/YXAef/tBgu9\r\nITjATPoHaHvAt7v8UR0mprYKWc75mh3YkduTgG02SpWmu8jUJUsaSwVHtuFj\r\nykT/jeqPqJVyZjISTiVzeAKa3Sg3XXlND7M=\r\n=0MDI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\nWARNING: Due to a link:https://github.com/cgewecke/hardhat-gas-reporter/issues/86[bug #86 in `hardhat-gas-reporter` plugin]\nwe had to workaround deployment scripts to obtain gas usage report after test\nexecution.\nWe introduced an alternative deployment path as a workaround.\nTo execute tests with the alternative deployment path run `yarn test:gas-reporter-workaround`.\nPlease keep in mind that `yarn test` is preferred way for testing. Use alternative\npath only if you want to get the gas report.\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix","test:gas-reporter-workaround":"GAS_REPORTER_BUG_WORKAROUND=true yarn test"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.32","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.38_1652196018824_0.8826292207388093","host":"s3://npm-registry-packages"}},"2.0.0-dev.39":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.39","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.39","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"50e9e674dea5496e09be7c0d8738b0f00f9283f3","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.39.tgz","fileCount":151,"integrity":"sha512-WhL6vVBEyneQLetyDazvQtR08W7eqHoNy6mtD8KardTQiVVlq8T3XcfyKLBNqdSyuXVONAreBvzz4udLvXEGSQ==","signatures":[{"sig":"MEUCIGd+zXycYXPAAL4vvPGsrq07+huTse1tWYP8BDIhjOifAiEAy2OxjDy095N/frS34tML1Io1FZTM3S72MMKY/XQzX2Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31586547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiepjpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrMNw/9E0x+cDeNa10/XR7OwMwvTJOxge19Nw/l8IJye7X13cimer9+\r\ngud/o8M9f52jvKmyIqV5A1/l7eK4kIGMCOngT4cReGaASUotdTgIRWRjz6PG\r\ntv3sZeqc5MVTz+OapecDR1SmZ+lbFHGzu927KKWTF2kVnKun4xCU0qp5IAOG\r\nhDMiZ5WvJzqpqTXEMCgasQ5OVD4qOyCdZk7qnPzjBG8Ml2VWyZkSqfnYT7ne\r\nSez0mrCKb5R8BiGOwoiIrD/JrZHZW0i7xkm0XlumDhWBJM0dq1pMSl2uyNpZ\r\nyOPN42ZRipWPaYxXEcd9mlsfoxWavKGWuqvD2721sUW6+4+t43Vaoe49bXdV\r\nbJy1anGFTgl1msuZCdDl/HoRcJ7fy6HREzfP7yTNeizME1YQ4srbee5CIBqu\r\nk7pVtoGdzDkI4OFg6vgvPftOKQdUwsJMbtDygiMqTEcoIYvSfPDOaPdhlPs1\r\nJZgELTR/t1BOP6gGAmyJk6Zjo+szusRdJIVJp/uXkl5aBDT+p7fwquEmvzdj\r\nkr/5zlo8RsiXv9dM73cekS8RUqBa0uN5OBOmP/ENLxcWU7W1c13KmgL9PmmB\r\n9Xk4g51tJiy9ubdyiSV5BnOYeK4OAdla8VGRpL7tgQHt++qnEYFzRP+sO4kd\r\nJEtqXa+apfg10DHVsXirhV86GTWv6Teuq8A=\r\n=66B7\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\nWARNING: Due to a link:https://github.com/cgewecke/hardhat-gas-reporter/issues/86[bug #86 in `hardhat-gas-reporter` plugin]\nwe had to workaround deployment scripts to obtain gas usage report after test\nexecution.\nWe introduced an alternative deployment path as a workaround.\nTo execute tests with the alternative deployment path run `yarn test:gas-reporter-workaround`.\nPlease keep in mind that `yarn test` is preferred way for testing. Use alternative\npath only if you want to get the gas report.\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test --deploy-fixture","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix","test:gas-reporter-workaround":"GAS_REPORTER_BUG_WORKAROUND=true yarn test"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.32","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.39_1652201705177_0.29071162492829217","host":"s3://npm-registry-packages"}},"2.0.0-dev.40":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.40","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.40","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ae558da6eda371a19f40b20d770e0a1aae43c397","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.40.tgz","fileCount":151,"integrity":"sha512-QhxBeLK40Br9NL2VxvLL2GL8iI832Jp0623oR+gfeqKBr2nychomKdU4ZRqVnMIDWrray6xgX/t22nGSLquIEg==","signatures":[{"sig":"MEQCIHOToObP371DC7/91jW0AgkOMNzbMK5nELKfobdWdRyiAiAEvJWXDCgDMRZLnOROxbXZLHbGIAxi0fjVfxmH3FNldg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31582332,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJie9IAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrBrA//dkTrP1s9TY60ilVIknXrgqpk/CCR111mKblyVQOI0S9EZ9+L\r\ni5SKlj5rTE28PXJ+wCbklkhgoURxtXCR/BzMVLPYhxeXSrmmNqv85JW2iS+z\r\nQRNVSCS7gFmrYBZt3Aub3wVq/qPPNVnSXSw1aQ3AXKbxpfrPlUM5eq0w28zX\r\nKkVN7dZglXAJHjkLKjMz8h0y50Y7R5NYM5EB772Mjh2c9TcUeHY212qP/KNH\r\nWkuOgl59idazJNhsfP9b+JTpPig8GJTZWr5RQ+dxbJacSIVixQIo32RtprUe\r\nyw9G96AvIotjgg2XCNxsa1ODKV1ebkGxhshHyVS5VhvRMJdnjI9cu3esoE36\r\nH7OCPewd39+uO7lfoLJzlBaBZvZJsh9823PIDNFTKRHQYGsmrMriWT8QCf3S\r\n70eYycxsU1dVE5roTPZC3Gb26ggA0CjXtdXCjAWP9U4joz7oskfEflEQ2aeL\r\nTil/wtQCqqux5lIgrXqSf9c1fwpBvn6+pNkvt34XksoGNUrS7j9G1UMwjHeV\r\n+d6ULvY9F/gr9ZBbtZYAmtI24HK4nKcejeG37XBBzz9X23ViUkIsXotS+/Qw\r\nSZIL8kwGSG6uchZr6K0IoivHakIzIe1mMd4deDobu90VT1YCLhi0KsxtkHyN\r\nFvnlawExbEqK1fQQNqb4NBbybMgwAyMSJnk=\r\n=smpl\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.16","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.1","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.33","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0-rc.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.40_1652281856468_0.8752423197784494","host":"s3://npm-registry-packages"}},"2.0.0-dev.41":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.41","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.41","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ef66594ed052a4c97d41b4168b6248adc9067db4","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.41.tgz","fileCount":151,"integrity":"sha512-JZvz+mq2yfTxryujReK4uF3OYsG8LN7TWNx7xgPRY+ApfsEjbjPPondmN5QPWaiGnI0yQ8Mvt+HsDlR96q1uag==","signatures":[{"sig":"MEYCIQCjJOCAzEQfu/KAIAFX1ljhoN71afrjX9uGs+y9aDnq6gIhAKFMg4Sg5Hnvd8byS5UuPuhk5tqNmaKzlAKnLVe8mioc","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":31581559,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifUe6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmozgw/6Ax2X6Y1pp+AhRfvY3HW4gabEcQsKk5qmuicMxGpQltSNRxlf\r\nNZrYaD3hHHzrVz0pVYS7PYhhcPzjOX+TCLqd5uEYDbvERMQW7J+9XSElYndY\r\nvYQqmfqA16DWdcPgqJlLFcveLzY0wnuzjapWxe9fQFivsmAvO2+GImgI37u5\r\nSgz68zxmcBNfxptqO8uQrTgu4ZrO7LAgIVZ0Ctsy+n+sW5P2z4T2VdUuZVQ8\r\naO4u4h17fseWx42mnhvDqH44W6+J3bkb4z/lxIddyzvBcNd5OA06wCJ5AZBx\r\n/0DTCKz9FcCMSgWfUXNlGKSa89rzW0nWOWwQ+tBBU4QdlciKGnlcFzEKwC6Y\r\nbWGC+fPVLw2RlBl1JtsdY9Ng5ECIPYvNdr4BJG81Vy+yJwvf45Onn3ayj7rN\r\nMSlmIfgRByGamveTeQbKTOtIsJ3FaVo/QTz3O15YYpH79MuU1Q6OWM4GTG32\r\nQFopau+nt4377l47GCh4M8r1CEDfaJzOfAyVtTewUyZlT8PpoDfuCZVi1peP\r\nmYTgpICtfVyPizU0cCdtTKIbz3Lc9SaLfOul4KQeLs7CnNJAw82PdSAX9Ozm\r\naEHqWjLkfv/pw2sauzjP4xZ2KShnOF//4rtjZSWiPWKPBxuvGjmrdvJmu90j\r\nxVzmuX393IHNmRBYHxVV31Nv1XPmfaMAcdk=\r\n=PBM/\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"~4.5.0","@keep-network/random-beacon":"^2.0.0-dev.36","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"~4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.41_1652377530446_0.4636746387422237","host":"s3://npm-registry-packages"}},"2.0.0-dev.42":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.42","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.42","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"49e3d19edcfcb39292336dd992641e07504b1fb1","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.42.tgz","fileCount":128,"integrity":"sha512-xv//i5wwuJJiA+G3BduflZCisCmIEca23GFEgoM/Ef6jIgjliMYa6/Ab5FMm0P5rcqXhGPXl8nCvmmtX08vDEA==","signatures":[{"sig":"MEYCIQCynX/V0ZOndSKwyi5iKJY8hXZ7D+VpgNO2CbiyPyLLNwIhAO80oxx2xN5A3Rup44FJMitXb8jS/YcFAaX6649gCwO3","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23706940,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifinHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmokyA/+LST5xisYNc7dAEqtXsvEh1vb6VKaoRw0ue6JC50Os5XMMrN4\r\nnR/LdvcfKY5DbUE5beGznXh8vagdTa/ytTFIOsB0NTIq8QNSiCrFn854kktY\r\nKTtoPkB4SMbsOQ+8Xfohvu8cp7Bzuwpfpmm1FP1VuUO0n0Qxs/Y6NiFNoeO0\r\nE/IP2PxgtwGz6yY/zT2U8v6NuBwHEfAixltoSFFNK5S+Ms4L5mE+xqUIb3+Y\r\ncWEmNCfX/Ys0HD2pfhlTpS5wMeXg2rxXArPmHi7wZzfb2uoTLsWJd8ae6m+2\r\nl1bSZFHV8RndVIa3tBEDqL9/5rVCFC1pxZTyF9r0DoMgJlkZ0tAmqddWTUF9\r\nGfGZtSi0tCwHUb5w+tsUX6JyO9/JHj3iQzX/5ezvcBQZGcL6Sqq2WJWvZw5a\r\n2uQksgpHR/Ui3kyHpCvwyE9TvtwAyfbL0dZGX7QI5R/h+A33LmC77kkVdlXp\r\nzuk2GsGUOwP1DLur23UKA393r2mYOyCEk4BcPTdQgEAeQhADgKdRtX3GNmnT\r\n+w+/c7qZlbUSsAMr87SJUklFCtKN5wRIiAYEiMFsGFB3lL+19gB2HI6pyGKe\r\nYTya/gsrD75Pup739CUt5zQgMVQxbVGnoNDOx/Fw+3bXiWA49fMrxk5HmecI\r\nIcx0bSYKiKhrDjNqLUPGbKu2iQDuwRH85qM=\r\n=/dSN\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.36","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.42_1652435399440_0.9461645493601374","host":"s3://npm-registry-packages"}},"2.0.0-dev.43":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.43","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.43","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"4b0f8b28dca914141a83c8d72f4ccabec4b9c47a","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.43.tgz","fileCount":126,"integrity":"sha512-HbnjZvZWlbDK4ZC3R0VDe/NOsTejavcFZvIRS4tb+UdkLh/KAeA09foZwOYpEo6ybxbfF8eYD2fgh1gcMMmFNQ==","signatures":[{"sig":"MEYCIQCd+JnjPukAuBEog/1o2Xob0140NwujI2KABQlcTxrAbwIhAO2prGPKdhEvZ8Nt9DcvHgHr3MSOPjXL8LBAAhcchJaQ","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23442542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJigm6+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrgQw//azzC49zCT7Cy8NEJJJOJY/oSi02VgqrROoDVKQYr/hqIIggF\r\n3pluPIy72feqS1pl/r85f2PhkcmKRg43YguP9hWApNtrFv6Y3AJUuKMfUgwm\r\nwzlbdEahbrU4taEBDcKMsXysQfqcqjUvdKGvn7jEHaOdwg6B1wB5wYf1WSRw\r\ncPfzabTOPJveDBMabsKPKmGVTh8/CVyu2GqOtb1muEZq7NcFtNbU/4Ckn/Qs\r\n5TsULszLI0Z5i5wMcotS6rYDP//stjGT0PD412WDmYURVNNNm6n6mYpL1Nsb\r\nNs13YGT4So7unwb8NNGQ3eQ7XkktYI5goppVaBzStDlaOBzDc2yQpRftMP5p\r\nwwiU/RpwyR5/H8mpWaEz/qk2U9jd6PLrp5lLXK8Y1z3CJJqUupmINctn5RUA\r\nvLsFR2hzLBIuM6Xq3K8rCcFjDuyMIEXt50dOyl1zeDzq3dg2kOM/Cz9/69gK\r\ntxn4G9sBJuj428u5HmxxC7LkqUwNmZ8jYTgTBujthptWsdVUFYoKwSrGtL5B\r\nbBUvkl+Mz0BRE3A2c/ulDhPUNsa1QzuYtoj2jwOTle0KLB7NIRJu7kwGGv4K\r\ni1599P745lC9WO/nd1e3bHxSdZ9VM8OUbygY/6B+3sdR52IbTG4iY9u+ppp2\r\nIcm/pTWQQNpJrnIvYnuEtOCzDAoS8r4Pn4U=\r\n=V5ud\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.37","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"npm:hardhat-deploy-ethers","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.5.1","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.43_1652715198643_0.6871050709090087","host":"s3://npm-registry-packages"}},"2.0.0-dev.44":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.44","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.44","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"182de040dff4a08dc4f92da2e5ee9dd3e9a35ecd","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.44.tgz","fileCount":126,"integrity":"sha512-YjfqZy4f5soBA2/a2KaXY54P0DNqHyUI89he03Hn0G7wSS/48ZXieD2GH6gzRhrF+ovr1pbU3Okm/rUBZd8hRg==","signatures":[{"sig":"MEUCIQDJmh2LX2YhfS5t0rBqd1kTZjVmcdHp3ZniYF0QOQFFOgIgR0c0H2nH+5RGMEht7vre63nVH6ZTkmGTotYIj+0TaEo=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23442128,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJig1XRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqi/w/+KMOkChAsKGgBzN0w08odvcsE8a9pPsbo2j+Ht0LY53Hqn54h\r\nP/Z74gjLi7+6gcO91Edv7GHVYORo9+i4tnmixR9IGWLIjzFZ0+Ub+oEBa6DQ\r\nXo188+ntsIxF/tFFY/GN2H9lampdQZevZNlUSNNgMn05AJ2jCqe94Yb7duZb\r\ngnxXQ3gPSZqOFcgZ+jXVDlLWzozhIa0Y6mEmsjdKoeYLzALUKyrxDxz8C2Oe\r\nEVhPUqczrt+5i7gQjy0WAxGXCSg2dnVj1TpSL6sQM1ONYL4Z9ox/HQ0J0Zqt\r\nn+iovgjqRTJ9lIZ3JivryJKSoxRIV8gBYlzK3njJJlSqScHpzcBxwVfTK3xs\r\nhbG7hQKazWZQ2hXuSDX1cu7+O/dhET8LjwobtT0GRk6Dai6n7wN8qAGgECVF\r\nv2pYcEIdSpJQFs+QdIbLq/PDxoVWz3IS1zRUwwT63utS2HKBzb7mec3fqfOV\r\nj7RnWAcLKSgFefv0XYcgRUExsHw435GzQan2UA8FKeSzD64PkFvBMr4CbrV/\r\nspVQyXgHzJr1D834W+GptkEUlvUwA7jawUI04dwb0qvA8boc1621eTuP+SsJ\r\n3ZMtdIkvhkWoPjAuQhh6DPkUDjQa2iLz1R6MqTB5LYw+bzLKR3be6vt+zCDC\r\n1HrkdLGoYtVocNqcK19XG4B4dn3H4zqu9kY=\r\n=eq5P\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.38","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.5","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.44_1652774353272_0.16333236370136062","host":"s3://npm-registry-packages"}},"2.0.0-dev.45":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.45","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.45","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7d2706b46d0e575c0c795f81697412a13ef54471","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.45.tgz","fileCount":125,"integrity":"sha512-qDpcmLpGdOBm7iQcPVdwMy3zaGJtbs6U3IxqzFe+ZvJgvUiVHJU2kuofrGxCDx8qVyi21VUMFpCfOr83WARa1Q==","signatures":[{"sig":"MEUCIQDK5ckV1Ll0LASpthdhChVjgRW42iDw1kdxjY30qpUjBAIgEluZ5DRjOqYTCgL+Yzmeb6drxl71qow6Nciny+hEpN0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23441417,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJig1nlACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr2ZA//R1mjY/insz7xDRDeq4hqRFC+So0GiYnnEqX70HOjOPBN/cWj\r\nKbBfaf/gsnv3bGfOKyI109X3Vf1+kCGIle6HTY70OTVnq72VG1VewCQ111vH\r\nBXgYIKijd1eOfJkV/rq+X6lIYmKywCk6Ru5ConckucJkEWU9FSmubiQMMF3p\r\ntBRsyI4iZLNuYQScSk6/Rkl2oLcT9SCp6ou4+9XLXm2xIVxE3YQHr0g85Cuo\r\nIhXzSvfnf8mibRr73rzou9m619MpoGC8ZolgqzewRUj1+fqfIX0ZmK+70DAj\r\nHjbyEVA8nx1J8tD3RYit784OIgsiO9tDPT7VKzAmNDZgST1YmnNeKOBP7/rv\r\n4pEP5GuhZKdnT+jcgTWBg0wLx9RZqb1ac79R0i0i5hkqynb7oGbsFOZuSuZP\r\ncW41rrba7LQCqTKZk6WMe0bzdwoUlK3Vt+048FOGk4uPkJhqWhLLmh4OIIfx\r\niGYqquKOr4era5bb5CL/t2LJj5NjM934EIW7RFhi5hUmJEy0Vwp9jWhEELdj\r\nG1d+Hci7kemiVHkZ+eRN1bTgEuT3HuT2eo4KWua5tecgh1Zfe+ffN3F2jE7e\r\n4Ubch9UqtSWxijQKZkAblri2oEeObiHuU3h/X6FwfL+Z8AXLkDwRdl0HSXuf\r\nrJLHcEZChoXkJ3z6uhl7CwQwoaDc+bB0MJA=\r\n=NNkU\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.39","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.5","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.45_1652775397276_0.48037101251897196","host":"s3://npm-registry-packages"}},"2.0.0-dev.46":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.46","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.46","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ac73146894d25e937c87716df0fb2c51110b3f0d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.46.tgz","fileCount":125,"integrity":"sha512-2pWH4WEVNMuv+e2Z4MfqdKYy0HItWJF2Oxg6e6BT/hXM+KeH5hQSFZmNABW614HT/ID0Ua31N0tz28Oc5h557g==","signatures":[{"sig":"MEUCIQDnce8DMJKs4tErTv4E1ICbNOLWzsq5LfA8+6Hj5evpvgIgF63Mb6pBmrMWbHEIvpVlV1QTSDwT0YvXnrE/LxMHvNM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23441924,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihPIxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoMVw//S8EWauyMe1tJaSzBadWBIZvW5FcI9jsmmU7bEBZRzdBZ8GBT\r\nv6dmyiHG1ZxfjXe63CuEcuap2HXnDHs7AFpzFAF3kS5iCE5Ho79OiUlqL2q1\r\nHVrLWJ0+/en6E59LkM9bdlgP8yCqG3o9JE3B+S1vkML9K29AZrpYMcLggCSE\r\nPEtDdUTisO3ib0GFFOvXwsOvglYT1b4o4EXq5RTAZcvK0/XRBfdq+kWgmV2l\r\nWlnQ7h93wg5D7kMH6z0Cd89WnmbYOeLaYug7KtNYLPB18zBfL7QlZ+0/Fqur\r\nO28cFaO/UTlmIwzAI4sC3aG+zvKpo6wmiYAAwQExibGTTOnoh0e96WV7CU+9\r\nB2X04b2Lc8NUO+jVUNnP6iu8JdgzPwqPLYso09AHX1UcGyikxiW2+bS3hX9d\r\nGSxSK+yOOXT7SvzB3bISfE1tRUyaQMNKxM94lnFaXx2zKBhrOCM4wJCJuEJY\r\n0PqpYUuQvfO49KyKon/AHMeOiCcCe4Ilk4Hv4KkzqVKtV83alofrZkSQIxpD\r\nWISYejy992/mMI1W/bjPn56T3IewartYvSKV8LnL7QiF0RESwYExJyIX57xR\r\n+Unev5r+e2GipGgHE7js20elKXlroJpI0KNXytMiCrya3ZkZ5GXiAykZXjQV\r\n8X2rgx5B/B6FIkieMIVaWTdiDe5S9nHnCkw=\r\n=/RRX\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.2","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.39","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":">1.2.0-dev <1.2.0-ropsten"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.8.3","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.9.27","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.5","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.46_1652879920724_0.6609719198843265","host":"s3://npm-registry-packages"}},"2.0.0-dev.47":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.47","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.47","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"43c6c8feccc2be9e001a7e35cb853364c270ad2c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.47.tgz","fileCount":129,"integrity":"sha512-oSpT3SjX3KQN2RCuVTZPjqNTw3pS6IYcPlslvT+vtZi/QWleF5OOd2kZxQ1A+gm8V0U5Bqfh0u/Bt1kn99eYNA==","signatures":[{"sig":"MEUCIDpkP7bYCL0vut93/v8MYk9A4eyFtM1C0yeuFDeQKR1uAiEA3iaZ+eshLCJe9BGzEwon7GcpPSyhIhy4NRiQFPvJGaY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29590088,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiyCKtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoQ+A/+KXzUwerpelkxwuCWJ+ky9a3aPchhAGWG7/X9dFpTtOfYa7/k\r\nCfpLtaLZ+0P/QqrpLFjaOvCq4bI8F76eb0IPgWgoFfwEQWydvKjBx2k/5yZw\r\nZWsL3We6n7woQ/M3GFxc3ZiaDsiDskyCGwJHnyZMNj46fshq8kgHwHO4wAVH\r\nYJZJx6kFAGoZYiHBDQ1RfeW16tVolqsJwJMR/T9lyh7Dgcl6D5axIcIeYWLF\r\nrf86Rn7Q7RTHJnTltPoXm5JMP0JFJ61qvoa0wrqh15NcQOOEFcgR3rEAMqNm\r\nF/5ZaW+d4IYmJE8wY751sbEWglbiF4oHhjioReaUc72U41C4eRjy9SH/GJET\r\nl7Ue6EaQ8ZxaQsG5WG3me7CSIAAd3QxTUTkjJx2LGO2A4e+EgWX8r6QOGpn7\r\nZDnTS6ovtmkr31gwlFgfh3wF8TfToW6sWXyAvt6vIQDhQL831Oi7VYhGwEaZ\r\nkLkFRrDAZoj9wcCYkL1+gsMehZ5qyB4WVmjEC7RB98yPbHL64jKrv/vgoB/b\r\nzfdAnN4/itpSPJt3OfGpIf55UujtVtGE+uOekRbDD0LAgT7pZdM2Wuu895s4\r\nUuWCYXe5+HOxBICO6tBVWC63utuORdiw87j+HyT0/tVSceqYZGWO3DDUlyFz\r\najift6Khw2Li0G3G7ucjNi1f2XX058V8Qng=\r\n=TmE0\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.46","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.8","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.47_1657283245409_0.9426005283527903","host":"s3://npm-registry-packages"}},"2.0.0-dev.48":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.48","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.48","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ddb6c728c899a4437b1d93a22c32840cd2ec3aef","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.48.tgz","fileCount":129,"integrity":"sha512-qspLuljwQA19dlqbgZ1dQ8Z04jrgbfpKH3A0//Z0JanEbMHW6yqJDlaR9glm/iNlL9LhU/+oknRAjdcIlhaVNQ==","signatures":[{"sig":"MEQCIEGGEWFgtk+Y4L+Dpopr2AT5nf6MBYgQgvbNX4u7r9psAiAUjtB0OFx3h+961EZv9uokljUW1RJH+8K+QVfhwA+JTg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29602684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiy8EuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo2iQ/7B1nazAD8QFE597Jkz7VtIuju/Jh0BaYz9RwZOQnCwg6LfU+p\r\n9AtoEOEg2/yGS7uEqp44464UGIoSStTenOeJQbM2/Ek6rwsYkZ6Kjqb29jRI\r\ngNGz0qrs7Pbfe65MGK4uJG3u7uwjCnrMUesn15U019B+AqiM5/4+nwBkesGg\r\n8pxywdzXA15Ka15Nn2pX/qJHJjOOP9exZc00CeZMuMnRS5nTcWLdg/Hxccr0\r\ntDqMo2+OCH55+SImgREk3dzqdi2znCfcl5ZM1SMLjm0iUyIn/yUO1cakneo5\r\n7Q2uYTsz6I0KDyZktK6n4LKHO76peL38/2NM2B+Z134/d+XtTk1DkQ1FsEYb\r\nW9tXDYBCwHYCcUS7KA/ZnUAfhfy8M1b11Ts+7Ej/xOeKaBKZfL0YRDdRJG1I\r\nl4mPEWwV5u6mKWv6MLKpvbazpalD9hupdP7+b9zcnjgMYd/VViUkDtb8EpKS\r\n/VhDx1H8NtVSwLGAxngdrCEvRUYuRYpqtIfKiiaIPDP00yHxW4JGbklEvkRN\r\nKbvlo2ffx/WTx32TmZtoh5MQ4Lw6fy3x4ylG1W5zPCWRZ3dsj6qNgtNy9vNC\r\nzNbFITDuPsIIi19Fah/4g63WVokgYZgIgqZ+OKkP31l3VarAf8stO+iw2mJ9\r\nOrn7NUC0ICJwL/vHYegAm8i15+YKXBpLmnk=\r\n=r9iy\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.47","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.48_1657520429856_0.6118107604631062","host":"s3://npm-registry-packages"}},"2.0.0-dev.49":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.49","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.49","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2b06d133c3a59023e0f0dd070e3eace098bf23f2","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.49.tgz","fileCount":129,"integrity":"sha512-vmaxCe4XCnOtrbuCI8Zr4xe4IhhM6XYxkM9xgRh5Lu/1V2i2En0tA7Q+WvtSw4aE5+/Bqmu/JeQ1V9+gtpx5rA==","signatures":[{"sig":"MEQCIDwf0NnMeAvS3Q/0QMkzeLSt/upAdFbCIt6THjpCPMk1AiB3cmunL1GcSTPY8ZQHDqNXPKxtZj0Y1oKhtD3Z1h9qSA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29602684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiy9q9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqxahAAn1TBk1gWIQODrNFBMTtM3er4zAhM/jYDG4fYcnqKavYoTuzm\r\nc9QR4n/qCPmB82y5ApkPIMw11I96JqEaoscq+5O0izi/7DJ+cLk247NmbR+E\r\nV8rVOYAT6bdfwTsMLvvdNhRvostHAU2pgu33uOlusZmYje0Kwhf/t4VQAwhr\r\nmFwnEQDQ9fVSChC/Bqkxnzr9Gu5W18oZl9t0IprfjlC23HFuFSMmTi4XDqJN\r\nXPgouhWj5lWi0g1krbUdFt8eicPy7trdR8n4/Vz+btROXzwYQKKnq8ljMwmg\r\n4OTQaaj/HZOugjtX6gDnQkDUXWUrpowtBzfIT/+S8ZN/M4hpUBIwZSkS8BjT\r\nLoyGHYshmn2spto93gKgfgPayHW211h8lNQ01mnRazuM2XuwUrtRiieTAFDp\r\nE3CKEFVnBNSc01q1YjfjacVCXLtoX5tpECihCCjQGwYmleA99N5fnJQc0JLN\r\nT2v/IZ5uGOE0/bNKF6JDCOQEe8br6xqpYKZTeeQ0Yvd8toLFEMyIagDgUOxc\r\nU7ZJQUYTk4w4N7Ss5WSsIsr44nccBv7aSQEWwwDDnpYcPmpnfoJ4U5VRNqDD\r\nyB8ISjBCjq6xYo133DfEUOTqjt0N0kY7QstjM/OS4pHSu0179+nPPtRMmRZi\r\nP9t3NoGk5wCMQrMph7snI2lD2ar9EziI/Kg=\r\n=V8u3\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"^2.0.0-dev.48","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"^1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.49_1657526973646_0.3177749072679106","host":"s3://npm-registry-packages"}},"2.0.0-dev.50":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.50","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.50","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"a084cb609b0fa4ff34b253d4bce3e670c3ea44da","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.50.tgz","fileCount":129,"integrity":"sha512-Rmf6mu0oiVO/xaRQL0p6lAEIEnaHzGJgjLTovJ0sF0RcRgy2UtUC2xsaBmGR30wa4iVgR+w+lXPrkWfCkCIWIA==","signatures":[{"sig":"MEUCIQCnrV86nyG7/Fgc1Yac1OtV3e8+xrGcN6c87bM7MLtEtQIgdihRTZdW73wQD+v/wtYd+v9SK4wulN2DnFLUXbrW0CY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29602682,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiy+5LACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqNBQ/+MQqUkGW49tX1ZPIKocKMd4YGqSeygYKm5xfPxj6fq5XDs7rJ\r\nYwm6Xsvm9vNDesO/XtqN9fVK1R+u2NALgUUAuw65SjIF2FyThTU5SnERwXN9\r\ntGTP0r5s7s6Z/4H40/A/guFLzRqd7BqU+6jzRh4tfMzzBC+yW+q5mLWo69J5\r\nYl9VUD7NdIMYa7CzGC45oR+zaoTsHpm2SdtbsyqacD5IHVI4wZyBI9LZJFJD\r\nELeqsruNSN4h/77X1c83S49pGVYHL8EZYcSmyLJjC2r6/VD+DWBGBDeXGhq2\r\npYJBRUsV46Cy7H/3svvUgjihZHzIdg6KNaHkZaARzYWpRy8/xNugVgpNpcrQ\r\nVpHAgfd4WHTxlyzAPDDeqY0W4OXYl3JRwazqMhGMzUYlX89H3e4AvybHQszz\r\nHe7VNNUjV7S5HuZuUR2YBYZAyAdcaHSpGJj5sjAZmHXeQoaVNON55PsS/Fnc\r\nHdFVF+QbqlxRgLli9U8zD+CaWu2cVbK7I9Xsg9Pt00Aa1d8lnSKoVGVN8SMR\r\nTnf57CIASuhTDps+hqJUxLo0FmgLAYwTpwUqweLjTMEhLe4VmjXNXe6K8vfA\r\nlLb0bOvSgveQ32TELdLqQEkLly3K1iNBivvRG0jlvQcQbxFPPBaeupYyYNQi\r\nlgvlb3FfVetWbVuc1+HvEeU5mx1ldCx4PRE=\r\n=kIIf\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.48","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.50_1657531979276_0.8046040661275404","host":"s3://npm-registry-packages"}},"2.0.0-dev.51":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.51","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.51","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7841af619a772aa12aeee9e0e15868cb66ec951c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.51.tgz","fileCount":129,"integrity":"sha512-0NnQW9XnptkdOfMZCGpByoE+l09bdbU4ZRP3DQH7Z4u8rIbsaVNiGddsxwvGCmbXGcU9RB/eru+JEHPCrSWDYw==","signatures":[{"sig":"MEUCIQCxVh9ZMBpcpDaAUN1M++/ujWwtFYexs0IGFb1/LRPjkwIgfwDB0fxCa5qT0W2Ug7Ox+iiMqr+tt9DpisS6zkFEt6o=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29710259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJizVwEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpckw//ZY7C6C4OysyNQynOwluwqJX+82XD+aqCuYx+0IQ28y+42zwZ\r\nYmgcxDl98YJbwtAT/7wY4U4C4YiZCr3BEZaWWvdrESouyaNTzp1HnU5f2Quk\r\nGQiLtsjwm0WIXRwT+vFwh/d3OElr8ic6oX6jA9wnkhRP0kbdvSCv+qlwT8z+\r\nuTuY243d/pg/FlvLknP4cC26plD3feoq1BqgYkzDci9hWt/tO0V1y0xiFJ7Z\r\nd5BfsOicb8a3V7rhT6sZNIWyqHdqQCXdVhrC7TYALsRDS62KL9mRNvh/wXTq\r\ncft0s+/5wOQsSBekzlafRGz+mjeP/BQkj9Kmi6wD9PEpPVzYMNUe12XicvvZ\r\n9CIMRfQbCxWmD7Us2jgceHqmqgU9k+vq+JZ3OwKE5+78rnE8jXAtxPAAMO8q\r\nFMOFz+vouQA5DEFx2LBA9wrUEG1JxEvCHy1WuooMKlkLf+GBAjSq3U/E7IFb\r\n2YVF9Jqs5cIISi6gYFJRchLCzFnbciwFs9qFl/uXC2MusPe8WblAB3vtsPWI\r\nGJhKosAlCOibwAf5HRY+CCRs6Ia5HJ4A1PxHpxHWJk2gdY79BQZC9yfI5cYI\r\nVDu6liIntNebQgR//3BZi7RehHb0uoFj5tNQAMZ6hjsDd8uieeBsoooF+251\r\nkwWAyVWI/tqUA+3wYghBwBsIjoO1n16+R1k=\r\n=1yVc\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.49","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.51_1657625604456_0.1111790506008592","host":"s3://npm-registry-packages"}},"2.0.0-goerli.0":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.0","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bfdfc5ee0a0a02d8dabaa32a253ef465026dfa07","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.0.tgz","fileCount":103,"integrity":"sha512-HlQV+aK4M0dXMg3g29imbZJSswFwineHWPa4/xxGn2RBY/jtcSazigTI60a3CrpayUhpXbJZE1oApJmHPX5Ccw==","signatures":[{"sig":"MEQCIDFQ+/oPqpRXJDNQANsSI8OwavnKkqXvITnVs2TT8I2qAiAd8xongfBKqx8Q+toCvQDc6l17o/GDPW2yN2ofKDrmJQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15003631,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiz9+vACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoylQ//WYHy51qI00SdtrHArg7z3KwU1hYZPZXuZa9qMdkYLKeGZLSG\r\n6F42fgoc2iYzwjizO8/fIivC8672Pxf0iNa8v2lSRf2ryzzdsqgnL0BOgqLx\r\nWKSmOIL+Jf7S3iX56/mJ66na5fud8tbUFHdiCFpA8dWxLuHAggupOtg3L00O\r\nBei/6cEGlDAz+Vcmj5n3ku/shcaPGZ7TV5WzTbw2QMSS5FJwAm+dQcPZXSBp\r\ndwt2oawKj8knVO65LP4AwNLLEWWjWTStmI8y7CZT4K4Sl1H89a0XV6vgKlbA\r\nvKcgFNYeGagNcAogpVCoj9OT6JQfAU4YsnivroeKpJq+fgHEUmdTnDYRH4Rc\r\nDoOsy3hA0D+UgqQrZ1csiqHJs1KbQyOCMAGbQNXMmLJsr+LiHnR0oKJ7vpBw\r\n2fEgvIG3h0tk2Bg+qjDbpRx7pCDIiyV8AWO15BmpAa67KPWuVG89DsXx8y3v\r\nR0eFv4MhKc0X0CK0YisDNjX92OsfBFogK9+oEgltdlMzUQcNbyIgHGHZV9ys\r\nhWrXeqy2WvROku6NtHYWlhf3ZS9UZ28vUkvKI0PfdKQ41R4vDQ9ufTWIa/QR\r\nizhwDZCoY8PfMEEeI2g1ucHMb/GDtmrKSx2YKbhcOmml3NgRpRTf6ybx2vkH\r\nfY5BOkaJahl9najm0Ubpax/m2zNXYk157X8=\r\n=K7Gi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"eebd4a571783550922d642fd56b589d1728eb9a8","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.13.1","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.19.3","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"goerli","@keep-network/sortition-pools":"^2.0.0-pre.9","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"goerli"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.0_1657790383219_0.2981179098647442","host":"s3://npm-registry-packages"}},"2.0.0-dev.52":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.52","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.52","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"869df434d420d8ed3a086a2a445c4eeec2c8d7e7","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.52.tgz","fileCount":129,"integrity":"sha512-lpnaspe3WFsIWMW/aTg6Ha6w4ElFFhzCTGYm11+qeEi5RQlrRniCWkwgE9a5ILDWgq+ZydAlMSJg81FB/mK07Q==","signatures":[{"sig":"MEUCIQCKdZThlHinP/Z/OGjRlBiDFVGBMgLnKrPppnVlS1c9awIgbZy8tJ7fUTYNcjBIAegJQVb0ZHFvlcaPZ02Q5PLtxf8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29685292,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi0DlpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmod5w/8Cxs68c935M1rvP4bOl+YJcPTdWF9sn7xaVMTF2omdNs151lx\r\nGzp4+ZnEJd36gqO1wCu5XQSAdKRlylraHe+BDqRpYDm/lNC4AASKWZHfHFV0\r\nlfocmzcRyePiVCpMC0h6JmVyYBMQ16snrvNIvj+5Ohr8PZEfqz8vih2YInzE\r\nas7jCmaVV7YdamNXnnDGy2XmG1kWt5ylkMRRCZMNP7bimf+CTWls45DusRvr\r\nCfwN8UWw/7AoOLOaxC+QqitPDPfp//FohV+YoPg/kB4aWjv3CZGvBhR8ULJt\r\n5k23GcEVziUpEE3tORZwTopdEo43ptFyzGPt9ZMoPciYZeRjE+jkrNNkXaZ9\r\ns5fKhCGmfGaCR367N0GGvryogCt/L1P08Ys6CyBAszJG3DyLtuKngLluovuG\r\nMeju8vAnQhfe6+IdDV39NRRvipsoYcPqpckLhGJ513+YkVUpwe24YBCjMTny\r\nHRgZ7f33yna8R/fGbJb2RNYS7WFbadTzByurxB0l35/cN22A0xcTWp4f5eXp\r\nSV1qpp22e+T5lV5kw3UtWxaBxYTiVDSbTudkQQDleG70SP6NTt0qutGDkZ33\r\nM11GKRwPFr0pvBYwv130D0gfhqoCnYCe/bKRlmzY5/XGyVfe6Nc/ZM+MtvhP\r\nqTVp8589xl3OAOuliX1SfnEPMsPagwtuJjc=\r\n=9SEG\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.49","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.52_1657813353665_0.18993917086611378","host":"s3://npm-registry-packages"}},"2.0.0-dev.53":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.53","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.53","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3f574bc1cc1f044eddfeaa6cdec7d4a5a1e832bc","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.53.tgz","fileCount":129,"integrity":"sha512-K62wveS6P3I8UiJh4Ii4B7PRGSqu7EGTzi9qxSb3ePy3Bn29uq2en6gTURdcFx3jvj/snCKzjtEA/R5erjujmw==","signatures":[{"sig":"MEYCIQCBq66EJmxoxRm+8qdEDXWZKRtYqo+jeMBo476yvtqyWAIhAL+/7DVfHVZEadUL0TKIb4gsQuOcL9nYXHE2tK4nxXXV","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29794197,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi3rdoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq/7hAAijZmdZ7GgULy/4gfTEDGJVEaAwJ6Bx+xvotmrNvKFZhehFST\r\nOKZ08aGp9Cz+09ZaLLD4wDmApL4Y+4iHaqFEnIMxyWubq/ZZBmTbkXm09ZL7\r\nTgM8M1FwOzKLZ03oYdSfo4DNvb+cJ8I1al4lghrO6bBLnDs6Uyp7prr+mgk7\r\nAusw/5lVo/ZwK1ajq52Eu+MW1IwahmSTYxa8cjUe3OnsVQaIdB0zp/MobS+i\r\nYxyVgIRGEvass9g/TKxnJp4E52zOPFGZQijfSn+qYNoA5oLtoCEtiIOzAGR+\r\nvCXP0Pc0k9EnOILuWdkLPpJFvgSVQukNwWiWUJz8iQX0SNHKnMES44JVqB0W\r\nXtE6vWn9C+Qbj/UZmrxg/lAmKs20AOzGpMV2cV1Gg9Yva3xDg8hXZxkSqpdl\r\nq2uJUR/C0J/6TzESwXzsSmeNiAEXPyMgz6WjknwEa6lvWM8s8KChe6Pn2l0E\r\nL7gm87dmQ9d+y9jaWKEugbuI2DZW4WxvLUXnqSclSguwq1Ifoqd7IGVDYrsR\r\nWujrVNkal3Yq4gNbGw+YS9M+Zfv6iH9oOJRKacc1Ho33aT4cHPrrNXBr5XiR\r\n4zRG0XZFY0v27ge4NlUB3uMYBLWj9l+Z+vxFQpKWX1yf2qWrY8IA4K4Za3Hh\r\nK8Fcd0xgh8ziTUMmgYlT7s2voyg2P88Xw/M=\r\n=lWZY\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.50","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.53_1658763112427_0.93626308798842","host":"s3://npm-registry-packages"}},"2.0.0-dev.54":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.54","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.54","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"356c0ca43d7b09bbcfbca7c43dfaa0590ac9be92","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.54.tgz","fileCount":129,"integrity":"sha512-mmLmfBqGXvyYqxR21qJLZuKoBG6VcZrj3WYShoUlOklBi1kyPh1NS1qedakRZDy53x59m7TGbl2C2Ud4p24vFw==","signatures":[{"sig":"MEUCIDViBZNp1afTRSsLZZ55SZICxKuEDPGyamHQm3FVsxuAAiEAytGMaunZgMEqLBR5jCMWDbMjrB/K3Bp/zr6dO9rnY8c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30017722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi3rlBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmosvw/9GDxprGXT9EyyXCB6c5Nl7kuRT83APb4oXC8gPiKljsF+k/aL\r\nmMmyhuWZpz7qlWVS8NGgtOEHJ3IirhlcGpCmInqaR7zgVFDA0kzNsgs3DAxQ\r\nHXCP2CTBHfUOtQcT0Uzoc5qHFj9utepVVT7xn/xcvAYX96ekOFzfWXAqxIna\r\nEDKych9wiRbeL1IHua9Hj3cuj0nlK2PfgDrRtyJKu0TY+IcMBqH+IhLP6TN7\r\n2FajL++FX342vHzoOERqFOsvRpGHKt1H/FMXnIJjbvxzM9LeCmzxR/8yrBWb\r\nrWPoZLuQnXr1bR3rnHH6CKRZo1gZThBUMLeU7ANtoTzuTeE6FmbgV1CF9gOM\r\nubhc5g8A4knHtwcUDsrPEpX6JT5l4d44rvlUERX7I8b7Ku4il7L+xckOALew\r\nCqdWrVy3SAJrkXLZqGQwlwzPtF9UHWDK2/tZySBXe35G/yQVdBKZQjpiRk71\r\nztXjlVkYs1XrTFclBU3VbSbV51BKDF/OrF3fXbzGz7mkDr6yHNEdDZYvPtoq\r\nJqLGk7ADHv1QmNjZv+lECC8OnNsphENyyaDw1jcAm412o/u0A4n+hF3n4hpx\r\n1qhfY/PXuZs+GyoxaOXbX3UO9J0rVRp55EdH8itqUg/u3Rs0hcK651UWuQfA\r\nN8OPl4nEUJ5EjL03KmWxd4osTSTrqaxEuMA=\r\n=qHKi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.52","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.9","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.54_1658763585522_0.24910131626784837","host":"s3://npm-registry-packages"}},"2.0.0-dev.55":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.55","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.55","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d7dc74c1ed4e928ccdc2855de6eae98bd2110045","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.55.tgz","fileCount":129,"integrity":"sha512-EL92Lr5jT4NJoqvJ3yzqrxhFu0a7DADT0DWoIibxn8PG8goAO4cy7ESuu3qXVR6MOs2tQ8PayEAc+cirre6Zig==","signatures":[{"sig":"MEUCIHIiIr6ZAQfQicijsQi156DtWhUjHoxC4rRBVybhrqm3AiEAnISZfic0Ax9H3KpJKHjqGeaSD/lL2X5AyX4qHFNJeiE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30017723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi3/DCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp95g//Zq8NkfTaiFdEY6OXpYxjlkHGnpc/SFBcA03X4n6+wYiy1EAC\r\nqYApytkgAQsSbct4wfmJLlbKkkGvJx1mcxxHxXZIoqmv/Dj4qad/w6K9l2CI\r\nRZU+gYFNcot0/zltLz44iNTuohNusXX40TNIAuTog/Sw19VjRFY+9SyUhAlu\r\nnfq8/pL5UnCElzpmR0R/3/Svj2J2PNGXtnTCKhjOxvDAV3NTygc7JoSeVNUx\r\n8gOf0WKS/+ucsfs5TBOVqiL0qBXQU/+Xl2gsJQkT7XFpqy62YjTDjZWB0P7T\r\ncjZq44YOdj34gNFQ8IcPIQGbcu/3FH6xFmErzhnGJu8wVkhnbgnBdR6klhIw\r\nZm2YPcs+ILPi1rWLebxD1Jl5c8dyoZsbKS6BzdGCiwnpr3x/XDLEN2STK6wg\r\nS0P94Wy0wGwlQ8IfRRJNNQoGtjH2Wkls8A3jD/vZLk59dbVOIWbW81HbQcXX\r\nTV7FlSJ0l3TA5zi2Hlzzw/1InmvwSgj7QzGmi09PeFzwmzczhpSH6MFlOsiE\r\nbqW8PspOGyftavwhR2+yuwlkYE549oW4+Blxs0GFHHMp40VlfEuDeXam2Usz\r\n3CYFYK4gbCyQxfG0icp9L2AFB4DhVHD3UwBYap9XAtX+/holyNaNden30ukQ\r\nNZVrP6GoZ59LLh8cR9PxiU9SAKQLndT4a+o=\r\n=VXi2\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.52","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.55_1658843330534_0.24907410089056947","host":"s3://npm-registry-packages"}},"2.0.0-dev.56":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.56","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.56","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"37cf542a44c21c265715eeed1994d220a6f77281","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.56.tgz","fileCount":129,"integrity":"sha512-yrAzsc66/RFFDMW4hSg6yYNKVv+aNn2M9kCEDrzAmHtFr/PUCS1cP+BQ+Cua0pZJPKsGwGN2vHgy2verfe2FPQ==","signatures":[{"sig":"MEUCIQCSl2Ho1pJZvtcWOvNOJfW5t+3sPlsvOlaWeqtF72px5QIgSmxGdbib4XkzloNZybyMvfIAuOiG50EjhtBRTMXXCV8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30017800,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi4kyMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq37g/+IF5YHzznDGlgQRbQcXAanNZ/669SFxWD8T3r7G8IkPDjid7G\r\nA0usVA+OK4EfcPJsG21zYeeUuejm4EsdkZNCWpjfpGfVADZRexDlL7OsGQ77\r\nE9m9Bt9a7dkLi+qK0c/Xh8NWDjKHzlMgF3ZB2ccn9zTUAJaiE+HjMMGh79hr\r\ngETwWQ9pMlaRcnGBIP6KW6WsXXaKP8+qCaGaFXX8Y2fbym/jFpvL5cFC1MRl\r\nghhmEo4fzhz/fF6nut0ovPD3zz4BkxezW3ycwz8a7hTR2hSdOZez1/49yshh\r\n8vI2abD5bS5I9ny/WuFtgl4FcFrfJNisKQsMPl/EVnlLzrVYh2rtyE0kwZ0d\r\nX3kQ7NTEz9kB86ZuGUkuat3f8xsF2izuR97XRmeFucVPQIEHIYL6NXNN2aJ9\r\nCYzWzdQmCe6vTezICbRXmJ4gv246ZaIobnQuwssqY7knRV1QboXZk1j2i5ZY\r\neiW8ZPSReMuvS4ugrdFsYoFAoDz2PJEYmb6aoqGIAKR0knK+ktcKww7kFEwr\r\nnbOyLDXTgJcXUg9oJbRGMKDcX2KbprMd/q54pvPHx8wwt0dZ7ACmscpYLFDi\r\nvYiX1bcjP84RD83VgtX8lJASHFpUF5CGQYP1/0HSKfg/D7UbeK4KwGgQuYq6\r\nvPI8sXYRYNmK63GyEramxKr8HILmUUlB8dM=\r\n=0Pek\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.52","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.56_1658997900548_0.7280984708882934","host":"s3://npm-registry-packages"}},"2.0.0-dev.57":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.57","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.57","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"de07516f1762e4f23f115e43b65100526fc513da","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.57.tgz","fileCount":129,"integrity":"sha512-HGuIibzTZUtRJ1fW1I1105y/YSUa/sLbfZhPu2uaU9htKSRVCB8dcTskSuiNvTJf207ePsE7DjCLGDK4ymA60g==","signatures":[{"sig":"MEUCIFJ4cWlf9mxa0TCCO3KjONLHH4hXLSpa2wzt/3ZVhVzKAiEA3dWsw+y0gMdGAK1gtqb+Pf0gFROVeRgdlVJngXVG8hQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30019904,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi56cwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpBJQ/+Mcew0WjNZSvK2LoRI/PaxMvQjvOJCiYHPFbWQQAeGWvRmk7f\r\nxVgs0dMxO57RexwHXoi9CkkYkdybEv5jnQ4/bxub3d97Ro0bZH+gREP3MhpL\r\nLFmYYScl8CoN7DmwanFJy3ePipUIkyde9+MLGCwsP918ebqU7tvSzmXenGFr\r\nXteZNOaANyLDcOVfyWfnKWs57iMhMd1nnl27sdt1ym9Tm6zWyKR+g1HRycr7\r\nSg6Q1X80uclsfUifB2NOtMKvC+4V+NDsMXCKdSnkOMEAhjtG/kB99EkzkzSA\r\njz3VKRwEWG5wTLNwgVnaO83qq7CDZXmj5fayEAmlsh9P0AMbhAA/eY38wGie\r\nGm7lRWUXRJOCNvg96k8jOGykGf7YHuaogJziP7aQ22bAJBELY68u8RXfWGB5\r\n+SiksRaqdK5tBxnF+/5D3Mdym6spmfag99beT6vCogZOtEqdlVdn7Y4mpU8k\r\nT35SjJ3JKtEKH8SwSgZcU9zGHko5k1K7nCYpoRs7QvmqWe9sSJmqko3cKIBV\r\nyM9+kea+snOhbETYv2lJ2913PNKTKO5dslY4LUc+7Dk4y5utW3rJ7SDJxpAC\r\n+4HFb8DQ+drrZq/QID1+8m2KzzxtTzqvsn9WAriRDCvSNaLp/ETOY6FfVs4x\r\n0et/+SnSzFuook2tce9CcYMfDOeFRG48NiI=\r\n=bx70\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.53","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.57_1659348784419_0.8509379992878021","host":"s3://npm-registry-packages"}},"2.0.0-dev.58":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.58","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.58","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"b00dbdf200e7956bb8fef2ea749cc2c0b09ed6c2","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.58.tgz","fileCount":133,"integrity":"sha512-Qv7DELPTHvFhf6g/MfmuNbT7s/SynU4SRNQXtLerYPvmONrfBvjh26xqjKnhIHUFrhZnrUWfSBWMtZ6cIIaReQ==","signatures":[{"sig":"MEUCICF0LpT8j3L6qWPH7Y7mbRC9RH7cbMRyujTcmfWczHiTAiEAnHklglFr93xmX8ozE4Cta2h9y3GCEsR2+Z9OJxRE6zE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30029290,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6TAPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpP9g/+MWTeui1LkQLIKDRfBSaun0eOC/xdL1cR6ZTXqWNRvVo1uUe2\r\nJIuSXBhtDzIfd02HKAGRfqsbZZcPfjTptVlt2gXMfsQ4+MaLLIrJGC+hSJYE\r\nDHXBEYAlYfDxheAOPy98RwoU+Ovj2GdvKkmH/O+VH3rRJJTHnSJcg2a3ipMY\r\n/0TUpNmD+KcABWNVli6XBlnDxOcIPXOTxjxFQu9OmxCoXk+lN8lQ1UjRHlBm\r\nDzme1n7g6gT5dBJjQyJAhufIiUyGZzzLV//0G81bgAzPXeMKD5T6HkwtJOyA\r\ns86u5jPHq5GkMRWQC8GIHZBGBzyfdmkGuB3ww6Sn+CB2tWxmtv+uAT9YR3Xx\r\nE98S0xHUWH4PjXikTXrbspLGVnAjgfDhuexhXFpvh/lqCOSGbnl82OgUZKRb\r\n/qzZwUI53dkxx8oOadz2mO2Pgb7nOAw2P6RwPdOb1/qmwua+Vj9GNcEKrLTX\r\nTmDVOty4hfcDoePG0y4bqkEihh+P+Pw8nO477BFrIhI/siq6xQ3KR+srXBDU\r\nHXenaiP8vc3TI/6ainHXtfIUirNF4OnAgt/y6n2gHbW45LU75wCb4cedhLH9\r\nni2vq2fHcHVr/N78pVpPzlmi3Tn2HEgocD7yIm9BMvWnZaajnQkYHG+BVerT\r\nAin2AiUjqELuz1opW8sVrE6pTfsbQNtbrHc=\r\n=GGFu\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.54","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.58_1659449359332_0.01841365014377061","host":"s3://npm-registry-packages"}},"2.0.0-dev.59":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.59","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.59","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f7dc5deced17c147cd72a4a589c3034ce0b90cf4","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.59.tgz","fileCount":133,"integrity":"sha512-BnJpZLCluejzHcy683wmKUsp9zmB3n6tu1RgcWkCG5uBBxyqitfffTR/lByGSktCkvBOpy/Lm741KFBrh2xK4A==","signatures":[{"sig":"MEYCIQCyaf068MW2V5KaPahJEUb/2XXWBEkIMEWqFTi60OAzDwIhANuI0puH3+PkfuBeYgyFrOFB1Zvgyx/rFlexlEE8GJeh","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30029273,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6TwBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqMhxAAgWg+QbuVjHNcVhI+vZQeBxprVu87cVlKJDpyXd3p36hDCTTY\r\nuE+tu7HI7K//MK4ij6CoVRbHtoYxyXqcTPRo9FPVEUQ7pb2oFS7h2hncbYsP\r\nV0ORSYmyXtttRwUI/KaoFrM3rhZqAIwm2ga1mf3qKNbcmSn4770mH7Lm1qIz\r\nKRNQ5sKTj4jeeFuBsM1Bm5OP5Ck4HHvmpMh9nvdgHGF0kOTqBTfMGKsCS4qQ\r\nrHSIc+eKFZbcoQLudia68TbbRdKtnNghrZfH3MdrJLm6UCpSBv2u6ZRQlSwX\r\nnfhq6+fHEQda3OsZmvf1/f9T5F2qqH0EMAxv15yCuxBcrHoDk5nj260o4oZk\r\nJd+8DC01DwKoqqtEfJfeRmpYNSpfdxjk3IJgTp8nXyOwEmmL8A3Q7o5zhxjV\r\nDZ6vKQ2UdkZZK/dHyUwx/7RLUyi7fcYVn9YBQecBK1qWzO0FiF3cKVo5WAyN\r\nrLv734tPA+Zrr+/5qFePke9xHnkdWkFTzsxL1Pb8LYNA/DEmfWgXEVjWjhE8\r\niMARYX45Y/yr4ptf8xBOaPBnkgLHMwoZi62oy23yRC+SJFT/VZq9Av/56/3j\r\n2fbzHdyGhUVPv9L0XHTJJQN5v9kZTFeHeLv3R98Yjl2QoWh/F2kIOku10Zq0\r\nYMdmrqBIG2LoSbbPUVVtwnD7cxYmt5CmNp0=\r\n=H1uE\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.54","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.17"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@keep-network/hardhat-helpers":"^0.6.0-pre.10","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.59_1659452417032_0.5900626510520808","host":"s3://npm-registry-packages"}},"2.0.0-goerli.1":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.1","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8a84d2c780b197ff1e676ee047617d3dede7e56f","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.1.tgz","fileCount":113,"integrity":"sha512-8wgWStcmWXJo0g9pRGQOPJ+DKzEkxcDr8WQO+FFzizsZU5OJk6RNBxToyqAeDQDAYqHoO2UEvt/Vf5qWn24Wfg==","signatures":[{"sig":"MEUCIG3p9fIE/er0pTWi6J7WTCW9XYwW7M5oYTBA4QcQNRdPAiEA+mDcqd8BO3rV+LrTLQgd4U/S8aUZhV/afxQfiiDEUZw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25622461,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8g2NACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoeLxAAoJC4zUrliyW4ThxWX/+Lmqi3KaMfhK0zmVdeMT2KICjS6+c0\r\nS+9RBlEcR+rbJ22h6UWgrJJj592xr2eVrsh311qiTPlnJseDjndXPBtj8OIc\r\nfxhRyFNp6cv1liq/sHpsKbPuHJFkBro03gw/FlIv00BB7A155Lyi/KNPcnUE\r\nXPhOLjeCnfgVdidwPRokiFW3I4RcOY4P5EhGOW2YE2B/138rsU+ba7fZEbu1\r\nJ6wdntdtFwg8pZTT0xWyt1cIlRpaG6Cn4RrM7jkz4t4CZng4fzvNx7BdmJCg\r\nlGgzuV65ijgSncpvy21H9YA3QZ0Hi54m8PcJt+zryue43itaAxnznVC21pKC\r\n21o2xadlr6dk/oWORlDQewLYf4efUELBY9RldPqI+Jd8I0HiZ41kUXVMHn01\r\nqy0GQWgj+TGHCRSKUM/5TTX5iW4U9n5f7oGRXr3sDV4ta8SXHH/APcywQs/O\r\nzVUaOHE0OHeXkdKaCW+/ksMVOpd2jiX4ToYE/i9Z5FXcCLogvKSTk2jQ9XKW\r\nFtelFNLVd46D1BysOorkcqVVQNfKnbP+KiVoreYSIPNT/IwWrdvkUpX6l7Pj\r\n58mfG8Wx7ss9+0t51CtJhhDQT+GhaU9UUpoX8yx/j5brCMjVA9q0fM81ne6h\r\nCwbAgbCgRyhyEYk4D8aor9LDBgYi040OqX0=\r\n=qrfS\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.3","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.1_1660030349173_0.8854959539134895","host":"s3://npm-registry-packages"}},"2.0.0-goerli.2":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.2","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"858069c51b5c0b6bf763c7c3bc942e3aaca743fa","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.2.tgz","fileCount":113,"integrity":"sha512-5OEsvkIPpgXL07I3yTC3EtMPvip1Uo1j4qkLmVq3mlVVCSWYmPRrS90WxuVhXKqMivqylzq0bWpJae0xd/UUvg==","signatures":[{"sig":"MEYCIQC8mHyFxMHcaDQvbnYKfBAA2cUY9LgeAotlwNPs9W5xpgIhAM6trxemRzG/sp9F90O6Bf+QFy7M/lTGbKUMK4sy3QFP","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25622448,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8ij7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqlVQ/9F4Olq1VlMFavvSMuFctvI4vdjYSPBfNBnJMMhYe8EB4Zps6x\r\nWSOaFMdRdR7+ztcvr3OJO7iZCy4jOsqMzm9rcLyZiLb5t+PKqC7e51ZFZYPl\r\noXYvnUtZuHGToJtMEJg1/pJtlkCrH9IOjGoMvtOnyHHLeevJtDF4jNLpn0bY\r\n5JPGHtoRZHKyV6x6O/bxO40u4SPi/46TUM71UMUzzr1VX3QFxdLLDHlVlzgk\r\nQpm1psICfHoHeo8CLrTB8aoBbG9AINWlVRsyEP50rK453C598ss3WZb3ozld\r\noZ0orkqe9N1GVqilnIBCtDEV8GQ+5pF4KZNnTdy+/hRSP/H3O6trt5gxAhoE\r\ntd0C5/6EpW5q7l4iZqTg5kkKfuse6NhSKIkF0zmrCi9TcqVUrt0OADa4yksT\r\nzsZx4LTWkHbBrlYiKahQ0K43EBYxygCGegZRIIlDqUJPdb48uxbejo7s0SM2\r\nV8cLvYFoJyzoyZxizSfuwwC/PRLRpTNTC3/8DfeBsqSpI3hYlL/gROrG3OXK\r\npWypKzZg8UgOoDp0GkdUm31peE6E6qpX2FE2iyx+FUysVK0MJ8mNotws/cRo\r\nuo95sPdg0rUwNpvG+MOdqukPbJEB6Lf4JYpGOqI7Rg59k9+rPGuXNBoFNHIY\r\nxTECL4eGiIfFH/7Ysf9UCuk3KzbnUlr2kJ0=\r\n=clcB\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true npm run deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.4","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.2_1660037371146_0.03102375192782736","host":"s3://npm-registry-packages"}},"2.0.0-dev.60":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.60","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.60","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"0e16136d09ec4bc74d53f3c54a8afb7aeafef80d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.60.tgz","fileCount":133,"integrity":"sha512-i2FXLYKgk/NiCmRMWDFiDx531A+SZktL50j8IAH6C7Pu1p4/sNBWUpC8ylrq9OJrSDrQct3cZjXVtmH/EclSXw==","signatures":[{"sig":"MEUCIH6bbZy372Ps5Gm3kL+cRA431aGyV0C80Bk+lWhTvk67AiEA/T+5YlQYJhXJGJ68iSUkNE3gTFKURg5uYEUVNQLBCdM=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29673353,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8lH3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrKAg//admgNs4QLUxyscLc2uJWQgKbRUTq1MtWwBUqxSz6d4fnM+MS\r\n3ipl+9hAyXG2bzYmZvQjw6/JIgU5NAJbYliSQ2xjb8hsGuracXRyZ3l5wDs1\r\nn126aHzzJISpGvT5WeI2yzqnqd6xid6TtPEyFhyF4HfATNdauhxyLnuQPyTW\r\n/uRo4XHBxAn+TCkAw6FePEGCMkqkgOW6Oaszr/jR21qj8nD1Pux+9euUf/c7\r\n0SlJ62KPbiW7WwhmoqcwjVpFYFNG36vbnMc+HTNWV4hq7/JCc9fRWh47QFui\r\nYpXJN9ETU8G0ZfJc5m7IpsT0RghF2+gDqO+jixQVQ8WicMfAzV+sPUGvPJXl\r\nZA+5N2jyvETdtaULdm8xKTDxDByrJ4rfhqSpnTcA7StcQllZECfaPCwMJ28T\r\nj6Oxp9UQaUw9gXFVAoIpXSzoMYRtnQ1OAv61coAO6gHSL5AcLnDgHJW1EGaD\r\nk9EqVfwgd73Koa/6HP3iXllnY5BRCobgXBPmCUTEDlBRf1/HbNQIJe8wzKwe\r\nHoYzV9zs8mbOiW1XsZ8KAOO7plcD8UWl13yoRvSW2QnRkPGhqxaJBq8ogilv\r\ngB8edyFbB14WJ+atB6xEkusWsVre00HiQuhi8PwSBeOQaccmTh/H5XFu3NZe\r\nveY6Ub/Z10ik87lzAtC+xwVUuLauiogGNgo=\r\n=wIy4\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.61","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.60_1660047863561_0.32357814261658446","host":"s3://npm-registry-packages"}},"2.0.0-dev.61":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.61","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.61","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"4bd5c6786ae100df1813cfa16c654c41513105a3","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.61.tgz","fileCount":133,"integrity":"sha512-vLo8z33qHi0O8rbdakvFyQbmxS3rsoHn5AL9BNwjzQqmhAjIBgaAlnXfUJe8kpWo8HzsXl+NsfsOO1vACnTHMw==","signatures":[{"sig":"MEUCIFsAQtrbGjrAwIsm0KuUoL74o4FRP55oYTYVQfkS/cWOAiEAz47TqomZh2T5ehvrlE4RWvfBGQ2KJz/yGECmOMv56g8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29673621,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8mJmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpAyg/+OiRy/7Udj5wjexNoisHN4fBYgjNTPwvEuLwEtYKdQjItIMkV\r\nVdapCM0/xBqN3BgISrxKtB/WkXItt5SrUa/jLNDNQLCd8ehEAHE+Bl+xfU/B\r\nUt8+lmixQq6zJhrsWoQ5KeD8LAzUYwUXshmEwyLoV1FJVhpgY8/hsm5MnCvh\r\njse5fAk617D/uoVpLAJvq5bbmFVi9p9qu0fhblr5UI60cQNy0nEUkLXwWG/E\r\n2rVfFuKfUVESgxNzDGSTnEJZaWbtJj6FrAQLsT4rd5Mf58Ll3CD/T9D9XWkt\r\nJY6QStWpuY1UlposHsYp2spwfMrB+0bEMM3zLYBh5RKTnVcLI3xE8Ng+gLX8\r\nyk2vmyVlKJBDEJVRue5eTRUv40IGLnvZE13A6gJIwIyBrE+nF732anfKuCFb\r\n53ixSC7D+G+JQb5aCzYOw+DhCT4309+cmp2hi/83uQs0yvZPShUzQJ7A0VU6\r\nsHbkeYoDSJIcvvqQue9dht/6r9HZJjigM8nAv+au1yDVGpozes9ZG5TZLFCk\r\n3fWKoY/EE+7oVrca1c3IeDq3/kAuk1veqBV+4AeiLSxX4IvRZMFeKIqY6l/Z\r\noDnGLq8NV1SD2bt/pIULG/YYGEpGVQEUUkDulgj2TEerlCKn21BVOsxZoSSq\r\nOqpAGzauJHI/CvxJbDTwHuARQvMUw6ct+wM=\r\n=3rmZ\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.61","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.61_1660052070267_0.29664697864971723","host":"s3://npm-registry-packages"}},"2.0.0-dev.62":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.62","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.62","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2450e0a21c97087413ed6d1b78b86ed4e4714829","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.62.tgz","fileCount":133,"integrity":"sha512-974OWHB084X/lkkwJtkv1jeQ7HWOxuSbSwg7f4Z+UMaDQA6E05PeR9CBjcpP8H56SGrO92It1tNIVpXMmGGFKg==","signatures":[{"sig":"MEQCIASYEHMmnYzDgcS2OolzObJMP2Pp27jet0NjbSmvcBvoAiBay9DtVN+dTqft4cHiZG/13VzNI9IqHRsS7SxG/Ctadg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29687886,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi86KuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqK2Q/9G4tUmnUZvzHIE9fKWtuGeN5cwIkP1081nnzQcxPtunkhurGs\r\nHoWROXceAO1JLqlMBYpwPKIZ9L2oN+oBxtgvI/LOn1ssjpyk+ra/MFCuBWWo\r\ntbIW8Vs64yULuTQY4Q6IGkCKbG0ix6bs2NBzawb09KbRugk0MOWTvYsKGE0m\r\nQcMLn1HB9jdlWNqiyZlF5RgPwum3excnptwmDxdox8wgnwi2/8FGqpZEitjL\r\npgXs6tugPxc8YrFuRIGYH5iEc3s2/GyTkVYMkNzHkb7VgyI9eSgzlYpxUJ6J\r\netp33+AZVCn9JrT7u8sg2rgy04/6m13M9FRBjLII4aUFjcR9LyqtMKDp5klm\r\nQWcqFqmJ6fSGmqhqggguu+KhcmLeQutEx+BOuAKIlOJIWS4pYU8do8i5u3D/\r\nHB/b5K0JJ/d7u1UTQx2l1De29R2GaGjvAQigvjLCy4Q0r8v5IRaL5u56yjbM\r\n/T2OfaePx+f0WUyZ1LWtqx5rQMddpfe+sH0TFkGP0xGDZEri1HND0M87II6A\r\n/QXtmp/H5gW+TReor7vA1meCh3udHNJMZuuzYNoYowFB3Pqt9zqmZq+33+m5\r\n2CH50m5I+YA9/jDzIeHvPqq1IzEsziJwC5o5+AyrnsEXViqUUD7AbEVJ8AiJ\r\nPAF0KUy/Lt90km2t7WoJCd8AXBDpDElZBqk=\r\n=tLug\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.62","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.62_1660134061969_0.8170517708874145","host":"s3://npm-registry-packages"}},"2.0.0-dev.63":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.63","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.63","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"85ac2e8a598ccb5ecdde05a4b96bf6b7f015a326","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.63.tgz","fileCount":133,"integrity":"sha512-OcHYQ9WjJRqXxSTrAl5X84UtYi+EWTQLZ0IjBDI/ro1sGI76QfpELxNj5GRMsfoDdlurMdpR4I4huoppREl0eA==","signatures":[{"sig":"MEUCIQDJ0freSyzwTc3mHBew95UYXJ8uobNHVdYczUC8dt1kEgIgaUO3mnYEBxJSWez0uTYdgPsbir8Lam5KpQfAsK6nt84=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29692343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi+dMTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpWTxAApN2pwUdJsNr9JlCTuEaZyaAAGugd4652+sZmYHVeDrcyJlbv\r\np/S6MCDphy7Ogc6YD+IzrouPNSxB0U1ejn3AvbVL7JQ7f3JIYJAU+SyPN/Wn\r\n+69K13YNtk3dYXVWkBxHmjvREVUuspKXPLTtZr+jlK5gRyPEXFtP1Jn1XZ3m\r\nEgY/qO7pw+beDfTsyeId1fU4Md6mL+/MEXuyrvvat1IKVzfMFFyUteBjvfxt\r\nWoDSxSem/tBlq/ZHZ8BFgKEP+AFWA0tVguxRDMeK5m8r95mkz7xS/M7pCW90\r\nPHmWOJ2CeMuBDXbOVubmLuya4048ZqjQHHKScV7GZBaDAl/ll75iP/qJApsm\r\noWWmQDVuPnMhzcVloRCDV6WYL/6GUqQsHEXkrpBlQsYy05PxQgqV/TLGyxlP\r\n/d/4lZFTq3L+OACZHKJwIWX5/xxvwWZ5nGuAqfsSVSPiNllue0Qs7nxv8u1m\r\nW50pzeb8h17poTZqIf1Z0g8xe55jXyrSpXlaaiIZ41uyR/qIONl1RkxKwsjm\r\nOY0GXr2QUykrf47s+UBOLery7TTJ5Wc6ZaeF3/DLfOeIpJUZZhRIWcLPzUoS\r\nlLK/4v66TI3sWcesd7MmMJYdNMYvb2a7yV6P2IXQiuILWt2fjai6m1vY23yV\r\nusW4Z1Qo9+T+weBk0M9AQCMOOJ3c2I/Doek=\r\n=sPuI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.63","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.63_1660539667546_0.9838737016627919","host":"s3://npm-registry-packages"}},"2.0.0-dev.64":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.64","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.64","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"4eefd8f4d20a1a7d88d1066cacce5f0d6bfa2907","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.64.tgz","fileCount":133,"integrity":"sha512-jsz8dX3rj5t+Ug1sloey01ZLs7kMr6OV8Ky0mAj/cDicBYCAHHWhjApfK755ccDQHVgW7+MTQpF1mDW7W8WlPg==","signatures":[{"sig":"MEUCIQDfZ6lmHcMPtq5W0080hyQr5xOZdobcCkevBZIVOvgXawIgaiYv6Uh0MtWWCv29c4YEyxo/29qxBYpSgC9s3tmvZuA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29692343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi+dNdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq/1hAAiSxj6DM4wPlyJMvyzfNVjpwnbyRjOTrWluaYzoDh+Wlrlh6i\r\nMGUevfznIweLyiL+za7cxRoTZEMI6L7lqvW3hrMM1Oddgk9NWx6X4iZ2ERaL\r\nEezmETju1PyOGqDUYcao/jRS2W2h6MZ472+J8F4oqCYEDHT5y0KccR5IEM0X\r\ne6ElMcPvncyP7N8oSs5HY5t6sR8qZpmVQU6f2Bk/fuJNn4QdJplasahVEBVB\r\nGi5W35hcsovTnqna6dWDi5nqoStEKRGZd28ng898U+wvTGTZfnH2R946lssR\r\nNP7wibVlE/vgtp/Qgldma+gxju+rNDNUTzK0zSdVYg7LGwBQ2AxslJKicTvC\r\n6bu5eRq37cRu/r+RevxRTWDrtPfIa7fxAtf71QCJeKvfLrA0tAYxyjamgVoW\r\nyTfWAC2/UAp9bkaeClsoOIwrPNj789A2A6PEvntLEmgTArcMQm2pCK9Jik65\r\nz/X4wRAUMFkB4UxzYRf7k2lewCYddpw6bFYkHY7R6TOZwKVF/68elMwuVGiq\r\nI1Sz0AFWRvrNIYV21gckdt0FAw4epepv8ZbWzljpKk5D828wypgREFVEbEOE\r\neI/0WRRtk7ZPbERpw3PDKV3QmFBhNeFmOCYHErpM9Wpidq30qm7/2Tz5sgLo\r\nkCII1nCVaHD9qT+paVzQMaw456WCcCeZE0s=\r\n=HhvB\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.63","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.18"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.64_1660539741098_0.09993847041495263","host":"s3://npm-registry-packages"}},"2.0.0-goerli.3":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.3","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"7e0cf24b872e476e8b4c8b08caa5c4e8a9114919","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.3.tgz","fileCount":113,"integrity":"sha512-H6lA9PZ4nO+6oJX4eta6g8icjZUl2XxcOytwUFYGqxVckaibHQMTX3tdUMNftSlgSUjgXK1bddpQ25HyOcJbjQ==","signatures":[{"sig":"MEQCIHPCd0T4jFVnBM37sHh9icv3gxRVBskmcgMLyjMXf/vwAiAO+UvsUhlNUwP5oRwv3MGvngeeiA5HYR4dsOe1kP6+/A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25636988,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi/SpTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmocAQ/+PsNlslw2IJnWLLaAgvGp++0zHr2NTPrLhSN9KjnDVkVbwK9U\r\n0IKstfxJ1QiYeUcBPwfwqgEATalv5o3ktdS9JlqKmu4qppBHx/+A7B9SSPN/\r\n+VsqiuzCoDlCXq7a3LAy1dqff0BWTYFpR7GsSXL82iVdIybeHGfxyBVR3pr6\r\nu4F4eK7zqqRYRcRBDsthsmSQGglAu0L3yWFdKrZpsgcXLMnVi5TFfoDTP3qx\r\nJuEB9JF+vwv/vx3NupEqcy2bdQkQXV6LWC+tbwkUovDTFuDoUQ9qWszjpIGS\r\nuf0VfPyYbYlKYCYYJNvII80PskYxf40uWp9CLm3MzzXBnsYEPegqE0w+Onx4\r\nO5Y7PnWQWqSEB35iJibOx2hlSFbrZ/g+Rt2/ihKZLySf69oCwJ+zPhWryzgC\r\n3GdAUkEXsDTou/RJ49ZmlUgDeVpCL8fEyVhpzVfqwI9wG0IO3zlQQZuz8b+/\r\nKSGqPMwffdMpVDso3CkABW+zaWJZRlZGwY0wBW07IOYCSqKoQovO45aKWRxY\r\nlcBGgTBNjZFHUeWrnYzIeNvTBqDsJTYTysHx8UP7Cpy+GTJsTiRakJqVVgAg\r\nZp8knQ9dsYlydD14p/c5Bie3TzqfuDHmFK4PEUGEODnOaIEHGVMrmr5azSaQ\r\nxUYzGzgAbZ6sPD4UvPod/zJpnp6vt8zZLiM=\r\n=St1Y\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.6","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.3_1660758611262_0.903871844865348","host":"s3://npm-registry-packages"}},"2.0.0-goerli.4":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.4","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"250aab9b154fc299f7c7ceef3cf57d083c33dd45","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.4.tgz","fileCount":113,"integrity":"sha512-Kmkp6oE8iSKZgxw6jlS7/9iCoeonmXdAPhfScFyw6ayKCYIKjn14cqwlth5mnU0SIWp+gV8g5MXMKSRDdwMWgA==","signatures":[{"sig":"MEUCIQCTESXwf9GambUgdyDZB0EhF8po9R6pO/9g5aaeDRvK2AIgSCKS41nO7m1HMPNMFCYeabgtw6imHPJ3w8DJOg/cxBw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25637428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBgNIACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoFoQ/+K6KLg0++J12zQ9JDap1l/DwYDlHoXsRUAdOCXbdX1Lt3pTyv\r\nq7Gx1Gg1c2jN7BmMYb0AAXkoVSzhD6qBrHPqzwHkcu/EpUFo0ZtpH3PwlIMP\r\nl2TASPLj8F7c0H0CCEV2pC1gx8NOUCtbDHkhExFdO3WFvEl6nsODi2T40Tb5\r\nPSf78jqZFPn/9z8wUkPTqIqVFO8vhLiVvlf8Rw1DTmg1pFQrBt1rsLJKezMu\r\naXa4Pth0zdTD7a42lrOk2FVJv/4kQc2I46fK5jly8h9p7rCVB4ZPcd73DaJs\r\n8BBMwm0L/NhrNIAuHXOyZpi7s+bDKGdQ2HzKPJw3Rw8IYtXHvecOdl+03nio\r\nl/oW9dwzqckUS+d8r+hUmV8dTtuvRJWwd1TVxXbbiMAI9zLh42GUAeDAD+GY\r\nMeTaz8jgCrc+osDj/PuHhh8cQN5W2ahF10QX4d24we4dIFZVX+XOFpZ3UyrB\r\nebhfov4faeKO5qTOvi/L7RYYQruepwe151xh1IOKCCJXdJyc417RO+MhzYRc\r\nBylZpYoUk3EbZmW573c4sz1UXQsZVY+Cc+53TA39ZjMfiiWtwP5TflsWueJa\r\nEZoFZIUmw1oWUKoQMXzXGVy0xg1zYGXeHEG3VvDYWBf/QMRdYrfc4wdh+oQT\r\n0cUfP+ieJRaWB2IiG68lAeTqZnwy4OYESqo=\r\n=jvcw\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.8","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.4_1661338440254_0.9465771247284245","host":"s3://npm-registry-packages"}},"2.0.0-goerli.5":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.5","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9d8abf819896d403e6c926097caee1e367851162","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.5.tgz","fileCount":113,"integrity":"sha512-BEVtxFANxAAsZNjK8ZXqkS79m7ArfcDH7HlJq9oPrZQUGLTOZRyFsLPiBrIfyO7S95TCNq+KbAPXtRyDma/EwA==","signatures":[{"sig":"MEUCIQCzkLd9/18lq3ZndjwoEniGmf6yIXpaXMdETsIm4BtSTAIgU5s6bWSoWJCPxjWotq2ZXzV6X2LGcIMgl0v17+UoeyE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25637432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjB3StACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr6Iw//eh4+KE7+JjCfIhrdfmvUVE5HtWg75a4HMCg8QYFX/HRltWUv\r\ncEr9f8x/LzM9aPb+Ljx+ArBjDU/LTLBT/vU6rbNypM2E3e1PUgEvexVf+LZo\r\nQDEDt5eM2DkQGR/yjYqNa7aKYuYYRlmiFpNn5sicWI3rsAGcEseRfwzrOdNG\r\nOSr6Dqm95gzZb71x3tcDmqpPKlYVBk7uz0uUqjNnUc8N3+Up3II5IpJTzbVX\r\nnTXXjSF6V5PS/9fn2OyUh/lghybuKERG5lfOxeDbILOZDYSV+vx6nuaqA/oC\r\nXwedXufZsy5tA8THVdDlPXj/9QzrOMQxFa4SH+wndvVHFexuzhQ2hO/Q0xex\r\n+KdcWrsMK+7+TIl2qRONygnIYdROYiAeKiK0/y4B9gH5fWILJePTnu47Uoyk\r\nD8HH68FhPLk+64YG7uEeop37ChgcHHt+wDHfs2izpbmTzzSloE+UFKbHXLLJ\r\nfXd7Sk/tEcYlLVRl0XeXRwvwMSEEZgFW/1Ht3QCJDIbnxTEop6SLB51CzgVG\r\nzp9n8/ww8YMZEv1RqWfHGaraJ+BGMhS2jCEWyXfbz1DPLIOIrmPJ9YHlsMH5\r\nHNrhHbdTjp6tm/QnpgWjqzY4Tc6CMPj1HB5g0yYxf2yBe2h5mS4fmCtYvjtO\r\n1dFVf0lW/eB7naCXicGuT/f+72x9xPUeZos=\r\n=k7Dy\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.9","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.5_1661433005269_0.1271285053939022","host":"s3://npm-registry-packages"}},"2.0.0-dapp-dev-goerli.0":{"name":"@keep-network/ecdsa","version":"2.0.0-dapp-dev-goerli.0","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dapp-dev-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"80ec2cec522206d6dbd3fcd44630abeb9a3eb077","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dapp-dev-goerli.0.tgz","fileCount":113,"integrity":"sha512-bcOQPvvQ8cYwAn1guBYks3e+NiJ0HJSHtgjSAayLsHv2TDRTSfXJT8hbHgz1a6J7WoV51geR2k7PmEZMv6GMsA==","signatures":[{"sig":"MEUCIQCoFqhs7IaIAD6KObeQfhxktHP3zCTCiFJJhnqyJgHKpQIgTX9QfODslMDCwxvdDRst34VSTVff7vYC5FtLjvL+vPw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25667304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjDzFyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpxzQ/9FYVLohXEHheh88eWjBWLkgv8J/M3YBz2UZU8n2QVTkJc8pnE\r\nyux6syB0qOllGinYl1w+St7SLIgL8sYITB9A9UyxYAu5NhTAbsJ2teoLRIB1\r\nVoXoD+lAp8NyadL3zVdWl9bcE0H6F/fy6CbnIaaGn1q6vTOlcNkLB1v6SMqw\r\nFXHSE3QzrYoB7YAURbYLmZ01yrSBXRhtun7FgBK05PqNDVV2PUbHkUBgOgUL\r\nx1JhjsQ6v4gL2PGp4qrAO5k3ybgm/MgrD7l/4cyoM0alb6ZZohKIiL6VpNX1\r\nvEEZWJ1SAq9/yhCFmcBOLp4z/kj5EzGP4I5NMSG6GxWU6uRwvr12w8uNVKge\r\n/bFfrduV9t3UHeduxoM/Y8UyKKlSefs9fttH+xCuj3KX6wNJc4GD4sfFqLYr\r\nARHG7rt3x0iB0jZaDAqy6tZLBYJzXMFCfWCjOooGWqCEpA2rvlhrv4HqrvaF\r\nzgxU9U19B7GvlO7rgB/poPLnrzNT5j5JguLxUnjuZZfSgDjlvS5LrHoepXfH\r\n86ui9PV67Thhha2aDA+4gk0WVGREXNgrGdJGBY/2tPhE/Y5uvjwVw+VJBnui\r\n3tqIS9JZCg2UXu6FnWfCjFRoBpGV73+Eki8YhF5XEOffTMBQSEuEfwI5huKy\r\nzG5iPwSUZiOogSUZ0CrllVDoG3hWSkEgd7Q=\r\n=Eqwm\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dapp-dev-goerli.0","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dapp-dev-goerli.0_1661940082140_0.12291145586645658","host":"s3://npm-registry-packages"}},"2.0.0-dapp-dev-goerli.1":{"name":"@keep-network/ecdsa","version":"2.0.0-dapp-dev-goerli.1","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dapp-dev-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"25e3d5a19ab68f88fbcd2160865e4b87ad5ae86b","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dapp-dev-goerli.1.tgz","fileCount":113,"integrity":"sha512-8VyyvWJ8k8/qs57z8/nxMuUui2i+4FL3MHHUPrIzwB+WiYbR7mOnNMO0/ucGuEwQ3B1LjLiWLc3101pp6JihVg==","signatures":[{"sig":"MEQCIHu12ly/gB3EZ2iwiOw4TgGo2xxUH2JMyRYBjoickeqQAiAIfftOHy70iE/2JhAVCzuPtD4Ewf2WrMdHD4f1FyHPKw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25667297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjENiQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqYuhAAgBYoKNRbPT6uhNdOxokiYwdNOxhtguTyuPrvT0ov7B2i6f7y\r\ngVlwWf+4ypwjaDAI1bbB+U+PxB85OHFM+B3qq1KUnD4uCvj+/fHt/hdQGT4J\r\nx1wwPZRDbGQYLaPjWGXPly1zUbgd5pGxf0cZvOCzBRIznEk3t7PAwyYZe8ig\r\nwrmchh4Hu1vK9+Ylwxv8zJvy+tZH42DCAzcyOJ1UkFRawEECAhmw1oXzG7uh\r\nyQCvvYNYNh6WveRVE8mtzIVjXAQQKEDowQiJUTs1rletwy9x+MAh/V9VdKLt\r\nTRRDf1BIEm+oEJF6r5n6JPFwK9dmgZjftvH0cNEwbd6F96SUTx2jRpd5WhFy\r\nC1F/StZSAZ7ramxTfSga+08u9fw2Fqc+Bt9mUsWYrtr9pkNBrDfiomwPI/PN\r\n3hizMGmejFLKE+1bUX6/4T6Ee9MT6SKYrEjFYGZ/031i2TI+rQ6balgpOOQo\r\nI1bgWbgAKFHJ54jMLRutmOqFYpaNtP4ce5qAnMHqY7ho5ThY9okaJSG657hC\r\noiyrCTRb25qEubgY/eIAdhhZTtjYARzi8SqItKhgWRAOpfD7ndsLQ3PCz/2o\r\nQevg/ULjp9dG+8SuUU05Rla8fPPM1Zr//2MIlZGC3MjZijGga9sjJY3HQUyi\r\nuHQLkUfO/S6WJl1JoadAmwe4Du8z/tjLHWI=\r\n=nPQH\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dapp-dev-goerli.1","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dapp-dev-goerli.1_1662048400301_0.17760796474223373","host":"s3://npm-registry-packages"}},"2.0.0-dev.65":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.65","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.65","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8083f8cd01c3aefb0f57b01ba548d44a0e25281a","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.65.tgz","fileCount":133,"integrity":"sha512-7O5nWOLwyHGB5/ZzSYqOOAgu1gpQj8f6yZM3CnbZTw62eZkrngVqhStM3KJAIH3bGxxOZHx2FESV0voFS0KMVg==","signatures":[{"sig":"MEQCIGw0NXHT0pqgVoyT0xZ83Yp4gfi0dDqKtKYAdS/glHkTAiAM6WlIfDI4fKIJbLSh5YblTXxpTkGh5/QFGR9m/odcdA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29686213,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjEcUoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpp/BAAihu7XcVef1jFtCKUX7CrxM5LCaRjbn29Sxy1Mykvu3LkX4nc\r\nm681gDCxSggW3fmEKoAIi5RXya4doDsxTIQijfUsW5IAE1xj+kO6HmwTwDeP\r\n7sv+uKQ09ATJb3Pq1gIVGohyrzNrn0fPD8WoQRn16j6szATAySvMDl72cGpw\r\naON8Y3RL/cmTdITCD1qOvoMXXtV8of7ebMcA+0iWdQ/JB5m/kF54IjwT5wdA\r\nVSPmQnZGh67CG8mYRH8C6bb/RS+5LJL8CdNEIrbyE9NvMTxnTEqv1+D3eYjY\r\nC3SfUhL3gC8evHUjMkGu1tgQb3NkXzi4DPcOJ+ASGNI8iD2lkHMRie5sQLUg\r\nMidCtyBE8A+m0YPTgylrH1hG37wrXHRxw1H5r/ScKQIRk/mTMy1cPcETZSNG\r\nSW72gx0UP2sZpnvgjIDaG3YGsm2JFqOrqGo+7SDbBtT/GluZ9HOanF6dJ9/+\r\nhPqZJFIUKkAfO9IgTKgRz+jh0HzulPcxkm8boDuwpwPHbcDTydi8C+udrwfP\r\nTcRx+AsZT/bKpc1MZETdfjIdF+iY230DDx6wqKaihTejdillFE3YJqVMPPRJ\r\nTx7zGGNxsvDSN21xaj7+sr7DNTVRgLBayQQAKSFqCrxdcE8OzP3uqV3qUxac\r\ndwGrCxID30eP1hFhQbRnV+34/p5iiGM8HEs=\r\n=CdVT\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.65","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.65_1662108968370_0.6577395704319413","host":"s3://npm-registry-packages"}},"2.0.0-dev.66":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.66","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.66","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"34a32f7999957e5782052fc68bf674e516430e3d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.66.tgz","fileCount":133,"integrity":"sha512-a/khM+Ucs00LasuKkZL3Ed8c37UPc2JhYIF/jRG7VHyfrJ5VOzsdZ1+oAYefFxne4dkuO/wwAM/NH95PFTkKxw==","signatures":[{"sig":"MEYCIQCmGumrAQD8GWPPk0nuhfmGbTfKoHKm/gtho1Bbb6rXSgIhANmMJeU983fuCRFSSpZQ03szF0fmg6LyuKgPk/dFGVjQ","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29672108,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjEdQNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrbVg/+IBPACQbivGL8Qe6BZDmZEGhJ/m1kofEcUlYdhFSMw4ptVJuO\r\nvbxPej3X2dOWyxzKac6Y52A3R40JlFiiMzg/G35lWS1gb8Tw8UVvYz4xnwUt\r\ngTqKvhymwoNpfgZPqxqUqiMntitf1xDt8ism+nP9Pd0R/nKLqCQHWl+uGRhL\r\nkj/Yi22Hv3xT+YLt6EOrdQTvT7KUS4xSkqXCxh763Ivycs78L5GoIT+5tesx\r\nvJCnHMlcRChOtL2djO05Yey7uBSoOOVbSp25dBE2+QwnN5ANIi0N7wE/X+JV\r\nn0SCSngQT9dQxGHPpEPTBXSZXrul6SnPNn22Jc4k5FZxmNvZA5OfucI7BEqr\r\nwTzQ9knh6UMkkbqzLeWNKT8zy+825GEOws7rStyvdByAw5Rya2uwcfOzMPeO\r\naVdec8HrlMfm+b/nJo5tSHh3mej9sazGz3l0jCsHjZzGCWiSuO3DMXz8WHFn\r\nCFCQ6dU4uINRtkoMlPKLwIbu+Ep6DRlnjQ/evmosKIzxpDXxojny36NV2EKB\r\nd+NG057E6boA9omo0/90NEAa/b7q8fwN7Mq/NsR3z64yKm4fOTaqYi/v3j5R\r\nxim3KGYfKe4cqfisZH9ymg+/se/KX1Y6/RPMekHRyBAhmcWUdaW3jIgErRLH\r\nvWDQ//kbOtU5Vb58ede53hvMQ6Ime3IxRqw=\r\n=vUGU\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.67","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.66_1662112781543_0.25626980725451776","host":"s3://npm-registry-packages"}},"2.0.0-dev.67":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.67","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.67","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3355b89f989a4eddbe0f0d06123863e0df805f11","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.67.tgz","fileCount":133,"integrity":"sha512-HCdJ3NWnI73dipSJQ8xn3sTLSXk7JsqcVLdhjgmtbNXYfRjOFue1KICAgTQdghiU/bIk7BVDhbM3QndpYEaBaQ==","signatures":[{"sig":"MEUCIDgWOXBtq3TnTEJrfocWEIezlJZII95cO0MXFOk6TJ61AiEA0y8oz/rdCJmA81CAcOaqie9nprtG57Xz2TgoG324IA4=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29674845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFx13ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqu4A//ZeeZfwdZoYxXr//th/hjPOJVVsc2SA8TVMfXjxMAQvh0Y2L/\r\n5LFLAQLx99h6c8ZLtdeC9ULXla/0jEZauZNnkISoU9UC9W3uE1z+vU1dBLaa\r\nCCtoDfo00Ux0ON7Cc4ONfUu9O75LK9GeV9qCoprJuDbM4TmNIzEkKY5DFqlj\r\nuJ+nzzJyCjkzdziezF0oSiaYl7aM+fgdBI+9TiSmX/JVOKE47Ala0b1d5rwl\r\nYhvMZYUrxM2Y0Vl0RSRkwJ06OjyVOdz8vWHSAHn/KhxTCswMd8UsmmyovqZu\r\noA1YuK1c61e/JebCrvXYQWBaoBRATpXKup+H85yV/xVgA0NWXbJHcRm80USN\r\nafH0vJkFnuulcjh+qPjZhPG6+Q1pE4PBmfP7H4e3a3tvUzDljsyhQKh3DBmA\r\njzt73exTRBWzlwDpHVlmHGcO0w3giR0NAjbGIwM80qPG3Er6/uCSML+/NmSn\r\n7m1rd0H9IwN+rlbaHZlUDuC4NfndofGZW/f+oGYJve0uhnCvqYcQWPn9GbAX\r\nzRzpfmUor6bUfShjvb90SOXoRLaltwc42BTgmxruls6FblXNlRzgAHyBINGx\r\noYq1//sIzQ1E8jFhOg9Zvjuv03kBZJIBP+QNlaVvdIHg2LQO4S1vnLBar+rv\r\nNyWdztXAQwRCyOronKOKqaHda7EfaSxwOvE=\r\n=P/N4\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.67","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.14","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.67_1662459254844_0.1527262340285851","host":"s3://npm-registry-packages"}},"2.0.0-dev.68":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.68","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.68","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6a3ae66bd7f0099e30aa6e29cdcd4e3891aabf19","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.68.tgz","fileCount":133,"integrity":"sha512-WPG9LD8GSQDKDEGrcdFONfS1RBQ5RhN2wVsEZdGHwzE5bDaR1kHrmlodCo3a8oASpLpcrYbeze30kYKw/wzWfw==","signatures":[{"sig":"MEQCICl4P09VPTs7Uhj+q60Hoiirs+Bf1gCk6r/c5r2gTGTNAiAqXu+pJ384TVIJc7vKJc1jAlcZrpnlwXIt/uBHIH3awA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29676276,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIB/nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrw9BAApPkF6su7QphXfAA2fqrtoqXm0v3kRuW+gdhIAKDCH47/I8O0\r\nSBy2iHSb9xvnlZIord8WLeNiIle9hVDEfeEVpqlglNZgZ0p4RVYc+2CqxYds\r\nieEWUka544MXgh28X2pUlF6MDJVydQ4pxG5ExCksk7Oq1NXPIy1XnqH/w1b/\r\nKbSRJP/sBWnL1lo5BO0HYQ5lTf0yelPz+kZSMmKcOC7qN6ynTM8Y0Y1MKU7G\r\nzF6WgmFuOP3Yj+uarxKXjVpsKHnF2WCoUBRL8DuD0VBBxQA2X/NyQFifPvo5\r\no9HqYkoIcNaG0gnQ11Jen/jgi6Bqdt0jEFzUpCgUN6cbxbhVpJqEfLZ30HnO\r\nAfunwIHm40Cf0FQxv8oZUPuBeV2Y6R8ZH90X4TTvD3hJ2pe6M7dWsto9JjMX\r\nXqi10k+QzFwmMZgnaoGM5K4A+Kq5ekyw3YSBzNsIhQbZH0Frx12BwPJa6X6/\r\nvAsclCpk0jDCkrlM2KfztZm4ZYixtQdgpUJlF/pnLDlhL9Ii+QusNfYrQhen\r\naT2Co+j3bXTRbCG4hfdfqP13bRymVqLBZc9rSD11zY8APG5d9zgQfjqgWOi1\r\n7RhsXFvcYZwoTM/iFQHrPJRVTy85KrQY1D1b6laPM2wmFH8A/kA+e3hssemI\r\nbiS1WQdH018wd94oVeg9CdVk2FQjSoTAJRg=\r\n=d5H7\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.68","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.68_1663049703482_0.9808334322221026","host":"s3://npm-registry-packages"}},"2.0.0-dev.69":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.69","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.69","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6d4c8b0383986bb2148a01eda96b254668922102","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.69.tgz","fileCount":133,"integrity":"sha512-IGCNVUIHv8QYgRaXZiFRPLjQrlHQMNilDMbQNFjuLbyUoRgAeL2mIZ/9EiywLut9cNZJE2sRAi3wHP4HqGr89A==","signatures":[{"sig":"MEUCIQCx1aknrLGgNU4NkMpZT1bW8CttBLXhe+AuhkDLxgi0RQIgCiRtTduNynPhMUXWfqZNkA7EQ8X5gEPrifYePkO9lGI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29676581,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIKhiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoqlg/+JYiWJfd9Cjq5kYowk6AUOFq4i3SDsao3jtUqDT2+cb4TOq0x\r\nXVNhWWTqmlExf3zJOEXavfoHRezSL7s5S3RTvjCXJtGIf7YjEXQRXkGUpeFf\r\ntEmOu4cPzEAqmkQIAmd4F2ZEo57tAv0K1+ynRApLBbxcH9bI4LmlZfAnt+Jd\r\n5/LZy+v+fa5Cgn83zZ6fqrXk4GDWYqOBEdHa9+DIRa7s3TtzYSUv5/fZRvo3\r\nrzcHbTJr1hZ4sf5zNFoCXyVO4o2ynJd+rInNnwAR7a7/qmpBMckGae8Ytlxr\r\nyiK5rp7Zn3OOWl+9YVjpONjUHu/o0MyNB2i9yo/xCpUw5VZ5GggKXGHa4iI9\r\nUPe8TK9SJYVWxp3QHdS4neZNY+pTMqcOgr/glEO/6PKRmoLLBbskwDPiVA3D\r\neEHBXao8NbZWMxlb1aqdfVbjxJBdZXDyZ2vG1TbGqbn9rKew6ceN+8u8cei/\r\nNUgT63psPEcN8k//cMBW6fgZ4rFHwKtlOdxzkvrxoQ9PoaL799CH3ejRshpj\r\ntC4LTxeux/yNkpgBaPD7i7oioJRdAq8OmUr0S1aFaZStlp9BiVvWEQlX11MR\r\nR1puMLDvD2kHL86ZLxvP2glnFs3ZEmD7jF/tRcnx41IFWIoeUYhOZ8+zLxmz\r\nZfuOsH4hmo99Y8RmGnHsl/H7lFr5XtfTjvA=\r\n=dHHa\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.69","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.69_1663084642202_0.22998037480610445","host":"s3://npm-registry-packages"}},"2.0.0-goerli.6":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.6","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8e8d73f02a9d4c9a1261549654eb0cb029659157","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.6.tgz","fileCount":114,"integrity":"sha512-4d8YorLb39+FCIJEJ82otqvZl49282djHgnbSotkfjIcutjAlK7J4RMyHm8cqykatUH2HD+rRqJC5HdaH7UvmQ==","signatures":[{"sig":"MEQCIFwVl925h5mzGzWQHtZlEXdl+O9aTGAbMPv/igamMznLAiBVA2PbU1D1ENqOqL//2A5piaRNR70+bpqTetx8ZTYGtw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26435538,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIZJdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoLIxAAlPYqxkR7WrV6IgdzYdJ0J0t4cdkIYmXNhfttxWjJ4uuMooIv\r\neoS0W8byAVopc5A8y6yJTgmraPvBodzfRQLfFWZkiktLbtUHP1ghBXx2O7B9\r\nGoICG72sS1+plDIfvA5ME5oZEtQX9+O+L7YbkUZJHsbB2/0XoNS76T5K7J1v\r\nosz+cBWr0EzpA469XUs72U/GgrnIEDCMoJ1vYEPZxTVPAIn5lr8LG9PdX0tf\r\nqBiIN04jmzPcDWIcgCo5LLZ1fXUffoOrebLWIetAQ3DiNRG8rjq56kcCiSbA\r\nlLbf1PoaWpon9iO+r7285iXqVouMV2vp8V1Z+WzM+CcocmrPM+WBmyGNiL+e\r\ne8B4Z9H8ce8D4H8xnHR8DCyptZcbprtI1AUJY3oFrzi+pgsgWx+VtF5P2s5E\r\nhxoe3E/8LE5a7oWx8kqxj6xIJZ2n/zHoS2AgZvz0eyP2/TvAVO+JBUqSBJ4D\r\n+XifT+5hgqI3htmPxy/DidSpp0WN0msP0+BCfiv/Kduyx+I8YeVbeM4Ea2oG\r\nH+lLfLJr2Y5ci7g28l2KfvYOBksYGCmEPSxCXyuhk8OG0UNgiS5uoH4qVhG8\r\n4Sf6XHeUEbJBtx57mb2jLlyn4P2lygipaEfD3rakQGs7hzuRvHb+DWGxTszX\r\n3MNR0tdQE5p51R/m2iYIqlmiC/EW2kXS4W0=\r\n=exWR\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.12","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.6_1663144541734_0.44010335609387496","host":"s3://npm-registry-packages"}},"2.0.0-dev.70":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.70","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.70","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"09c383ead2080d07527d4fd9ee7add4417b884dd","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.70.tgz","fileCount":133,"integrity":"sha512-Qbcvbb/p9d7EnLyrKNFyE+hLL6BVJsyhAlVcTpFjJx4tss9RqsVO0TUo4PtdP0T5HbY0Q3P8WFJFI+bnaFcCDg==","signatures":[{"sig":"MEUCIQCwM8kp3W8YC6J2K2x0+eWyug8A95m4HpeWoHBNzoVnsQIgDHmrMHPuL0aC+kTxAQ10FauSh9QWeYO7f0Ch+Y3u0Sk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29676985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIZmzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpr6w//eiYuDuWA+oJIKHTyQa8tYxV4tqlrHFYQaznrRMtDLTJdSL3w\r\nI1K0wNe8AFOYZup2A0ZW6Xtrxst7BU254tWmTLa1wsWrqevfph/AnZsJE4KM\r\nV580Cd8m2wUInOh4IkhOSTgRgdURBadapKRyYSbClzV7Px90JC8tVq9P5Val\r\ngXrIgEQxnLLRTtRvZSQDIHmibE4mkoUbo+UKJeQayNA8GKN7lEch9LhhtFQO\r\nO9/oLku1LcEFPm9LtGiT+hCRx8H7skyR4vQ9KNyDQBYhjlexIlt/kgThzU7r\r\nN6yXV2Ui2iHT6KbNZZ11vhqgDDLbFB8ls53bYYdKygGkUHsVwAJM5pHnMjap\r\nJAPsQ46X7tj/74JVgCXV5hYJouCZmlpAVW3NfB1XXIOBrezMGhxPyG5+zzg0\r\nuRPoneWB/qzVgYYusqXiesTxxUYrtGH5YP3DhGvXg4Oa0AdwWhkGoK6qotz5\r\nc5fsqzgm6hz9YU9hnCjaVzepd9wdFZnSFZrClpPJWs5gmcKz9E0o9eDRgAKp\r\n69tf9zNzwRwNSBVQvaIPKUUah3goXjAOTqMHTMRDRRg5P/2+gXOu/PUfn6yv\r\nMhc7YW06WcJg/JkBLyKIu23Yb3C9kg7cVH5OkUysVSxwUFkyHRwyg0YJYpdI\r\neI3vKUB5Yu30EUtlc2jF79cWEBzGcFUCVOU=\r\n=MSbe\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.70","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.70_1663146419581_0.3026218002302843","host":"s3://npm-registry-packages"}},"2.0.0-goerli.7":{"name":"@keep-network/ecdsa","version":"2.0.0-goerli.7","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-goerli.7","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"487ee2deedd2a607a8c071aab65637c13911fe0d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-goerli.7.tgz","fileCount":114,"integrity":"sha512-EBSVSgo8Q8GzYfTZzBA1VPBI778rKPDN5XpwDQfJhgDxk7XzKTzDm4wSPgJBQHqOHwFZXHbwYcvEX2MJ76vx0w==","signatures":[{"sig":"MEUCIGZrwUjvZvJBHXBo3FzQRoHjYprGAzmMIstfnl3/w0MLAiEAo9e8QXBI/fucEyvDAoN22UU/iG1hwxd3TiMvx851uX0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26435918,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIZ7nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpigg/+N3nEzUiKFxWaIBJ8YA3NU0NNqyBo4a52gndhDPPnX3NoUqfl\r\nz1RNqORmEKG7oQiJdAlIamG2AFYjM0Q7OC5vLoa+zAdMKW3c0i8ewrZ/D/3v\r\njIePQC/cJQ9eTlMBJh2Q7mJfEVbHVc52+04klt8gwMQa3pxGaAGku53jcdn8\r\nCEE5O70JDwLA1UuWqyauSKiqaV0pPWEdHvV56Bm3oTEA4m71QjyMxNzYPL7k\r\no/8A6zTcDVSTWUG8IWQWPqfLByoyzaoh8q9tijxaKszPrnzFOPTa01YvgAw0\r\nhsrzWAV991kQG4aoYf8D8Rnc5AGF4AQRm9tgowGWBKZGZNi1ft9boyCCVfxn\r\nfgQYC5IhdlXOGH7zPn1lCow+Yn24z2SSmbtZk4KO/5bwTG5U/hidVjt70ezi\r\nfWdn8dqTjeRVfE/DtIHlm536vN7NBWzT5kyI2DO7VhRz6kNcMdqY3RIVT3G1\r\nymHm+7a4VRBAHQ0AhYzxfZc7RDz2v67Nl6dLcKs1pUr2v6BL52FQPNBTP5gm\r\n1yw7NDFuMQXDJb2ftFNA/ue2WFMcOQP1Rv4GG8CZiTlF2+ssNxeqwJEwQLuO\r\nw31DPxRjdLF2rE/z5Dm92PMUqha66XjZHVk8m/xGn+kg83Kn65U+l3A7bZhn\r\nbTguB/5d82I59HYIFcBk5WOQihlPnPqYfww=\r\n=zpV1\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-goerli.12","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-goerli.1"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-goerli.7_1663147751161_0.5463560342190266","host":"s3://npm-registry-packages"}},"2.0.0-dev.71":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.71","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.71","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5e47b19c70f4c53a6f8232c2b1dd6b865f4daa88","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.71.tgz","fileCount":133,"integrity":"sha512-bC/OYtYIvDD6N8A9L2C/0CY7yO2R2ycPHM7qHWlrhAyB38Lb8sm1l4qFEYdmWB16so/6in5esBGGtYZCLZe4WA==","signatures":[{"sig":"MEQCIDXOkAFR6cTSVEWxWgCpecziKCiJgOfn7gbaY7xFY0Y7AiBp71n20vt3xAi22PDkU0AAvZDDBzBBUbaWTTKX/iQThw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29715293,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJFXRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqRyBAAj96MdIgHEhHR7hT2X8FMPpDsGRATdl+yQt0xakzfQ6zXOAUC\r\nACyhHUjq6Vpq0qDCp6wnH94fPdHI8QepuIxTb8RY0xV4LmNynwDm2yWVsgvk\r\ndPpFRmd0v9Jwd0GevbCf4ITdBancu8/WFL5URvahC3KxfpCR8JDkX8G5poPU\r\nlA/vgINYvMOyT4JxD8UdbcWUyYIbWukjLnbh9qws0AKjUb8I/2R3d8lUNfp5\r\nLrL6J1S3EE5rw3zPzuKtLPu7HgsK2qyDMKRWZN83AUPsVnDFAbOiRCYlzR8z\r\nDvZD/CgjYdUGWqQNbWMEtzvVrofetpkIOyi/f59Auo3HfnMnfBmCc/oVcNRp\r\n27RexAiaoFDitU0u9bYLpwj3Ji0vqo1GrDQEoT4DdtkQdplCK5lqztKD60WT\r\nb3epUwxRx/pgY91tas5uTZ8HFng8XhT4QGFO/8z/eLgp2W2DNw5eekGDhuoJ\r\n8E8R8KLqhzNfZpnUaPC9gq9RWWQaW3QKYArW+wLJoSXncnH+eYSaISjBowUd\r\nkwnxmCqqqajmNzuI55cuK+5C1AIqeuN1EFRJw6v9onatVBN3z3Ddlg7FEAck\r\nv0GQse/eCAjfVvCwRIC0mjSfKbL0shybHk9Q3nwxKrB9fZEiC6ZNt3sKufm1\r\nx5RehRssn6EGRCmWyv6rKq95fOGUQgKpUdI=\r\n=bPJo\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.70","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.71_1663325649631_0.36551994834430346","host":"s3://npm-registry-packages"}},"2.0.0-dapp-dev-goerli.2":{"name":"@keep-network/ecdsa","version":"2.0.0-dapp-dev-goerli.2","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dapp-dev-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"30df7960cc3e0670f32e6c5b05f6d44cb1bc9572","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dapp-dev-goerli.2.tgz","fileCount":113,"integrity":"sha512-2HgxzZYR43QVTYTl/m2qLK8t/3adEuVbNCn5/ZAzL7h6qvWD8H3eXPlcz5cjqQ7T707X+yAho6mTAJr16qHMEQ==","signatures":[{"sig":"MEUCIQC7sQ7FQCA4DVJJnBNqqJCiHCQ5LcYecoM9B0Wtz3FcYgIgLtUAxa5QM69a/3JmxZaEfk38PRBUupC0BSHsSjC3TqY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":25668704,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjKIN4ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr8zQ//QySLe+f3l/hdAq+HiM9Do7nlNoxMwjEXGqAR6I8jNW8MB4xI\r\nR9ax429JmaLnZtxb/EifV/SA+q23NcWseJJ5SOzNJSfIjD4oGl0VHVBbA047\r\nVifZ+CegvCu3TYVhHeD08zKQj1SmyfAl5M4iIJW2ztJAPv1QoWyhEO54dO40\r\nE0NbUrZrQ6RuHQWMpwEh2dK5pLepv/cYB+TI2qy4wckz2KZEmP6x8lAB5Xct\r\nRhUmjCIAS2VTj90EC90GMALT+OjKYfbUIGdcS3+mSGA6ttTkaNxjdRPZWFb1\r\nYl8gB0Gcn/XfW9WGSPHrM/jjx3QW+FhQVEw2joAwJD2V9Zvi9YzlgkMle2ow\r\nXHLt8mXWBVdK70pByxUIqSZDrcg0NNEccKeeyNP5JUtkm4EioFqZ6/tqdPhd\r\nUMPPqoIdQk+tevRhhQIADg8vyJdwBzstsq4oYzF1vnDHodLcv568VGDa2nEW\r\nW2UrlKe+KWtAe5v0FlTef9PLYGp/Se1tGfEOXBYnbPo4J+LBPWi5pTk+iTLz\r\nGcw90ExOXbIggWo7Gonf0/bHf8QZi3NNlCBe1/8eYJU0rlc90t7L5ojuDopw\r\nny4Gn9/vmI/hmZfH417V9av34hk1JqSjVzgUxn5VJFeuYkBocXotnAyslj+g\r\ncAvup+gr6dJIzmM/lhxQXytKbRzF9+yBfco=\r\n=sieg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dapp-dev-goerli.1","@keep-network/sortition-pools":"^2.0.0-pre.13","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.11","@openzeppelin/hardhat-upgrades":"^1.17.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dapp-dev-goerli.2_1663599480167_0.153365459297782","host":"s3://npm-registry-packages"}},"2.0.0-dev.72":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.72","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.72","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e0cac7e2ed9e89d1dc4cf2d1d4518377ee0863e7","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.72.tgz","fileCount":134,"integrity":"sha512-h0/88rE6gsWEfuSCgmja6yDzWOBH+Vp3o1Mr/uUpBn/q5L6JpBLNGKE3t1lMk+XILUnJ7ExRgiqfSR7yx1gfsg==","signatures":[{"sig":"MEUCIB+eY+Q3d/JCUB0NlRg0pxxe8PPuPRxhJNN3vtKri84PAiEAuQ+Kfiol5PGb7wRwzvNY6fSL+PxAOmcxuv2ed5CLH1Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30101812,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjLXHbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqn3A//ewi6Q3Al+YkZ2m0ccVR7Na+dtCjjb6ZsOBqr4xMTqf1voQ44\r\nWDHgKnUFgK7OHLbfaueqCCbeN5dVfjJIYCeoKQL01ImM568oKd2AiCksDbBc\r\nvBBkocd/6C0e/e615kcnUcXOXcXnhB8ybeJjGngE4n84IHM2c6b605fDdPaR\r\nQ9tREKshgJxR3id42us/Makx1f+ryHPAGdfN0vMH3SUOrFce36AESqpxukdc\r\naTTHIDFjOYce5yPWe9tQR3vILr8UR3ZUgHzbZsCRn9VrXYqYqsmZhUb2tS/J\r\noBl+lHOgOKWIFPLLgrUY/oyI7gcgxFH54rsvWj5RyBVTiWZ30GfO0Ucu7U8k\r\nmdfrotdh3YNYR3/hiRvqorq8NET5GxBJy/xNfWOdb/fXzZjilpHcM+YfLDp6\r\nKpl9S94pmjPJe9gukDYflaxWaY00iTNWEMllwjsxhxvV/bTzuVV/cS0qbfGB\r\nRDpP3DLQZTEJ1x8pfrQ/tqFCeSodC9kA6lqAxTu+KtKZbpAPIn3+CnORYCEe\r\nNYleazK1bu3VPLgIJ904XzbhiWfFVBBd/lbmdzCugrMBvR3st+lVzPp3tpvY\r\nnJ5LQQn8FByvglEPAsPRkl5sSIRhssJSvkPh+4CjYsExSenvw0xfzgnSIAFY\r\nO+iS+wBFFi9mf5L7IXHLdqImhmrK6Bh0HT0=\r\n=pLtm\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.72","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.72_1663922650869_0.085273080625897","host":"s3://npm-registry-packages"}},"2.0.0-dev.73":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.73","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.73","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6f7a86b496e36ef4c3cc579c1a3994f9396d09a3","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.73.tgz","fileCount":134,"integrity":"sha512-mG0TJX6XdsUHSMVePRB1NYx1N6MX6AgWi4RPUZBBd0b9ocKm345jucoxmtkXpTLVZ6Kw9zcAupToN5aYEGOCJQ==","signatures":[{"sig":"MEQCIDRHB7TAMxcesYqjQ0xV0A1nZ8FN5wrCpeJlml7sSu4cAiAwsN/dsOeeUVucFDEFNa8KNbru8m+KfYC6ADN7xGd4PA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30102614,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjMaqzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqS1g/8C8cb9OXNt7MHNwiGkgJkZrlTaELHSISgh1FxqFukLdcX1spp\r\ngxiKVXjCxDxz/hfAaM/twR8AddSXFqUf2fJcs+CiVX1WlUrjyuuwd+zYXJuf\r\nuWwcYzhvX9hJ1PH9/quIt5jFypcTNSvrFijo6FbtOHdgcuTGQ6/zxiM1JEkt\r\n8/R3fiX6EbPW0r8tWwSGTNwoq192JS/HwVA7v9+Z7KP6/OACFegIwJt7y3OO\r\nLK0UMQRSuGpRPeC4g7G6Shjhnxu/o+QwCah0Rl6EFlT+6zkJxRGKEl3/ZhnM\r\ncyIRX+R9vaDnTSpQRs+/5cszRXxZ9dW6nD+gP1/XOwouAa/Jc+NjcfRiDYVR\r\nN7JvudTc/PioGjgLIAo//c7o0hkgGOm8jpTsyh7qJTj21WI7PXhjei0CUY3K\r\nsG21reL7nkjVJg7X+CE2vNn/WlYxEuWCVjF4ydPAWTOp/l2/upxoEdaF4VQa\r\nz8/J4m0bKE3rFqOZyz7YXK3wN3Id17n0/6EjFZT0mMgTM6VZWuNqzagc/Lew\r\nrpjxZp9NBKDjkxe4BKGi4IYTE14WEw08+T3noNZOZ3W/z5MlwWk0O4nGJdM5\r\nelb92/+Inv5JXZvziC2lmR2yZyR4JQtg/R9g3qxCEYTQF0za1BU604BYx7rd\r\nYQPhaU5iLB/5jH4ncnHCxm0iG4tcTstUaOs=\r\n=3AcR\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.73","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.73_1664199346825_0.5399732604010108","host":"s3://npm-registry-packages"}},"2.0.0-dev.74":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.74","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.74","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"5027faf270ce0b9c55dafafe052a5ea58f51b038","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.74.tgz","fileCount":137,"integrity":"sha512-0UKAlgnGggZvBny/MZI4kaF3FbhOxXTxJtpjiAUjwqp11kHHYxqPc6das7qYGPim/AINoRUI/yFF/vBem2xDlQ==","signatures":[{"sig":"MEYCIQCzm1lv0Cdk06jENq8FOVlSc1lFHW+Av9Z2k0FtK7QtAQIhAKyNPCUIhRA4lltTnbsdG3Rz1DEMR59jBsTF+qY8kCli","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30106650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjMhcBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq8qg//b8qR7gZqDtTDcNQWPMFEY1UJnGDb9T0gvmmnt64VeK8plZIv\r\nbXWbDl0Bp7AA/cTDvPif2yRd2L2O1UBr/sXkzOM49VDgKJ8t2wlgKbk51g1g\r\nHB6IF3tp9zoRaqOfKq0J4Hn/w85jpiQtKklxcm5Io0GkH6Nm5SqPFC6EbRiA\r\nAYM9nWluw4f9yYZllhnvbjhMVb23SQxUOLImLzuhLu/EEvXcwFjFl5T+MXzQ\r\ngO4FG6KNxeXg78b34ts2JeavZ1nk90PA10LYobB/dsl0FZU6PVbOMK6103nw\r\nVhAfH4tZsVNALqD0kX56p5ZJZrm9UKFRpR/vip+Bu3jwOPIIfGwBpAqAFONc\r\nx8zYzcHXmStjkGOFRqMZrpotzDL64mqpr2QQlnfWYV5PV8bZR2S35FKG2IVx\r\nj53rkccnU9p5BH7HQZvm+1/7Ss5v88yAGnH5akKL8D9VgYQlFgg9fNxZ2ERw\r\nSxx0NdM8hLQq391aZJIraEm6COTqVdQGWQnzNsmlkMQaNIRKOAd/8HVi4xso\r\nUqO1flXj0COtdh10gjRHml8TVnm5slVoz9V0xZKuew7Fbfx2tA1rVEvgio6E\r\nzoP8+7iKVCkb4NYkrdG4/31Vt/srP73CZAdyxT/gMbrGFgR43/iVdpNfanN+\r\nhkuWwZe3pVIdKjTRcqz9lR9IBT0zlzKSa2A=\r\n=/cQG\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.73","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.15","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.22"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.74_1664227073596_0.8889966602173904","host":"s3://npm-registry-packages"}},"2.0.0-dev.75":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.75","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.75","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"d324b30180026f5138088a4362c87ca4a1cf559e","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.75.tgz","fileCount":137,"integrity":"sha512-TZZtEjj/3jC3YpNJm4LefgLeTV8dcJjv0fbJqPjgRiJde4FixFLQwugchRkHJQ7TD+SzLTC4AAn2rtAbW/HBvQ==","signatures":[{"sig":"MEUCIQDjet7AlqPWjBK9XJhvPnkYpk342dAhIbOOfgviOrLOzwIgZdJw970TcFjEhfEXyknxt7ZZT2slWeKhDoGb/+HYkiw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30098228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNDv3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqHiQ//cGOpr/0xZ9/pzMGEgLMCaQ8n0XGGws1m5d1qz2NKpfw/t5/l\r\n2wq50meSqCQYzNWKM7pMnAzL441+Qd2zScptGQSejUAVzRDQ6+MgaHtkkNn4\r\nyVQRY2jXhveKKXadBbTIA2TG3yMHlaRXDpqoojPwV/ThzDNh/9iR7zDWxkNn\r\nguekcNw/LzZb9X7DWqihyWh/T4unGLdpLAJZ9cD7MVzpetGsZ5PqFAaWkphq\r\nDgQlot8Ix8RUTt3lImzIqaFdWeAhg0yM8psyxz67jaLRzBxoBL3sSmGh2ZFR\r\neiPOVCz8G2AuDFHSPScE70V1WnaupDFwtZdaiYXMhT2MhoXrelGliFQTgtiK\r\nc0EINd+NgJ8fSDMNdDstz0d3BxfPbfsUJA47y1hgD77NhY7rrWIGApD/5z1H\r\nxOrshGCvOzCvtoWcNH0hMP8ks/2RsbqRTbbNlf4nuwkjyho0ICER9oJ20HuZ\r\ndwnUZiipu64RuPB1oJ6MJl8aUlNfNpILJG+Xez/1m28QpLTYlIlhldx+S36t\r\nTm5+Lm1jdlXVtqUezwqwf89RwomCcMmbBeX3jqod7ESwORjxAkzl4UFGjz0m\r\n2wscQVnkHnlCqjxLaQXf297hdW5JPv9gsiY0JeTlf4GEVmOvzmVVmvlG+wMY\r\nXQsu9f0a9VqPWPHXO0D9gPcvM3Z8TLOcXx4=\r\n=wWws\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.75","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.75_1664367607495_0.4575395423820836","host":"s3://npm-registry-packages"}},"2.0.0-dev.76":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.76","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.76","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"00af68342fba569b5b963e65570e09125452a0b9","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.76.tgz","fileCount":137,"integrity":"sha512-iVw6IHekPon4XmJg7eNey6NJhNBsp7YEDRDP1tZiy9Nhh5Vlxg0G82Udven+BfuTehLUnOPcWpuEd22sxaQZhQ==","signatures":[{"sig":"MEUCIQCSYqEhLNAX6dBsawNZIbo9kw070cmvNBwcKP3AkH4SmwIgFHLP4ZpXK3gYtWoebH7RF+2oGdUShIrH5Jbq8ZBmp+Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30134387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNEMAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq3eRAAksEBGs4w1Yj7GTutrrxwD/HWw/4VUK7SKT6AT4vMfPI/K3e8\r\nimEdYQAQnC78/49YlyANy4fOi/m+deUG4umFQnMvPK1JW/EsD5u7W1VNjxmS\r\n8IQp91hWzy1pkbfh153sIlC142OEfiQuVSvUXdM50jx2OjaS9s0U70azZHq6\r\n4vHFZoYJaH5E7fw6q9WhPxywLQNcPgTjsOKJtaG1rUqXloucfEO9ppHO1Or0\r\neQzZNel+kstpiW4uBOhfRG3kGxGHM4Vn9ddSDSC4PN4vT6b57rlv51zdWK1h\r\nziQ2iV1KvWkrtUm3BsML8/BWnrSShWbpyoZIMSgSA+JFPBXG94BU36hB0mFE\r\nH+86symUs+u3NXoh9SH3A0Wet7TyrzFyMcI/UeYrPdnRYXWgV/B04k2XSM0G\r\nEdUNLHYsBGMV2dyvyN3q+FOIsi+d0rZlIdaEPX5lKltq+VfM1k3z6EGo90aS\r\nan0WXxVfEj3KZ84F6R8OKdO4gM465nVdJg55cDpcZ8PN8Kz75dD7mZYDw0ua\r\nGkKv5rMHqVpnHoFUJGrhaRAONp+9g4+skwENI2Gd5QY2P+WXvOr5ZYaS78Vm\r\nQHmo83Cni5aaMqi48bYEq6J0Op2PyLZLrPQcNDDYWHDKF1c/Y73RsLTv18tp\r\nSkU7Lm3ImXG94xG3mMdDeHK3oz97/vr9dRs=\r\n=exmW\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.75","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.76_1664369408601_0.9712580042194572","host":"s3://npm-registry-packages"}},"2.0.0-dev.77":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.77","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.77","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"b617896c0cfc5963e1cfc0ec89ba79a1bfcc9eb5","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.77.tgz","fileCount":137,"integrity":"sha512-ZQTTsDZqLm2zLKdyMDwXbVvARwpCbQI2AHzXnzGluWyb4QyK7UmxL2XvVthzGOmMXbSUiCdNNG2+1orh8PGzww==","signatures":[{"sig":"MEUCIQCs5rRlhsdEdczyL5FytRBcEsTr0Vz3dxgWz6SdCX3swQIgajkUEBQwnrNZXIVelpdFFAF8yr17fhV0fahb/MQmzF0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30136172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNE0tACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpKyg//dV9Is20xZXGJdrv0yzeIc4fH5yEvWtPIGX7LyPHeHqMTt1zX\r\nsj3YR7g3jJdUlbUEEpdhTC4irPCAkTWBs8YMCMr7kzuqEj785ujm7HmsgLTc\r\n/FRrBg93zruC6undrIlUpAqnJfHE/Blxe+3Ml1xIuStg6lqGmFuhFgDsfuK4\r\nyV8epyUCvFL7hILcxmXG2Juo3YmiDo6MmlJ0u+uuEP6F4e0jp+QXxp2AXjg5\r\nY/dUT5hwL/8Wyrj5lJ8gzSn8xS9Pe4a0z8Fnv2ht0VC1YX4G1CI7kfbcYieQ\r\nsHkLhrUWjnk2mRbB5mnYT7EmRbo5sNtseoNAggDJRdfPWr29EqmZ5efztUV1\r\nLjxhMKa7abshc/ZjBd/yiQZHbmG0Jcs6vXzKJZicRQrUETB8MiK2WJkz1mZz\r\nm422loCy6EbUvl7rQR79TG5IB+E4P9PJSjMZFznaPBIJ2mCqw8fQnwktPf+4\r\n6NFFJsrfg7VdnAmYSSpNUHjlbetuJD21Ix37cHcBoFrMhUDA9HAGV4SNqF/V\r\nBSBhoXvYJlzwOYmzbCabzj7f++gGFX7VrGG+RRzAdkeu6rV01hft98NA5rHw\r\nJ4ElUaJozPRVEW45u7xKtoK9YqNK2AVd/hLDcLQzyf+P/RvdQLknXIKzJzIc\r\njRIGgiApplYDQyUCOgtpfnWIioWHS6eeqrE=\r\n=3Nu5\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.76","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.77_1664372013289_0.5414517556371379","host":"s3://npm-registry-packages"}},"2.0.0-dev.78":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.78","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.78","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"20b94b64ad2d2c48d58f1b83301df7f7f219e692","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.78.tgz","fileCount":139,"integrity":"sha512-HMpCgoh1ztVkxKbVq0wmVPwI7WBaxy1xHXXVAUWkaN7cGl4g56SvGxCB1Ttmtr94rQ3ZjRuCEJHYF2rloyMVJQ==","signatures":[{"sig":"MEUCIGNi+wLgNDOgw+TUCY/RJT7RJxOTZwQBrhgRVY+N2N9qAiEAxWkI/367ix0vBX7qi55lH/uMnp94QCHfhOVskXGNJVU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30143027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNGieACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoCsw/7Bn+1Y+6uQhuKzIVAeEf79dqCyRb67f4+OKHq+jVyWYHtgcZp\r\njBzvlgOfYqUd0Ix7pplPq71exidN+5rcWiHycHEywW8GjYxY2UmbZfHHWU8Y\r\naWStS212GGJ2RqnW99IHvRqfas67J4dD1o/yLd8vMmuNoo8+d3OzidGqd3Af\r\nFaj19eJNNG81Cpkn68cQzkhKzKFB+uYI962J1d2hjVZnlZHbyVmQ3bmeQhQL\r\nc1XJuCp8fo0/TIW/kbm+bf35+JazYmAdXkAgzf+PcU9l/zZ8Peh7RIR9hOGW\r\nr/z3iM4mhtaP5mgyjPCXE9ZDkm1bgmVFHMsjni316DZsUmSnLj3mjjk3o0V5\r\nTZrjomXWoEqqN1m2WO0gY7vDBs8WnH49JY5lTi9ZX9JRma9Xgm7mPFYhhuK3\r\nJ6ZqvYhCSaOnIzfHcoKuaUvBwrHzMQO3f5PNbr05y2495a5tBQ4VJyxTU+TF\r\nPdUkPGvjtzBWQzWxK2sdCwim8O4bEbwMDHfbj3juNf5q8PQXqwOEjPot9RY5\r\nQiY0Lfx2Gswmg24e6WZ9JhZTt/pGiuF9ehQEEQClfespq91LZAFX6byBgbEm\r\nHFffFqtRs/Fh/0b463iQ7+Cp19JqloT4Xb4k6Fz87Ld5/7OQl9G4t+4izRzc\r\nab1gyUBYqUanlNR1D5JlaEyPYXLo6kXIz6M=\r\n=K4Nk\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.76","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.78_1664379038025_0.6115245650397054","host":"s3://npm-registry-packages"}},"2.0.0-dev.79":{"name":"@keep-network/ecdsa","version":"2.0.0-dev.79","license":"MIT","_id":"@keep-network/ecdsa@2.0.0-dev.79","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"8302ac0b85925e6a1046c3a80bb4db2f62a62152","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0-dev.79.tgz","fileCount":139,"integrity":"sha512-IMZ614b85Pu3ZJ4/JX+oAYsiVs5hnkhTPVAOyUtGMvgnY7GsNWo5exyDL/VWGlJMxU7rdS/V/T27avduuXg6Hw==","signatures":[{"sig":"MEUCIBKN/NHS0g6zr8TZutWC5Vr6LUw6LFqNqmrbuCgS7UqPAiEA59blO7Ug7hE6GiUVoB5EJPuGiBZEpgaCnrskTd7iTKU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30141798,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNW4SACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmourA/7BXbjbtWkurjupYrptIGxOYHBavCNwG9qTE0/+Zn1pJpD/POX\r\njMVmuk2Bgc7ZrenNXE+U1S8vM0V5X3AR99eGbxJQAzaOMWdy786cUhzp36pT\r\n4GWfSSnmSzh48XtvRBk0FhEfEBMNpXZc4OPFWZOAsPUEXZVHnn7ieW0epSIt\r\n2PI8e7o6HoE44LwoAVG1+vhnP1Flzb+qX2W/3aiTkZW4hiHw4B7oU5EK5eu1\r\nRJAYuHg/GUZV9qz76soddcDj+VvJUHev29GFT5zpJpk/849BbKH7j/J+Puco\r\n+v6bwBEg/yJ2neAgqO6CdxPTuRPOzSap/r/634W1bbbciREx+PcoKQLQG0V1\r\nhVaKLwH3/G+bNvcDOvKVM2VvJQFQnHC7e7Z/BM+za0sQuMZdk65Anzk7iuYz\r\nHrVWLjgD6LWdItPoAUQQIfM17G1gPGIn+RKnEf0xR96hOmRTsQgEsTnw8bap\r\n+s3Qu02yfC0gbGuYAC1+m3ScYQ4YDBu3liVwAN8Y0JOvLCIBxPQNE1jH5NOW\r\nMtQRkJ5GqOmqZM5oVpyTA7nUBdZuuv/oKyzmh2P8uaiEjw+gubHJCqb0vMIm\r\nZP6LHBimpfIdj8kcMBs844jxTuQgeRDu3wrnhe4dYWZVGyl/gAMhATzlHf5U\r\np/OmjlcMX7+LF9S/1dqDK3rFHlgI+HjR2LY=\r\n=0wut\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.77","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.0-dev.24"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0-dev.79_1664445970649_0.4096548334883212","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"@keep-network/ecdsa","version":"2.0.0","license":"MIT","_id":"@keep-network/ecdsa@2.0.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"96d301cd272e61334bec173b5c4945758fc80853","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.0.tgz","fileCount":114,"integrity":"sha512-KXSUOkZIKHR3I4H99CpgkPtuneI9AxgGo8+DmqGR56QB28kJCEXm7GC/yTInWS1lb7LzDwkjIm9VYAIGdLrsZw==","signatures":[{"sig":"MEUCIQC1FIslbfCSH6qB/FiGau1mSo6tH3wheZlpeLKcZkLlWgIgKrVRTEzQGZ8qWUgw5kglDzgmcA7LLncJ7FaHaD8U+7c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":15554703,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNa9CACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmptdw//d4jSMoXp+6sm4AKZgVDJdQtjT9X+9J/pl6yh2eGR0VZwYxyz\r\nGD6tKrYexOYpW3UuATEecjeyg4FxijZX/MxqK5sGhzHTXZvC1KMELkIA/rWF\r\n4cr8nT/NMLCEtFGJpK7YUOMDh50XQV59ebJuchnG/U59Z7RJYoTAOUQyctMz\r\n5nDJiUWTKYQz4Qm+5+72dz8dHulC0fZCEdJfDDhRN82AN4w0YGys0ofLsmrB\r\nyyVcEZQKyT/lDBUTIvPCjTq8dPHIx9+eiOjMPzYXL3flus6wRbWR4mibX6US\r\nea3bnE6sHuGFFNmNiMpqccCSBbFGrVz3i1JrMGDF/P5p+LMLnt/rakxFaAas\r\nkrnPpXRbBrKota9oRp0vr7FOF8UlZXxfC1LJ43Zfqrr4sDOEv+AZRlvi2R7n\r\n+rE2GM8YHOq6UhO+ORAqn8dWx0Er86Ey3+A89prA0Q++bsyaBExcO5m07mUM\r\n09zgrTwgy9rleCQn5pwddc31RCTWnSOWWvsuafwZ+wxcIwFpVLc0YB5gNShH\r\nqTLa+VG6KZgVSovvelgRsGQocM/tqkfqKY8YGii54phMHnC4Ot/NhjJknnJJ\r\nL0C8G8w0TJOospmBwGAQRsHojr6zmdG5kFYfRWlkmrOEosaKj+Jyojm8pxKw\r\nWD6JBd9dg3WhQLwk8RJnHGHKQ+wIryBecUo=\r\n=lFrM\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">= 14.0.0"},"gitHead":"02d55a03926d4f368682ef4273d81fb132d158a2","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"nkuba8","email":"kuba@akena.co"},"_npmVersion":"8.15.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"16.17.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0","@keep-network/sortition-pools":"2.0.0","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.1"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.0_1664462657920_0.9996744989716682","host":"s3://npm-registry-packages"}},"2.1.0-dev.0":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.0","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3af765c1dceb9d8d3f191c1a44dadc956f22ae01","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.0.tgz","fileCount":139,"integrity":"sha512-6qlWnOhVfMnItcfLfyWUvdlQ6P9hmWa9T1OModZP0i1drxSKeINTrBq50u8Z6hrORs/J2anPLuNW10tpdNIT0Q==","signatures":[{"sig":"MEQCICgxmU84ei4ecKjGaSsTtH6Rk5zqu1Vy24G+Y6nAB6RYAiAQdbRRPvuraJMiOpreJ49ycrksJqFnOwa57cIuECl8iQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30141796,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNbr7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqgwxAAnpUSTj7Qx3cBnRK0pK9PejuRAiUvBeQPcpHpN0OgaMYuQoAq\r\na4uaLtmYsCkZYsjSJuQGTsA+mQmmrdCtfb422kD/IggI/zM698LtAZwrFDU7\r\n/gcUrd9de5FvnkTHqnHyT4cMuRZqPjRu9/9TGmBKBiNoyvVpKViPTmyWY0qr\r\nL9ajcS9earMgSp1j+p8rVVuRrUHTT3YBkvb3TUgVCTEe+HsLulbyyonMg9rl\r\nBopTygitPTByzhZif+1q7IC//jYu9qgY7KJwYV/xfqajmet6iRWGI/y76L/s\r\n3Rluh0gAGd7JjZEOA/Rb/qB0qk+iQRBkFXY1s7l9ijccPSqY3QalmKJRMhMw\r\nkACV+WWrPJCWOAZcFBx8yimknulOFb+rogPTcx6yh9gyg+7tZ3ZTtTZ4E1WD\r\n+Cwdg5Ksln2+HsNOzXg71uhsispRY22fJdpaY394XAQ/lqYrMS6rzw3jgfKn\r\nQukvBh9E6Z04dj3twT2otxJ0PQiSAs1LJalZLTVSvXi4c0eLtcpIEIwNYWa2\r\nB3gjAbJcv5SUBuNGTBP4tHcV0LBU2Vs46RJaUtELZtFuf9pyd9cJtDSlS/Ju\r\ndpSb7bm/T8OA4uVy5PB5X1xCWafUiy+IuPXJZsYTOlIIbOiCOBwVcTz6WOtt\r\ntA+XGYfDxAXQ8cVxdLi7iF9F6pNPw5R2c2A=\r\n=YOGX\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0-dev.78","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.0_1664465659097_0.5926906699375472","host":"s3://npm-registry-packages"}},"2.1.0-goerli.0":{"name":"@keep-network/ecdsa","version":"2.1.0-goerli.0","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"aef4e5cdfea8ff0827321f05e22e0bb9aa16e2e5","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-goerli.0.tgz","fileCount":120,"integrity":"sha512-SYpDPE07iA8r1juCliqvJSYz9YCxogTc4GUfMRL9/2FhKxXuV1VWhpibVcRk4QJt6RwioFvHUUFQwu6w0FeBJA==","signatures":[{"sig":"MEYCIQCSGIrK5QmcBpuRZBVFLSnnF++bhFVJXzEiZeixNLFmMgIhAJHcjjcv642ti64lf+99W2U2C+jJzw9CQxAPObSET5TE","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26756816,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNdTbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrUJg/9EVxD/nt1W3+PDynlIEk9DSNul2F1Oo1Dwfpq0xuNIxCfjS1x\r\nD3xN/mbf1xP66AFeInm8ajEbFbT6ajHIor+xm/wdGitXBiyIbg+dVTgZDTM4\r\nX69254VdbKVIztS2Mw40glNz6B32UpRihiXqXnand/CjMEcARcCm2P02hqXQ\r\n7rgvw4lEgnuMXt58fsoJkQru3Q3C2O9ndRRnhD3h9mQGDvYJ0VfVBgp5fE6m\r\ngAakdhJYJCbLViDzScOJ1B2pJa9dDYOl2aakVi8jNYkAY1E2BIBlmZxFcf9y\r\nnpKOn3yDCOEqVAd2Rn0bR5YO11DrtaXQ6lIy/bzfzUDFGcWofmXWoCx11krJ\r\n6Q2M0wP+8IJFMaCKh9K1DZLFLlA+PjxNVLghEOj7ANK+g13x+vev6n1V7gry\r\nyHCbOm7E/B+l6e7bvTAVIa8Ivrak2AR2l6SI46Qr8CKUsJJSHfHlbIpQr70T\r\nI9JqqFXlOn0uQb7LouUxlHuKHS5++UBB944peWuXQbl/yEU8beuKPtRRa/f1\r\nX1ifg2n9KFvqVtouo8hgyLDGFzZJS0YvNUaKiq3zo7P4Hjn5X2yTvPMCLogg\r\n1f4v9TisWXaVN94eJGwxuNGsilnHC0ZwLvJA7XaUJwcP3aNRRSNglET7D/wJ\r\no/HcxoQY2YZ6wEwTfd1uOj0VMUXCfyWovxE=\r\n=s71p\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-goerli.0","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-goerli.0_1664472283650_0.9485048125273006","host":"s3://npm-registry-packages"}},"2.1.0-goerli.1":{"name":"@keep-network/ecdsa","version":"2.1.0-goerli.1","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"80421b51bef222a23beb8cdd7fa4818748b94c36","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-goerli.1.tgz","fileCount":120,"integrity":"sha512-bQgZOAjBaCe5RRpNpXzv5e1bqysfnWXj4pIkiOd3+qUe1mfRuL0S5qYtJ0Cx4BvdGVS9t9nF7XLqMxAeJjgxDw==","signatures":[{"sig":"MEUCIETj46Kam51GZ8UohtCxdHgfpaCvMxGxON4Yj8dhoy2QAiEAt57c1v1N0OeBlnVSQLs4A/AY9r2o49VJbCbClc4XX8c=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26757555,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjNpgWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpyaQ//cFekdSXpLuSVvu0vXC/MWOyFPIptx8ZZfxapoCzdRu2O+jWm\r\nHPJFcxlTCIhcafoAQP+8Eyrnnb29kgF1ZY7n9/6TnOyHvBnunDWqDvO2l4sT\r\n3it76cfIemJ2PLatZzcHoZ+4hrbEgjjjIIpRuNrjwM0UatbW+InoO1eam3lp\r\nQ8U1lHlwqdwvg116yL4UYPmX1HswLTBAFVxiR9GIV74O2C7WJ4jNdhwWFdZl\r\nhNWpgRzgczyJWvzJRigNKb9JAZqQa9ln6whphVwDThAhz7vUSCfi3AXlU4fM\r\nLZWiKDbI+A/1+caaQqyCVJ+3wvc1in0ghpvEn673Dfpu6XISAXyFTgSoawL3\r\n/r2FlDu8h11DlYzR5vt4oCchCr6X25O12wXY+RY/VGLdvyHUO34i2xbzwZk0\r\ng0GRvgJSXYyP6sHCcqQ+sNHpxHFlntipYohxHyfd5Hfk7Jg9XTtlx8WaqP0x\r\nIgx3sQPfOYpwHcmg1RcGiJTAtPq5gzwuBFXg0yRWtDj7GGDvL1w8sqofVEpq\r\nwgvv3XKTKCHcbKEDqnlVUcVCaPDPCnLGvQKyORYoaNCgPcSJnDhgtx0+1qqd\r\nsSX92Fw2SRVSH3x5IBCMB6CMJ3aAhE1LNNM/PrOYrazkrhYp5tGve8PIYAws\r\nOubrtYnPgz49TDVzNYID08wgJMyacNmhgpc=\r\n=nrxi\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"6.14.17","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"14.20.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-goerli.1","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-goerli.1_1664522262296_0.2588935855475525","host":"s3://npm-registry-packages"}},"2.1.0-dev.1":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.1","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"68e33b0847bb321e571271b0495844efc09deff5","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.1.tgz","fileCount":134,"integrity":"sha512-dokicZscdcLkbmV0ujg9Tzxz6u2MJyA9H8T/as2p1dsQg7a0STvYnNsaw5yyJQsNblNPrlRoAlWEHZyPhbgYoQ==","signatures":[{"sig":"MEQCIBH7MyCWQV8S/SELZSfmjq7Mcr9tGNC5zwV+Q/9OzH27AiA7Qog/jaJtq7a2VIOTxknttccUV28Qiv6AKinEPR6BRg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19774927,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjoslPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqKzhAAiHZbj03ikdjbPyrmZpEDwU3+e/ZqPmzEmnQvLfrFy0HDjWcL\r\nHbmIZwC58ZkXQpYVX0gtSh+jq2tMxvtGXRu4gJDzp+524QAo6s9/oCbXM8Ms\r\nuYXykuWwpXp2y5J4NZ4tD0jm2W4D94iRKo+iElg8i8TBxOKgm//W2wIMYgA0\r\na867uuKg1Jn3NQ50pHoKJyCUaRlANLRzBjSC3HlJ5W+igl8I6TOMAUzJAU/Y\r\n9MHONqBbzeEK9DxS63crLabq8wP5qcDyMzj+bSTG0X2GA9qDTOunT69WXrEV\r\nl9If3gZ7lkyDWj8MKreQqtxcWRL60DAdX43bB//NaJunq5x5WiBvBGFmRHt5\r\nFzWxp5nOVvV6ndbJlEvwudMSFj6EoBlB20qU4Oo3fmRGo4WDvk1zQjid/lkB\r\nSUtl8A6E130Cy0bLJOm8B1n0XKX99FL7vdJit4cfXBsAYrzuB6w8IgiwFJee\r\nAaE8116IyZ0nH7u8sWPJTIxx2CZRh3H9+Ym78yGktx5BkfrxRcuyzv02HELK\r\nhId66VE09PLIL4XvbQoy1g7TyAR24Dn64fjG8p3v3V9oigRMIkviiDCw8Z2V\r\nNKcMXIebIgJpwK4siASfMBG40rpiUrTwMuiYI82MryopsNYnqj0l9l5Db61z\r\nwsjBasu70e397VAiKdlXW/vaDljJTW4ZUH8=\r\n=vd5O\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"b8dcba4a37e68febff082bd2c6282b5fac1f7d04","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.1","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.2"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":"^1.0.13","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.1_1671612751289_0.553839623821357","host":"s3://npm-registry-packages"}},"2.1.0-dev.2":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.2","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bb130ef37d6374909dc4bde858d5664e08b1ebce","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.2.tgz","fileCount":134,"integrity":"sha512-ERzuqvQFkyN5+2MAGiCgDW2kroTrm1Dnd7yRch3yf+HctjPZ6ocfgubhgqFGwCqG4VLXfS/NIezqORege3N6og==","signatures":[{"sig":"MEQCIEXG4ojOn5ZjSz2L/0/hILt19QUV00WHlra0py95eESXAiBbfK6ydFnR4Iytc65js0IOdiqOGgMTzEjhaJe3nSTlqA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19774935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjpZXKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp8HQ//QtJWPibQ3Wc+6Ioitg3vEBbFOVgVFDpb0/HCjStCFB4MiV74\r\nV+Ql09BYu0i3496MjcfpyJ3JurKNiD/pdIJV3FAMGz+iC4CvDrewUx7U1YeZ\r\n67oDqdUAma+2OK6Hau92G7gtNMBiza+H3i7t6rCTthIyZ8LZQekI1kYs9khd\r\n/SOmgpsa93bBuT/O/hfo46EsvgLqsugl8JUbZ1SkluYPHkOt6YwL4V8+3YDi\r\nK/x9r/5ZfntVc+gOzyAGZB9kuZxr7LueDiLZ0EdwQx7hdwHAKtwIyoorebNJ\r\ne6/DvqXSHlRN20duP2on9sS3gBnuXZBpYCilWN4DbwDBlSKrd4MHAXI6GQjE\r\n8WO+s8dvzu0etndEpEIk0ZW77wgBWy8HWr/hje4pPuTuesNFNDbMpBS2Xvcb\r\n6P4wqx/omPY0YDeLVvNIax9bPKGwP2LN9SfoXLVlJuNV5kG7vJAHGZydNRwE\r\n6zB/WFaZA2eU+ptK0UeZCAaKJrInvgbB3mSEZfOlhk7U+SSJBBNLm2sz531Q\r\nbLwaOU82e2bAwqpmpfMJUgn/NKVBmh113t32xFfJCZ1W8hQxULZ3kT/ZCNgr\r\n1e2W34Z7peoXlDIJi+TfvjfLI185dG/uP6cpqDNHQHmdQVWhBxj/7bTnKxM5\r\nbgvkwg7aEJWGfl4poW0EkxQwjEstWCdkbX0=\r\n=oiNL\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"3d7f75d51cf35560bf78d330ae71e7e1bcc9d358","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.1","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.2"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.2_1671796170169_0.6232776031434366","host":"s3://npm-registry-packages"}},"2.1.0-dev.3":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.3","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c027ced228158abb7d67e4e826b860c6685e8c8d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.3.tgz","fileCount":135,"integrity":"sha512-axLXqcSGck6jL6gO4unjX2IbM0W/SyfJGjhLX6X063UMujwCUGR+R/daXbjpJFxxFxRHrENBKA/EaLZHsyIYhw==","signatures":[{"sig":"MEUCIDlvud9JsCE+JJy5KPrAUcPBog/oKY2QXWrOy7O8pDl1AiEA3WlkcYRe+zQM7p/bFcs7XCDR9FMQ2UMo9QfmvVFdhos=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19810964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjtZReACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpbUQ/+MgFBdSSrt3VtwSjamTaaMAxIjHFnzMHwhHuh7WI3yMzdcsxF\r\n6dnmyv+89GKPVcGzqMS9sWLpK7p2V8j8iFHVEOpjOHl07wYgXdKfUkji4oPz\r\niIzTQz5QMTJjGeuHry5ZjBgUgiVjKSjsiV2AlmmTXwjWg2FIQbrRJyif78ow\r\n76UeJG77meMQsnTil7oPNq1UjBEhZYrQUL3UA5CTDPjG7AnTg/I7zQZV/W6C\r\ntnGmlnFUwsMq52Nmosmb4PpQJL79ddJEMESAhQFBHHPCBENj/wWj/C7Y5Lmf\r\nCIia4fKrNdcgVEZVXhm/rSdvzEyjsZfUlFx7PtITlW6W7SElYuoeEOpX1Y1c\r\nW2S9L67SfUMBU2sOyc00OYfsUJL/fDUj7kLZroMSLsODAFV+jB5h5S1AQJoR\r\nzv6YupW3Dd1oylqU0ckmuo+79bynbaTRoJwZia+qVuB9pvJLaUT1Q0B3qbpG\r\nwQ43w+CnaWnMTZIT4RGY9+AYa2nBdDIje/IcDo4VFF72inGwjw+qHBAGlo9b\r\nLHb9sFGmpSh32rGWZFym9+owQnNk1rI2ZnXpz+pGBVs4l3kjLp/IVy8VpjUN\r\nSyZ8X7HHdXzrj7O7rqplGQ4A7GWPUtRBXEkTYAEZO8Y9U8WTkHSecAuGAmQv\r\n9HLMT0vG+b93GQpmxG2JfRvzOcmJAx8gQAs=\r\n=t1mI\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ff77e6eefdd9123da3f72388b1f470a444dd7346","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.4","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.3_1672844382355_0.37615686308266616","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.0":{"name":"@keep-network/ecdsa","version":"2.1.0-dapp-dev-goerli.0","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dapp-dev-goerli.0","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"933981ebdb5a3023f83384a00066ba42017a3785","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dapp-dev-goerli.0.tgz","fileCount":115,"integrity":"sha512-gfxhCfyrbQGUdSiPson/zQYR863gShIQy2zgJ1BaJ/xOqtEoGqVFuUH44FqO24hS+snVczU+vH9VyAUAXUSiPg==","signatures":[{"sig":"MEQCIA9oX+D5G1et1wRJtAjor6iahxAr4QePLuLN1iZqhYnqAiAWPULAAte5183xpnODdo2yIzUZ31V/8paPwIrfaxxmIw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16409844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjtsMOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr80g/+OoQCD33puzJFP69UM0BIzaKFprVVR0cTpTYrbE3vsqE1VcF5\r\nbNTuELlECnE++VEuo0OFINLmg1Yg2UMitdyBFbXSswBAllQr8Oxng1E5l6gX\r\n+hY0OhlNNJAnYNhHAmXVG4ecFl9m8X80PGPTFCth4A8aFItp6+JsYLiVCYwn\r\n25aaif9rg/qDP0CzXYJmGcLHqa0naO1Mxlhwa/DjC7TLsFSRywu+emUoTOwf\r\nhV0c5/ggLrMx8iPVwjEZgZ4S3JBQvmRy07VYHyDgiJzkAW8J6dgSNj+HRxiJ\r\nrokah/nSBQJewgsxLAEq82w1JgRX/PRUP7/0qQjz0TBiIpAi34gVBir13qf+\r\nyj6IM1OZB9F72pDKuD7yJ/P0IcUezTksIy39ryfzQYZu+ve+A8FCHgoRIXot\r\nhqFvYcbIOqgXJEgp9b83E1bl3JEBCBzQj/i0EQPdeSr4FIJkKlplWKT84xhj\r\n/6nEUXF6vieTTvFM1IIgPwPIodkv9if8BsPEPYfBbp++mnneSlEfHsHRNY90\r\nTqjwl2qtC9mMO649r6+ETiRpPfhtfV4QfOtFfFOnmKyHBeFXjSuwgIFYzZGe\r\nXC7DcZLUPNRSwTYRJA8f0P4zQ3SfW21qJipKp5V0jODgvJeqEla/QZqoyQ7P\r\nD1wsbbRfSoDQdv2u9r+edagLxLc4l6zifDo=\r\n=Bnme\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.2","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dapp-dev-goerli.0","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dapp-dev-goerli.0_1672921870645_0.7214549539911832","host":"s3://npm-registry-packages"}},"2.1.0-dev.4":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.4","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f9997a44ad23bd6688f02327687e3078ccd1847d","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.4.tgz","fileCount":135,"integrity":"sha512-OCyxLttxUFo4Gtd9NU5gtE53RRupD57DuzUKNrHPHjfeW0M/Y848TIGsbaRiijOXn6//Jp/hmrl1rLyFqTtbew==","signatures":[{"sig":"MEUCIQC8NtcBuFE8FQU8WnQxvufi6Zwsu3FgtwTsQ7H5FKniPQIgEGENkW3eF/2VDPOUnxZWigvNfIu1t5HH9VZCkBykx34=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19811257,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjuASJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpgwRAAjLm8EY+C/ZcwWxouWyDp9kWQwCcCJMFGmDzk51inThsNCWtx\r\nXRZhUI83Nmqg1IPEeSuBS2Zz/twx+wHjKP9VjgWwXnRfw/RFuyEX+/Rht41h\r\nSASTRXiQaZOiuCom8f6MTAnjNJ4UJsddPvZ6ALnHhq8Q8sxAVjTzMetMGCs1\r\nu0qIoeO88KsqWtbx2oOh1ux5UtHghXXTzKuYGj7VC+WwB3fq9H+xaAW88eEI\r\niX7qntTV5bGba7YDjYeoJ+iQeM6PZvArZPXJIasSTG8CbegGUE13g5voNGDI\r\n4LfKKFe67eAMlUHeThf0ifBO70ZTpaGG5K8yocfH2EVV7xWCa7iQKD5Z5Say\r\na2CHbBXSkQtmrPatLIjSH+37zSqrnCt3oSpEbtV2JKzwlgIhaBQKQU62lQBU\r\nKdS6/j2QRjh4h7Hz1cjgkeudu6JfZizNuSlhuUwXfGXR7GIfjbx8sfEQWLLA\r\nd2OEWRAa7XO3QjvrIdwQphQieNVjWhgeQdiK/iml+oTZQ9B4jA8etsqYhVGM\r\nKMWl8fqM2N6qSiVbDm7uzg5LMp24XUCICoP/I+NDJ/ZRuYCt9IJi4h/b2heX\r\nXAj28JeRyRxwQorAXWHPvMH+zQpIRPOcvFZBR9hQKHZx9sRN1lQD8z1d9zHs\r\nYrBho7AZF0Lo/Dv78B7Ybj1SRRvWXvZRNJ4=\r\n=na7l\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"a0817e53bc6ce1c85013ee254d24b88cc78b881a","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.2","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.12.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.4","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.4_1673004169511_0.9774152904190516","host":"s3://npm-registry-packages"}},"2.1.0-dev.5":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.5","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.5","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3fd53d09e36337a59b42dffe0dce5eff5c45e6ff","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.5.tgz","fileCount":143,"integrity":"sha512-O7oCh66aw+V0YyxZobarwUngiC0edIW8OuKPq2+qV8iqCfSPS9DfaiSIFqzJ/QzHZ2KI1vEhgIfuNDkn25FnOA==","signatures":[{"sig":"MEYCIQDCxBp+uu5CtvkoaR42M3kJnCnRGYD8P8sdV5wMR+gKoAIhAPs30qg4MpWIC2JWLGJNsrRWvD1qhFG6PDoiaj1qqAir","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":19830721,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjv+m8ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrZexAAjpN+e7w5P9t7HG4RcJXUUvk0Jvca/IZ8jFuXg7NPNQsV8uqC\r\nrIHHhhvXgo6EiAOgMK29HspEGRnnrL7blsMsTs4bqhizKAeFzWffKnap5XNI\r\ni4m6Iwhgbvi14h8xd9l/QL6bEo5HkNVcuWRw6GScOdP5Ocxyzlypif+Xf2Sl\r\nxdL8xBz2aOv5Lvv5ybIzzjI5O4FawcAtI0e2XVXOId+rn0tcCVEHINKE0/uV\r\nR6LqcgAbP03dPumfQHTqig6C3kql3KSZJKfauXvU42IpPfVg2uAaEcs/Cc3l\r\nlMeplH7L3xdC7hCjuArUnv6cQ+oWCp5MnSDjOueeUCx8fQurEa84V8yGoT6X\r\nzlp428hTrnPZgAIEPZ7dB65bVz5Ft6maJqzXwkK83q7NooAOHlgGehIF3S0m\r\n9rIR2ysEjz6S+t15+rSjjlaepWWRh8bvD3WZrCn/0TJj7PPihHafrlEPFtBa\r\nCxGnmR+XnqGM+pz6USHB4WFXYqZSgAhjeiLcE6GH3V2FaQf5Eo7dApGyjikX\r\ne+BWTng4x+Eu8FdgzgA2wP9istsH1N9Uxzlzh+oDksnWP0UjY/EUfI2feRJy\r\nRKwkR7KAYJtKS4OcYKR1CpfZuZyJR1dIvvSjmeGb8wSsTFLnMKBNJz2wWXLj\r\n2HEJVIp5o6PQqwN49NFUpPgfLTYYk255ueU=\r\n=8lRk\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"29f1a4c39ddadaf15a4f5e74361485973b982799","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.4","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.5_1673521595902_0.9024568983490842","host":"s3://npm-registry-packages"}},"2.1.0-dev.6":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.6","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.6","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ccc690f784b6e802a5b80b2dfb7127d96e548a25","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.6.tgz","fileCount":144,"integrity":"sha512-1D74OPVzzxxVcG8za/niuxmwEdDc5R6KNuHsUvsFkcHNJE1UgQNw+QdrI+k3M2so6YrO4L5lP7vTvIvBDbEMNQ==","signatures":[{"sig":"MEUCIBfTU8IyDJSaThUxvcUsOGVoF7FWpAKq4AS4qjVBsfm/AiEAoq1D6+1f3LvoCb+fBJPUrGtu+BP0dQMHqIcRVItze28=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":20157064,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwYoYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqtcRAAgmYn9IGzwpM+Q2aBaOaXfm//vk7+Du7+JKFXZzfdwv5Mwxc+\r\nIrP8F3fGL3Gny39B3T4sTlnamjP6dJ4fYW8QpRp/4mfntTyuruLHn0EXAeLw\r\nQiZUxQDivHH+8LhavQYODLD5E5inBMn4oMMmdk4+rC9ar2CQzP3U4hvYF+3/\r\n4dAJ7oggD/f07Lcf7JAGObNNBZeRrs51XtpceXo6qjqjox/8cHX5I4Xkneov\r\nC8SRwSUEai4VIkfemD9XCN8oJt5pwSeoxkyGzKSwWA6/HiTszAbZrbd15SA4\r\niG90GtuO3vhaHl14ikx95RP/6Vs4X9YrN5zlIzr0MKxrv5xEjcVkZkBErWgr\r\nu1N93/KxI27FjBxg2CbRQCVojz5u3AoLTkf8qlM/i0HLCvFAiI6LCcglpcX4\r\n+45Kjzpo7whrdzYRyHcv+QLxb9LDYI/n4wBuF4qrC37FNHf7ij47Jrvfzo9x\r\nz5lkLrdATJsYmR05q+QkIY3rCoalUB951gTiHLdH7kfViIb0YOQK72JrHg9m\r\nYKmfHxceHdEiay5sRad2Yy4rHv2xJE9XaBe+MsSR8CjB13+P/YeHJiJ4M49i\r\nnw3/OThGZwZ60GHJ4agq4klHBIbCA+0/EozpiZYeQ+1mJC+8VAoxkYSm0pZu\r\nJ1vVT8Rdh894ayWGWV9BfHMmWEmUfNFFC50=\r\n=IY+k\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"467493c68f59bb3f6e97c4b826f692f39662b9e2","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.5","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.6_1673628184663_0.6128629769344021","host":"s3://npm-registry-packages"}},"2.1.0-goerli.2":{"name":"@keep-network/ecdsa","version":"2.1.0-goerli.2","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"70164f2c16684386cdbef572cc55f95f033441d0","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-goerli.2.tgz","fileCount":123,"integrity":"sha512-P9Y7W3XUhkqPZt2AhaGuSwrwN6+lu1nN7FKEy+zGsxgq1aBBogBdhLSrtNJwFHcl8KEHFMg/Ha1zGxO/uGk39A==","signatures":[{"sig":"MEYCIQChU+wmFa+EbCfQ6AwCXMC7g1vknGfwwwlPVJ/dXDG7nQIhAPwUCczC57x4eIBGIK6bsH9O1A1uaoUwFDVXrRSYVW6V","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16414096,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwZFtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpwjw/9FgeMU+XFv46FvZi09ri/ycatvpReM2Oa1O9GJ7Mi9zQxwlUz\r\n0EBAO0ntvn9yPu+/F7sxqVaB3A5GGwNraR7HdGv3ZTZ4vhCBKt5XeHc32Wo7\r\nu2qt4I7mR8hps8giQ7eWDnUvJsdkCzUZx+zsq4I3C+YSG7/SGIqoxWR0LmJ8\r\nvMeG3Ei0qVAUXuq7p1BZrXrOrZDyXwhQd34IkUCSwc1rIY456XktR3K9H4w8\r\nwiWhc6VEACxLVSx5qKxX7CPjiSOzpYih2mneoEvfS+PimjRGbdqYxL8k/cpw\r\nPJUllpGYz12RnL0OMdSykjGqDzqmaLP/DIF7QY1jdF/3iaOSNMqnYyQhtnU+\r\nKfLQ4BoGrPcooc3UFj71EgW0Fp9rsH9mGCrxGfPJ3dJrOx1oDeHRTZDH5qiC\r\nfRIF/fb5FRF2gJaPhutgGG4l2zToIR/IV3u1sv7RV+wMl5EimlSVhskh2pzD\r\nKQWMitft0rLRyrKabsCGNce7xr+LHxRZF+C89tEO51uM+jmpHeLEk2hLQBGx\r\n2GvJLJ4gTtpz0xpwq9ozLMz5u4NhAvn/0dXkAG7u6Pf0nLIE14EVb1Z+o1fC\r\nFGw3kxKmW8wMtXcDUKJa3WUaLKTrpfIqi9vXlfLkkGnv9JEI/iRLf/W7pQWq\r\nsWwhwt0Fcj450r6H67ybv5OpXKz5xeeUnQo=\r\n=1E20\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"e14d15b1b9058508cf6dc5f2a3d2a9c48d57a881","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-goerli.4","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-goerli.2_1673630061064_0.33519280206915614","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.1":{"name":"@keep-network/ecdsa","version":"2.1.0-dapp-dev-goerli.1","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dapp-dev-goerli.1","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1b3a63b206aec4b65007058d8490a6498b80065c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dapp-dev-goerli.1.tgz","fileCount":115,"integrity":"sha512-RHAykEWFYZHBpBJlO6hIkBnjk/mutt6sNbs4w31B2qgMBMaZx3S9HmKs99BB0A7CLG7PtKNcKd6brmlxwnuAXw==","signatures":[{"sig":"MEYCIQDZklWCtlQACVkTvF+9adoIEatvTyXpiiKB0TT7ZgmEgAIhAPNUsRNkGMDkDzsaWXV8z1xs+DjTDuu/45sjQgKU3xFs","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16409845,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjySccACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmovpw//brEr0SQBgP73rjRTHn4iJL/VWxFevsFrNhr5D0ZvC9YneOz2\r\n18R+MKELP51fn8Iu90wZjmEnjtywvE1fuMoutHvaBXgItGdMoTyjNCbCrx7o\r\ndhYzyMCsVeEOzNKmkqgaCWiUCtMjTSg3kgnootDgOkN3XtTu6sRCBPhopSS9\r\n0uRMC8S/x8OgRnMHvgDzpNcxq28kUw0OliB6KCO/XWDpIW6oQlTnXb0cplLW\r\nEfWAaWUFlsr7F5g/Y+hojogywKWsXOLbNOB14sl0KdsZaHbNKeAmuw1xsOAw\r\n4zGJ/GyHkGOJ3RUy3LRX7hsAabVUvNNuWypszufsPR1fJ4wGJ4WtT4lmbbae\r\ncrxnTMDfjb4e3zohIcipqAhQXFk5j4xpddHpDjEMTM8BYBhBu2kTojGXEjHT\r\nbdv81nv3iNBv6l2+I2jkqRFcSBit9ikZqnIW+QpyWCH0ssscPL1/ye1k9M2i\r\npJOQK/RN56fPq5xTelsZz7zv1SypJMZ0JwDe6wXzettIbRNpR/xvehAswcb/\r\nxX0e59TtIJJFRK+TjBqy1RZ82AVAs/XQqCiwd0K1qYU0uJNYM9vJT0oFeop+\r\nWVB26FaVEoHQe3JUoTJaAJUri9ktyI6cEBbbOlK/o3zJh1xEY0MPQI9O74ma\r\nqVq0jupXHVgQ/XBFhX/wZxNOpvTXMxSMbHM=\r\n=YaiD\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dapp-dev-goerli.1","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dapp-dev-goerli.1_1674127132309_0.8917507348299356","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.2":{"name":"@keep-network/ecdsa","version":"2.1.0-dapp-dev-goerli.2","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dapp-dev-goerli.2","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"68ce2fbcf4182a8f8e80fb732656115f34c663ad","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dapp-dev-goerli.2.tgz","fileCount":115,"integrity":"sha512-rMZ5iMyz2nQ39YTnbyY7w71pGvbJRbiiPxHKK9WtvMXoolwb0knOAJc7vAXHJZcdhQrRZGqtpfY725IaKd6p4A==","signatures":[{"sig":"MEQCIE8gBiQPWMQXofS9aMSJUvJexUFbJPx6Kd48ZGZb1dWFAiA4akGn3CrCGopNYJvu/2/22vUivxnCjivOZ7yy/Kup5w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16409838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjynqFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrS4BAAobzeuTWzMydYWkXI9mMCP5Le6grridEbPhXVzBfMolw384So\r\nSH+ruxs8+kkK3EB5Skm22XFbxVv1Od+G/f1+kLrk1YTnqFOeMalCUmsujLxq\r\nSwI4x2RRq2OKG/yi1CD4aEkpoCfJl7GzH2mrtD+D24IiLkcQY2IY3QN38N7p\r\nRHS3yAAV3UWnEtGgBEMVkBZU93mHB18/ylzdaKGSDSBRG65C6tp1QPJFo6E5\r\n/dea6icomsIx9H8PeCH7fIOlBkjnGAydAIczApvuviptkUGvF/vfhcT2Wu39\r\nw+afoQljlLr3iU4tpoRmc0u6ldE3kufunoAnHCz6fY6JkZecvPxyW0NahJpl\r\nDQTBa8fDVcpeSVffgdf4YRfcspx2cdsYaEvbqJwQ/QE6majZXipmvHbjaW79\r\ngKEWNC7x55s4iavSqp1d8qDuOEhIzN39YniMnpfIjN0MWfjj5BXhIV6TVmaL\r\nY+6W+R88y5B4FHJJ/j+Oyox/aOUr3hWlTF9omotOyC82jzKBFKL4dgnsvwoV\r\nXmPQ60gW8WFkNQOcYveZJ27gtdPbXQYmKkbzOoLG8Rb9yDP8AYz8Vee4jP7X\r\ntAU8XCU1nuCImgwDo3XfWre51hRs4mhlPTq6KNSw/t6cSl7+anpT174AIMlr\r\ncDjS2ejgNnCt1OGcx0C73/KiCxGiovt2Z8w=\r\n=Uo4P\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dapp-dev-goerli.3","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dapp-dev-goerli.2_1674214021563_0.840713024925839","host":"s3://npm-registry-packages"}},"2.1.0-goerli.3":{"name":"@keep-network/ecdsa","version":"2.1.0-goerli.3","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3493c5b21cd4acdc0fe3ca218b7c40e924125f04","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-goerli.3.tgz","fileCount":123,"integrity":"sha512-AwmeTHMrLXKesZdUKX1MGplPxE9pBp2AeQgMAgeyfc+aG6af2hRbZlzzruLJnvnra1YwcpI479Ce1d2yd+YoQQ==","signatures":[{"sig":"MEUCIFfBNXF9ZatXGH7rgOitZguVNSOhAvzjoah6fS7tlAlfAiEAhmrTBP3ayqTffZzpPKm+lIESvA+TybnEzMVcGBp64cU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16414089,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjyo4XACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmojFRAAgpCm8cHoKevD5is/Qn+W4mc6N2XLRrCDOZ4wQYxa9lji0TYC\r\neNmQmddPmaShVJic2bUeHGhbpFXJj0haeyZNEBiTrWRAy/dst9cVRIDwd4uL\r\n5CvWhPA5GuMk6GZoUcPaXw6HV/bgtTiL92NV3srxdC4ErzboQW6UfSXe5MBy\r\nOSlssW88qWg+4bP6SzSykiLVgdw3o/YtSX5SiqOtQu0+zCVPIdt2ABuwZeYQ\r\nsE6x3sxPIhw9AEOjoN5Nucdgx6jB8dXexxzM2uswXsEbG87LMF4Y+kfjDsjf\r\nXMiTsVQEfsH2Rp7sE4DInsl47VHr1QR19a1j+NLpBMTRmw+RxcKjSOdBZbxh\r\nX0alaHwKHC1Z61y+EOu+FiPOPpNiFA9fQvyTg0Gn30VTkNg0Er0Z9vYUK0PP\r\nkdiJ/19769MBNtLhyH+vPK2w+o9LGO7cBaY+f3LOM4aIRUtnInt+SoXB6wOO\r\n73vNxFBaDgKXIFoRI3GCq4VXTqsARrSH8ISvuujgriZhqKmrllXLLg4z7Aoc\r\nEcEl1qno46QIJyMMKNAUf+XvJYs165BvRyOvsU1qfEGoUSJjm8S5/Cuz12rr\r\nrTpqu74xFAr6x8vLc76WCRAwwjsFMqyhBf8u4mLYupUyNWN1gS/JWVYvCXQl\r\ngXIk1gZu6xvvXyQFE3k4EEZVUkFE1uf4jSs=\r\n=92HU\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ba1879c9fa21f094b0cf890ccc3eaffd012879ee","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-goerli.5","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-goerli.3_1674219031517_0.13827053952447566","host":"s3://npm-registry-packages"}},"2.1.0-goerli.4":{"name":"@keep-network/ecdsa","version":"2.1.0-goerli.4","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-goerli.4","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"9ff035b2a1dd000dfdab8617ab3da5c41e7ec6da","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-goerli.4.tgz","fileCount":123,"integrity":"sha512-JsBrLeJyC8Lob6MYDsqG5hyYTXWYYARGmiP0kFRT+9B/tDr3HeWGOkCU0uAdMn4dlmu1IPyR5nBbl+9Atyoa1w==","signatures":[{"sig":"MEUCIQCNJMFGWCA28xa1p162mS4iHWabT7mZJQYMC4jl5RabngIgVQAGEMHSiiYXHSyA+6gWZmCwVhwCb+vBo7a82mgIoRQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16414083,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjzyCiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpdtg/8DJj1eh+iBnJheIu/h3j5sx6XbGW4rzml6u82zQ1Er7uHRdHy\r\nA4thw6FnMl84MO0yJbsGJLHh4qsSzV15yGaDnlJQhBoKSWcQEA9kcS9o1d34\r\nGAEKbsRqblOq7N9Pbp5QOWjMxg1k1dsvtFZBkIfNQmTr/hWkuNb4ZZnj5idX\r\nazJS5+ohML5n8YWZsoEYLRh0M1aFN7mpMFRkSODSLd82BB9ibUJ+X4cH37FF\r\n2hhLrXy2N9ITM21CsBL2N6rZepm8TOfujphxQTZs6doDwYX+3AoZl9NUQJ8P\r\nF9pz+DAi0JU5X0lCuyN3Zg8cZvIK2qapB24r0dC/ylpvtNaPSLi4pbX13tu4\r\ny+EirNsNwLHWZcyy5TPRalmBinilhCyTjl6dXWHQoTxjHFAzeOJVqH5a5HfU\r\nucnd9/7VKWGJJz25VX902fnQRmbH7iPwCsHMaJfGhTh6CUqs/OdvfAzzS7OL\r\nP3OuJtvJG3eILPXsZg6j3FeMACkfjALCo0aJwke4IYyMiFzH/o/+yVJqsJxF\r\nhdqulkJSq2X7GHvdwzKJPhfhNuAJNCGWJXTJQaKiTCGEsevZqxcOnDHpdoNE\r\nFMEWfRUEz4r3bWhwPc5RXoLRgEVOON7fif4E1wmNYFSUaIQtjOfHbrTeQZ35\r\n0/F4+r/erDTmvPWUiNmCWh1Nzeth8PlJuLs=\r\n=KuTf\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ba1879c9fa21f094b0cf890ccc3eaffd012879ee","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-goerli.6","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-goerli.4_1674518690344_0.7020992353887772","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-goerli.3":{"name":"@keep-network/ecdsa","version":"2.1.0-dapp-dev-goerli.3","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dapp-dev-goerli.3","maintainers":[{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2428215e38c7fad04ac4b216c19da4d4b97cff59","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dapp-dev-goerli.3.tgz","fileCount":115,"integrity":"sha512-Fq8rdFDbvhN/ipuZRx7pUaSyOCYqAg/Kw75ZmMkCYOFhbMgn9HdBBb9QQ4GHoT20s2jj37et27tV+FgFhvj88g==","signatures":[{"sig":"MEQCIB2YmfLk1gX2VSZ08xmpqIn8ZZ6VAiQ6jCx1+LZaR3vtAiBh6VAgfOUkAKOCBhdSobk6sqGbAufK0iok6/Isnz3SJg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":16409840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjz8tuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzVRAAlMQeUSQkLnIkvneTqkxT534YgooYLLIAeCQulOxdXtAL6Xth\r\nYYzrC7/iE1IGcaY98m8Yy7KVMByQAJEcOF3wOWow3tgXNcI8BdRzlvrIAvE9\r\nwDH4GkD63qqedRq5YzjYgr86zgKdO84k/CpqpO//0Gvf3N+NJXhLK37QGC2P\r\nmx+JorPF/YSJlTAINtzyRwNjFp3NmH0MVPBECoUtJ8V+QxmnsUsKFq4mTzIZ\r\nc8CVRN0zelmjk0n1Qw1TpJ+sLMOhB3pqc2eeEA5EBJxBVHQwO4y/V53nXtYn\r\nMIsoo1efRDYISl3mBDRxEnh4JAhiuMjWO/z+iGd67LPE816q+UeYStks1lYc\r\nDJP2RGQ+MiSLER0Uw+hAY6UtSgYtyyfq/3O1TvgL4eqm+6S8tR8V/tC2/Ft2\r\nyg9kYDfQGolU0G5/ZnAlvs7mevF3WRdhzIB5Imtn6jIe0VvW/tTFZBUaSFM/\r\nLXv9gjM8fy7hddnVOT6K9+CfuH45Rz4KkWH+barIJWgV0qnoVS4MeeI+lNKb\r\nwyCiLqiHEUoPpSYgYXX/uRrvpxItwusA2j+PiqheoRIt+7dhAzxPc3t8FCEm\r\nHqli0mgJB70fNHNywVfzUlZh0EjC9VOpWclTI1rLEDfRfmjB5Dd9snvVFKx+\r\nJx1HNzQhFB8ll/8+LKAg/gTA6j690JOlMFE=\r\n=yEio\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"1f0f292c73e7b196b05d189862d0fd1111772f4f","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"deprecated":"Package deprecated due to deprecation of the Goerli testnet.","_npmVersion":"8.19.3","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.13.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dapp-dev-goerli.4","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.2-dapp-dev-goerli.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dapp-dev-goerli.3_1674562414695_0.7512794174638227","host":"s3://npm-registry-packages"}},"2.1.0-dev.7":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.7","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.7","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ab4cba37f02b6d1c42643d3aaae7285524908bca","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.7.tgz","fileCount":149,"integrity":"sha512-4ExuTTA6ulFZSO3boM1UVpcTc9gXJM+1auSd+WLiSXqP2aCvPsdcWm7x+kGJpdlynW09zGealOPF+qOdp4ib0Q==","signatures":[{"sig":"MEUCICTMGJV0oLM9iIT2sKA0iS+ZZ8bsW2a4q/Sxs9FxIFffAiEAmg/ieCrLOQU9TKz+nxiN27s0g147aKP6myOHX1fXBZU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30527469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSvqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmotQA/+IsVwgaIuHLL+cyp3NkTO3sA48626fx0Q+zMxENWvYMynl7oU\r\n822tZ74HzsA117/b5grE+UhYTdR7+1XzRNShlG9HWlOsBpMFJTFmQDk3xY7g\r\nl1zSNrQUZYSGosb7EFCPQLGBoOYGWRExODFMMcsM6sZ5PIDQMQxjqIxKrN3Q\r\n1UL/IEZ9FvDEJ4egZ1em/m/VlSL5Et9ooVjOCQbCM8pHDirsJ6y+3fO/S2z9\r\nuD8UzeP1/zKtzBpZ5rlpdCxPXhzTiMbQIAOh6Lp+HHzGv0BDQIY2OylGJsX1\r\nPTXkikcubIxiLvgMGGK6GToijBjRFY1YoUol2/u7IHsntvOYXbLkUSLgDRpB\r\nyTtBnobC8FX1eMbbjz0tinqugaNnr59W5TuNeU2ZPbyMcQ58TFoiP412jzl6\r\nwRehyLrV3dSUgKqVkAYcJmmzSnXj0tWa1aKhNN8BQTT3BBMNgyr4BQt52KIE\r\npDt6AKXEx/48f92T6rf11V5ryItnOINZ9fzNVGuKUNnc18gFS5NwoFKpMAyS\r\na+II/FK1DnvK86CN/B3gjn2sUa1hKsOPbo+KxgWkFNGdJFhrg9cifaAiOQOf\r\ntJr7iFsmEwLfSzNoEd1oXWW7fop7bOz3KQdzwCkJjRpkmwy048GYs86/SxBR\r\n4YIg9mJHCV7VdfyzNt3P6rnSqKp1ZRFyTYQ=\r\n=PxKV\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"bca71244193d37e5bb63aa6f1763b010b868f8fe","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.5","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.7_1681730538150_0.24427260283424745","host":"s3://npm-registry-packages"}},"2.1.0-dev.8":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.8","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.8","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"4aab22b3f5e1b14d8e51a7dc46b1460aeab59fae","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.8.tgz","fileCount":149,"integrity":"sha512-+VPibcAzQm+V4ILklvSBR+WIuYIKXvVnnzTKdi1c2dH5AzEfT4v9IqXefuN9ZvNT1fqnnkE+VWfi1tSgbYI95w==","signatures":[{"sig":"MEUCIQCdiyy+jh6DTeUnOkQzNis28ah0MAd48/nZK/2EOZXvAgIgY27j33rHkx3Av4DSDih6zKOQJ6udO5CU7nyJ42RF/lw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30527469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSx1ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoTDBAAl5Ca9i9F4tb/NOUWwvOzad4A+kNUc5PNRrwpEpeUWT7OKvSw\r\nYC9gCrn3KBkiAI7ztuBZWyaCc8YJ9EB/7iW55XBKfHfe5YkaKDfw11pnHHLN\r\n0lZ8xK4eVGQkReGeUUkQg1plBWP4nhi2ly311UnWcTM5EMz8DBYGnDwSFNSZ\r\nw+Q4qe1hyvBj0o/rxj27efHVoiOvsaKIhbE1L+5r1S+bn7sNhT25utc48SoO\r\nbRwXqYuxFZTKyKZldWOiRcHigBjj8AmmvAC/rP7dji4GvPQ74J8Vfw/ft7Zy\r\nGGe+jSI83ZcAav7v5ETMD0BLt7gd/9b62a20FKKyOSl4nLhl5tcjd3HVwZas\r\nRbXUxZur/sauI/RJmMlRwWSVDj5hsGwJyvhQn2D3heGP09Psuu9CnVtxok66\r\nlE0dBqZyoaAAmsb7O3yGql7kxe4Gd5UFuIpMJgGA2UeSigQv6HJsy4rMN5CF\r\nIhvUtUhBQnZJwZ/3oFW4c7t+REB8stpAYRAv0hmW3+FJJ3GZGfXYe5YFsxhS\r\n/ikrVkJ2JXpX+tUVvcxBncwn1aRdDrshe1KmCt9aGhWpl3cfYXIo/QKkYevQ\r\nDAwUHNUQ14E4hvun+CAN5rJlbpUxtG7sz6D21DRnwOF3igwvf5AwAoNw2vPg\r\nxcjBJM+X+gGX8FaKz6tE83bdgVFDW2VJMuI=\r\n=KBVp\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"2fd6dd63efc71876dcd0afea539e6509ac25a2c6","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.5","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.8_1681730677478_0.11367255944150023","host":"s3://npm-registry-packages"}},"2.1.0-dev.9":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.9","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.9","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"59cbff61d267371760bb63068b74d8cf32fa9964","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.9.tgz","fileCount":149,"integrity":"sha512-sbHyXfdE7mAmwVKgYKQ1L1VyECZDKeIfkc2jZhAFQgjE/jVaEDBa97dpgBMBQaFuqNk7OIUXP2fpmRuf21F8gA==","signatures":[{"sig":"MEYCIQC4pw9Pj15uQG0JgqbkQ4eNPYcpR3hiHr1VYdEB144YvwIhAOoM1yihkPC/Zume/PZ2gZKma10p25tZL92N94A68XSv","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30527469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPSzDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpRYQ//RJ+1ZfX72c38f+MX7mMLkZ1qSG+ephZ8mLS4Umwz7aoCD7zd\r\n7M8xF9leKc8WUDqj3S+LD9C5booP/u8mv6tYGgxBsi9IEv/QMO4f03nFh9Fq\r\n6+oJDW+VaBSuUwklcporwrLrf+/0umqMe9vfX5UiAb4mllCXg1R2hgEEjoBg\r\ndPsiVsI2JCNtCzhyafSuUo+JgxK94j36VnbQndyTccP3UglwkUgkR5cVSx8K\r\nQzkDLN+K2fByS1CTidaSYUpx/e+jSorNRSv4T29fQ7ZrTVjqJha5mwJZ4rzX\r\nE2QeeIEL+G6GCEASuo0EDXsthPKV469iQcEAfHLnbzP7r3E/sUZdViLvEFJ9\r\nDxmUT2ENimWRC5isEvn2Eeeey4C1AQz6ZBUG5kzlsvGVDmw7d6+jUvNpaMOY\r\nCbhQKX3rEUGFV1mK0hgRAWrVj6hQ/Av57SmyyjMzhIxY1CNXuVI/rEU29QYe\r\nI+ackmcDQmd+U6kmmP1xpuTcClYtNMFzyZBin9TuCFgr84MQlKWAWBjYRBDK\r\nJqzxat6jEqY0flAQ5H5guvMutHEAEWsbtGMYMDoKRh6wfxGFgQzxnxCCSDaU\r\n+2w7Su9JsnQvkTqlfS1su6DeeWZ2HBVCokquZuZNH06fpXTk45OQcJ0NmNJh\r\nZcuN/e86wfl4A8o0DUlE4E62+fAnf1tT8Rg=\r\n=SiHg\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"099ce4f111351353e6c3de9e263f71b3304bdd69","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.6","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.9_1681730755472_0.5145743430087502","host":"s3://npm-registry-packages"}},"2.1.0-dev.10":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.10","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.10","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"aa9075416a960a640298e4095f3fb94de83866fb","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.10.tgz","fileCount":149,"integrity":"sha512-axUrpyoGvIVKqNVohdzuqlwHGb9QoCdmfdHGy6mONncMkEJsc/BZqUMOSlJtW42QVddMfG672kvgNZF5zsTjNQ==","signatures":[{"sig":"MEYCIQDwMNOhMZ1/9vy2k8aRkc36CbKvVwXT1IOYY+sDBcwNYQIhAJrtxOBU1dvp2P7vFSQ6ovU1BTF3vNxGC0fh6Ahf3pCy","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30733299,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkPlWvACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoYuQ//TvJRWXD0Y7Q2QwP5s5LaTr4j8WddQ+wTOb8sGoeR6NWLGNyL\r\nKF22BfZEr/GyZ5NBMtEyhm/dlCaJ8ueZXbQ3vEekcUFH8pKvrbRYcgoKRAHk\r\nC3vlK2/mSE+7adnhTnc94/ez+uaQMYVoRaOFW3TfwLv0vwbHSNJLrpadm+bg\r\n4XkZBj716+ZblwWYLFPyLbZDuaDt4nVop1K6cngFABtSQq7Q0nOVO7tAytHk\r\nk4/HopZS5i6d92Qd1Kug47DwctHMDNpGnz6WStkYC2D53L74BDJ6fNb8rE0w\r\nca9Xt2y2JsqXf3lRoiki7iCsPsZBu9yUIse+CiZg0v4VcIM1oyiWA3tQIDVG\r\nTJAhVfZkCsGVhHc3AEiuWqQG3Sve0eROI30HFnHQbKHcitH0D/FAGUQbk411\r\nKiJDGRUR4yYK4+WnE6Vl4N72Y8JmGNQ+2NwSlY1q9DsrWWUDlDved8wtAjup\r\n7WRIQQR8UtA1eTRNwL9l5pnWUBWuVziPVf7wwWv0hKpdb9iBHmtNOTiifP7U\r\nhKAkBkAYFec3ZKQozQX7MfdSN6ITFIjNO75EmM9h0x/inf15r04EaEHjK82j\r\nXGKpSoa4BNorL3b95bMO619Y4nJF/fo+lshmSd0E27qqIqBY8P5SeparEoTm\r\nhxPV2+Ti0OupEKI36rA6vLi0fsvdfCdz0MM=\r\n=f9BD\r\n-----END PGP SIGNATURE-----\r\n"},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"73b46321ae1d0db57900d7bfb7c33db71db5d8ae","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.8","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.3"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.10_1681806767186_0.43659968444071184","host":"s3://npm-registry-packages"}},"2.1.0-dev.11":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.11","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.11","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"c25fa6cfebe1ca7964329b54c44526a782391234","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.11.tgz","fileCount":149,"integrity":"sha512-5tTJr9UyW+H0HnV3bu8MkKcy+K9Gi6gaHZ+1WK8LQvba/T38ay//9Gg6dZWmhPT9mKIlW4s0zjOYkjQwq7W7fw==","signatures":[{"sig":"MEQCIBP/yaJNndS1CkcP5fsMwJJOjJtqeBIBOVtUjhSioQLuAiAvj2YUWW3NHxwMdWgpEtr4DNhhFvWot6lNJDVumrgQlg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30763403},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"de484cb2176a6fcbaed5bef047388072d64fff56","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.1","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.16.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.10","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.11_1686152864860_0.8468046321074214","host":"s3://npm-registry-packages"}},"2.1.0-dev.12":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.12","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.12","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"e7740e77027bf0002a12d2a747d9f00bc9418b42","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.12.tgz","fileCount":149,"integrity":"sha512-gAV+JX+wdNSywK0LdNmGPT7xZszBUU8ENls1Dp777/tagMDhZd4exMWYt/sB4V+LAxlZC0RXsmm3khD1S/COhA==","signatures":[{"sig":"MEUCIQC7sKOUkd5JhnFtRaSvPGgvIKXDLDqharZbN/3BpVDitAIga+Fm6FurTEbuYINnOOFWdWJ3XT0E/Xta3DhFI18wUN8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30716107},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"fabd15b8e3b280abd1cc43e629873cec163e7ef7","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.12","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.12_1686672480727_0.2569330598849846","host":"s3://npm-registry-packages"}},"2.1.0-dev.13":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.13","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.13","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"31f2f28e74485dcbe03f782f5f67ac299203f3f1","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.13.tgz","fileCount":149,"integrity":"sha512-Gv9nNQQkE/VTitFSiJqQUXczIbHWOBVG7UH6h3MDo7Pcgl4iuXhM5nmQdRd9vqjU42rI+TyPuSErWjg2+QEEEw==","signatures":[{"sig":"MEQCIFv3zAImk90aZyi4qU+TGH8IDXjwf3zIrwx9elwXqde0AiBVhQWeM/ZBEx1QwNZj/tXsX4dGlWF98KZqMLjIQYDvkA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30716107},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"e78ce3b1178badf78903c8bd31676654faa60e06","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.13","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.13_1686733808123_0.5168541274067757","host":"s3://npm-registry-packages"}},"2.1.0-dev.14":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.14","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.14","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1391174fddb6d13bab8309b482b6cdea0cd1ef84","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.14.tgz","fileCount":149,"integrity":"sha512-kvUIws/XPr1MHTEXByR+lfqPfj8Mg0V9dEnsm/cMO7N7UaB55eBF+LDEVTDgDlmaYFJfr/iC3v6EkPyz9BLNtQ==","signatures":[{"sig":"MEUCIFfQc5Lo3ijHOKfQRFWUUHOmUsn6D4JESM5Yex5PzmjBAiEAlZ031IHh9ZwVmQFDci43qONxQECagJhPJTuY7uUn+/8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30716327},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"b2c6e58d12ebc417d75403c6ab95a7417b2f9a61","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.14","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.5"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.14_1687939571207_0.04856403762965278","host":"s3://npm-registry-packages"}},"2.1.0-dev.15":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.15","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.15","maintainers":[{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"ee631a42e165f30c75aae8c54aace765b77e272a","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.15.tgz","fileCount":149,"integrity":"sha512-iUE3SwDSNc/k1oui7Z+fDGhhGyOzpe4/f/oKvDUMHqXx0BQG3QCrOz9KqWuPFXTXMav4LxLbt12WyDITAl/hjw==","signatures":[{"sig":"MEQCIGhJB0g/cvIJQa/NXNzELnFLv32zl/9nWVWL4/tFf7n+AiBHsWpmnJOK7zpNv5wZOgw7Ydmoij9Eoo6SWQLAYh7Cpw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30716326},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"f48d6767d22da0adcbe4a4b21c6209446bb50b72","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.15","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.6"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.15_1688013739989_0.7973833022546701","host":"s3://npm-registry-packages"}},"2.1.0-dev.16":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.16","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.16","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"bd9d084b4f2b7bdda0a85a8f69200832d7c24fde","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.16.tgz","fileCount":149,"integrity":"sha512-8k8XiPXneRtBN/4jVX+0qxqya8232cglRM/GrfYMv/niQkgvdolK2prXyd3LO7d4ot4WcyfLYU7uJfjjmc5rEw==","signatures":[{"sig":"MEUCIDUEp1Hin/goliGMPaSCSw9Pxb/R5v2vbeAB6bJZlwPUAiEA4VRNRRpcDuvH/LZtvAmR/uwlZfxjY2dcybzDnOkaYfE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":30717081},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"94ed595d967bd038da14bd1a9bebbc77843bb389","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.16","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.8"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.16_1695629107463_0.8292234098581079","host":"s3://npm-registry-packages"}},"2.1.0-sepolia.0":{"name":"@keep-network/ecdsa","version":"2.1.0-sepolia.0","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-sepolia.0","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"1f1921574eed9d894df62f5a0bca2e28e2d7582f","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-sepolia.0.tgz","fileCount":128,"integrity":"sha512-WWNU9KM/w6Jxw66fu/KHyYgxWMCURhKKMP2XTQ6eo2CzxhmHWlFiWGtYIEp2xXcyrx0oYRtUCrBTKpv2k9EYyQ==","signatures":[{"sig":"MEUCIQC8mJmcxYdANW+CWbes64ABsxWhQUWmYvwAibsZLcj8hwIgGu16GDqYW8RywYw3oFhfBROAn5SYDGG0b1VfmJYV6GY=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26981888},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"ece4f94825a344730123c41b6b55b30036b2474e","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},"_npmVersion":"9.6.7","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.17.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"development","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"development"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-sepolia.0_1697543270014_0.7280183471564317","host":"s3://npm-registry-packages"}},"2.1.0-sepolia.1":{"name":"@keep-network/ecdsa","version":"2.1.0-sepolia.1","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-sepolia.1","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"6e985148ade6415013e5d8cbca167d029c7b0c2c","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-sepolia.1.tgz","fileCount":128,"integrity":"sha512-WWG8Y1NW3nh30AShvFtVT+qijLUYuQNfWDox28DAv5KrgX9Od3a/l1HkRWA3qHEdakHqZ/ZDdR5/bGliT11PwQ==","signatures":[{"sig":"MEUCIAabej3ErfO6JwJbLxILOtDfm3cdUy0c2DnA21CInM1nAiEAq2aIHvhic5OQAWBWrO+oaoVXyn/vfV+zs3WXJR8CxZA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26981972},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"324f66fb3f1003f6cfeb7d4149ae3f1d902dba2e","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-sepolia.1","@keep-network/sortition-pools":"github:keep-network/sortition-pools#test-fork","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-sepolia.0"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-sepolia.1_1697551387080_0.8162575608911944","host":"s3://npm-registry-packages"}},"2.1.0-dapp-dev-sepolia.0":{"name":"@keep-network/ecdsa","version":"2.1.0-dapp-dev-sepolia.0","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dapp-dev-sepolia.0","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"3c35dfd4a6da99500d5db4c0ba13a5091c57c891","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dapp-dev-sepolia.0.tgz","fileCount":128,"integrity":"sha512-9XkotYmxLc5fc0UcUdL/FlQMi04GmfyMwv9PR97TfyfmGnFPl4spEVHmtTMZ+g/PIAMdfHoBsKVVAH+2/gGnEA==","signatures":[{"sig":"MEQCIBmJM/GpUoHr5qCPKk5mkmrf+X7QW1pygT2e7s9Rx5EzAiBN4wLpW+QeIO7pTJibrBBCYobwYdozgHFW5KlxmqAKjA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":27011419},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"809e73369a06662895ee22b3e46f539bc73bab59","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},"_npmVersion":"9.6.7","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.17.1","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"dapp-development-sepolia","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"dapp-development-sepolia"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dapp-dev-sepolia.0_1699371719694_0.7851491919212052","host":"s3://npm-registry-packages"}},"2.1.0-dev.17":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.17","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.17","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"2e6abea11c094adfa9f52dfe0e7d3befa5fe2dfb","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.17.tgz","fileCount":143,"integrity":"sha512-48zLogWDObqf3uGffDd8N2iP7m3rBinF+tAl7DidY/87eqWNydhpbO4j1H5i9vZ+Axi6nOQcuvpLlS7p9pjOFw==","signatures":[{"sig":"MEQCIGKB5eS52EqXifZt1/Bm8PaJd/t/qvxbTCvClLiMSp+bAiAlpaInXxfzHpWGb06OESzl/z+nEfb1Pu5Av6X/QIaCMQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29932206},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"98ef3fd2ef26f22331fdb7f52ac3c86703af0eae","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.17","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.11"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.17_1702559880564_0.27563631684937984","host":"s3://npm-registry-packages"}},"2.0.1":{"name":"@keep-network/ecdsa","version":"2.0.1","license":"MIT","_id":"@keep-network/ecdsa@2.0.1","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"f9de5e4ae68c48493b8a46d1d956c8bc157b1145","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.0.1.tgz","fileCount":128,"integrity":"sha512-51Ef0FGak9dzuTF4e80ed1G2+3v+kQYPieYa3hX7eKg3XG0/C/xOtMSpIOxU7IMdYBHeeH+hRIqDiL5AuOidOg==","signatures":[{"sig":"MEUCIGCTJJmzSkyJ9mea/VGAHKOeYgTbmpxGPpdSuMxl+I3kAiEArOxMaT8/MopRdTXJNhZTMm2YZvGFXlsMhUZyzk0pTNA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":26702167},"engines":{"node":">= 14.0.0"},"gitHead":"b3f4aea4d616bec2b40b8a4cee50756deef6c5da","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},"_npmVersion":"9.8.1","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.18.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.0.0","@keep-network/sortition-pools":"2.0.0","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.2.1"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.0.1_1703079048368_0.6932664232618686","host":"s3://npm-registry-packages"}},"2.1.0-dev.18":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.18","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.18","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"16fc82fb82bfc0ae442aeb345ce45fca2bde7bdc","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.18.tgz","fileCount":143,"integrity":"sha512-VjgQL5wROhUHrVnu2glkLi0x6wj3Q0AW4f843cr/PgMhQoJ6LG6WQoE6OANbg4WbNIx5Tcf9/9FZ2m1k+IYQXQ==","signatures":[{"sig":"MEUCIQCJs5g95uDW/1zjF98GecbQvj0sFAscoaiebPc0nciDawIgUITFOPP0DsJqNWeh5bQlg1HxVeEdvQ8m1inmPEU1HX0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29931468},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"d9a8706d5794d70014e14e8c52e7274471e0fb03","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.17","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.11"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.18_1706528698115_0.6279555719012697","host":"s3://npm-registry-packages"}},"2.1.0-dev.19":{"name":"@keep-network/ecdsa","version":"2.1.0-dev.19","license":"MIT","_id":"@keep-network/ecdsa@2.1.0-dev.19","maintainers":[{"name":"michalinacienciala","email":"michalina.cienciala@keep.network"},{"name":"dimpar","email":"dmitry.paremski@gmail.com"},{"name":"lukasz-zimnoch","email":"lukasz.zimnoch@keep.network"},{"name":"shadowfiend","email":"antonio@thesis.co"},{"name":"nkuba8","email":"kuba@akena.co"},{"name":"thesis-heimdall","email":"heimdall@thesis.co"},{"name":"pdyraga","email":"piotr.dyraga@thesis.co"}],"dist":{"shasum":"749af8bd65f70135d1734f7c5f9bff82ae308fdb","tarball":"https://registry.npmjs.org/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.19.tgz","fileCount":143,"integrity":"sha512-cyqRqK/sOqyaXZWY/O9ij6EINQuJ+bHLMiuufOFyP5YCj4GCuNqOcCytGAZPT+mED/0J/xn0vm+fgiCBq/uJkQ==","signatures":[{"sig":"MEYCIQCcv2+9wblJb1ou1E6UOoqatU3YCQDWtla71f5NjJlwngIhAPuWQnKA2w2UJFAGH2JCSe2xEMq6dYM/pDbrj2Gn3UjA","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":29923585},"readme":":toc: macro\n:icons: font\n\n= Keep ECDSA v2\n\nhttps://github.com/keep-network/keep-core/actions/workflows/contracts-ecdsa.yml[image:https://img.shields.io/github/actions/workflow/status/keep-network/keep-core/contracts-ecdsa.yml?branch=main&event=push&label=ECDSA%20contracts%20build[ECDSA contracts build status]]\n\nThe Keep Network offers threshold ECDSA protocol to generate ECDSA wallets\nwithout any single signer having access to the corresponding private key. This\nfunctionality is used by TBTC v2 to manage Bitcoin wallets used by the TBTC Bridge.\n\nifdef::env-github[]\n:tip-caption: :bulb:\n:note-caption: :information_source:\n:important-caption: :heavy_exclamation_mark:\n:caution-caption: :fire:\n:warning-caption: :warning:\nendif::[]\n\ntoc::[]\n\n== Overview\n\nKeep ECDSA allows creating threshold ECDSA wallets where where `n` parties share\nthe power to issue digital signatures under a single public key. A threshold `t`\nis specified such that any subset of `t + 1` players can jointly sign, but any\nsmaller subset cannot.\n\n`WalletRegistry` smart contract is an on-chain registry of ECDSA wallets\ncontrolled by an off-chain network of nodes. The distributed key generation\nprotocol used by the off-chain network of nodes should have three properties:\n\n- The signing group as a whole should have an ECDSA public key, which will be\n  shared on the host chain (Ethereum) and will correspond to the Bitcoin wallet\n  owned by that signing group.\n- Each member of the signing group should have a threshold ECDSA secret key\n  share, which can be used to create a threshold ECDSA signature share for any\n  transactions involving the signing group’s wallet.\n- Each member of the signing group should be able to combine a threshold number\n  of signature shares from itself and other members of the group to produce a\n  signed version of a given transaction to be performed on behalf of the signing\n  group.\n\n== Prior Work\n\nSmart contracts for the first version of Keep ECDSA are available in\nlink:https://github.com/keep-network/keep-ecdsa/tree/main/solidity[`keep-ecdsa` repository].\nThe new version is optimised for larger groups by implementing optimistic\nselection of group members during DKG protocol. Staker rewards are redesigned\nand allocated for all sortition pool members. Most parameters are now governable.\n\n== The Mechanism\n\n=== Wallet Creation\n\nA new wallet is created on request from the `walletOwner` address. Signing group\ncreation starts with an owner's call to `WalletRegistry.requestNewWallet()`.\nThis transaction locks the sortition pool and sends a request to the Random\nBeacon for a new relay entry. From this moment, no operator can enter\nor leave the pool. Once a new relay entry appears on the chain and gets\ndelivered to `WalletRegistry` by the Random Beacon contract via\n`WalletRegistry.__beaconCallback` callback function call, all off-chain\nclients perform group selection calling `WalletRegistry.selectGroup()` view\nfunction for free. Relay entry provided by the Random Beacon is used as a seed\nfor the group selection. After determining signing group members, clients should \nperform off-chain distributed key generation (DKG).\n<<operator-only,One of the group members>> submits the result to the chain\ncalling `WalletRegistry.submitDkgResult(DKG.Result calldata dkgResult)`\nfunction. Once the result is submitted, a challenge period starts.\n\nDuring the challenge period, anyone can notify that the submitted DKG result is\nmalicious by calling `WalletRegistry.challengeDkgResult(DKG.Result calldata dkgResult)`\nfunction. A malicious DKG result may contain corrupted data, group members not\nselected by the pool, or incorrect supporting signatures. If such malicious\nresult is submitted and successfully challenged, the result submitter gets\nslashed and the malicious result is immediately discarded. The address which\nnotified about malicious DKG result is <<punishment,rewarded>>. DKG timeout\ntimer is reset, and group members have another chance to submit a valid result.\n\nOnce the challenge period passes, and no valid challenge is reported, the DKG\nresult submitter should mark the DKG result as approved calling\n`WalletRegistry.approveDkgResult(DKG.Result calldata dkgResult)`.\nThis transaction also unlocks the sortition pool.\nThe submitter receives an ETH reimbursement for both `submitDkgResult` and\n`approveDkgResult` transactions as described in\n<<transaction-incentives,Transaction Incentives>> section. In case the original\nsubmitter does not call the `approveDkgResult` function within a specific number\nof blocks, anyone can do that and receive the submitter's reimbursement.\n\nIn case the DKG result was not submitted before the timeout, anyone can \nnotify about the timed out DKG by calling `WalletRegistry.notifyDkgTimeout()`\nfunction and unlock the sortition pool as part of this transaction. \nIn case the relay entry was not produced by the Random Beacon on time,\nanyone can notify a seed timeout by calling `WalletRegistry.notifySeedTimeout()`\nand unlock the sortition pool as a part of this transaction.\n\nOff-chain clients are expected to follow the <<operator-only,submission order>>\nwhen submitting DKG result to avoid front-running and minimize the cost, but no\nordering is enforced on-chain.\n\nThe sortition pool weights operators by their authorized stake amount and allows\nselecting the same operator multiple times. Inactive/disqualified members during\nthe off-chain DKG protocol are marked as ineligible for <<rewards,rewards>> for\na governable period of time when the DKG result is approved.\n\nEach ECDSA wallet created in the system remains active until it is closed\nby the `walletOwner` with a call to `WalletRegistry.closeWallet()`.\n\n=== Signing\n\n`WalletRegistry` does not expose functions for requesting and submitting ECDSA\nsignatures. Wallet signing group needs to monitor the `walletOwner` contract and\nis responsible for handling requests from the `walletOwner` - this logic is not\na part of `WalletRegistry`. For TBTC v2, the `walletOwner` is the `Bridge` contract.\nThe wallet signing group reacts on the state changes in the `Bridge` by\nproducing appropriate ECDSA signatures, moving funds on Bitcoin, and proving it\nto the `Bridge` contract.\n\n=== Timeouts\n\n==== DKG Timeout\n\nThere is a governable timeout for DKG to complete and for the result to be\nsubmitted. DKG timeout includes the time it takes to execute off-chain protocol\nto generate a key, and the time it takes to submit the result.\nWhen DKG timeout is exceeded, anyone can call `RandomBeacon.notifyDkgTimeout()`.\nThis function unlocks the sortition pool and clears up DKG data, but no slashing\nfor DKG timeout is executed and no one is marked as ineligible for rewards.\n\n==== DKG Seed Timeout\n\nThere is a governable timeout for a new signing group selection seed to be\nprovided.\n\nFor a signing group member selection to be executed by the sortition pool,\nRandom Beacon needs to provide a group selection seed. Request to the Random\nBeacon is one of the first steps of the new wallet creation process in\n`WalletRegistry.requestNewWallet()`\n\nWhen Random Beacon did not provide a seed and a timeout for a seed is exceeded,\nanyone can call `WalletRegistry.notifySeedTimeout()`. This function unlocks the\nsortition pool and clears up DKG data, but no slashing for DKG timeout is\nexecuted by `WalletRegistry` and no one is marked as ineligible for rewards.\nRandom Beacon has its own mechanism of slashing for not providing relay entry\non time.\n\n[[inactivity]]\n=== Inactivity notification and Heartbeat failures\n\nOff-chain clients are free to execute any heartbeat protocol they want to ensure\nsigning group member key share is still available and nodes are operating properly.\n\n[TIP]\nOne example of a heartbeat protocol is signing some piece of information every\nn-th block. Wallet signing group members need to ensure the signed piece of\ninformation can not be used in a fraudulent way and can not be used to accuse\nthem for committing a fraud in TBTC `Bridge`.\n\nGroup members can agree to punish members who are permanently inactive and issue\nan operator inactivity claim. If the required threshold of group members signed\nthe operator inactivity claim, they can submit it to\n`WalletRegistry.notifyOperatorInactivity(Inactivity.Claim calldata claim, uint256 nonce, int32[] calldata groupMembers)`\nfunction and have the group members who are inactive excluded from the sortition\npool <<rewards,rewards>> for a governable time period.\n\nThis approach is theoretically susceptible to group members colluding together,\nbut because a reasonably high number of operators is needed to sign a claim and\noperators signing the claim receive nothing in return,\nwe consider this approach safe and good enough. An important advantage of this\napproach is that honest players can decide off-chain when it makes sense to\nsubmit an operator inactivity claim and mark someone as ineligible for rewards.\nFor example, marking an operator ineligible for rewards for the next two weeks\nhas a higher impact than prolonging reward ineligibility for 10 minutes for an\noperator that was already marked as ineligible for rewards. This approach does\nnot increase the gas cost of a happy path and leaves some freedom to group\nmembers. They can mark as ineligible operators who turned off their nodes,\noperators whose nodes never participate in signing because they are\nmisconfigured, or operators who notoriously miss their turn in submitting relay\nentries.\n\n`Inactivity.Claim` has an additional boolean field of `heartbeatFailed`. If too\nmany members are inactive during the heartbeat failing, it means that the wallet\nis at risk of losing the possibility to sign transactions. `walletOwner`\n(TBTC `Bridge`) is informed about a failed heartbeat by\n`IWalletOwner.__ecdsaWalletHeartbeatFailedCallback` callback function call and starts the process of moving funds out\nof the problematic wallet.\n\n[[rewards]]\n=== Rewards\n\nT rewards are allocated to all operators registered in the ECDSA sortition\npool, excluding operators who were marked as ineligible for rewards as a result\nof being reported by other group members as <<inactivity,inactive>> or as\na result of being inactive or disqualified during the DKG. Rewards are allocated\nproportionally to the operator's weight in the pool. \n\n[[transaction-incentives]]\n=== Transaction Incentives\n\nThere are three types of transactions: <<operator-only,Operator-Only>>,\n<<public-knowledge,Public-Knowledge>>, and <<punishment,Punishment>>.\n\n[[operator-only]]\n==== Operator-Only\nOperator-Only transactions are where only the operators have access to the\ninformation required to assemble the transaction with the right input\nparameters.\n\nIn order to avoid all operators racing to submit the transaction at the same\ntime, we have an off-chain informal agreement to submit based on the operator's\nposition in the group (can use the hash of the group's pubkey).\n\nIf the designated operator does not submit their transaction before a timeout\nexpires, the duty moves to the next operator and the group can sign a\ntransaction to mark that operator as <<inactivity,inactive>>. Since there is no\nslashing reward, and since this transaction can only be submitted by an operator,\nthis transaction is also Operator-Only.\n\nIn order to compensate the operator for posting the transaction, the gas spent\nwill be reimbursed by a DAO-funded ETH pool in the same transaction. It is\nimportant to note, that the system has a governable cap for the gas price to\nprotect against malicious operators trying to drain the pool (see `Reimbursable`\nand `ReimbursementPool` smart contracts).\n\nOperator-only transactions are `submitDkgResult`,\n`notifyOperatorInactivity`, and `approveDkgResult` for a certain number of\nblocks, before a timeout for the original DKG result submitter to call this\nfunction elapses.\n\n[[public-knowledge]]\n==== Public-Knowledge\nPublic-Knowledge transactions are where anyone has access to the information\nrequired to assemble the transaction and the transaction does not lead to\npunishment.\n\nIn order to prevent wasting gas on racing to submit, such transactions need to\nbe executed rarely, and off-chain clients should follow the informal agreement\nabout the submission order.\n\nTo compensate these transactions, whoever posts them will have the gas spent\nreimbursed by a DAO-funded ETH pool in the same transaction.\n\nThe only two public knowledge transactions are `notifyDkgTimeout` and\n`notifySeedTimeout`.\n\n`approveDkgResult` turns into a public knowledge transaction in case the\noriginal submitter has not approved the result before the timeout.\n\n[[punishment]]\n==== Punishment\nPunishment transactions are where anyone has access to the information required\nto assemble the transaction (like <<public-knowledge,Public-Knowledge>>) and\nthe transaction leads to slashing.\n\nIn these transactions, maintaining system health is more important than\noptimizing gas via preventing racing, so we offer up bounties in the form of\na notifier reward from slashed tokens to whichever submitter submits first. We\ndo not compensate gas. Notification rewards are distributed by Threshold Network\n`TokenStaking` contract.\n\nThe only punishment transaction in `WalletRegistry` itself if `challengeDkgResult`.\nAdditionally, `walletOwner` can implement its own punishment transactions, and\nslash the signing group members with a call to `WalletRegistry.seize` function.\n\n== System Diagram\n\nimage::system-diagram.png[System Diagram]\n\n== Parameters\n\n[%header,cols=\"3m,4,^1,^2m\"]\n|=== \n^|Property Name\n^|Description\n|Governable\n|Default Value\n\n4+s|DKG\n\n|groupSize\n|Size of a signing group for a wallet.\n|No\n|`100`\n\n|groupThreshold\n|The minimum number of group members needed to interact according to the protocol\nto produce a signature\n|No\n|`51`\n\n|activeThreshold\n|The minimum number of active and properly behaving group members during the DKG\nneeded to accept the result.\n|No\nd|`90` +\n_90% of groupSize_\n\n|singnatureByteSize\n|Size in bytes of a single signature produced by operator supporting DKG result.\n|No\n|`65`\n\n|seedTimeout\n|Time in blocks for Random Beacon to provide group selection seed.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultChallengePeriodLength\n|Time in blocks during which the submitted DKG result can be challenged.\n|Yes\nd|`11_520 blocks` +\n_~48h assuming 15s block time_\n\n|resultSubmissionTimeout\n|Time in blocks during which a DKG result is expected to be submitted.\n|Yes\nd|`2000 blocks` +\n_100 members * 20 blocks = 2000 blocks_\n\n|submitterPrecedencePeriodLength\n|Time in blocks during which only the DKG result submitter is allowed to approve it.\n|Yes\n|`20 blocks`\n\n\n4+s|Slashing\n\n|maliciousDkgResultSlashingAmount\n|Slashing amount for submitting malicious DKG result.\n|Yes\nd|`400e18` +\n_400 T_\n\n|dkgMaliciousResultNotificationRewardMultiplier\n|Percentage of the staking contract malicious behavior notification reward which\nwill be transferred to the notifier reporting about a malicious DKG result.\n|Yes\n|`100`\n\n|sortitionPoolRewardsBanDuration\n|Duration of the sortition pool rewards ban imposed on operators who were\ninactive/disqualified during off-chain DKG or were voted by the group as\ninactive for other reasons.\n|Yes\n|`2 weeks`\n\n4+s|Gas offsets\n\n|dkgResultSubmissionGas\t\n|Calculated gas cost for submitting a DKG result. This will be refunded as part\nof the DKG approval process.\n|Yes\n|`290_000`\n\n|dkgResultApprovalGasOffset\n|Gas that is meant to balance the DKG result approval's overall cost.\n|Yes\n|`72_000`\n\n|notifyOperatorInactivityGasOffset\n|Gas that is meant to balance the operator inactivity notification cost.\n|Yes\n|`93_000`\n\n|notifySeedTimeoutGasOffset\n|Gas that is meant to balance the DKG seed delivery timeout notification cost.\n|Yes\n|`7_250`\n\n|notifyDkgTimeoutNegativeGasOffset\n|Gas that is meant to balance the DKG timeout notification cost.\n|Yes\n|`2_300`\n\n4+s|Authorization\n\n|minimumAuthorization\n|The minimum authorization amount required so that operator can participate in\nthe Random Beacon.\n|Yes\nd|`40_000e18` +\n_40 000 T_\n\n|authorizationDecreaseDelay\n|Delay in seconds that needs to pass between the time authorization decrease is\nrequested and the time that request gets approved.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n|authorizationDecreaseChangePeriod\n|Time period in seconds before the authorization decrease delay end, during\nwhich the authorization decrease request can be overwritten.\n|Yes\nd|`3_888_000 seconds` +\n_45 days_\n\n4+s|Wallet Registry\n\n|walletOwner\t\n|Wallet owner address capable of requesting new wallets, closing and slashing\nexisting ones.\n|Yes\nd|TBTC `Bridge` contract address\n\n|randomBeacon\t\n|Random Beacon contract address, needed to produce seed for wallet signing group\nmember selection.\n|Yes\nd|`RandomBeacon` contract address\n\n|===\n\n== Build\n\nThe contracts use https://hardhat.org/[*Hardhat*] development\nenvironment. To build and deploy contracts, please follow the instructions\npresented below.\n\n=== Prerequisites\n\nPlease make sure you have the following prerequisites installed on your machine:\n\n- https://nodejs.org[Node.js] >=14\n- https://yarnpkg.com[Yarn] >=1.22\n\n=== Build contracts\n\nTo build the smart contracts, install node packages first:\n\n```sh\nyarn install\n```\n\nOnce packages are installed, you can build the smart contracts using:\n\n```sh\nyarn build\n```\n\nCompiled contracts will land in the `build/` directory.\n\n==== TypeScript Typings\n\nTypings are generated for the contracts in `typechain/` directory.\n\n=== Test contracts\n\nThere are multiple test scenarios living in the `test` directory.\nYou can run them by doing:\n\n```sh\nyarn test\n```\n\n=== Deploy contracts\n\nTo deploy contract execute:\n\n```\nyarn deploy --network <NETWORK>\n```\n\nAfter the Bridge contract from tbtc-v2 is deployed it has to be set as the\n`walletOwner` in the `WalletRegistry`:\n\n```\nnpx hardhat --network <NETWORK> initialize-wallet-owner --wallet-owner-address <BRIDGE_ADDRESS>\n```\n","engines":{"node":">= 14.0.0"},"gitHead":"17735c2101b1e638085e135ac9ce7d61163b0102","scripts":{"lint":"npm run lint:eslint && npm run lint:sol && npm run lint:config","test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat test","build":"hardhat compile","clean":"hardhat clean && rm -rf cache/ export/ external/npm typechain/ export.json","deploy":"hardhat deploy --export export.json","format":"npm run lint","prepack":"tsc -p tsconfig.export.json && hardhat export-artifacts --including-no-public-functions export/artifacts","lint:fix":"npm run lint:fix:eslint && npm run lint:fix:sol && npm run lint:config:fix","lint:sol":"solhint 'contracts/**/*.sol' && prettier --check '**/*.sol'","format:fix":"npm run lint:fix","deploy:test":"USE_EXTERNAL_DEPLOY=true TEST_USE_STUBS_ECDSA=true hardhat deploy","lint:config":"prettier --check '**/*.@(json|yaml)'","lint:eslint":"eslint .","lint:fix:sol":"solhint 'contracts/**/*.sol' --fix && prettier --write '**/*.sol'","prepublishOnly":"hardhat prepare-artifacts --network $npm_config_network","lint:config:fix":"prettier --write '**/*.@(json|yaml)'","lint:fix:eslint":"eslint . --fix"},"_npmUser":{"name":"thesis-heimdall","email":"heimdall@thesis.co"},"_npmVersion":"9.5.0","description":"Keep ECDSA Wallet","directories":{},"_nodeVersion":"18.15.0","dependencies":{"@openzeppelin/contracts":"^4.6.0","@keep-network/random-beacon":"2.1.0-dev.18","@keep-network/sortition-pools":"^2.0.0-pre.16","@openzeppelin/contracts-upgradeable":"^4.6.0","@threshold-network/solidity-contracts":"1.3.0-dev.12"},"_hasShrinkwrap":false,"readmeFilename":"README.adoc","devDependencies":{"chai":"^4.3.4","eslint":"^7.32.0","ethers":"^5.5.3","hardhat":"^2.10.0","solhint":"^3.3.6","ts-node":"^10.4.0","prettier":"^2.5.1","typechain":"^6.1.0","typescript":"^4.5.4","@types/chai":"^4.3.0","@types/node":"^17.0.10","@types/mocha":"^9.1.0","hardhat-deploy":"^0.11.11","ethereum-waffle":"^3.4.0","solidity-docgen":"^0.6.0-beta.35","chai-as-promised":"^7.1.1","@typechain/hardhat":"^4.0.0","prettier-plugin-sh":"^0.8.1","solhint-config-keep":"github:keep-network/solhint-config-keep","@typechain/ethers-v5":"^8.0.5","hardhat-gas-reporter":"^1.0.8","@defi-wonderland/smock":"^2.0.7","hardhat-contract-sizer":"^2.3.0","@types/chai-as-promised":"^7.1.5","@thesis-co/eslint-config":"github:thesis/eslint-config","prettier-plugin-solidity":"^1.0.0-beta.19","@nomiclabs/hardhat-ethers":"^2.0.6","@nomiclabs/hardhat-waffle":"^2.0.2","@tenderly/hardhat-tenderly":">=1.0.13 <1.2.0","hardhat-dependency-compiler":"^1.1.2","@nomiclabs/hardhat-etherscan":"^3.1.0","@keep-network/hardhat-helpers":"^0.6.0-pre.15","@openzeppelin/hardhat-upgrades":"^1.20.0","@keep-network/hardhat-local-networks-config":"^0.1.0-pre.4"},"_npmOperationalInternal":{"tmp":"tmp/ecdsa_2.1.0-dev.19_1707407809963_0.7496412629822782","host":"s3://npm-registry-packages"}}},"time":{"created":"2022-03-15T20:30:10.031Z","modified":"2026-08-11T14:04:46.902Z","2.0.0-dev.0":"2022-03-15T20:30:10.349Z","2.0.0-dev.1":"2022-03-15T20:36:48.263Z","2.0.0-dev":"2022-03-17T13:13:11.072Z","2.0.0-dev.2":"2022-03-17T13:14:16.411Z","2.0.0-dev.3":"2022-03-23T13:51:41.111Z","2.0.0-dev.4":"2022-03-23T13:57:42.184Z","2.0.0-dev.5":"2022-03-24T11:55:33.443Z","2.0.0-dev.6":"2022-03-29T13:33:05.708Z","2.0.0-dev.7":"2022-03-29T15:18:57.592Z","2.0.0-dev.8":"2022-03-30T11:44:43.724Z","2.0.0-dev.9":"2022-03-30T13:34:42.243Z","2.0.0-dev.10":"2022-04-01T09:52:14.659Z","2.0.0-dev.11":"2022-04-01T11:52:17.337Z","2.0.0-dev.12":"2022-04-06T13:46:06.110Z","2.0.0-dev.13":"2022-04-06T15:38:24.257Z","2.0.0-dev.14":"2022-04-07T09:51:14.064Z","2.0.0-dev.15":"2022-04-07T13:58:30.498Z","2.0.0-dev.16":"2022-04-11T15:56:46.131Z","2.0.0-dev.17":"2022-04-20T11:06:48.245Z","2.0.0-dev.18":"2022-04-21T15:58:01.636Z","2.0.0-dev.19":"2022-04-22T10:58:55.458Z","2.0.0-dev.20":"2022-04-22T13:15:18.240Z","2.0.0-dev.21":"2022-04-22T14:05:16.127Z","2.0.0-dev.22":"2022-04-22T18:27:38.579Z","2.0.0-dev.23":"2022-04-23T18:59:12.367Z","2.0.0-dev.24":"2022-04-25T09:51:56.772Z","2.0.0-dev.25":"2022-04-25T10:17:52.925Z","2.0.0-dev.26":"2022-04-25T10:51:26.898Z","2.0.0-dev.27":"2022-04-25T19:18:56.530Z","2.0.0-dev.28":"2022-04-25T19:28:42.546Z","2.0.0-dev.29":"2022-04-26T10:22:35.219Z","2.0.0-dev.30":"2022-04-27T07:46:50.340Z","2.0.0-dev.31":"2022-04-27T11:23:03.993Z","2.0.0-dev.32":"2022-04-29T12:12:50.256Z","2.0.0-dev.33":"2022-04-29T15:03:36.214Z","2.0.0-dev.34":"2022-04-29T16:14:50.853Z","2.0.0-dev.35":"2022-05-01T21:19:03.952Z","2.0.0-dev.36":"2022-05-06T10:46:54.242Z","2.0.0-dev.37":"2022-05-10T14:27:23.372Z","2.0.0-dev.38":"2022-05-10T15:20:19.157Z","2.0.0-dev.39":"2022-05-10T16:55:05.517Z","2.0.0-dev.40":"2022-05-11T15:10:56.806Z","2.0.0-dev.41":"2022-05-12T17:45:30.791Z","2.0.0-dev.42":"2022-05-13T09:49:59.670Z","2.0.0-dev.43":"2022-05-16T15:33:18.892Z","2.0.0-dev.44":"2022-05-17T07:59:13.584Z","2.0.0-dev.45":"2022-05-17T08:16:37.582Z","2.0.0-dev.46":"2022-05-18T13:18:41.023Z","2.0.0-dev.47":"2022-07-08T12:27:25.716Z","2.0.0-dev.48":"2022-07-11T06:20:30.076Z","2.0.0-dev.49":"2022-07-11T08:09:33.879Z","2.0.0-dev.50":"2022-07-11T09:32:59.556Z","2.0.0-dev.51":"2022-07-12T11:33:24.719Z","2.0.0-goerli.0":"2022-07-14T09:19:43.522Z","2.0.0-dev.52":"2022-07-14T15:42:33.913Z","2.0.0-dev.53":"2022-07-25T15:31:52.807Z","2.0.0-dev.54":"2022-07-25T15:39:45.823Z","2.0.0-dev.55":"2022-07-26T13:48:50.929Z","2.0.0-dev.56":"2022-07-28T08:45:00.803Z","2.0.0-dev.57":"2022-08-01T10:13:04.721Z","2.0.0-dev.58":"2022-08-02T14:09:19.619Z","2.0.0-dev.59":"2022-08-02T15:00:17.439Z","2.0.0-goerli.1":"2022-08-09T07:32:29.412Z","2.0.0-goerli.2":"2022-08-09T09:29:31.385Z","2.0.0-dev.60":"2022-08-09T12:24:23.875Z","2.0.0-dev.61":"2022-08-09T13:34:30.513Z","2.0.0-dev.62":"2022-08-10T12:21:02.302Z","2.0.0-dev.63":"2022-08-15T05:01:07.867Z","2.0.0-dev.64":"2022-08-15T05:02:21.362Z","2.0.0-goerli.3":"2022-08-17T17:50:11.609Z","2.0.0-goerli.4":"2022-08-24T10:54:00.568Z","2.0.0-goerli.5":"2022-08-25T13:10:05.560Z","2.0.0-dapp-dev-goerli.0":"2022-08-31T10:01:22.470Z","2.0.0-dapp-dev-goerli.1":"2022-09-01T16:06:40.706Z","2.0.0-dev.65":"2022-09-02T08:56:08.753Z","2.0.0-dev.66":"2022-09-02T09:59:41.830Z","2.0.0-dev.67":"2022-09-06T10:14:15.118Z","2.0.0-dev.68":"2022-09-13T06:15:03.743Z","2.0.0-dev.69":"2022-09-13T15:57:22.447Z","2.0.0-goerli.6":"2022-09-14T08:35:41.973Z","2.0.0-dev.70":"2022-09-14T09:06:59.798Z","2.0.0-goerli.7":"2022-09-14T09:29:11.450Z","2.0.0-dev.71":"2022-09-16T10:54:09.879Z","2.0.0-dapp-dev-goerli.2":"2022-09-19T14:58:00.381Z","2.0.0-dev.72":"2022-09-23T08:44:11.149Z","2.0.0-dev.73":"2022-09-26T13:35:47.211Z","2.0.0-dev.74":"2022-09-26T21:17:53.883Z","2.0.0-dev.75":"2022-09-28T12:20:07.857Z","2.0.0-dev.76":"2022-09-28T12:50:08.874Z","2.0.0-dev.77":"2022-09-28T13:33:33.492Z","2.0.0-dev.78":"2022-09-28T15:30:38.283Z","2.0.0-dev.79":"2022-09-29T10:06:10.919Z","2.0.0":"2022-09-29T14:44:18.227Z","2.1.0-dev.0":"2022-09-29T15:34:19.437Z","2.1.0-goerli.0":"2022-09-29T17:24:43.923Z","2.1.0-goerli.1":"2022-09-30T07:17:42.534Z","2.1.0-dev.1":"2022-12-21T08:52:31.550Z","2.1.0-dev.2":"2022-12-23T11:49:30.395Z","2.1.0-dev.3":"2023-01-04T14:59:42.598Z","2.1.0-dapp-dev-goerli.0":"2023-01-05T12:31:10.931Z","2.1.0-dev.4":"2023-01-06T11:22:49.761Z","2.1.0-dev.5":"2023-01-12T11:06:36.164Z","2.1.0-dev.6":"2023-01-13T16:43:04.879Z","2.1.0-goerli.2":"2023-01-13T17:14:21.354Z","2.1.0-dapp-dev-goerli.1":"2023-01-19T11:18:52.750Z","2.1.0-dapp-dev-goerli.2":"2023-01-20T11:27:01.916Z","2.1.0-goerli.3":"2023-01-20T12:50:31.753Z","2.1.0-goerli.4":"2023-01-24T00:04:50.597Z","2.1.0-dapp-dev-goerli.3":"2023-01-24T12:13:34.941Z","2.1.0-dev.7":"2023-04-17T11:22:18.373Z","2.1.0-dev.8":"2023-04-17T11:24:37.753Z","2.1.0-dev.9":"2023-04-17T11:25:55.744Z","2.1.0-dev.10":"2023-04-18T08:32:47.538Z","2.1.0-dev.11":"2023-06-07T15:47:45.199Z","2.1.0-dev.12":"2023-06-13T16:08:01.032Z","2.1.0-dev.13":"2023-06-14T09:10:08.363Z","2.1.0-dev.14":"2023-06-28T08:06:11.513Z","2.1.0-dev.15":"2023-06-29T04:42:20.335Z","2.1.0-dev.16":"2023-09-25T08:05:07.833Z","2.1.0-sepolia.0":"2023-10-17T11:47:50.254Z","2.1.0-sepolia.1":"2023-10-17T14:03:07.451Z","2.1.0-dapp-dev-sepolia.0":"2023-11-07T15:41:59.941Z","2.1.0-dev.17":"2023-12-14T13:18:00.950Z","2.0.1":"2023-12-20T13:30:48.629Z","2.1.0-dev.18":"2024-01-29T11:44:58.364Z","2.1.0-dev.19":"2024-02-08T15:56:50.334Z"},"license":"MIT","description":"Keep ECDSA Wallet","maintainers":[{"email":"antonio@thesis.co","name":"shadowfiend"},{"email":"kuba@akena.co","name":"nkuba8"},{"email":"heimdall@thesis.co","name":"thesis-heimdall"},{"email":"piotr.dyraga@thesis.co","name":"pdyraga"},{"email":"lukasz.zimnoch@keep.network","name":"lukasz-zimnoch"},{"email":"michalina.cienciala@keep.network","name":"michalinacienciala"},{"email":"dmitry.paremski@gmail.com","name":"dimpar"}],"readme":"","readmeFilename":""}