From 33c187af305b424176234494d994553f7b9a9bce Mon Sep 17 00:00:00 2001 From: Kartik Chopra Date: Fri, 13 Dec 2024 13:25:41 -0500 Subject: [PATCH] feat: adds BLS signature verification for provider registration (#523) * feat: adds initial structure for the contract * feat: updates tests with bls verfication * chore: gets rid of stale todos * chore: remove todo * feat: adds overrideable BLS key addition * feat: adds use of bls sigs in p2p layer * feat: adds bls key registration to e2e test * chore: updates go mod files * chore: updates ordering of contract functions * chore: attempt to sync mod files * chore: fixes solidty linter * chore: updates e2e testing * feat: updates how staking is implemented * chore: ensures decoding of hex from rpc * chore: use real encoding in stake * chore: fix go mod * chore: update go mod * chore: fix stake response encoding * chore: fix e2e test * chore: correctly generate message and signature * chore: fixes lint * chore: correctly format address * chore: update genesis and runner * chore: fix location of pre-compile * chore: bump geth * chore: only set cancun time * chore: correctly configure genesis file * chore: restrict to only bootnode build * chore: add back signer * chore: adds back all jobs * feat: bumps geth and revert genesis change * chore: don't put any keys in the integeration test cluster * feat: adds api call to providers * chore: log ethaddress * chore: add more logs * chore: set eth address * fix: txn opts for BLS registration and errors (#531) * chore: add errors to verification * chore: add errors to verification * chore: add errors to verification --------- Co-authored-by: Alok * chore: bump abigen * fix: ci runner * fix: test * chore: test * fix: remove debug * chore: fix add stake test * chore: fix add stake test --------- Co-authored-by: aloknerurkar Co-authored-by: Alok Co-authored-by: mrekucci --- contracts-abi/abi/ProviderRegistry.abi | 140 +++- .../ProviderRegistry/ProviderRegistry.go | 287 +++++++- contracts/contracts/core/ProviderRegistry.sol | 95 ++- .../interfaces/IProviderRegistry.sol | 16 +- contracts/test/core/OracleTest.sol | 36 +- contracts/test/core/PreconfManagerTest.sol | 33 +- contracts/test/core/ProviderRegistryTest.sol | 103 ++- .../BLSVerifyPreCompileMockTest.sol | 70 ++ external/geth | 2 +- go.work.sum | 1 + p2p/gen/go/providerapi/v1/providerapi.pb.go | 660 +++++++++--------- .../providerapi/v1/providerapi.swagger.yaml | 9 + p2p/go.mod | 1 + p2p/go.sum | 2 + p2p/integrationtest/provider/client.go | 48 +- p2p/pkg/rpc/provider/service.go | 78 ++- p2p/pkg/rpc/provider/service_test.go | 125 +++- p2p/rpc/providerapi/v1/providerapi.proto | 10 +- testing/go.mod | 1 + testing/go.sum | 2 + testing/pkg/tests/staking/staking.go | 37 +- 21 files changed, 1262 insertions(+), 494 deletions(-) create mode 100644 contracts/test/precompiles/BLSVerifyPreCompileMockTest.sol diff --git a/contracts-abi/abi/ProviderRegistry.abi b/contracts-abi/abi/ProviderRegistry.abi index edf97cf4b..3e5fdb8f3 100644 --- a/contracts-abi/abi/ProviderRegistry.abi +++ b/contracts-abi/abi/ProviderRegistry.abi @@ -58,6 +58,24 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "addVerifiedBLSKey", + "inputs": [ + { + "name": "blsPublicKey", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "signature", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "bidderSlashedAmount", @@ -104,11 +122,6 @@ "name": "provider", "type": "address", "internalType": "address" - }, - { - "name": "blsPublicKeys", - "type": "bytes[]", - "internalType": "bytes[]" } ], "outputs": [], @@ -305,6 +318,24 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "overrideAddBLSKey", + "inputs": [ + { + "name": "provider", + "type": "address", + "internalType": "address" + }, + { + "name": "blsPublicKey", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "owner", @@ -446,13 +477,7 @@ { "type": "function", "name": "registerAndStake", - "inputs": [ - { - "name": "blsPublicKeys", - "type": "bytes[]", - "internalType": "bytes[]" - } - ], + "inputs": [], "outputs": [], "stateMutability": "payable" }, @@ -621,6 +646,35 @@ "outputs": [], "stateMutability": "payable" }, + { + "type": "function", + "name": "verifySignature", + "inputs": [ + { + "name": "pubKey", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "message", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "signature", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "withdraw", @@ -667,6 +721,25 @@ ], "stateMutability": "view" }, + { + "type": "event", + "name": "BLSKeyAdded", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "blsPublicKey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, { "type": "event", "name": "BidderWithdrawSlashedAmount", @@ -918,12 +991,6 @@ "type": "uint256", "indexed": false, "internalType": "uint256" - }, - { - "name": "blsPublicKeys", - "type": "bytes[]", - "indexed": false, - "internalType": "bytes[]" } ], "anonymous": false @@ -1040,6 +1107,11 @@ "name": "AtLeastOneBLSKeyRequired", "inputs": [] }, + { + "type": "error", + "name": "BLSSignatureInvalid", + "inputs": [] + }, { "type": "error", "name": "BidderAmountIsZero", @@ -1295,11 +1367,43 @@ } ] }, + { + "type": "error", + "name": "PublicKeyLengthInvalid", + "inputs": [ + { + "name": "exp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "got", + "type": "uint256", + "internalType": "uint256" + } + ] + }, { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, + { + "type": "error", + "name": "SignatureLengthInvalid", + "inputs": [ + { + "name": "exp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "got", + "type": "uint256", + "internalType": "uint256" + } + ] + }, { "type": "error", "name": "StakeTransferFailed", diff --git a/contracts-abi/clients/ProviderRegistry/ProviderRegistry.go b/contracts-abi/clients/ProviderRegistry/ProviderRegistry.go index 5c961014c..b1acfcabe 100644 --- a/contracts-abi/clients/ProviderRegistry/ProviderRegistry.go +++ b/contracts-abi/clients/ProviderRegistry/ProviderRegistry.go @@ -31,7 +31,7 @@ var ( // ProviderregistryMetaData contains all meta data concerning the Providerregistry contract. var ProviderregistryMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"fallback\",\"stateMutability\":\"payable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"ONE_HUNDRED_PERCENT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"PRECISION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UPGRADE_INTERFACE_VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"bidderSlashedAmount\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blockBuilderBLSKeyToAddress\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"delegateRegisterAndStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"blsPublicKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"delegateStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"eoaToBlsPubkeys\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"feePercent\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getAccumulatedPenaltyFee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBLSKeys\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getEoaFromBLSKey\",\"inputs\":[{\"name\":\"blsKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProviderStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_penaltyFeeRecipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_feePercent\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_penaltyFeePayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isProviderValid\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyWithdrawPenaltyFee\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"minStake\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"penaltyFeeTracker\",\"inputs\":[],\"outputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"accumulatedAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"lastPayoutBlock\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"payoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"preconfManager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"providerRegistered\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"providerStakes\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxiableUUID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerAndStake\",\"inputs\":[{\"name\":\"blsPublicKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setFeePayoutPeriodBlocks\",\"inputs\":[{\"name\":\"_feePayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setMinStake\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setNewFeePercent\",\"inputs\":[{\"name\":\"newFeePercent\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setNewPenaltyFeeRecipient\",\"inputs\":[{\"name\":\"newFeeRecipient\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setPreconfManager\",\"inputs\":[{\"name\":\"contractAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setWithdrawalDelay\",\"inputs\":[{\"name\":\"_withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slash\",\"inputs\":[{\"name\":\"amt\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"bidder\",\"type\":\"address\",\"internalType\":\"addresspayable\"},{\"name\":\"residualBidPercentAfterDecay\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"stake\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstake\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"upgradeToAndCall\",\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"withdraw\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawSlashedAmount\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawalDelay\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdrawalRequests\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"BidderWithdrawSlashedAmount\",\"inputs\":[{\"name\":\"bidder\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeePayoutPeriodBlocksUpdated\",\"inputs\":[{\"name\":\"newFeePayoutPeriodBlocks\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeePercentUpdated\",\"inputs\":[{\"name\":\"newFeePercent\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeeTransfer\",\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FundsDeposited\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FundsSlashed\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InsufficientFundsToSlash\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"providerStake\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"residualAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"penaltyFee\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"MinStakeUpdated\",\"inputs\":[{\"name\":\"newMinStake\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PenaltyFeeRecipientUpdated\",\"inputs\":[{\"name\":\"newPenaltyFeeRecipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PreconfManagerUpdated\",\"inputs\":[{\"name\":\"newPreconfManager\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProviderRegistered\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"stakedAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"blsPublicKeys\",\"type\":\"bytes[]\",\"indexed\":false,\"internalType\":\"bytes[]\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TransferToBidderFailed\",\"inputs\":[{\"name\":\"bidder\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unstake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"timestamp\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Withdraw\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"WithdrawalDelayUpdated\",\"inputs\":[{\"name\":\"newWithdrawalDelay\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"AtLeastOneBLSKeyRequired\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"BidderAmountIsZero\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"BidderWithdrawalTransferFailed\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"DelayNotPassed\",\"inputs\":[{\"name\":\"withdrawalRequestTimestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"currentBlockTimestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ERC1967InvalidImplementation\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967NonPayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EnforcedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ExpectedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedInnerCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FeeRecipientIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InsufficientStake\",\"inputs\":[{\"name\":\"stake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidBLSPublicKeyLength\",\"inputs\":[{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expectedLength\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidFallback\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidReceive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoStakeToWithdraw\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NoUnstakeRequest\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotPreconfContract\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"preconfManager\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PendingWithdrawalRequest\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PreconfManagerNotSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProviderAlreadyRegistered\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ProviderCommitmentsPending\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"numPending\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ProviderNotRegistered\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ReentrancyGuardReentrantCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StakeTransferFailed\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"TransferToRecipientFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnauthorizedCallContext\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnsupportedProxiableUUID\",\"inputs\":[{\"name\":\"slot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"UnstakeRequestExists\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]}]", + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"fallback\",\"stateMutability\":\"payable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"ONE_HUNDRED_PERCENT\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"PRECISION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UPGRADE_INTERFACE_VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addVerifiedBLSKey\",\"inputs\":[{\"name\":\"blsPublicKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"bidderSlashedAmount\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"blockBuilderBLSKeyToAddress\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"delegateRegisterAndStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"delegateStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"eoaToBlsPubkeys\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"feePercent\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getAccumulatedPenaltyFee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBLSKeys\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getEoaFromBLSKey\",\"inputs\":[{\"name\":\"blsKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProviderStake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_penaltyFeeRecipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_feePercent\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_penaltyFeePayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isProviderValid\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyWithdrawPenaltyFee\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"minStake\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"overrideAddBLSKey\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"blsPublicKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"penaltyFeeTracker\",\"inputs\":[],\"outputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"accumulatedAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"lastPayoutBlock\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"payoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"preconfManager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"providerRegistered\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"providerStakes\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxiableUUID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerAndStake\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setFeePayoutPeriodBlocks\",\"inputs\":[{\"name\":\"_feePayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setMinStake\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setNewFeePercent\",\"inputs\":[{\"name\":\"newFeePercent\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setNewPenaltyFeeRecipient\",\"inputs\":[{\"name\":\"newFeeRecipient\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setPreconfManager\",\"inputs\":[{\"name\":\"contractAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setWithdrawalDelay\",\"inputs\":[{\"name\":\"_withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slash\",\"inputs\":[{\"name\":\"amt\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"provider\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"bidder\",\"type\":\"address\",\"internalType\":\"addresspayable\"},{\"name\":\"residualBidPercentAfterDecay\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"stake\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstake\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"upgradeToAndCall\",\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"verifySignature\",\"inputs\":[{\"name\":\"pubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"message\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdraw\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawSlashedAmount\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawalDelay\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdrawalRequests\",\"inputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"BLSKeyAdded\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"blsPublicKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BidderWithdrawSlashedAmount\",\"inputs\":[{\"name\":\"bidder\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeePayoutPeriodBlocksUpdated\",\"inputs\":[{\"name\":\"newFeePayoutPeriodBlocks\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeePercentUpdated\",\"inputs\":[{\"name\":\"newFeePercent\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FeeTransfer\",\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FundsDeposited\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FundsSlashed\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InsufficientFundsToSlash\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"providerStake\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"residualAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"penaltyFee\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"MinStakeUpdated\",\"inputs\":[{\"name\":\"newMinStake\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PenaltyFeeRecipientUpdated\",\"inputs\":[{\"name\":\"newPenaltyFeeRecipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PreconfManagerUpdated\",\"inputs\":[{\"name\":\"newPreconfManager\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProviderRegistered\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"stakedAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TransferToBidderFailed\",\"inputs\":[{\"name\":\"bidder\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unstake\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"timestamp\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Withdraw\",\"inputs\":[{\"name\":\"provider\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"WithdrawalDelayUpdated\",\"inputs\":[{\"name\":\"newWithdrawalDelay\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"AtLeastOneBLSKeyRequired\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"BLSSignatureInvalid\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"BidderAmountIsZero\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"BidderWithdrawalTransferFailed\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"DelayNotPassed\",\"inputs\":[{\"name\":\"withdrawalRequestTimestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"withdrawalDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"currentBlockTimestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ERC1967InvalidImplementation\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967NonPayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EnforcedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ExpectedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedInnerCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FeeRecipientIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InsufficientStake\",\"inputs\":[{\"name\":\"stake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidBLSPublicKeyLength\",\"inputs\":[{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expectedLength\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidFallback\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidReceive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoStakeToWithdraw\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NoUnstakeRequest\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotPreconfContract\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"preconfManager\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PendingWithdrawalRequest\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PreconfManagerNotSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProviderAlreadyRegistered\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ProviderCommitmentsPending\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"numPending\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ProviderNotRegistered\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PublicKeyLengthInvalid\",\"inputs\":[{\"name\":\"exp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"got\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ReentrancyGuardReentrantCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SignatureLengthInvalid\",\"inputs\":[{\"name\":\"exp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"got\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"StakeTransferFailed\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"TransferToRecipientFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnauthorizedCallContext\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnsupportedProxiableUUID\",\"inputs\":[{\"name\":\"slot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"UnstakeRequestExists\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]}]", } // ProviderregistryABI is the input ABI used to generate the binding from. @@ -853,6 +853,37 @@ func (_Providerregistry *ProviderregistryCallerSession) ProxiableUUID() ([32]byt return _Providerregistry.Contract.ProxiableUUID(&_Providerregistry.CallOpts) } +// VerifySignature is a free data retrieval call binding the contract method 0x2222e36f. +// +// Solidity: function verifySignature(bytes pubKey, bytes32 message, bytes signature) view returns(bool) +func (_Providerregistry *ProviderregistryCaller) VerifySignature(opts *bind.CallOpts, pubKey []byte, message [32]byte, signature []byte) (bool, error) { + var out []interface{} + err := _Providerregistry.contract.Call(opts, &out, "verifySignature", pubKey, message, signature) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// VerifySignature is a free data retrieval call binding the contract method 0x2222e36f. +// +// Solidity: function verifySignature(bytes pubKey, bytes32 message, bytes signature) view returns(bool) +func (_Providerregistry *ProviderregistrySession) VerifySignature(pubKey []byte, message [32]byte, signature []byte) (bool, error) { + return _Providerregistry.Contract.VerifySignature(&_Providerregistry.CallOpts, pubKey, message, signature) +} + +// VerifySignature is a free data retrieval call binding the contract method 0x2222e36f. +// +// Solidity: function verifySignature(bytes pubKey, bytes32 message, bytes signature) view returns(bool) +func (_Providerregistry *ProviderregistryCallerSession) VerifySignature(pubKey []byte, message [32]byte, signature []byte) (bool, error) { + return _Providerregistry.Contract.VerifySignature(&_Providerregistry.CallOpts, pubKey, message, signature) +} + // WithdrawalDelay is a free data retrieval call binding the contract method 0xa7ab6961. // // Solidity: function withdrawalDelay() view returns(uint256) @@ -936,25 +967,46 @@ func (_Providerregistry *ProviderregistryTransactorSession) AcceptOwnership() (* return _Providerregistry.Contract.AcceptOwnership(&_Providerregistry.TransactOpts) } -// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0xd9b735e7. +// AddVerifiedBLSKey is a paid mutator transaction binding the contract method 0x7fd358e4. // -// Solidity: function delegateRegisterAndStake(address provider, bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistryTransactor) DelegateRegisterAndStake(opts *bind.TransactOpts, provider common.Address, blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.contract.Transact(opts, "delegateRegisterAndStake", provider, blsPublicKeys) +// Solidity: function addVerifiedBLSKey(bytes blsPublicKey, bytes signature) returns() +func (_Providerregistry *ProviderregistryTransactor) AddVerifiedBLSKey(opts *bind.TransactOpts, blsPublicKey []byte, signature []byte) (*types.Transaction, error) { + return _Providerregistry.contract.Transact(opts, "addVerifiedBLSKey", blsPublicKey, signature) } -// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0xd9b735e7. +// AddVerifiedBLSKey is a paid mutator transaction binding the contract method 0x7fd358e4. // -// Solidity: function delegateRegisterAndStake(address provider, bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistrySession) DelegateRegisterAndStake(provider common.Address, blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.Contract.DelegateRegisterAndStake(&_Providerregistry.TransactOpts, provider, blsPublicKeys) +// Solidity: function addVerifiedBLSKey(bytes blsPublicKey, bytes signature) returns() +func (_Providerregistry *ProviderregistrySession) AddVerifiedBLSKey(blsPublicKey []byte, signature []byte) (*types.Transaction, error) { + return _Providerregistry.Contract.AddVerifiedBLSKey(&_Providerregistry.TransactOpts, blsPublicKey, signature) } -// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0xd9b735e7. +// AddVerifiedBLSKey is a paid mutator transaction binding the contract method 0x7fd358e4. // -// Solidity: function delegateRegisterAndStake(address provider, bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistryTransactorSession) DelegateRegisterAndStake(provider common.Address, blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.Contract.DelegateRegisterAndStake(&_Providerregistry.TransactOpts, provider, blsPublicKeys) +// Solidity: function addVerifiedBLSKey(bytes blsPublicKey, bytes signature) returns() +func (_Providerregistry *ProviderregistryTransactorSession) AddVerifiedBLSKey(blsPublicKey []byte, signature []byte) (*types.Transaction, error) { + return _Providerregistry.Contract.AddVerifiedBLSKey(&_Providerregistry.TransactOpts, blsPublicKey, signature) +} + +// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0x3d75c0ba. +// +// Solidity: function delegateRegisterAndStake(address provider) payable returns() +func (_Providerregistry *ProviderregistryTransactor) DelegateRegisterAndStake(opts *bind.TransactOpts, provider common.Address) (*types.Transaction, error) { + return _Providerregistry.contract.Transact(opts, "delegateRegisterAndStake", provider) +} + +// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0x3d75c0ba. +// +// Solidity: function delegateRegisterAndStake(address provider) payable returns() +func (_Providerregistry *ProviderregistrySession) DelegateRegisterAndStake(provider common.Address) (*types.Transaction, error) { + return _Providerregistry.Contract.DelegateRegisterAndStake(&_Providerregistry.TransactOpts, provider) +} + +// DelegateRegisterAndStake is a paid mutator transaction binding the contract method 0x3d75c0ba. +// +// Solidity: function delegateRegisterAndStake(address provider) payable returns() +func (_Providerregistry *ProviderregistryTransactorSession) DelegateRegisterAndStake(provider common.Address) (*types.Transaction, error) { + return _Providerregistry.Contract.DelegateRegisterAndStake(&_Providerregistry.TransactOpts, provider) } // DelegateStake is a paid mutator transaction binding the contract method 0xf094cc39. @@ -1020,6 +1072,27 @@ func (_Providerregistry *ProviderregistryTransactorSession) ManuallyWithdrawPena return _Providerregistry.Contract.ManuallyWithdrawPenaltyFee(&_Providerregistry.TransactOpts) } +// OverrideAddBLSKey is a paid mutator transaction binding the contract method 0xed5219de. +// +// Solidity: function overrideAddBLSKey(address provider, bytes blsPublicKey) returns() +func (_Providerregistry *ProviderregistryTransactor) OverrideAddBLSKey(opts *bind.TransactOpts, provider common.Address, blsPublicKey []byte) (*types.Transaction, error) { + return _Providerregistry.contract.Transact(opts, "overrideAddBLSKey", provider, blsPublicKey) +} + +// OverrideAddBLSKey is a paid mutator transaction binding the contract method 0xed5219de. +// +// Solidity: function overrideAddBLSKey(address provider, bytes blsPublicKey) returns() +func (_Providerregistry *ProviderregistrySession) OverrideAddBLSKey(provider common.Address, blsPublicKey []byte) (*types.Transaction, error) { + return _Providerregistry.Contract.OverrideAddBLSKey(&_Providerregistry.TransactOpts, provider, blsPublicKey) +} + +// OverrideAddBLSKey is a paid mutator transaction binding the contract method 0xed5219de. +// +// Solidity: function overrideAddBLSKey(address provider, bytes blsPublicKey) returns() +func (_Providerregistry *ProviderregistryTransactorSession) OverrideAddBLSKey(provider common.Address, blsPublicKey []byte) (*types.Transaction, error) { + return _Providerregistry.Contract.OverrideAddBLSKey(&_Providerregistry.TransactOpts, provider, blsPublicKey) +} + // Pause is a paid mutator transaction binding the contract method 0x8456cb59. // // Solidity: function pause() returns() @@ -1041,25 +1114,25 @@ func (_Providerregistry *ProviderregistryTransactorSession) Pause() (*types.Tran return _Providerregistry.Contract.Pause(&_Providerregistry.TransactOpts) } -// RegisterAndStake is a paid mutator transaction binding the contract method 0x140f2e60. +// RegisterAndStake is a paid mutator transaction binding the contract method 0x84d180ee. // -// Solidity: function registerAndStake(bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistryTransactor) RegisterAndStake(opts *bind.TransactOpts, blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.contract.Transact(opts, "registerAndStake", blsPublicKeys) +// Solidity: function registerAndStake() payable returns() +func (_Providerregistry *ProviderregistryTransactor) RegisterAndStake(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Providerregistry.contract.Transact(opts, "registerAndStake") } -// RegisterAndStake is a paid mutator transaction binding the contract method 0x140f2e60. +// RegisterAndStake is a paid mutator transaction binding the contract method 0x84d180ee. // -// Solidity: function registerAndStake(bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistrySession) RegisterAndStake(blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.Contract.RegisterAndStake(&_Providerregistry.TransactOpts, blsPublicKeys) +// Solidity: function registerAndStake() payable returns() +func (_Providerregistry *ProviderregistrySession) RegisterAndStake() (*types.Transaction, error) { + return _Providerregistry.Contract.RegisterAndStake(&_Providerregistry.TransactOpts) } -// RegisterAndStake is a paid mutator transaction binding the contract method 0x140f2e60. +// RegisterAndStake is a paid mutator transaction binding the contract method 0x84d180ee. // -// Solidity: function registerAndStake(bytes[] blsPublicKeys) payable returns() -func (_Providerregistry *ProviderregistryTransactorSession) RegisterAndStake(blsPublicKeys [][]byte) (*types.Transaction, error) { - return _Providerregistry.Contract.RegisterAndStake(&_Providerregistry.TransactOpts, blsPublicKeys) +// Solidity: function registerAndStake() payable returns() +func (_Providerregistry *ProviderregistryTransactorSession) RegisterAndStake() (*types.Transaction, error) { + return _Providerregistry.Contract.RegisterAndStake(&_Providerregistry.TransactOpts) } // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. @@ -1419,6 +1492,151 @@ func (_Providerregistry *ProviderregistryTransactorSession) Receive() (*types.Tr return _Providerregistry.Contract.Receive(&_Providerregistry.TransactOpts) } +// ProviderregistryBLSKeyAddedIterator is returned from FilterBLSKeyAdded and is used to iterate over the raw logs and unpacked data for BLSKeyAdded events raised by the Providerregistry contract. +type ProviderregistryBLSKeyAddedIterator struct { + Event *ProviderregistryBLSKeyAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ProviderregistryBLSKeyAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ProviderregistryBLSKeyAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ProviderregistryBLSKeyAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ProviderregistryBLSKeyAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ProviderregistryBLSKeyAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ProviderregistryBLSKeyAdded represents a BLSKeyAdded event raised by the Providerregistry contract. +type ProviderregistryBLSKeyAdded struct { + Provider common.Address + BlsPublicKey []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBLSKeyAdded is a free log retrieval operation binding the contract event 0xc81315c03024fb67ddfe902ce0153b3d56572c0569de9564fcb90cc174a960bf. +// +// Solidity: event BLSKeyAdded(address indexed provider, bytes blsPublicKey) +func (_Providerregistry *ProviderregistryFilterer) FilterBLSKeyAdded(opts *bind.FilterOpts, provider []common.Address) (*ProviderregistryBLSKeyAddedIterator, error) { + + var providerRule []interface{} + for _, providerItem := range provider { + providerRule = append(providerRule, providerItem) + } + + logs, sub, err := _Providerregistry.contract.FilterLogs(opts, "BLSKeyAdded", providerRule) + if err != nil { + return nil, err + } + return &ProviderregistryBLSKeyAddedIterator{contract: _Providerregistry.contract, event: "BLSKeyAdded", logs: logs, sub: sub}, nil +} + +// WatchBLSKeyAdded is a free log subscription operation binding the contract event 0xc81315c03024fb67ddfe902ce0153b3d56572c0569de9564fcb90cc174a960bf. +// +// Solidity: event BLSKeyAdded(address indexed provider, bytes blsPublicKey) +func (_Providerregistry *ProviderregistryFilterer) WatchBLSKeyAdded(opts *bind.WatchOpts, sink chan<- *ProviderregistryBLSKeyAdded, provider []common.Address) (event.Subscription, error) { + + var providerRule []interface{} + for _, providerItem := range provider { + providerRule = append(providerRule, providerItem) + } + + logs, sub, err := _Providerregistry.contract.WatchLogs(opts, "BLSKeyAdded", providerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ProviderregistryBLSKeyAdded) + if err := _Providerregistry.contract.UnpackLog(event, "BLSKeyAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBLSKeyAdded is a log parse operation binding the contract event 0xc81315c03024fb67ddfe902ce0153b3d56572c0569de9564fcb90cc174a960bf. +// +// Solidity: event BLSKeyAdded(address indexed provider, bytes blsPublicKey) +func (_Providerregistry *ProviderregistryFilterer) ParseBLSKeyAdded(log types.Log) (*ProviderregistryBLSKeyAdded, error) { + event := new(ProviderregistryBLSKeyAdded) + if err := _Providerregistry.contract.UnpackLog(event, "BLSKeyAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // ProviderregistryBidderWithdrawSlashedAmountIterator is returned from FilterBidderWithdrawSlashedAmount and is used to iterate over the raw logs and unpacked data for BidderWithdrawSlashedAmount events raised by the Providerregistry contract. type ProviderregistryBidderWithdrawSlashedAmountIterator struct { Event *ProviderregistryBidderWithdrawSlashedAmount // Event containing the contract specifics and raw log @@ -3499,15 +3717,14 @@ func (it *ProviderregistryProviderRegisteredIterator) Close() error { // ProviderregistryProviderRegistered represents a ProviderRegistered event raised by the Providerregistry contract. type ProviderregistryProviderRegistered struct { - Provider common.Address - StakedAmount *big.Int - BlsPublicKeys [][]byte - Raw types.Log // Blockchain specific contextual infos + Provider common.Address + StakedAmount *big.Int + Raw types.Log // Blockchain specific contextual infos } -// FilterProviderRegistered is a free log retrieval operation binding the contract event 0x43b9a402bb7e7ede3b3d372b9a0be4a09659bc821f4662eb999faa8756553042. +// FilterProviderRegistered is a free log retrieval operation binding the contract event 0x90c9734131c1e4fb36cde2d71e6feb93fb258f71be8a85411c173d25e1516e80. // -// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes[] blsPublicKeys) +// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount) func (_Providerregistry *ProviderregistryFilterer) FilterProviderRegistered(opts *bind.FilterOpts, provider []common.Address) (*ProviderregistryProviderRegisteredIterator, error) { var providerRule []interface{} @@ -3522,9 +3739,9 @@ func (_Providerregistry *ProviderregistryFilterer) FilterProviderRegistered(opts return &ProviderregistryProviderRegisteredIterator{contract: _Providerregistry.contract, event: "ProviderRegistered", logs: logs, sub: sub}, nil } -// WatchProviderRegistered is a free log subscription operation binding the contract event 0x43b9a402bb7e7ede3b3d372b9a0be4a09659bc821f4662eb999faa8756553042. +// WatchProviderRegistered is a free log subscription operation binding the contract event 0x90c9734131c1e4fb36cde2d71e6feb93fb258f71be8a85411c173d25e1516e80. // -// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes[] blsPublicKeys) +// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount) func (_Providerregistry *ProviderregistryFilterer) WatchProviderRegistered(opts *bind.WatchOpts, sink chan<- *ProviderregistryProviderRegistered, provider []common.Address) (event.Subscription, error) { var providerRule []interface{} @@ -3564,9 +3781,9 @@ func (_Providerregistry *ProviderregistryFilterer) WatchProviderRegistered(opts }), nil } -// ParseProviderRegistered is a log parse operation binding the contract event 0x43b9a402bb7e7ede3b3d372b9a0be4a09659bc821f4662eb999faa8756553042. +// ParseProviderRegistered is a log parse operation binding the contract event 0x90c9734131c1e4fb36cde2d71e6feb93fb258f71be8a85411c173d25e1516e80. // -// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes[] blsPublicKeys) +// Solidity: event ProviderRegistered(address indexed provider, uint256 stakedAmount) func (_Providerregistry *ProviderregistryFilterer) ParseProviderRegistered(log types.Log) (*ProviderregistryProviderRegistered, error) { event := new(ProviderregistryProviderRegistered) if err := _Providerregistry.contract.UnpackLog(event, "ProviderRegistered", log); err != nil { diff --git a/contracts/contracts/core/ProviderRegistry.sol b/contracts/contracts/core/ProviderRegistry.sol index 3d5c63770..01c062e67 100644 --- a/contracts/contracts/core/ProviderRegistry.sol +++ b/contracts/contracts/core/ProviderRegistry.sol @@ -183,6 +183,12 @@ contract ProviderRegistry is emit FeePayoutPeriodBlocksUpdated(_feePayoutPeriodBlocks); } + function overrideAddBLSKey(address provider, bytes calldata blsPublicKey) external onlyOwner { + require(providerRegistered[provider], ProviderNotRegistered(provider)); + eoaToBlsPubkeys[provider].push(blsPublicKey); + blockBuilderBLSKeyToAddress[blsPublicKey] = provider; + } + /// @dev Requests unstake of the staked amount. function unstake() external whenNotPaused { require(providerStakes[msg.sender] != 0, NoStakeToWithdraw(msg.sender)); @@ -228,6 +234,35 @@ contract ProviderRegistry is emit BidderWithdrawSlashedAmount(msg.sender, amount); } + /** + * @dev Adds a verified BLS key to the provider's account. + * @param blsPublicKey The BLS public key to be added. + * @param signature The signature (96 bytes) used for verification. + */ + function addVerifiedBLSKey( + bytes calldata blsPublicKey, + bytes calldata signature + ) external { + address provider = msg.sender; + + require(providerRegistered[provider], ProviderNotRegistered(provider)); + require(blsPublicKey.length == 48, PublicKeyLengthInvalid(48, blsPublicKey.length)); + require(signature.length == 96, SignatureLengthInvalid(96, signature.length)); + + + bytes32 message = keccak256(abi.encodePacked(provider)); + + // Verify the BLS signature + bool isValid = verifySignature(blsPublicKey, message, signature); + require(isValid, BLSSignatureInvalid()); + + // Add the BLS public key to the provider's account + eoaToBlsPubkeys[provider].push(blsPublicKey); + blockBuilderBLSKeyToAddress[blsPublicKey] = provider; + + emit BLSKeyAdded(provider, blsPublicKey); + } + /** * @dev Manually withdraws accumulated penalty fees to the recipient * to cover the edge case that oracle doesn't slash/reward, and funds still need to be withdrawn. @@ -272,20 +307,18 @@ contract ProviderRegistry is /** * @dev Register and stake function for providers. - * @param blsPublicKeys The BLS public keys of the provider. * The validity of this key must be verified manually off-chain. */ - function registerAndStake(bytes[] calldata blsPublicKeys) public payable whenNotPaused { - _registerAndStake(msg.sender, blsPublicKeys); + function registerAndStake() public payable whenNotPaused { + _registerAndStake(msg.sender); } /** * @dev Register and stake on behalf of a provider. * @param provider Address of the provider. - * @param blsPublicKeys BLS public keys of the provider. */ - function delegateRegisterAndStake(address provider, bytes[] calldata blsPublicKeys) public payable whenNotPaused onlyOwner { - _registerAndStake(provider, blsPublicKeys); + function delegateRegisterAndStake(address provider) public payable whenNotPaused onlyOwner { + _registerAndStake(provider); } /// @dev Ensure the provider's balance is greater than minStake and no pending withdrawal @@ -295,6 +328,43 @@ contract ProviderRegistry is require(withdrawalRequests[provider] == 0, PendingWithdrawalRequest(provider)); } + + /** + * @dev Verifies a BLS signature using the precompile + * @param pubKey The public key (48 bytes G1 point) + * @param message The message hash (32 bytes) + * @param signature The signature (96 bytes G2 point) + * @return success True if verification succeeded + */ + function verifySignature( + bytes calldata pubKey, + bytes32 message, + bytes calldata signature + ) public view returns (bool) { + // Input validation + require(pubKey.length == 48, "Public key must be 48 bytes"); + require(signature.length == 96, "Signature must be 96 bytes"); + + // Concatenate inputs in required format: + // [pubkey (48 bytes) | message (32 bytes) | signature (96 bytes)] + bytes memory input = bytes.concat( + pubKey, + message, + signature + ); + + // Call precompile + (bool success, bytes memory result) = address(0xf0).staticcall(input); + + // Check if call was successful + if (!success) { + return false; + } + + // If we got a result back and it's not empty, verification succeeded + return result.length > 0; + } + function _stake(address provider) internal { require(providerRegistered[provider], ProviderNotRegistered(provider)); require(withdrawalRequests[provider] == 0, PendingWithdrawalRequest(provider)); @@ -302,20 +372,13 @@ contract ProviderRegistry is emit FundsDeposited(provider, msg.value); } - function _registerAndStake(address provider, bytes[] calldata blsPublicKeys) internal { + function _registerAndStake(address provider) internal { require(!providerRegistered[provider], ProviderAlreadyRegistered(provider)); require(msg.value >= minStake, InsufficientStake(msg.value, minStake)); - require(blsPublicKeys.length != 0, AtLeastOneBLSKeyRequired()); - uint256 numKeys = blsPublicKeys.length; - for (uint256 i = 0; i < numKeys; ++i) { - bytes memory key = blsPublicKeys[i]; - require(key.length == 48, InvalidBLSPublicKeyLength(key.length, 48)); - blockBuilderBLSKeyToAddress[key] = provider; - } - eoaToBlsPubkeys[provider] = blsPublicKeys; + providerStakes[provider] = msg.value; providerRegistered[provider] = true; - emit ProviderRegistered(provider, msg.value, blsPublicKeys); + emit ProviderRegistered(provider, msg.value); } // solhint-disable-next-line no-empty-blocks diff --git a/contracts/contracts/interfaces/IProviderRegistry.sol b/contracts/contracts/interfaces/IProviderRegistry.sol index 089060927..e2804081a 100644 --- a/contracts/contracts/interfaces/IProviderRegistry.sol +++ b/contracts/contracts/interfaces/IProviderRegistry.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.26; interface IProviderRegistry { /// @dev Event emitted when a provider is registered - event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes[] blsPublicKeys); + event ProviderRegistered(address indexed provider, uint256 stakedAmount); /// @dev Event emitted when funds are deposited event FundsDeposited(address indexed provider, uint256 amount); @@ -36,6 +36,9 @@ interface IProviderRegistry { /// @dev Event emitted when the fee percent is updated event FeePercentUpdated(uint256 indexed newFeePercent); + /// @dev Event emitted when a BLS key is added + event BLSKeyAdded(address indexed provider, bytes blsPublicKey); + /// @dev Event emitted when there are insufficient funds to slash event InsufficientFundsToSlash( address indexed provider, @@ -67,8 +70,11 @@ interface IProviderRegistry { error PendingWithdrawalRequest(address sender); error BidderAmountIsZero(address sender); error BidderWithdrawalTransferFailed(address sender, uint256 amount); - - function registerAndStake(bytes[] calldata blsPublicKeys) external payable; + error PublicKeyLengthInvalid(uint256 exp, uint256 got); + error SignatureLengthInvalid(uint256 exp, uint256 got); + error BLSSignatureInvalid(); + + function registerAndStake() external payable; function stake() external payable; @@ -78,7 +84,11 @@ interface IProviderRegistry { address payable bidder, uint256 residualBidPercentAfterDecay ) external; + + function addVerifiedBLSKey(bytes calldata blsPublicKey, bytes calldata signature) external; + function overrideAddBLSKey(address provider, bytes calldata blsPublicKey) external; + function isProviderValid(address committerAddress) external view; function getEoaFromBLSKey(bytes calldata blsKey) external view returns (address); diff --git a/contracts/test/core/OracleTest.sol b/contracts/test/core/OracleTest.sol index db6823b4a..6dbc39625 100644 --- a/contracts/test/core/OracleTest.sol +++ b/contracts/test/core/OracleTest.sol @@ -10,6 +10,7 @@ import {BlockTracker} from "../../contracts/core/BlockTracker.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {WindowFromBlockNumber} from "../../contracts/utils/WindowFromBlockNumber.sol"; import {ECDSA} from "@openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; +import {MockBLSVerify} from "../precompiles/BLSVerifyPreCompileMockTest.sol"; contract OracleTest is Test { using ECDSA for bytes32; @@ -29,6 +30,7 @@ contract OracleTest is Test { bytes public sharedSecretKey; bytes public constant validBLSPubkey = hex"80000cddeec66a800e00b0ccbb62f12298073603f5209e812abbac7e870482e488dd1bbe533a9d44497ba8b756e1e82b"; bytes[] public validBLSPubkeys = [validBLSPubkey]; + bytes public dummyBLSSignature = hex"bbbbbbbbb1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2"; uint256 public constant withdrawalDelay = 24 hours ; // 24 hours uint256 public constant protocolFeePayoutPeriodBlocks = 100; struct TestCommitment { @@ -59,6 +61,10 @@ contract OracleTest is Test { ); function setUp() public { + address BLS_VERIFY_ADDRESS = address(0xf0); + bytes memory code = type(MockBLSVerify).creationCode; + vm.etch(BLS_VERIFY_ADDRESS, code); + testNumber = 2; testNumber2 = 2; sharedSecretKey = bytes("0xsecret"); @@ -178,7 +184,11 @@ contract OracleTest is Test { vm.deal(provider, 200000 ether); vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 250 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bytes32 index = constructAndStoreCommitment( @@ -226,7 +236,11 @@ contract OracleTest is Test { vm.deal(provider, 200000 ether); vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 250 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bytes32 index = constructAndStoreCommitment( @@ -280,7 +294,11 @@ contract OracleTest is Test { vm.deal(provider, 200000 ether); vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 250 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bytes32 index1 = constructAndStoreCommitment( @@ -357,7 +375,11 @@ contract OracleTest is Test { vm.deal(provider, 200000 ether); vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 250 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bytes32 index1 = constructAndStoreCommitment( @@ -472,7 +494,11 @@ contract OracleTest is Test { vm.deal(provider, 200000 ether); vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 250 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bytes32[] memory commitments = new bytes32[](4); diff --git a/contracts/test/core/PreconfManagerTest.sol b/contracts/test/core/PreconfManagerTest.sol index 4c8f558d3..6a48a9ada 100644 --- a/contracts/test/core/PreconfManagerTest.sol +++ b/contracts/test/core/PreconfManagerTest.sol @@ -10,7 +10,7 @@ import {BlockTracker} from "../../contracts/core/BlockTracker.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {WindowFromBlockNumber} from "../../contracts/utils/WindowFromBlockNumber.sol"; import {IProviderRegistry} from "../../contracts/interfaces/IProviderRegistry.sol"; - +import {MockBLSVerify} from "../precompiles/BLSVerifyPreCompileMockTest.sol"; contract PreconfManagerTest is Test { struct TestCommitment { uint256 bidAmt; @@ -42,10 +42,15 @@ contract PreconfManagerTest is Test { bytes public validBLSPubkey2 = hex"90000cddeec66a800e00b0ccbb62f12298073603f5209e812abbac7e870482e488dd1bbe533a9d44497ba8b756e1e82c"; bytes public validBLSPubkey3 = hex"a0000cddeec66a800e00b0ccbb62f12298073603f5209e812abbac7e870482e488dd1bbe533a9d44497ba8b756e1e82d"; bytes[] public validMultiBLSPubkeys = [validBLSPubkey, validBLSPubkey2, validBLSPubkey3]; + bytes public dummyBLSSignature = hex"bbbbbbbbb1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2"; uint256 public withdrawalDelay; uint256 public protocolFeePayoutPeriodBlocks; address public oracleContract; function setUp() public { + address BLS_VERIFY_ADDRESS = address(0xf0); + bytes memory code = type(MockBLSVerify).creationCode; + vm.etch(BLS_VERIFY_ADDRESS, code); + _testCommitmentAliceBob = TestCommitment( 2, 2, @@ -207,8 +212,14 @@ vm.prank(address(this)); // Optional: Ensure the committer has enough ETH if needed for the operation vm.deal(committer, 1 ether); - vm.prank(committer); - providerRegistry.registerAndStake{value: 1 ether}(validBLSPubkeys); + vm.startPrank(committer); + providerRegistry.registerAndStake{value: 1 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); + + // Step 2: Store the commitment vm.prank(committer); @@ -506,7 +517,10 @@ vm.prank(address(this)); ); vm.deal(committer, 11 ether); vm.startPrank(committer); - providerRegistry.registerAndStake{value: 10 ether}(validMultiBLSPubkeys); + providerRegistry.registerAndStake{value: 10 ether}(); + for (uint256 i = 0; i < validMultiBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validMultiBLSPubkeys[i], dummyBLSSignature); + } bytes32 commitmentIndex = preconfManager.storeUnopenedCommitment( commitmentDigest, @@ -626,7 +640,6 @@ vm.prank(address(this)); ); // Step 2: Store the commitment (address committer, ) = makeAddrAndKey("bob"); - providerRegistry.registerAndStake{value: 10 ether}(validBLSPubkeys); bytes32 commitmentIndex = storeCommitment( committer, _testCommitmentAliceBob.bidAmt, @@ -961,8 +974,14 @@ vm.prank(address(this)); // Ensure the committer has enough ETH for the required stake vm.deal(committer, 2 ether); - vm.prank(committer); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + vm.startPrank(committer); + providerRegistry.registerAndStake{value: 2 ether}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); + + // Request a withdrawal to create a pending withdrawal request vm.prank(committer); diff --git a/contracts/test/core/ProviderRegistryTest.sol b/contracts/test/core/ProviderRegistryTest.sol index 0c125b237..19ce5a07c 100644 --- a/contracts/test/core/ProviderRegistryTest.sol +++ b/contracts/test/core/ProviderRegistryTest.sol @@ -8,7 +8,7 @@ import {PreconfManager} from "../../contracts/core/PreconfManager.sol"; import {BlockTracker} from "../../contracts/core/BlockTracker.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {IProviderRegistry} from "../../contracts/interfaces/IProviderRegistry.sol"; - +import {MockBLSVerify} from "../precompiles/BLSVerifyPreCompileMockTest.sol"; contract ProviderRegistryTest is Test { uint256 public testNumber; ProviderRegistry public providerRegistry; @@ -21,9 +21,10 @@ contract ProviderRegistryTest is Test { BlockTracker public blockTracker; uint256 public withdrawalDelay; bytes public validBLSPubkey = hex"80000cddeec66a800e00b0ccbb62f12298073603f5209e812abbac7e870482e488dd1bbe533a9d44497ba8b756e1e82b"; + bytes public dummyBLSSignature = hex"bbbbbbbbb1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2"; bytes[] public validBLSPubkeys = [validBLSPubkey]; uint256 public penaltyFeePayoutPeriodBlocks; - event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes []blsPublicKeys); + event ProviderRegistered(address indexed provider, uint256 stakedAmount); event WithdrawalRequested(address indexed provider, uint256 timestamp); event WithdrawalCompleted(address indexed provider, uint256 amount); event FeeTransfer(uint256 amount, address indexed recipient); @@ -37,6 +38,10 @@ contract ProviderRegistryTest is Test { ); function setUp() public { + address BLS_VERIFY_ADDRESS = address(0xf0); + bytes memory code = type(MockBLSVerify).creationCode; + vm.etch(BLS_VERIFY_ADDRESS, code); + testNumber = 42; feePercent = 10 * 1e16; minStake = 1e18 wei; @@ -109,7 +114,9 @@ contract ProviderRegistryTest is Test { vm.deal(provider, 3 ether); vm.prank(provider); vm.expectRevert(bytes("")); - providerRegistry.registerAndStake{value: 1 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 1 wei}(); + + } function testFail_ProviderStakeAndRegisterInvalidBLSKey() public { @@ -118,18 +125,23 @@ contract ProviderRegistryTest is Test { vm.expectRevert("Invalid BLS public key length"); bytes[] memory invalidBLSPubkeys = new bytes[](1); invalidBLSPubkeys[0] = abi.encodePacked(uint256(134)); - providerRegistry.registerAndStake{value: 1 wei}(invalidBLSPubkeys); + providerRegistry.registerAndStake{value: 1 wei}(); + + } function test_ProviderStakeAndRegister() public { vm.deal(provider, 3 ether); - vm.prank(provider); + vm.startPrank(provider); vm.expectEmit(true, false, false, true); - emit ProviderRegistered(provider, 1e18 wei, validBLSPubkeys); - - providerRegistry.registerAndStake{value: 1e18 wei}(validBLSPubkeys); + emit ProviderRegistered(provider, 1e18 wei); + providerRegistry.registerAndStake{value: 1e18 wei}(); + for (uint256 i = 0; i < validBLSPubkeys.length; i++) { + providerRegistry.addVerifiedBLSKey(validBLSPubkeys[i], dummyBLSSignature); + } + vm.stopPrank(); bool isProviderRegistered = providerRegistry.providerRegistered( provider ); @@ -152,9 +164,13 @@ contract ProviderRegistryTest is Test { function testFail_ProviderStakeAndRegisterAlreadyRegistered() public { vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + vm.expectRevert(bytes("")); - providerRegistry.registerAndStake{value: 1 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 1 wei}(); + + } function testFail_Receive() public { @@ -236,7 +252,9 @@ contract ProviderRegistryTest is Test { providerRegistry.setPreconfManager(address(this)); vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + address bidder = vm.addr(4); vm.expectCall(bidder, 1000000000000000000 wei, new bytes(0)); @@ -254,7 +272,9 @@ contract ProviderRegistryTest is Test { vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + address bidder = vm.addr(4); vm.expectCall(bidder, 1000000000000000000 wei, new bytes(0)); @@ -267,7 +287,9 @@ contract ProviderRegistryTest is Test { function testFail_ShouldRetrieveFundsNotPreConf() public { vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + address bidder = vm.addr(4); vm.expectRevert(bytes("")); providerRegistry.slash(1 ether, provider, payable(bidder), providerRegistry.ONE_HUNDRED_PERCENT()); @@ -278,7 +300,9 @@ contract ProviderRegistryTest is Test { vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + address bidder = vm.addr(4); vm.prank(address(this)); @@ -296,7 +320,9 @@ contract ProviderRegistryTest is Test { vm.deal(provider, 3 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 3 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 3 ether}(); + + address bidder = vm.addr(4); vm.prank(address(this)); @@ -314,7 +340,9 @@ contract ProviderRegistryTest is Test { vm.deal(provider, 3.1 ether); vm.prank(provider); - providerRegistry.registerAndStake{value: 3.1 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 3.1 ether}(); + + address bidder = vm.addr(4); vm.prank(address(this)); @@ -332,7 +360,9 @@ contract ProviderRegistryTest is Test { address bidder = vm.addr(4); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + providerRegistry.setPreconfManager(address(this)); providerRegistry.slash(1e18 wei, provider, payable(bidder), 50 * providerRegistry.PRECISION()); assertEq( @@ -344,7 +374,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(11); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2 ether}(); + + vm.roll(350); // roll past protocol fee payout period @@ -371,7 +403,9 @@ contract ProviderRegistryTest is Test { uint256 percent = providerRegistry.ONE_HUNDRED_PERCENT(); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + providerRegistry.setPreconfManager( address(preconfManager) ); @@ -398,7 +432,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(8); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + vm.expectRevert(bytes("")); address wrongNewProvider = vm.addr(12); vm.prank(wrongNewProvider); @@ -409,7 +445,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(5); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + assertEq( providerRegistry.providerStakes(newProvider), 2e18 wei, @@ -426,7 +464,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(8); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + providerRegistry.unstake(); vm.warp(block.timestamp + 24 hours); // Move forward in time vm.expectRevert("Provider Commitments still pending"); @@ -437,7 +477,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(8); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + vm.prank(newProvider); providerRegistry.unstake(); assertEq( @@ -453,7 +495,9 @@ contract ProviderRegistryTest is Test { uint256 percent = providerRegistry.ONE_HUNDRED_PERCENT(); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + providerRegistry.setPreconfManager( address(preconfManager) ); @@ -480,7 +524,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(8); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + vm.prank(newProvider); providerRegistry.unstake(); vm.warp(block.timestamp + 23 hours); // Move forward less than 24 hours @@ -500,7 +546,9 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(8); vm.deal(newProvider, 3 ether); vm.prank(newProvider); - providerRegistry.registerAndStake{value: 2e18 wei}(validBLSPubkeys); + providerRegistry.registerAndStake{value: 2e18 wei}(); + + vm.prank(newProvider); vm.expectRevert( abi.encodeWithSelector(IProviderRegistry.NoUnstakeRequest.selector, newProvider) @@ -512,7 +560,8 @@ contract ProviderRegistryTest is Test { address newProvider = vm.addr(7); vm.prank(address(this)); - providerRegistry.delegateRegisterAndStake{value: 2e18 wei}(newProvider, validBLSPubkeys); + providerRegistry.delegateRegisterAndStake{value: 2e18 wei}(newProvider); + assertEq( providerRegistry.providerStakes(newProvider), 2e18 wei, diff --git a/contracts/test/precompiles/BLSVerifyPreCompileMockTest.sol b/contracts/test/precompiles/BLSVerifyPreCompileMockTest.sol new file mode 100644 index 000000000..8cb035c10 --- /dev/null +++ b/contracts/test/precompiles/BLSVerifyPreCompileMockTest.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSL 1.1 +pragma solidity 0.8.26; + +import {Test} from "forge-std/Test.sol"; + +contract BLSVerifyPreCompileMock is Test { + address constant BLS_VERIFY = address(0xf0); + + function setUp() public { + // Deploy the mock contract to the precompile address + bytes memory code = type(MockBLSVerify).creationCode; + vm.etch(BLS_VERIFY, code); + } + + function testVerifySignature() public view { + // Store the values as constants or immutable variables since they're fixed test data + bytes memory pubKeyData = hex"b67a5148a03229926e34b190af81a82a81c4df66831c98c03a139778418dd09a3b542ced0022620d19f35781ece6dc36"; + bytes32 messageData = keccak256(abi.encodePacked("test message")); + bytes memory signatureData = hex"bbbbbbbbb1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2"; + + // Call verifySignature with the abi.encodePacked data to convert to calldata + bool result = this.verifySignature( + pubKeyData, + messageData, + signatureData + ); + assertTrue(result, "Signature verification should succeed."); + } + /** + * @dev Verifies a BLS signature using the precompile + * @param pubKey The public key (48 bytes G1 point) + * @param message The message hash (32 bytes) + * @param signature The signature (96 bytes G2 point) + * @return success True if verification succeeded + */ + function verifySignature( + bytes calldata pubKey, + bytes32 message, + bytes calldata signature + ) public view returns (bool) { + // Input validation + require(pubKey.length == 48, "Public key must be 48 bytes"); + require(signature.length == 96, "Signature must be 96 bytes"); + + // Concatenate inputs in required format: + // [pubkey (48 bytes) | message (32 bytes) | signature (96 bytes)] + bytes memory input = bytes.concat( + pubKey, + message, + signature + ); + + // Call precompile + (bool success, bytes memory result) = BLS_VERIFY.staticcall(input); + + // Check if call was successful + if (!success) { + return false; + } + + // If we got a result back and it's not empty, verification succeeded + return result.length > 0; + } +} + +contract MockBLSVerify { + function VERIFY(bytes calldata) external pure returns (bool) { + return true; + } +} diff --git a/external/geth b/external/geth index 7cb1fc132..9992ef494 160000 --- a/external/geth +++ b/external/geth @@ -1 +1 @@ -Subproject commit 7cb1fc132d506e294e07ae146576ada50db2e8a4 +Subproject commit 9992ef494e2dd9b6a4e0f2512939e84628b0c6b1 diff --git a/go.work.sum b/go.work.sum index 75012cf83..4418417fd 100644 --- a/go.work.sum +++ b/go.work.sum @@ -788,6 +788,7 @@ github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyX github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625 h1:ckJgFhFWywOx+YLEMIJsTb+NV6NexWICk5+AMSuz3ss= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= diff --git a/p2p/gen/go/providerapi/v1/providerapi.pb.go b/p2p/gen/go/providerapi/v1/providerapi.pb.go index 297f6ed45..5cff8e579 100644 --- a/p2p/gen/go/providerapi/v1/providerapi.pb.go +++ b/p2p/gen/go/providerapi/v1/providerapi.pb.go @@ -79,6 +79,7 @@ type StakeRequest struct { Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` BlsPublicKeys []string `protobuf:"bytes,2,rep,name=bls_public_keys,json=blsPublicKeys,proto3" json:"bls_public_keys,omitempty"` + BlsSignatures []string `protobuf:"bytes,3,rep,name=bls_signatures,json=blsSignatures,proto3" json:"bls_signatures,omitempty"` } func (x *StakeRequest) Reset() { @@ -127,6 +128,13 @@ func (x *StakeRequest) GetBlsPublicKeys() []string { return nil } +func (x *StakeRequest) GetBlsSignatures() []string { + if x != nil { + return x.BlsSignatures + } + return nil +} + type StakeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -445,7 +453,7 @@ var file_providerapi_v1_providerapi_proto_rawDesc = []byte{ 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcc, 0x05, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd0, 0x07, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xb2, 0x01, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x99, 0x01, 0x92, 0x41, 0x3b, 0x32, 0x30, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, @@ -458,338 +466,354 @@ var file_providerapi_v1_providerapi_proto_rawDesc = []byte{ 0x2a, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x90, 0x02, 0x0a, 0x0f, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0xe7, 0x01, + 0x75, 0x6e, 0x74, 0x12, 0xfe, 0x01, 0x0a, 0x0f, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0xd5, 0x01, 0x92, 0x41, 0x3b, 0x32, 0x20, 0x42, 0x4c, 0x53, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x8a, 0x01, 0x16, 0x5e, 0x28, 0x30, 0x78, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x39, 0x36, 0x7d, 0x24, 0xba, 0x48, - 0xa5, 0x01, 0xba, 0x01, 0xa1, 0x01, 0x0a, 0x0e, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x93, 0x01, 0xba, 0x01, 0x8f, 0x01, 0x0a, 0x0e, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x34, 0x38, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x68, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x30, 0x78, 0x20, 0x70, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x2e, 0x1a, 0x42, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, - 0x3e, 0x20, 0x30, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, - 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x28, - 0x30, 0x78, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, - 0x39, 0x36, 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x0d, 0x62, 0x6c, 0x73, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x3a, 0xf3, 0x01, 0x92, 0x41, 0xef, 0x01, 0x0a, 0x53, 0x2a, - 0x0d, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x28, - 0x53, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x69, - 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0xd2, 0x01, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0xd2, 0x01, 0x0e, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, - 0x65, 0x79, 0x32, 0x97, 0x01, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, - 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x38, 0x30, 0x30, 0x30, 0x30, 0x63, - 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, 0x30, 0x30, 0x65, 0x30, 0x30, 0x62, 0x30, - 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, 0x39, 0x38, 0x30, 0x37, 0x33, 0x36, - 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, 0x32, 0x61, 0x62, 0x62, 0x61, 0x63, - 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, 0x38, 0x38, 0x64, 0x64, 0x31, 0x62, - 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, 0x34, 0x39, 0x37, 0x62, 0x61, 0x38, - 0x62, 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, 0x7d, 0x22, 0x9f, 0x03, 0x0a, - 0x0d, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0d, 0x62, 0x6c, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x3a, 0xcd, - 0x02, 0x92, 0x41, 0xc9, 0x02, 0x0a, 0x4a, 0x2a, 0x0e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x38, 0x47, 0x65, 0x74, 0x20, 0x73, 0x74, 0x61, - 0x6b, 0x65, 0x64, 0x20, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x2e, 0x32, 0xfa, 0x01, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, - 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x22, 0x39, 0x30, 0x30, 0x30, 0x30, - 0x63, 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, 0x30, 0x65, 0x30, 0x30, 0x62, 0x30, - 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, 0x39, 0x38, 0x30, 0x37, 0x33, 0x36, - 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, 0x32, 0x61, 0x62, 0x62, 0x61, 0x63, - 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, 0x38, 0x38, 0x64, 0x64, 0x31, 0x62, - 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, 0x39, 0x37, 0x62, 0x61, 0x38, 0x62, - 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, 0x2c, 0x20, 0x22, 0x38, 0x30, 0x30, - 0x30, 0x30, 0x63, 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, 0x30, 0x65, 0x30, 0x30, - 0x62, 0x30, 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, 0x39, 0x38, 0x30, 0x37, - 0x33, 0x36, 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, 0x32, 0x61, 0x62, 0x62, - 0x61, 0x63, 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, 0x38, 0x38, 0x64, 0x64, - 0x31, 0x62, 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, 0x39, 0x37, 0x62, 0x61, - 0x38, 0x62, 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, 0x5d, 0x7d, 0x22, 0xa5, - 0x01, 0x0a, 0x12, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x77, 0x92, - 0x41, 0x74, 0x0a, 0x4f, 0x2a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x38, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x20, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, - 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x2e, 0x32, 0x21, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, - 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d, 0x22, 0x0e, 0x0a, 0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xec, 0x11, 0x0a, 0x03, 0x42, 0x69, 0x64, 0x12, 0x8f, - 0x02, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x42, 0xf1, 0x01, 0x92, 0x41, 0x78, 0x32, 0x64, 0x48, 0x65, 0x78, 0x20, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, - 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, - 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, - 0xba, 0x48, 0x73, 0xba, 0x01, 0x70, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, - 0x73, 0x12, 0x36, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, 0x1a, 0x2b, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, - 0x34, 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0xfc, 0x01, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0xdc, 0x01, 0x92, 0x41, 0x76, 0x32, 0x6b, 0x41, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x45, 0x54, 0x48, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x77, 0x69, - 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, - 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, 0x06, 0x5b, 0x30, 0x2d, 0x39, 0x5d, - 0x2b, 0xba, 0x48, 0x60, 0xba, 0x01, 0x5d, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x23, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x2a, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, - 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, - 0x20, 0x3e, 0x20, 0x30, 0x52, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x74, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x51, 0x92, 0x41, 0x47, 0x32, 0x45, 0x4d, 0x61, 0x78, 0x20, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, - 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, - 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, - 0x2e, 0xba, 0x48, 0x04, 0x22, 0x02, 0x20, 0x00, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x64, 0x69, 0x67, - 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x3d, 0x92, 0x41, 0x31, 0x32, 0x2f, - 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, - 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2e, 0xba, - 0x48, 0x06, 0x7a, 0x04, 0x10, 0x01, 0x18, 0x40, 0x52, 0x09, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, - 0x65, 0x73, 0x74, 0x12, 0xc2, 0x01, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x42, 0x8d, 0x01, 0x92, 0x41, 0x2d, 0x32, 0x2b, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x64, 0x65, 0x63, - 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x5a, 0xba, 0x01, 0x57, 0x0a, 0x15, 0x64, 0x65, - 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, - 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, - 0x3e, 0x20, 0x30, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xb8, 0x01, 0x0a, 0x13, 0x64, 0x65, 0x63, - 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x87, 0x01, 0x92, 0x41, 0x2b, 0x32, 0x29, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x64, 0x65, - 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x56, 0xba, 0x01, 0x53, 0x0a, 0x13, 0x64, - 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x12, 0x2c, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, - 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, - 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, - 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x8a, 0x02, 0x0a, 0x13, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, - 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x09, 0x42, 0xd9, 0x01, 0x92, 0x41, 0x49, 0x32, 0x47, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x78, 0x20, 0x68, 0x61, - 0x73, 0x68, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, - 0x6f, 0x72, 0x20, 0x62, 0x65, 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x2e, - 0xba, 0x48, 0x89, 0x01, 0xba, 0x01, 0x85, 0x01, 0x0a, 0x13, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, - 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x41, 0x72, - 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, - 0x1a, 0x2b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, 0x20, 0x72, 0x2e, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, - 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x11, 0x72, - 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0x9d, 0x02, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x42, 0xf1, 0x01, 0x92, 0x41, - 0x6e, 0x32, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x4c, 0x50, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, - 0x20, 0x72, 0x61, 0x77, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, + 0x69, 0x78, 0x2e, 0x1a, 0x30, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, + 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x28, 0x30, 0x78, + 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x39, 0x36, + 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x0d, 0x62, 0x6c, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x93, 0x02, 0x0a, 0x0e, 0x62, 0x6c, 0x73, 0x5f, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x42, 0xeb, 0x01, + 0x92, 0x41, 0x50, 0x32, 0x34, 0x42, 0x4c, 0x53, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x42, 0x4c, 0x53, 0x20, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x8a, 0x01, 0x17, 0x5e, 0x28, 0x30, 0x78, + 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x39, + 0x32, 0x7d, 0x24, 0xba, 0x48, 0x94, 0x01, 0xba, 0x01, 0x90, 0x01, 0x0a, 0x0e, 0x62, 0x6c, 0x73, + 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x4b, 0x62, 0x6c, 0x73, + 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x39, 0x36, 0x2d, 0x62, + 0x79, 0x74, 0x65, 0x20, 0x68, 0x65, 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x30, 0x78, + 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2e, 0x1a, 0x31, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, + 0x6c, 0x6c, 0x28, 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, + 0x27, 0x5e, 0x28, 0x30, 0x78, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, + 0x39, 0x5d, 0x7b, 0x31, 0x39, 0x32, 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x0d, 0x62, 0x6c, 0x73, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x3a, 0xf3, 0x01, 0x92, 0x41, 0xef, + 0x01, 0x0a, 0x53, 0x2a, 0x0d, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x32, 0x28, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0xd2, 0x01, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0xd2, 0x01, 0x0e, 0x62, 0x6c, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x32, 0x97, 0x01, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x73, 0x5f, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x63, 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, 0x30, 0x30, 0x65, + 0x30, 0x30, 0x62, 0x30, 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, 0x39, 0x38, + 0x30, 0x37, 0x33, 0x36, 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, 0x32, 0x61, + 0x62, 0x62, 0x61, 0x63, 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, 0x38, 0x38, + 0x64, 0x64, 0x31, 0x62, 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, 0x34, 0x39, + 0x37, 0x62, 0x61, 0x38, 0x62, 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, 0x7d, + 0x22, 0x9f, 0x03, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6c, + 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x6c, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x73, 0x3a, 0xcd, 0x02, 0x92, 0x41, 0xc9, 0x02, 0x0a, 0x4a, 0x2a, 0x0e, 0x53, 0x74, 0x61, + 0x6b, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x38, 0x47, 0x65, 0x74, + 0x20, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x64, 0x20, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x32, 0xfa, 0x01, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x3a, 0x20, 0x22, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x73, 0x5f, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x22, 0x39, + 0x30, 0x30, 0x30, 0x30, 0x63, 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, 0x30, 0x65, + 0x30, 0x30, 0x62, 0x30, 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, 0x39, 0x38, + 0x30, 0x37, 0x33, 0x36, 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, 0x32, 0x61, + 0x62, 0x62, 0x61, 0x63, 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, 0x38, 0x38, + 0x64, 0x64, 0x31, 0x62, 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, 0x39, 0x37, + 0x62, 0x61, 0x38, 0x62, 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, 0x2c, 0x20, + 0x22, 0x38, 0x30, 0x30, 0x30, 0x30, 0x63, 0x64, 0x64, 0x65, 0x65, 0x63, 0x36, 0x36, 0x61, 0x38, + 0x30, 0x65, 0x30, 0x30, 0x62, 0x30, 0x63, 0x63, 0x62, 0x62, 0x36, 0x32, 0x66, 0x31, 0x32, 0x32, + 0x39, 0x38, 0x30, 0x37, 0x33, 0x36, 0x30, 0x33, 0x66, 0x35, 0x32, 0x30, 0x39, 0x65, 0x38, 0x31, + 0x32, 0x61, 0x62, 0x62, 0x61, 0x63, 0x37, 0x65, 0x38, 0x37, 0x30, 0x34, 0x38, 0x32, 0x65, 0x34, + 0x38, 0x38, 0x64, 0x64, 0x31, 0x62, 0x62, 0x65, 0x35, 0x33, 0x33, 0x61, 0x39, 0x64, 0x34, 0x34, + 0x39, 0x37, 0x62, 0x61, 0x38, 0x62, 0x37, 0x35, 0x36, 0x65, 0x31, 0x65, 0x38, 0x32, 0x62, 0x22, + 0x5d, 0x7d, 0x22, 0xa5, 0x01, 0x0a, 0x12, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x3a, 0x77, 0x92, 0x41, 0x74, 0x0a, 0x4f, 0x2a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x38, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x20, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x32, 0x21, 0x7b, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d, 0x22, 0x0e, 0x0a, 0x0c, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xec, 0x11, 0x0a, 0x03, 0x42, + 0x69, 0x64, 0x12, 0x8f, 0x02, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0xf1, 0x01, 0x92, 0x41, 0x78, 0x32, 0x64, 0x48, 0x65, + 0x78, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x8a, 0x01, 0x0f, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, + 0x7b, 0x36, 0x34, 0x7d, 0xba, 0x48, 0x73, 0xba, 0x01, 0x70, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x36, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, 0x1a, 0x2b, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, + 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0x24, 0x27, 0x29, 0x29, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x12, 0xfc, 0x01, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0xdc, 0x01, 0x92, 0x41, 0x76, 0x32, + 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x45, 0x54, 0x48, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x69, + 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x61, 0x79, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x8a, 0x01, 0x06, 0x5b, + 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0xba, 0x48, 0x60, 0xba, 0x01, 0x5d, 0x0a, 0x0a, 0x62, 0x69, 0x64, + 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x2a, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x30, 0x2d, + 0x39, 0x5d, 0x2b, 0x24, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x74, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x51, 0x92, 0x41, 0x47, 0x32, 0x45, + 0x4d, 0x61, 0x78, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0xba, - 0x48, 0x7d, 0xba, 0x01, 0x7a, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x20, 0x72, 0x61, 0x77, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x1a, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, - 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, - 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x27, 0x29, 0x29, 0x52, - 0x0f, 0x72, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x3a, 0xd2, 0x04, 0x92, 0x41, 0xce, 0x04, 0x0a, 0x70, 0x2a, 0x0b, 0x42, 0x69, 0x64, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x30, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, - 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, - 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0xd2, 0x01, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, - 0x68, 0x65, 0x73, 0xd2, 0x01, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0xd2, - 0x01, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xd2, 0x01, 0x09, - 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x32, 0xd9, 0x03, 0x7b, 0x22, 0x74, 0x78, - 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x22, 0x66, 0x65, 0x34, 0x63, 0x62, - 0x34, 0x37, 0x64, 0x62, 0x33, 0x36, 0x33, 0x30, 0x35, 0x35, 0x31, 0x62, 0x65, 0x65, 0x64, 0x66, - 0x62, 0x64, 0x30, 0x32, 0x61, 0x37, 0x31, 0x65, 0x63, 0x63, 0x36, 0x39, 0x66, 0x64, 0x35, 0x39, - 0x37, 0x35, 0x38, 0x65, 0x32, 0x62, 0x61, 0x36, 0x39, 0x39, 0x36, 0x30, 0x36, 0x65, 0x32, 0x64, - 0x35, 0x63, 0x37, 0x34, 0x32, 0x38, 0x34, 0x66, 0x66, 0x61, 0x37, 0x22, 0x2c, 0x20, 0x22, 0x37, - 0x31, 0x63, 0x31, 0x33, 0x34, 0x38, 0x66, 0x32, 0x64, 0x37, 0x66, 0x66, 0x37, 0x65, 0x38, 0x31, - 0x34, 0x66, 0x39, 0x63, 0x33, 0x36, 0x31, 0x37, 0x39, 0x38, 0x33, 0x37, 0x30, 0x33, 0x34, 0x33, - 0x35, 0x65, 0x61, 0x37, 0x34, 0x34, 0x36, 0x64, 0x65, 0x34, 0x32, 0x30, 0x61, 0x65, 0x61, 0x63, - 0x34, 0x38, 0x38, 0x62, 0x66, 0x31, 0x64, 0x65, 0x33, 0x35, 0x37, 0x33, 0x37, 0x65, 0x38, 0x22, - 0x5d, 0x2c, 0x20, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x22, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x2c, 0x20, 0x22, 0x62, 0x69, 0x64, 0x44, - 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x39, 0x64, 0x4a, 0x69, 0x6e, 0x77, 0x4c, - 0x2b, 0x46, 0x5a, 0x36, 0x42, 0x31, 0x78, 0x73, 0x49, 0x51, 0x51, 0x6f, 0x38, 0x74, 0x38, 0x42, - 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x62, 0x4a, 0x77, 0x59, 0x38, 0x36, 0x6c, 0x2f, 0x59, 0x75, 0x37, - 0x79, 0x41, 0x48, 0x31, 0x35, 0x39, 0x51, 0x72, 0x50, 0x48, 0x55, 0x30, 0x71, 0x6a, 0x32, 0x50, - 0x2b, 0x59, 0x46, 0x6a, 0x2b, 0x6c, 0x6c, 0x62, 0x75, 0x49, 0x31, 0x5a, 0x79, 0x67, 0x64, 0x78, - 0x47, 0x73, 0x58, 0x38, 0x2b, 0x50, 0x33, 0x62, 0x79, 0x4d, 0x45, 0x41, 0x35, 0x69, 0x67, 0x3d, - 0x3d, 0x22, 0x2c, 0x20, 0x22, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x37, 0x32, 0x35, 0x33, 0x36, - 0x35, 0x33, 0x30, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x22, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, - 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x37, 0x32, - 0x35, 0x33, 0x36, 0x35, 0x33, 0x30, 0x32, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x22, 0x72, 0x65, 0x76, - 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, - 0x5b, 0x22, 0x66, 0x65, 0x34, 0x63, 0x62, 0x34, 0x37, 0x64, 0x62, 0x33, 0x36, 0x33, 0x30, 0x35, - 0x35, 0x31, 0x62, 0x65, 0x65, 0x64, 0x66, 0x62, 0x64, 0x30, 0x32, 0x61, 0x37, 0x31, 0x65, 0x63, - 0x63, 0x36, 0x39, 0x66, 0x64, 0x35, 0x39, 0x37, 0x35, 0x38, 0x65, 0x32, 0x62, 0x61, 0x36, 0x39, - 0x39, 0x36, 0x30, 0x36, 0x65, 0x32, 0x64, 0x35, 0x63, 0x37, 0x34, 0x32, 0x38, 0x34, 0x66, 0x66, - 0x61, 0x37, 0x22, 0x5d, 0x7d, 0x22, 0x81, 0x06, 0x0a, 0x0b, 0x42, 0x69, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x62, 0x69, 0x64, 0x5f, 0x64, 0x69, 0x67, - 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x34, 0x92, 0x41, 0x31, 0x32, 0x2f, - 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, - 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x2e, 0x52, - 0x09, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x5f, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x23, - 0x92, 0x41, 0x14, 0x32, 0x12, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x2e, 0xba, 0x48, 0x09, 0x82, 0x01, 0x06, 0x10, 0x01, 0x1a, - 0x02, 0x01, 0x02, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0xb4, 0x01, 0x0a, 0x12, - 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x84, 0x01, 0x92, 0x41, 0x80, 0x01, 0x32, - 0x7e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, - 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x20, 0x62, - 0x79, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, - 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, - 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x72, - 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x11, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x12, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, - 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x3a, 0xb8, - 0x02, 0x92, 0x41, 0xb4, 0x02, 0x0a, 0x82, 0x01, 0x2a, 0x0c, 0x42, 0x69, 0x64, 0x20, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, - 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, - 0x69, 0x64, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x2e, 0xd2, 0x01, 0x09, 0x62, - 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0xd2, 0x01, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0xd2, 0x01, 0x16, 0x64, 0x65, 0x63, 0x61, 0x79, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x32, 0xac, 0x01, 0x7b, 0x22, 0x62, - 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x39, 0x64, 0x4a, 0x69, - 0x6e, 0x77, 0x4c, 0x2b, 0x46, 0x5a, 0x36, 0x42, 0x31, 0x78, 0x73, 0x49, 0x51, 0x51, 0x6f, 0x38, - 0x74, 0x38, 0x42, 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x62, 0x4a, 0x77, 0x59, 0x38, 0x36, 0x6c, 0x2f, - 0x59, 0x75, 0x37, 0x79, 0x41, 0x48, 0x31, 0x35, 0x39, 0x51, 0x72, 0x50, 0x48, 0x55, 0x30, 0x71, - 0x6a, 0x32, 0x50, 0x2b, 0x59, 0x46, 0x6a, 0x2b, 0x6c, 0x6c, 0x62, 0x75, 0x49, 0x31, 0x5a, 0x79, - 0x67, 0x64, 0x78, 0x47, 0x73, 0x58, 0x38, 0x2b, 0x50, 0x33, 0x62, 0x79, 0x4d, 0x45, 0x41, 0x35, - 0x69, 0x67, 0x3d, 0x3d, 0x22, 0x2c, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3a, - 0x20, 0x22, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, - 0x44, 0x22, 0x2c, 0x20, 0x22, 0x64, 0x65, 0x63, 0x61, 0x79, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x20, 0x31, 0x32, - 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x7d, 0x32, 0x91, 0x06, 0x0a, 0x08, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x42, 0x69, 0x64, 0x73, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, - 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x72, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x30, 0x01, 0x12, 0x7d, 0x0a, - 0x11, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x42, 0x69, - 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, - 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x28, 0x01, 0x12, 0x69, 0x0a, 0x05, - 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x2f, 0x7b, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x12, 0x67, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x69, 0x6e, 0x2e, 0xba, 0x48, 0x04, 0x22, 0x02, 0x20, 0x00, 0x52, 0x0b, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0a, 0x62, 0x69, 0x64, + 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x3d, 0x92, + 0x41, 0x31, 0x32, 0x2f, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, + 0x65, 0x72, 0x2e, 0xba, 0x48, 0x06, 0x7a, 0x04, 0x10, 0x01, 0x18, 0x40, 0x52, 0x09, 0x62, 0x69, + 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0xc2, 0x01, 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, + 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x8d, 0x01, 0x92, 0x41, 0x2d, 0x32, 0x2b, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, + 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x5a, 0xba, 0x01, 0x57, + 0x0a, 0x15, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x52, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0xb8, 0x01, 0x0a, + 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x87, 0x01, 0x92, 0x41, 0x2b, + 0x32, 0x29, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, 0x74, 0x20, 0x77, + 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x65, 0x6e, 0x64, + 0x73, 0x20, 0x64, 0x65, 0x63, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x2e, 0xba, 0x48, 0x56, 0xba, 0x01, + 0x53, 0x0a, 0x13, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x65, 0x72, 0x2e, 0x1a, 0x0e, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, + 0x20, 0x3e, 0x20, 0x30, 0x52, 0x11, 0x64, 0x65, 0x63, 0x61, 0x79, 0x45, 0x6e, 0x64, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x8a, 0x02, 0x0a, 0x13, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x42, 0xd9, 0x01, 0x92, 0x41, 0x49, 0x32, 0x47, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x78, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x76, + 0x65, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x62, 0x65, 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, + 0x64, 0x65, 0x64, 0x2e, 0xba, 0x48, 0x89, 0x01, 0xba, 0x01, 0x85, 0x01, 0x0a, 0x13, 0x72, 0x65, + 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x12, 0x41, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x2e, 0x1a, 0x2b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x61, 0x6c, 0x6c, 0x28, 0x72, + 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, + 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x36, 0x34, 0x7d, 0x24, 0x27, 0x29, + 0x29, 0x52, 0x11, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x12, 0x9d, 0x02, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x42, + 0xf1, 0x01, 0x92, 0x41, 0x6e, 0x32, 0x6c, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x4c, 0x50, 0x20, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x20, 0x72, 0x61, 0x77, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, + 0x64, 0x64, 0x65, 0x72, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0xba, 0x48, 0x7d, 0xba, 0x01, 0x7a, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x5f, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x72, 0x61, 0x77, + 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x72, 0x61, 0x77, 0x20, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x1a, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x2c, 0x20, 0x72, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x66, 0x41, 0x2d, 0x46, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, + 0x27, 0x29, 0x29, 0x52, 0x0f, 0x72, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0xd2, 0x04, 0x92, 0x41, 0xce, 0x04, 0x0a, 0x70, 0x2a, 0x0b, 0x42, + 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x30, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x62, 0x69, 0x64, 0x64, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0xd2, 0x01, 0x08, 0x74, + 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0xd2, 0x01, 0x09, 0x62, 0x69, 0x64, 0x41, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0xd2, 0x01, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0xd2, 0x01, 0x09, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x32, 0xd9, 0x03, + 0x7b, 0x22, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x22, 0x66, + 0x65, 0x34, 0x63, 0x62, 0x34, 0x37, 0x64, 0x62, 0x33, 0x36, 0x33, 0x30, 0x35, 0x35, 0x31, 0x62, + 0x65, 0x65, 0x64, 0x66, 0x62, 0x64, 0x30, 0x32, 0x61, 0x37, 0x31, 0x65, 0x63, 0x63, 0x36, 0x39, + 0x66, 0x64, 0x35, 0x39, 0x37, 0x35, 0x38, 0x65, 0x32, 0x62, 0x61, 0x36, 0x39, 0x39, 0x36, 0x30, + 0x36, 0x65, 0x32, 0x64, 0x35, 0x63, 0x37, 0x34, 0x32, 0x38, 0x34, 0x66, 0x66, 0x61, 0x37, 0x22, + 0x2c, 0x20, 0x22, 0x37, 0x31, 0x63, 0x31, 0x33, 0x34, 0x38, 0x66, 0x32, 0x64, 0x37, 0x66, 0x66, + 0x37, 0x65, 0x38, 0x31, 0x34, 0x66, 0x39, 0x63, 0x33, 0x36, 0x31, 0x37, 0x39, 0x38, 0x33, 0x37, + 0x30, 0x33, 0x34, 0x33, 0x35, 0x65, 0x61, 0x37, 0x34, 0x34, 0x36, 0x64, 0x65, 0x34, 0x32, 0x30, + 0x61, 0x65, 0x61, 0x63, 0x34, 0x38, 0x38, 0x62, 0x66, 0x31, 0x64, 0x65, 0x33, 0x35, 0x37, 0x33, + 0x37, 0x65, 0x38, 0x22, 0x5d, 0x2c, 0x20, 0x22, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, + 0x20, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x2c, 0x20, 0x22, + 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x39, 0x64, 0x4a, + 0x69, 0x6e, 0x77, 0x4c, 0x2b, 0x46, 0x5a, 0x36, 0x42, 0x31, 0x78, 0x73, 0x49, 0x51, 0x51, 0x6f, + 0x38, 0x74, 0x38, 0x42, 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x62, 0x4a, 0x77, 0x59, 0x38, 0x36, 0x6c, + 0x2f, 0x59, 0x75, 0x37, 0x79, 0x41, 0x48, 0x31, 0x35, 0x39, 0x51, 0x72, 0x50, 0x48, 0x55, 0x30, + 0x71, 0x6a, 0x32, 0x50, 0x2b, 0x59, 0x46, 0x6a, 0x2b, 0x6c, 0x6c, 0x62, 0x75, 0x49, 0x31, 0x5a, + 0x79, 0x67, 0x64, 0x78, 0x47, 0x73, 0x58, 0x38, 0x2b, 0x50, 0x33, 0x62, 0x79, 0x4d, 0x45, 0x41, + 0x35, 0x69, 0x67, 0x3d, 0x3d, 0x22, 0x2c, 0x20, 0x22, 0x64, 0x65, 0x63, 0x61, 0x79, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x37, + 0x32, 0x35, 0x33, 0x36, 0x35, 0x33, 0x30, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x22, 0x64, 0x65, + 0x63, 0x61, 0x79, 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x3a, 0x31, 0x37, 0x32, 0x35, 0x33, 0x36, 0x35, 0x33, 0x30, 0x32, 0x30, 0x30, 0x30, 0x2c, 0x20, + 0x22, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x66, 0x65, 0x34, 0x63, 0x62, 0x34, 0x37, 0x64, 0x62, 0x33, + 0x36, 0x33, 0x30, 0x35, 0x35, 0x31, 0x62, 0x65, 0x65, 0x64, 0x66, 0x62, 0x64, 0x30, 0x32, 0x61, + 0x37, 0x31, 0x65, 0x63, 0x63, 0x36, 0x39, 0x66, 0x64, 0x35, 0x39, 0x37, 0x35, 0x38, 0x65, 0x32, + 0x62, 0x61, 0x36, 0x39, 0x39, 0x36, 0x30, 0x36, 0x65, 0x32, 0x64, 0x35, 0x63, 0x37, 0x34, 0x32, + 0x38, 0x34, 0x66, 0x66, 0x61, 0x37, 0x22, 0x5d, 0x7d, 0x22, 0x81, 0x06, 0x0a, 0x0b, 0x42, 0x69, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x62, 0x69, 0x64, + 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x34, 0x92, + 0x41, 0x31, 0x32, 0x2f, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x64, + 0x65, 0x72, 0x2e, 0x52, 0x09, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x5f, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x42, 0x23, 0x92, 0x41, 0x14, 0x32, 0x12, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x2e, 0xba, 0x48, 0x09, 0x82, 0x01, + 0x06, 0x10, 0x01, 0x1a, 0x02, 0x01, 0x02, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0xb4, 0x01, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x84, 0x01, 0x92, + 0x41, 0x80, 0x01, 0x32, 0x7e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x61, + 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x20, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x4a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x13, 0x0a, + 0x0f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, + 0x10, 0x02, 0x3a, 0xb8, 0x02, 0x92, 0x41, 0xb4, 0x02, 0x0a, 0x82, 0x01, 0x2a, 0x0c, 0x42, 0x69, + 0x64, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x69, 0x64, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x2e, + 0xd2, 0x01, 0x09, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0xd2, 0x01, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0xd2, 0x01, 0x16, 0x64, 0x65, 0x63, 0x61, 0x79, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x32, 0xac, + 0x01, 0x7b, 0x22, 0x62, 0x69, 0x64, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, + 0x39, 0x64, 0x4a, 0x69, 0x6e, 0x77, 0x4c, 0x2b, 0x46, 0x5a, 0x36, 0x42, 0x31, 0x78, 0x73, 0x49, + 0x51, 0x51, 0x6f, 0x38, 0x74, 0x38, 0x42, 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x62, 0x4a, 0x77, 0x59, + 0x38, 0x36, 0x6c, 0x2f, 0x59, 0x75, 0x37, 0x79, 0x41, 0x48, 0x31, 0x35, 0x39, 0x51, 0x72, 0x50, + 0x48, 0x55, 0x30, 0x71, 0x6a, 0x32, 0x50, 0x2b, 0x59, 0x46, 0x6a, 0x2b, 0x6c, 0x6c, 0x62, 0x75, + 0x49, 0x31, 0x5a, 0x79, 0x67, 0x64, 0x78, 0x47, 0x73, 0x58, 0x38, 0x2b, 0x50, 0x33, 0x62, 0x79, + 0x4d, 0x45, 0x41, 0x35, 0x69, 0x67, 0x3d, 0x3d, 0x22, 0x2c, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, + 0x45, 0x50, 0x54, 0x45, 0x44, 0x22, 0x2c, 0x20, 0x22, 0x64, 0x65, 0x63, 0x61, 0x79, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x3a, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x7d, 0x32, 0x91, 0x06, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x0b, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x42, 0x69, 0x64, 0x73, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x22, 0x21, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x2f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x30, + 0x01, 0x12, 0x7d, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x42, 0x69, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x6e, 0x64, 0x5f, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x28, 0x01, + 0x12, 0x69, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x61, + 0x6b, 0x65, 0x2f, 0x7b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x12, 0x67, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x73, + 0x74, 0x61, 0x6b, 0x65, 0x12, 0x6e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, - 0x12, 0x6e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, - 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1d, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x74, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, - 0x12, 0x76, 0x0a, 0x0d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x6b, - 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, - 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x76, 0x31, - 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x63, 0x0a, 0x07, 0x55, 0x6e, 0x73, 0x74, - 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x75, 0x6e, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x42, 0xbc, 0x02, - 0x92, 0x41, 0x74, 0x12, 0x72, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, - 0x41, 0x50, 0x49, 0x2a, 0x55, 0x0a, 0x1b, 0x42, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x20, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x31, - 0x2e, 0x31, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x76, 0x2f, 0x6d, 0x65, - 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, - 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x0b, 0x31, 0x2e, 0x30, 0x2e, - 0x30, 0x2d, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x10, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, - 0x65, 0x76, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x70, 0x32, - 0x70, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0e, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1a, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, - 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0f, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x73, + 0x74, 0x61, 0x6b, 0x65, 0x12, 0x76, 0x0a, 0x0d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x53, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x1a, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x63, 0x0a, 0x07, + 0x55, 0x6e, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x75, 0x6e, 0x73, 0x74, 0x61, 0x6b, + 0x65, 0x42, 0xbc, 0x02, 0x92, 0x41, 0x74, 0x12, 0x72, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x20, 0x41, 0x50, 0x49, 0x2a, 0x55, 0x0a, 0x1b, 0x42, 0x75, 0x73, 0x69, 0x6e, + 0x65, 0x73, 0x73, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, + 0x73, 0x65, 0x20, 0x31, 0x2e, 0x31, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x65, + 0x76, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2f, 0x62, 0x6c, 0x6f, + 0x62, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x0b, + 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x0a, 0x12, 0x63, 0x6f, 0x6d, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, + 0x10, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x65, 0x76, 0x2f, 0x6d, 0x65, 0x76, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x2f, 0x70, 0x32, 0x70, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, + 0x02, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x1a, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x5c, + 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/p2p/gen/openapi/providerapi/v1/providerapi.swagger.yaml b/p2p/gen/openapi/providerapi/v1/providerapi.swagger.yaml index db9561cb9..d84e1835b 100644 --- a/p2p/gen/openapi/providerapi/v1/providerapi.swagger.yaml +++ b/p2p/gen/openapi/providerapi/v1/providerapi.swagger.yaml @@ -114,6 +114,15 @@ paths: type: string pattern: ^(0x)?[a-fA-F0-9]{96}$ collectionFormat: multi + - name: blsSignatures + description: BLS signatures corresponding to the BLS public keys. + in: query + required: false + type: array + items: + type: string + pattern: ^(0x)?[a-fA-F0-9]{192}$ + collectionFormat: multi /v1/provider/unstake: post: summary: Unstake diff --git a/p2p/go.mod b/p2p/go.mod index 4c0c9b35b..3dbc51efb 100644 --- a/p2p/go.mod +++ b/p2p/go.mod @@ -7,6 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/armon/go-radix v1.0.0 github.com/bufbuild/protovalidate-go v0.6.0 + github.com/cloudflare/circl v1.5.0 github.com/cockroachdb/pebble v1.1.2 github.com/ethereum/go-ethereum v1.14.11 github.com/go-logr/logr v1.4.2 diff --git a/p2p/go.sum b/p2p/go.sum index 75b523568..baf487325 100644 --- a/p2p/go.sum +++ b/p2p/go.sum @@ -52,6 +52,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= diff --git a/p2p/integrationtest/provider/client.go b/p2p/integrationtest/provider/client.go index 630812858..80b21c400 100644 --- a/p2p/integrationtest/provider/client.go +++ b/p2p/integrationtest/provider/client.go @@ -3,11 +3,17 @@ package main import ( "context" "crypto/tls" + "encoding/hex" "errors" + "fmt" "log/slog" "math/big" "time" + "github.com/cloudflare/circl/sign/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + debugapi "github.com/primev/mev-commit/p2p/gen/go/debugapi/v1" providerapiv1 "github.com/primev/mev-commit/p2p/gen/go/providerapi/v1" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" @@ -18,6 +24,7 @@ import ( type ProviderClient struct { conn *grpc.ClientConn client providerapiv1.ProviderClient + debugClient debugapi.DebugServiceClient logger *slog.Logger senderC chan *providerapiv1.BidResponse senderClosed chan struct{} @@ -74,6 +81,7 @@ func NewProviderClient( b := &ProviderClient{ conn: conn, + debugClient: debugapi.NewDebugServiceClient(conn), client: providerapiv1.NewProviderClient(conn), logger: logger, senderC: make(chan *providerapiv1.BidResponse), @@ -91,15 +99,24 @@ func (b *ProviderClient) Close() error { return b.conn.Close() } -func (b *ProviderClient) CheckAndStake(keys []string) error { +func (b *ProviderClient) CheckAndStake(_ []string) error { stakeAmt, err := b.client.GetStake(context.Background(), &providerapiv1.EmptyMessage{}) if err != nil { b.logger.Error("failed to get stake amount", "err", err) return err } + topology, err := b.debugClient.GetTopology(context.Background(), &debugapi.EmptyMessage{}) + if err != nil { + b.logger.Error("failed to get topology", "err", err) + return err + } + + ethAddress := topology.GetTopology().Fields["self"].GetStructValue().Fields["Ethereum Address"].GetStringValue() + b.logger.Info("stake amount", "stake", stakeAmt.Amount) + b.logger.Info("eth address", "address", ethAddress) stakedAmt, set := big.NewInt(0).SetString(stakeAmt.Amount, 10) if !set { b.logger.Error("failed to parse stake amount") @@ -111,9 +128,36 @@ func (b *ProviderClient) CheckAndStake(keys []string) error { return nil } + hashedMessage := crypto.Keccak256(common.HexToAddress(ethAddress).Bytes()) + ikm := make([]byte, 32) + privateKey, err := bls.KeyGen[bls.G1](ikm, nil, nil) + if err != nil { + b.logger.Error("failed to generate private key", "error", err) + return fmt.Errorf("failed to generate private key: %w", err) + } + publicKey := privateKey.PublicKey() + signature := bls.Sign(privateKey, hashedMessage) + + // Verify the signature + if !bls.Verify(publicKey, hashedMessage, signature) { + b.logger.Error("failed to verify generated BLS signature") + return fmt.Errorf("failed to verify generated BLS signature") + } + + pubkeyb, err := publicKey.MarshalBinary() + if err != nil { + b.logger.Error("failed to marshal public key", "error", err) + return fmt.Errorf("failed to marshal public key: %w", err) + } + b.logger.Info("generated BLS key pair", + "public_key", hex.EncodeToString(pubkeyb), + "signature", hex.EncodeToString(signature)) + + // Register a provider _, err = b.client.Stake(context.Background(), &providerapiv1.StakeRequest{ Amount: "10000000000000000000", - BlsPublicKeys: keys, + BlsPublicKeys: []string{hex.EncodeToString(pubkeyb)}, + BlsSignatures: []string{hex.EncodeToString(signature)}, }) if err != nil { b.logger.Error("failed to register stake", "err", err) diff --git a/p2p/pkg/rpc/provider/service.go b/p2p/pkg/rpc/provider/service.go index f6b1a0f15..e86bbc58c 100644 --- a/p2p/pkg/rpc/provider/service.go +++ b/p2p/pkg/rpc/provider/service.go @@ -45,7 +45,8 @@ type Service struct { type ProviderRegistryContract interface { ProviderRegistered(opts *bind.CallOpts, address common.Address) (bool, error) Stake(opts *bind.TransactOpts) (*types.Transaction, error) - RegisterAndStake(opts *bind.TransactOpts, blsPublicKey [][]byte) (*types.Transaction, error) + RegisterAndStake(opts *bind.TransactOpts) (*types.Transaction, error) + AddVerifiedBLSKey(opts *bind.TransactOpts, blsPublicKey []byte, signature []byte) (*types.Transaction, error) GetProviderStake(*bind.CallOpts, common.Address) (*big.Int, error) GetBLSKeys(*bind.CallOpts, common.Address) ([][]byte, error) MinStake(*bind.CallOpts) (*big.Int, error) @@ -53,6 +54,7 @@ type ProviderRegistryContract interface { ParseFundsDeposited(types.Log) (*providerregistry.ProviderregistryFundsDeposited, error) ParseUnstake(types.Log) (*providerregistry.ProviderregistryUnstake, error) ParseWithdraw(types.Log) (*providerregistry.ProviderregistryWithdraw, error) + ParseBLSKeyAdded(types.Log) (*providerregistry.ProviderregistryBLSKeyAdded, error) Withdraw(opts *bind.TransactOpts) (*types.Transaction, error) Unstake(opts *bind.TransactOpts) (*types.Transaction, error) } @@ -236,19 +238,15 @@ func (s *Service) Stake( if err != nil { return nil, status.Errorf(codes.Internal, "checking registration: %v", err) } + if !registered { - blsPubkeyBytesArray := make([][]byte, len(stake.BlsPublicKeys)) - for i, key := range stake.BlsPublicKeys { - stake.BlsPublicKeys[i] = strings.TrimPrefix(key, "0x") - blsPubkeyBytes, err := hex.DecodeString(stake.BlsPublicKeys[i]) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "decoding bls public key: %v", err) - } - blsPubkeyBytesArray[i] = blsPubkeyBytes + if len(stake.BlsPublicKeys) == 0 { + return nil, status.Error(codes.InvalidArgument, "missing BLS keys") } - - tx, txErr = s.registryContract.RegisterAndStake(opts, blsPubkeyBytesArray) - + if len(stake.BlsSignatures) == 0 { + return nil, status.Error(codes.InvalidArgument, "missing BLS signatures") + } + tx, txErr = s.registryContract.RegisterAndStake(opts) } else { tx, txErr = s.registryContract.Stake(opts) } @@ -258,29 +256,65 @@ func (s *Service) Stake( receipt, err := s.watcher.WaitForReceipt(ctx, tx) if err != nil { - return nil, status.Errorf(codes.Internal, "waiting for receipt: %v", err) + return nil, status.Errorf(codes.Internal, "waiting for receipt for registration: %v", err) } if receipt.Status != types.ReceiptStatusSuccessful { return nil, status.Errorf(codes.Internal, "receipt status: %v", receipt.Status) } + var stakeResponse *providerapiv1.StakeResponse for _, log := range receipt.Logs { if registration, err := s.registryContract.ParseProviderRegistered(*log); err == nil { s.logger.Info("stake registered", "amount", registration.StakedAmount) - return &providerapiv1.StakeResponse{ - Amount: registration.StakedAmount.String(), - BlsPublicKeys: stake.BlsPublicKeys, - }, nil - } - if deposit, err := s.registryContract.ParseFundsDeposited(*log); err == nil { + stakeResponse = &providerapiv1.StakeResponse{ + Amount: registration.StakedAmount.String(), + } + } else if deposit, err := s.registryContract.ParseFundsDeposited(*log); err == nil { s.logger.Info("stake deposited", "amount", deposit.Amount) - return &providerapiv1.StakeResponse{Amount: deposit.Amount.String()}, nil + stakeResponse = &providerapiv1.StakeResponse{Amount: deposit.Amount.String()} + } + } + + for i, _ := range stake.BlsPublicKeys { + blsPublicKey, err := hex.DecodeString(strings.TrimPrefix(stake.BlsPublicKeys[i], "0x")) + if err != nil { + return nil, status.Errorf(codes.Internal, "decoding bls public key: %v", err) + } + blsSignature, err := hex.DecodeString(strings.TrimPrefix(stake.BlsSignatures[i], "0x")) + if err != nil { + return nil, status.Errorf(codes.Internal, "decoding bls signature: %v", err) + } + + opts, err = s.optsGetter(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "getting transact opts for adding BLS key: %v", err) + } + + s.logger.Info("adding verified bls key", "blsPublicKey", hex.EncodeToString(blsPublicKey), "blsSignature", hex.EncodeToString(blsSignature)) + tx, txErr = s.registryContract.AddVerifiedBLSKey(opts, blsPublicKey, blsSignature) + if txErr != nil { + return nil, status.Errorf(codes.Internal, "adding verified bls key: %v", txErr) + } + receipt, err = s.watcher.WaitForReceipt(ctx, tx) + if err != nil { + return nil, status.Errorf(codes.Internal, "waiting for receipt for adding verified bls key: %v", err) + } + if receipt.Status != types.ReceiptStatusSuccessful { + return nil, status.Errorf(codes.Internal, "receipt status: %v", receipt.Status) + } + + s.logger.Info("verified bls key added", "key", stake.BlsPublicKeys[i]) + + for _, log := range receipt.Logs { + if blsKeyEvent, err := s.registryContract.ParseBLSKeyAdded(*log); err == nil { + s.logger.Info("verified bls key added", "key", blsKeyEvent.BlsPublicKey) + stakeResponse.BlsPublicKeys = append(stakeResponse.BlsPublicKeys, hex.EncodeToString(blsKeyEvent.BlsPublicKey)) + } } } - s.logger.Error("no registration event found") - return nil, status.Error(codes.Internal, "no registration event found") + return stakeResponse, nil } func (s *Service) GetStake( diff --git a/p2p/pkg/rpc/provider/service_test.go b/p2p/pkg/rpc/provider/service_test.go index cf7f825a9..a4c127449 100644 --- a/p2p/pkg/rpc/provider/service_test.go +++ b/p2p/pkg/rpc/provider/service_test.go @@ -2,7 +2,10 @@ package providerapi_test import ( "context" + "crypto/rand" + "encoding/hex" "errors" + "fmt" "math/big" "net" "os" @@ -11,9 +14,11 @@ import ( "time" "github.com/bufbuild/protovalidate-go" + "github.com/cloudflare/circl/sign/bls" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" providerregistry "github.com/primev/mev-commit/contracts-abi/clients/ProviderRegistry" preconfpb "github.com/primev/mev-commit/p2p/gen/go/preconfirmation/v1" providerapiv1 "github.com/primev/mev-commit/p2p/gen/go/providerapi/v1" @@ -25,10 +30,11 @@ import ( ) type testRegistryContract struct { - stake *big.Int - topup *big.Int - minStake *big.Int - blsKey []byte + stake *big.Int + topup *big.Int + minStake *big.Int + blsKey []byte + blsSignature []byte } func (t *testRegistryContract) ProviderRegistered(opts *bind.CallOpts, address common.Address) (bool, error) { @@ -38,9 +44,14 @@ func (t *testRegistryContract) ProviderRegistered(opts *bind.CallOpts, address c return true, nil } -func (t *testRegistryContract) RegisterAndStake(opts *bind.TransactOpts, blsPublicKey [][]byte) (*types.Transaction, error) { +func (t *testRegistryContract) RegisterAndStake(opts *bind.TransactOpts) (*types.Transaction, error) { t.stake = opts.Value - t.blsKey = blsPublicKey[0] + return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil +} + +func (t *testRegistryContract) AddVerifiedBLSKey(opts *bind.TransactOpts, blsPublicKey []byte, blsSignature []byte) (*types.Transaction, error) { + t.blsKey = blsPublicKey + t.blsSignature = blsSignature return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil } @@ -89,6 +100,13 @@ func (t *testRegistryContract) ParseUnstake(log types.Log) (*providerregistry.Pr }, nil } +func (t *testRegistryContract) ParseBLSKeyAdded(log types.Log) (*providerregistry.ProviderregistryBLSKeyAdded, error) { + return &providerregistry.ProviderregistryBLSKeyAdded{ + Provider: common.Address{}, + BlsPublicKey: t.blsKey, + }, nil +} + func (t *testRegistryContract) Unstake(opts *bind.TransactOpts) (*types.Transaction, error) { return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil } @@ -187,19 +205,19 @@ func TestStakeHandling(t *testing.T) { client, _ := startServer(t) validBLSKey := "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456" - + validSignature := "bbbbbbbbb1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2" t.Run("register stake", func(t *testing.T) { type testCase struct { - amount string - blsPublicKey string - err string + amount string + blsPublicKeys []string + blsSignatures []string + err string } for _, tc := range []testCase{ { - amount: "", - blsPublicKey: "", - err: "amount must be a valid integer", + amount: "", + err: "amount must be a valid integer", }, { amount: "0000000000000000000", @@ -210,28 +228,39 @@ func TestStakeHandling(t *testing.T) { err: "amount must be a valid integer", }, { - amount: "1000000000000000000", - blsPublicKey: "0x", - err: "bls_public_key must be a valid 48-byte hex string, with optional 0x prefix.", + amount: "1000000000000000000", + blsPublicKeys: []string{"0x"}, + err: "bls_public_key must be a valid 48-byte hex string, with optional 0x prefix.", + }, + { + amount: "1000000000000000000", + blsPublicKeys: []string{"0x12345"}, + err: "bls_public_key must be a valid 48-byte hex string, with optional 0x prefix.", }, { - amount: "1000000000000000000", - blsPublicKey: "0x12345", - err: "bls_public_key must be a valid 48-byte hex string, with optional 0x prefix.", + amount: "1000000000000000000", + blsPublicKeys: []string{validBLSKey}, + err: "missing BLS signatures", }, { - amount: "1000000000000000000", - blsPublicKey: validBLSKey, - err: "", + amount: "1000000000000000000", + blsPublicKeys: []string{validBLSKey}, + blsSignatures: []string{validSignature}, + err: "", }, { - amount: "1000000000000000000", - blsPublicKey: validBLSKey, - err: "", + amount: "1000000000000000000", + err: "", }, } { - stake, err := client.Stake(context.Background(), - &providerapiv1.StakeRequest{Amount: tc.amount, BlsPublicKeys: []string{tc.blsPublicKey}}) + stake, err := client.Stake( + context.Background(), + &providerapiv1.StakeRequest{ + Amount: tc.amount, + BlsPublicKeys: tc.blsPublicKeys, + BlsSignatures: tc.blsSignatures, + }, + ) if tc.err != "" { if err == nil || !strings.Contains(err.Error(), tc.err) { t.Fatalf("expected error staking: %s got %v", tc.err, err) @@ -243,8 +272,8 @@ func TestStakeHandling(t *testing.T) { if stake.Amount != tc.amount { t.Fatalf("expected amount to be %v, got %v", tc.amount, stake.Amount) } - if stake.BlsPublicKeys[0] != tc.blsPublicKey { - t.Fatalf("expected bls_public_key to be %v, got %v", tc.blsPublicKey, stake.BlsPublicKeys[0]) + if len(tc.blsPublicKeys) > 0 && stake.BlsPublicKeys[0] != tc.blsPublicKeys[0] { + t.Fatalf("expected bls_public_key to be %v, got %v", tc.blsPublicKeys[0], stake.BlsPublicKeys[0]) } } } @@ -461,15 +490,51 @@ func TestBidHandling(t *testing.T) { } } +func TestBLSKeys(t *testing.T) { + // Generate a BLS signature to verify + message := []byte("adb4257612d45f12570533308b20ac77dbfeb85a") + hashedMessage := crypto.Keccak256(message) + ikm := make([]byte, 32) + privateKey, err := bls.KeyGen[bls.G1](ikm, nil, nil) + if err != nil { + t.Fatalf("Failed to generate private key: %v", err) + } + publicKey := privateKey.PublicKey() + signature := bls.Sign(privateKey, hashedMessage) + + // Verify the signature + if !bls.Verify(publicKey, hashedMessage, signature) { + t.Fatalf("Failed to verify generated BLS signature") + } + + pubkeyb, _ := publicKey.MarshalBinary() + fmt.Printf("Public Key: %s\n", common.Bytes2Hex(pubkeyb)) + fmt.Printf("Message: %s\n", common.Bytes2Hex(hashedMessage)) + fmt.Printf("Signature: %s\n", common.Bytes2Hex(signature)) + +} + func TestWithdrawStakedAmount(t *testing.T) { t.Parallel() client, _ := startServer(t) t.Run("withdraw stake", func(t *testing.T) { + iv := make([]byte, 32) + _, _ = rand.Read(iv) + blsPrivKey, _ := bls.KeyGen[bls.G1](iv, []byte{}, []byte{}) + pubKey := blsPrivKey.PublicKey() + pubKeyBytes, _ := pubKey.MarshalBinary() + value := common.Hex2Bytes("0x53c61cfb8128ad59244e8c1d26109252ace23d14") + hash := crypto.Keccak256Hash(value) + signature := bls.Sign(blsPrivKey, hash.Bytes()) + + t.Logf("Public Key: %s", hex.EncodeToString(pubKeyBytes)) + _, err := client.Stake(context.Background(), &providerapiv1.StakeRequest{ Amount: "1000000000000000000", - BlsPublicKeys: []string{"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"}, + BlsPublicKeys: []string{hex.EncodeToString(pubKeyBytes)}, + BlsSignatures: []string{hex.EncodeToString(signature)}, }) if err != nil { t.Fatalf("error depositing stake: %v", err) diff --git a/p2p/rpc/providerapi/v1/providerapi.proto b/p2p/rpc/providerapi/v1/providerapi.proto index c0f8fa32b..3f168b986 100644 --- a/p2p/rpc/providerapi/v1/providerapi.proto +++ b/p2p/rpc/providerapi/v1/providerapi.proto @@ -92,7 +92,15 @@ message StakeRequest { }, (buf.validate.field).cel = { id: "bls_public_key", message: "bls_public_key must be a valid 48-byte hex string, with optional 0x prefix.", - expression: "size(this) > 0 && this.all(r, r.matches('^(0x)?[a-fA-F0-9]{96}$'))" + expression: "this.all(r, r.matches('^(0x)?[a-fA-F0-9]{96}$'))" + }]; + repeated string bls_signatures = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "BLS signatures corresponding to the BLS public keys." + pattern: "^(0x)?[a-fA-F0-9]{192}$" + }, (buf.validate.field).cel = { + id: "bls_signatures", + message: "bls_signatures must be a valid 96-byte hex string, with optional 0x prefix.", + expression: "this.all(r, r.matches('^(0x)?[a-fA-F0-9]{192}$'))" }]; }; diff --git a/testing/go.mod b/testing/go.mod index a8a71c205..f42c8a48f 100644 --- a/testing/go.mod +++ b/testing/go.mod @@ -4,6 +4,7 @@ go 1.23 require ( github.com/armon/go-radix v1.0.0 + github.com/cloudflare/circl v1.5.0 github.com/ethereum/go-ethereum v1.14.11 github.com/primev/mev-commit/bridge/standard v0.0.1 github.com/primev/mev-commit/contracts-abi v0.0.1 diff --git a/testing/go.sum b/testing/go.sum index de12dee7e..362da558d 100644 --- a/testing/go.sum +++ b/testing/go.sum @@ -22,6 +22,8 @@ github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a h1:f52TdbU4D5nozMAhO9TvTJ2ZMCXtN4VIAmfrrZ0JXQ4= diff --git a/testing/pkg/tests/staking/staking.go b/testing/pkg/tests/staking/staking.go index 8f383d796..5393f072e 100644 --- a/testing/pkg/tests/staking/staking.go +++ b/testing/pkg/tests/staking/staking.go @@ -3,7 +3,6 @@ package staking import ( "bytes" "context" - "crypto/rand" "encoding/hex" "encoding/json" "fmt" @@ -11,6 +10,9 @@ import ( "net/http" "slices" + "github.com/cloudflare/circl/sign/bls" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" providerregistry "github.com/primev/mev-commit/contracts-abi/clients/ProviderRegistry" providerapiv1 "github.com/primev/mev-commit/p2p/gen/go/providerapi/v1" "github.com/primev/mev-commit/testing/pkg/orchestrator" @@ -95,19 +97,37 @@ func Run(ctx context.Context, cluster orchestrator.Orchestrator, cfg any) error } stakeAmount := big.NewInt(0).Mul(amount, big.NewInt(10)) + // Generate a BLS signature to verify + message := common.HexToAddress(p.EthAddress()).Bytes() + hashedMessage := crypto.Keccak256(message) + ikm := make([]byte, 32) + privateKey, err := bls.KeyGen[bls.G1](ikm, nil, nil) + if err != nil { + l.Error("failed to generate private key", "error", err) + return fmt.Errorf("failed to generate private key: %w", err) + } + publicKey := privateKey.PublicKey() + signature := bls.Sign(privateKey, hashedMessage) - blsPubkeyBytes := make([]byte, 48) - _, err = rand.Read(blsPubkeyBytes) + // Verify the signature + if !bls.Verify(publicKey, hashedMessage, signature) { + l.Error("failed to verify generated BLS signature") + return fmt.Errorf("failed to verify generated BLS signature") + } + + pubkeyb, err := publicKey.MarshalBinary() if err != nil { - l.Error("failed to generate mock BLS public key", "err", err) - return fmt.Errorf("failed to generate mock BLS public key: %w", err) + l.Error("failed to marshal public key", "error", err) + return fmt.Errorf("failed to marshal public key: %w", err) } // Register a provider resp, err := providerAPI.Stake(ctx, &providerapiv1.StakeRequest{ Amount: stakeAmount.String(), - BlsPublicKeys: []string{hex.EncodeToString(blsPubkeyBytes)}, + BlsPublicKeys: []string{hex.EncodeToString(pubkeyb)}, + BlsSignatures: []string{hex.EncodeToString(signature)}, }) + if err != nil { l.Error("failed to register stake", "error", err) return fmt.Errorf("failed to register stake: %w", err) @@ -118,7 +138,7 @@ func Run(ctx context.Context, cluster orchestrator.Orchestrator, cfg any) error return fmt.Errorf("invalid stake amount returned: %s", resp.Amount) } - reqBytes, err := json.Marshal([]string{hex.EncodeToString(blsPubkeyBytes)}) + reqBytes, err := json.Marshal([]string{hex.EncodeToString(pubkeyb)}) if err != nil { l.Error("failed to create bls key upload req", "error", err) return fmt.Errorf("failed to marshal bls keys: %w", err) @@ -235,8 +255,7 @@ func RunAddDeposit(ctx context.Context, cluster orchestrator.Orchestrator, _ any // Register a provider resp, err := providerAPI.Stake(ctx, &providerapiv1.StakeRequest{ - Amount: amount.String(), - BlsPublicKeys: getStakeResp.BlsPublicKeys, + Amount: amount.String(), }) if err != nil { l.Error("failed to register stake", "error", err)