diff --git a/contracts/solidity/TEERegistry.json b/contracts/solidity/TEERegistry.json index bdd2b66f..03777e4c 100644 --- a/contracts/solidity/TEERegistry.json +++ b/contracts/solidity/TEERegistry.json @@ -40,6 +40,21 @@ "name": "AttestationInvalid", "type": "error" }, + { + "inputs": [], + "name": "HeartbeatSignatureInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "HeartbeatTimestampInFuture", + "type": "error" + }, + { + "inputs": [], + "name": "HeartbeatTimestampTooOld", + "type": "error" + }, { "inputs": [], "name": "InvalidTEEType", @@ -63,17 +78,22 @@ }, { "inputs": [], - "name": "PCRAlreadyExists", + "name": "OHTTPConfigInvalid", "type": "error" }, { "inputs": [], - "name": "PCRNotApproved", + "name": "OHTTPConfigSignatureInvalid", + "type": "error" + }, + { + "inputs": [], + "name": "PCRAlreadyExists", "type": "error" }, { "inputs": [], - "name": "PCRTypeMismatch", + "name": "PCRNotApproved", "type": "error" }, { @@ -114,6 +134,74 @@ "name": "AWSCertificateUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "HeartbeatReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "publicKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "keyConfigHash", + "type": "bytes32" + } + ], + "name": "OHTTPConfigRegistered", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -147,6 +235,12 @@ "internalType": "bytes32", "name": "pcrHash", "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "teeType", + "type": "uint8" } ], "name": "PCRRevoked", @@ -237,7 +331,7 @@ "type": "bytes32" } ], - "name": "TEEEnabled", + "name": "TEEDisabled", "type": "event" }, { @@ -250,7 +344,7 @@ "type": "bytes32" } ], - "name": "TEEDisabled", + "name": "TEEEnabled", "type": "event" }, { @@ -310,6 +404,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "KEM_ID_X25519_HKDF_SHA256", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OHTTP_CONFIG_DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "TEE_OPERATOR", @@ -337,16 +457,16 @@ "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "X25519_PUBLIC_KEY_SIZE", + "outputs": [ { - "internalType": "bytes32", - "name": "teeId", - "type": "bytes32" + "internalType": "uint256", + "name": "", + "type": "uint256" } ], - "name": "enableTEE", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -407,40 +527,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "pcrRecords", - "outputs": [ - { - "internalType": "bool", - "name": "approved", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "teeType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "approvedAt", - "type": "uint256" - }, - { - "internalType": "string", - "name": "version", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "awsRootCertificate", @@ -458,21 +544,41 @@ "inputs": [ { "internalType": "bytes32", - "name": "inputHash", + "name": "teeId", "type": "bytes32" }, { - "internalType": "bytes32", - "name": "outputHash", - "type": "bytes32" + "internalType": "uint8", + "name": "keyId", + "type": "uint8" }, { - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "ohttpPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "ohttpKeyConfig", + "type": "bytes" } ], - "name": "computeMessageHash", + "name": "computeOHTTPConfigHash", "outputs": [ { "internalType": "bytes32", @@ -551,51 +657,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "getApprovedPCRs", - "outputs": [ - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getActiveTEEs", - "outputs": [ - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "teeId", - "type": "bytes32" - } - ], - "name": "getPaymentAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -604,45 +665,20 @@ "type": "bytes32" } ], - "name": "getTEEPublicKey", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", + "name": "enableTEE", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "internalType": "bytes32", - "name": "teeId", - "type": "bytes32" + "internalType": "uint8", + "name": "teeType", + "type": "uint8" } ], - "name": "getTEE", + "name": "getActiveTEEs", "outputs": [ { "components": [ @@ -695,6 +731,295 @@ "internalType": "uint256", "name": "lastHeartbeatAt", "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" + } + ], + "internalType": "struct TEERegistry.TEEInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getApprovedPCRs", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "pcrHash", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + } + ], + "internalType": "struct TEERegistry.PCRKey[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + } + ], + "name": "getEnabledTEEs", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + } + ], + "name": "getOHTTPConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + } + ], + "name": "getTEE", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "paymentAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "endpoint", + "type": "string" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "tlsCertificate", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "pcrHash", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "registeredAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastHeartbeatAt", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" } ], "internalType": "struct TEERegistry.TEEInfo", @@ -705,6 +1030,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + } + ], + "name": "getTEEPublicKey", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "getTEETypes", @@ -777,16 +1121,39 @@ "inputs": [ { "internalType": "bytes32", - "name": "teeId", + "name": "role", "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" } ], - "name": "getTLSCertificate", + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", "outputs": [ { - "internalType": "bytes", + "internalType": "bool", "name": "", - "type": "bytes" + "type": "bool" } ], "stateMutability": "view", @@ -796,34 +1163,52 @@ "inputs": [ { "internalType": "bytes32", - "name": "role", + "name": "teeId", "type": "bytes32" }, { - "internalType": "address", - "name": "account", - "type": "address" + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" } ], - "name": "grantRole", + "name": "heartbeat", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "heartbeatMaxAge", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" + "internalType": "uint8", + "name": "teeType", + "type": "uint8" }, { - "internalType": "address", - "name": "account", - "type": "address" + "internalType": "bytes32", + "name": "pcrHash", + "type": "bytes32" } ], - "name": "hasRole", + "name": "isPCRApproved", "outputs": [ { "internalType": "bool", @@ -842,7 +1227,7 @@ "type": "bytes32" } ], - "name": "isTEEEnabled", + "name": "isTEEActive", "outputs": [ { "internalType": "bool", @@ -857,11 +1242,11 @@ "inputs": [ { "internalType": "bytes32", - "name": "pcrHash", + "name": "teeId", "type": "bytes32" } ], - "name": "isPCRApproved", + "name": "isTEEEnabled", "outputs": [ { "internalType": "bool", @@ -891,6 +1276,45 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "pcrRecords", + "outputs": [ + { + "internalType": "bool", + "name": "approved", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "approvedAt", + "type": "uint256" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -922,6 +1346,48 @@ "internalType": "uint8", "name": "teeType", "type": "uint8" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct TEERegistry.OHTTPConfigInput", + "name": "ohttp", + "type": "tuple" } ], "name": "registerTEEWithAttestation", @@ -1002,6 +1468,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxAge", + "type": "uint256" + } + ], + "name": "setHeartbeatMaxAge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1104,17 +1583,64 @@ "internalType": "uint256", "name": "lastHeartbeatAt", "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "keyId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "kemId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kdfId", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "aeadId", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" } ], "stateMutability": "view", "type": "function" } ], - "bytecode": "0x608060405234620000965762000015336200009a565b50620000213362000118565b505f8051602062002d0e8339815191525f81815260208190527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbec805490829055604051927fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff8380a4612b479081620001a78239f35b5f80fd5b6001600160a01b03165f8181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff166200011457818052816020526040822081835260205260408220600160ff1982541617905533915f8051602062002cee8339815191528180a4600190565b5090565b6001600160a01b03165f8181527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb60205260408120549091905f8051602062002d0e8339815191529060ff16620001a157808352826020526040832082845260205260408320600160ff198254161790555f8051602062002cee833981519152339380a4600190565b50509056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146120dc5750806308c84e70146120c0578063097775c6146120a457806315c9bdb414611a08578063248a9ca3146119dc5780632f2ff15d146119a057806336568abe146119595780633c1a88b01461191f578063418b207d1461174257806343ed3274146115e9578063476cb030146115ab5780634b19d4631461152457806358db61a8146114dd5780635c36901c146114a85780639138c99e1461145257806391cc00e91461140857806391d14854146113c0578063971cfbf114610c90578063a217fddf14610c76578063b19ffd1c14610c09578063b1c551ca14610bbd578063b778f86914610b5f578063b82644b714610af2578063c0abde45146108a0578063c206baa314610874578063cbdfc4e01461066d578063ccdf049314610568578063d046a7fa146104f1578063d2e03427146103f7578063d547741f146103bb578063d5ed579d14610393578063d6741a421461031e578063dd31611314610300578063e10a2ea4146102c6578063e44cc48214610243578063f33d2c00146101f95763f44a0780146101b3575f80fd5b346101f5575f3660031901126101f5576101f16040516101dd816101d6816121f8565b0382612340565b604051918291602083526020830190612182565b0390f35b5f80fd5b346101f55760203660031901126101f5576004356001600160401b0381116101f55761023561022e602092369060040161212f565b3691612502565b818151910120604051908152f35b346101f5576020806003193601126101f5576004356001600160a01b038116908190036101f5575f526009815260405f209060405190818184549182815201935f52815f20915f905b8282106102af576101f1856102a381890382612340565b60405191829182612361565b83548652948501946001938401939091019061028c565b346101f55760203660031901126101f55760ff6102e1612172565b165f526001602052602060ff600160405f200154166040519015158152f35b346101f55760203660031901126101f55761031c6004356127b3565b005b346101f55760203660031901126101f55760ff610339612172565b165f52600160205261038260405f20604051906103618261035a8184612278565b0383612340565b600260ff600183015416910154604051938493606085526060850190612182565b911515602084015260408301520390f35b346101f55760203660031901126101f55760206103b16004356126c9565b6040519015158152f35b346101f55760403660031901126101f55761031c6004356103da61215c565b90805f525f6020526103f2600160405f2001546123f2565b61248f565b346101f5575f3660031901126101f5575f80600454905b8181106104b1575061041f82612613565b9161042d6040519384612340565b80835261043c601f1991612613565b013660208401375f805b82811061045b57604051806101f18682612361565b8061047961046b6104849361264c565b919054600392831b1c6126c9565b610489575b5061262a565b610446565b6104928261264c565b9054911b1c6104aa6104a38561262a565b9487612638565b528561047e565b6104c96104bd8261264c565b90549060031b1c6126c9565b6104dc575b6104d79061262a565b61040e565b916104e96104d79161262a565b9290506104ce565b346101f5575f3660031901126101f557604051600780548083525f918252602080840193927fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892915b828210610551576101f1856102a381890382612340565b83548652948501946001938401939091019061053a565b346101f55760203660031901126101f5576004355f52600660205260405f2060018060a01b038082541690600183015416916040516105ae816101d68160028601612278565b604051916105ca836105c38160038501612278565b0384612340565b60ff604051936105e8856105e18160048701612278565b0386612340565b61064460058401549561063660068601549361062860086007890154980154986040519c8d9c8d5260208d015260408c61014091829101528c0190612182565b908a820360608c0152612182565b9088820360808a0152612182565b9460a087015281811660c087015260081c16151560e08501526101008401526101208301520390f35b346101f55760403660031901126101f557610686612172565b6001600160401b03906024358281116101f5576106a960ff91369060040161212f565b9190926106b461239b565b1692835f52600192602091848352600260405f20015461086257604051906106db82612325565b6106e6368685612502565b82528382018681526040830191428352885f5287865260405f20935180519182116107d85761071f8261071987546121c0565b8761255d565b8690601f83116001146107f75791806002959492610768945f926107ec575b50505f19600383901b1c1916908a1b1785555b5115158885019060ff801983541691151516179055565b5191015560025490600160401b8210156107d8576107af827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107d394016002556125a2565b81549060031b9060ff89831b921b19161790556040519383859485528401916125f3565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508c8061073e565b899291601f19831691875f52895f20925f5b8b82821061084c575050916107689593918560029998969410610834575b505050811b018555610751565b01515f1960f88460031b161c191690558c8080610827565b8385015186558e97909501949384019301610809565b60405163095faf7360e01b8152600490fd5b346101f55760603660031901126101f5576020610898604435602435600435612ad8565b604051908152f35b346101f5576003196080368201126101f557600435906001600160401b03908183116101f55760609083360301126101f5576024358181116101f5576108ea90369060040161212f565b9091610903604435946108fb61239b565b60040161273c565b9380151580610adc575b610aa6575b506040519260808401848110838211176107d857604052600193848152602092600384830192428452604081015f815261094d368988612502565b94606083019586528a5f5283885261097860405f2093511515849060ff801983541691151516179055565b518983015551600282015501915180519182116107d85781906109a58261099f86546121c0565b8661255d565b8590601f8311600114610a47575f92610a3c575b50505f19600383901b1c191690861b1790555b60045490600160401b8210156107d85785610a29610a13847fa3cbee87ba6def0cc8a2c5a3cf7a54f0209c6f939b07979dde08d447d6c3666d986107d3960160045561264c565b819391549060031b91821b915f19901b19161790565b90556040519383859485528401916125f3565b0151905088806109b9565b90889350601f19831691855f52875f20925f5b89828210610a905750508411610a78575b505050811b0190556109cc565b01515f1960f88460031b161c19169055888080610a6b565b8385015186558c97909501949384019301610a5a565b606435420190814211610ac8575f526003602052600260405f20015584610912565b634e487b7160e01b5f52601160045260245ffd5b50805f52600360205260ff60405f20541661090d565b346101f5576020806003193601126101f55760ff610b0e612172565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610b48576101f1856102a381890382612340565b835486529485019460019384019390910190610b31565b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600460405f200160405192838092612278565b60405163f6523b6560e01b8152600490fd5b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600360405f200160405192838092612278565b346101f55760203660031901126101f5576004355f52600360205260405f2060ff8154166101f16003600184015493610c5160028201549161035a6040518095819301612278565b6040519485941515855260208501526040840152608060608401526080830190612182565b346101f5575f3660031901126101f55760206040515f8152f35b346101f5575f3660031901126101f5576040518060029182549081835260208093019283855f528282805f20925f5b8780601f83011061123857610ddf95549184828210611225575b82821061120f575b8282106111f9575b8282106111e3575b8282106111ce575b8282106111b8575b8282106111a2575b82821061118c575b828210611176575b828210611160575b82821061114a575b828210611134575b82821061111e575b828210611108575b8282106110f2575b8282106110dc575b8282106110c6575b8282106110b0575b82821061109a575b828210611084575b82821061106e575b828210611058575b828210611042575b82821061102c575b828210611016575b828210611000575b828210610fea575b828210610fd4575b828210610fbe575b828210610fa8575b828210610f92575b5010610f84575b50905096939295960382612340565b610de883612613565b92610df66040519485612340565b808452601f199283610e0783612613565b015f5b818110610f525750505f5b828110610ed557505050604051936040850191604086525180925260609182860196905f5b818110610ebe57505050848603818601528351918287528187019082808560051b8a01019601945f985b858a10610e715788880389f35b909192939495968580828585600195030188528a5190604080610e9b84518b85528b850190612182565b938581015115158685015201519101529901950199019892919095949395610e64565b825160ff1689529783019791830191600101610e3a565b80610ee7610f489299989596996125a2565b9060ff918291549060031b1c165f528360019182885260405f209060405193610f0f85612325565b604051610f20816101d68187612278565b85528201541615158884015201546040820152610f3d8289612638565b5261047e8188612638565b9693929596610e15565b968094959897604051610f6481612325565b606081525f838201525f604082015282828a010152019794939697610e0a565b60f81c815201839089610dd0565b6001919460ff8560f01c16815201930184610dc9565b6001919460ff8560e81c16815201930184610dc1565b6001919460ff8560e01c16815201930184610db9565b6001919460ff8560d81c16815201930184610db1565b6001919460ff8560d01c16815201930184610da9565b6001919460ff8560c81c16815201930184610da1565b6001919460ff8560c01c16815201930184610d99565b6001919460ff8560b81c16815201930184610d91565b6001919460ff8560b01c16815201930184610d89565b6001919460ff8560a81c16815201930184610d81565b6001919460ff8560a01c16815201930184610d79565b6001919460ff8560981c16815201930184610d71565b6001919460ff8560901c16815201930184610d69565b6001919460ff8560881c16815201930184610d61565b6001919460ff8560801c16815201930184610d59565b6001919460ff8560781c16815201930184610d51565b6001919460ff8560701c16815201930184610d49565b6001919460ff8560681c16815201930184610d41565b6001919460ff8560601c16815201930184610d39565b6001919460ff8560581c16815201930184610d31565b6001919460ff8560501c16815201930184610d29565b6001919460ff8560481c16815201930184610d21565b6001919460ff8560401c16815201930184610d19565b6001919460ff8560381c16815201930184610d11565b6001919460ff8560301c16815201930184610d09565b6001919460ff8560281c16815201930184610d01565b6001919460ff85831c16815201930184610cf9565b6001919460ff8560181c16815201930184610cf1565b6001919460ff8560101c16815201930184610ce9565b6001919460ff8560081c16815201930184610ce1565b6001919460ff8516815201930184610cd9565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018491928491610cbf565b346101f55760403660031901126101f5576113d961215c565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f526006602052602060018060a01b03600160405f20015416604051908152f35b346101f55760203660031901126101f55760043561146e61239b565b805f52600360205260405f2060ff1981541690557f021cbdaf8e2ddc626ca016468398743e833c1d67b00106b10077757586679a205f80a2005b346101f55760203660031901126101f5576004355f526006602052602060ff600660405f20015460081c166040519015158152f35b346101f55760a03660031901126101f5576084356001600160401b0381116101f5576103b1611512602092369060040161212f565b906064356044356024356004356129fc565b346101f55760203660031901126101f55760ff61153f612172565b61154761239b565b16805f526001602052600260405f2001541561159a57805f526001602052600160405f200160ff1981541690557f30e2b43b128fac88e776d92f693d539845d727cdb1978017ddff47a2e5319a035f80a2005b604051629f97d960e31b8152600490fd5b346101f5576003196020368201126101f557600435906001600160401b0382116101f55760609082360301126101f55761089860209160040161273c565b346101f5576020806003193601126101f5576001600160401b036004358181116101f55761161b90369060040161212f565b9161162461239b565b82116107d85760059161163783546121c0565b601f811161170b575b505f92601f82116001146116a95781611673945f9161169e575b508260011b905f198460031b1c19161790553691612502565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661165a565b601f19821693815f52855f20905f5b878782106116f55750508361167396106116dc575b5050600182811b01905561022e565b8401355f19600385901b60f8161c1916905585806116cd565b60018394829394890135815501930191016116b8565b61173290845f52855f20601f8401861c810191878510611738575b601f01861c0190612547565b84611640565b9091508190611726565b346101f55760203660031901126101f5576004355f61012060405161176681612309565b82815282602082015260606040820152606080820152606060808201528260a08201528260c08201528260e0820152826101008201520152805f526006602052600760405f20015415610bab575f52600660205260405f206008604051916117cd83612309565b80546001600160a01b03908116845260018201541660208401526040516117fb816101d68160028601612278565b6040840152604051611814816101d68160038601612278565b606084015260405161182d816101d68160048601612278565b6080840152600581015460a084015260ff600682015481811660c0860152831c16151560e08401526007810154610100840152015461012082015260405180916020825260018060a01b03815116602083015260018060a01b03602082015116604083015260408101516101206118e66118b561014093846060880152610160870190612182565b6118d1606086015191601f1992838983030160808a0152612182565b906080860151908783030160a0880152612182565b9260a081015160c086015260ff60c08201511660e086015260e08101511515610100860152610100810151828601520151908301520390f35b346101f5575f3660031901126101f55760206040517fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf8152f35b346101f55760403660031901126101f55761197261215c565b336001600160a01b0382160361198e5761031c9060043561248f565b60405163334bd91960e11b8152600490fd5b346101f55760403660031901126101f55761031c6004356119bf61215c565b90805f525f6020526119d7600160405f2001546123f2565b612413565b346101f55760203660031901126101f5576004355f525f6020526020600160405f200154604051908152f35b346101f55760c03660031901126101f5576004356001600160401b0381116101f557611a3890369060040161212f565b6024356001600160401b0381116101f557611a5790369060040161212f565b90926044356001600160401b0381116101f557611a7890369060040161212f565b6064359591906001600160a01b03871687036101f5576001600160401b03608435116101f557611aad3660843560040161212f565b909360a4359660ff881688036101f557335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff16156120665760ff88165f52600160205260ff600160405f200154161561205457611b19368284612502565b6020815191012098895f526006602052600760405f20015461204257611b6797604091611b9e611b8d611b7a85519c8d95869563f7cf40d360e01b87526080600488015260848701916125f3565b84810360031901602486015287896125f3565b838103600319016044850152898b6125f3565b8281036003190160648401526121f8565b03816109005afa968715612037575f905f98611ff6575b5015611fb057611bc4876126c9565b15611f9f57611bfc611c1496611c089460209c6040519d8e91611be683612309565b3383526001600160a01b03169101523691612502565b60408b01523691612502565b60608801523691612502565b608085015260a084015260ff811660c0840152600160e084018190524261010085018190526101208501525f83815260066020908152604091829020865181546001600160a01b03199081166001600160a01b03928316178355928801519482018054909316941693909317905584015180516001600160401b0381116107d857806002840192611ca98261099f86546121c0565b602090601f8311600114611f3b575f92611f30575b50508160011b915f199060031b1c19161790555b606084015180516001600160401b0381116107d857806003840192611cfb8261099f86546121c0565b602090601f8311600114611ecc575f92611ec1575b50508160011b915f199060031b1c19161790555b60808401519384516001600160401b0381116107d857602095611d5782611d4e60048701546121c0565b6004870161255d565b8690601f8311600114611e5157918060089492610120945f92611e46575b50508160011b915f199060031b1c19161760048501555b60a081015160058501556006840160ff60c08301511681549061ff0060e08501511515871b169161ffff19161717905561010081015160078501550151910155600754825f526008845260405f2055611de48261267c565b335f5260098352611df88260405f206126a2565b60ff81165f52600a8352611e0f8260405f206126a2565b60ff60405191168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3604051908152f35b015190508980611d75565b90600485015f52875f20915f5b601f1985168110611eaa575092600894926001926101209583601f19811610611e92575b505050811b016004850155611d8c565b01515f1960f88460031b161c19169055898080611e82565b919289600181928685015181550194019201611e5e565b015190508780611d10565b5f858152602081209350601f198516905b818110611f185750908460019594939210611f00575b505050811b019055611d24565b01515f1960f88460031b161c19169055878080611ef3565b92936020600181928786015181550195019301611edd565b015190508780611cbe565b5f858152602081209350601f198516905b818110611f875750908460019594939210611f6f575b505050811b019055611cd2565b01515f1960f88460031b161c19169055878080611f62565b92936020600181928786015181550195019301611f4c565b60405162d85d7f60e71b8152600490fd5b604051630368f92d60e11b815260206004820152601f60248201527f4174746573746174696f6e20766572696669636174696f6e206661696c6564006044820152606490fd5b9750506040873d60401161202f575b8161201260409383612340565b810103126101f5576020612025886127a6565b970151968b611bb5565b3d9150612005565b6040513d5f823e3d90fd5b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201527fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf6024820152604490fd5b346101f55760203660031901126101f55761031c600435612922565b346101f5575f3660031901126101f55760206040516109008152f35b346101f55760203660031901126101f5576004359063ffffffff60e01b82168092036101f557602091637965db0b60e01b811490811561211e575b5015158152f35b6301ffc9a760e01b14905083612117565b9181601f840112156101f5578235916001600160401b0383116101f557602083818601950101116101f557565b602435906001600160a01b03821682036101f557565b6004359060ff821682036101f557565b91908251928382525f5b8481106121ac575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161218c565b90600182811c921680156121ee575b60208310146121da57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916121cf565b6005545f9291612207826121c0565b9081815260019283811690815f1461225d575060011461222657505050565b9092935060055f52602092835f2092845f945b8386106122495750505050010190565b805485870183015294019385908201612239565b91935050602093945060ff191683830152151560051b010190565b905f9291805491612288836121c0565b9182825260019384811690815f146122e657506001146122a9575b50505050565b90919394505f52602092835f2092845f945b8386106122d257505050500101905f8080806122a3565b8054858701830152940193859082016122bb565b9294505050602093945060ff191683830152151560051b0101905f8080806122a3565b61014081019081106001600160401b038211176107d857604052565b606081019081106001600160401b038211176107d857604052565b90601f801991011681019081106001600160401b038211176107d857604052565b602090816040818301928281528551809452019301915f5b828110612387575050505090565b835185529381019392810192600101612379565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156123d45750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156123d45750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f1461248a57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f1461248a5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b9291926001600160401b0382116107d8576040519161252b601f8201601f191660200184612340565b8294818452818301116101f5578281602093845f960137010152565b818110612552575050565b5f8155600101612547565b9190601f811161256c57505050565b612596925f5260205f20906020601f840160051c83019310612598575b601f0160051c0190612547565b565b9091508190612589565b906002548210156125df5760025f52600582901c7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b0381116107d85760051b60200190565b5f198114610ac85760010190565b80518210156125df5760209160051b010190565b6004548110156125df5760045f5260205f2001905f90565b6007548110156125df5760075f5260205f2001905f90565b600754600160401b8110156107d857610a1381600161269e9301600755612664565b9055565b805490600160401b8210156107d857600182018082558210156125df575f5260205f200155565b5f52600360205260405f2060ff81541615612705576002015480151590816126fa575b506126f657600190565b5f90565b90504210155f6126ec565b505f90565b903590601e19813603018212156101f557018035906001600160401b0382116101f5576020019181360383136101f557565b61276b6127a061274c838061270a565b60209491949161275e8382018261270a565b958260408894019061270a565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612340565b51902090565b519081151582036101f557565b5f8181526020600681526040908183206007810154156129115780546001600160a01b0316331415806128f5575b6128e45760068101805460ff8160081c16156128db5761ff0019169055426008918201558484528152818320546007545f1991908281019081116128c757808203612897575b505060075480156128835784939192837f784ce1077bc44f5b33e1724f6a1b9469423f16a15680c26d150787fb36349d179694600893019061286882612664565b909182549160031b1b191690556007558684525281205580a2565b634e487b7160e01b85526031600452602485fd5b6128a090612664565b90549060031b1c806128b4610a1384612664565b9055855260088352838520555f80612827565b634e487b7160e01b86526011600452602486fd5b50505050505050565b82516398b31e6f60e01b8152600490fd5b50838052838252828420338552825260ff8385205416156127e1565b825163f6523b6560e01b8152600490fd5b5f8181526006602052604090818120916007830154156129ec5782546001600160a01b0316331415806129ce575b6129be57600683019283549360ff8560081c166129b6576101007ff1107fa7ae6c5cdb1675a2191d02d30d03220d684e051e5c0632ca0458fe79749561ff0019161790556008429101556007549084835260086020528220556129b28361267c565b80a2565b505050505050565b516398b31e6f60e01b8152600490fd5b508180528160205280822033835260205260ff818320541615612950565b5163f6523b6560e01b8152600490fd5b949092915f9586526006602052604086209160ff600684015460081c1615612acf57612a31612a5894612a6f93602097612ad8565b906040519687958695633f1333cd60e11b8752606060048801526003606488019101612278565b9260248601526003198584030160448601526125f3565b03816109005afa918215612ac3578092612a8857505090565b9091506020823d8211612abb575b81612aa360209383612340565b81010312612ab85750612ab5906127a6565b90565b80fd5b3d9150612a96565b604051903d90823e3d90fd5b50505050505090565b9160405191602083019384526040830152606082015260608152608081018181106001600160401b038211176107d8576040525190209056fea2646970667358221220b43915f7eedd07d3c720e5ec23e1ec88df5e10dfaa5f60b76e9737aa7042660d64736f6c634300081400332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf", - "deployedBytecode": "0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146120dc5750806308c84e70146120c0578063097775c6146120a457806315c9bdb414611a08578063248a9ca3146119dc5780632f2ff15d146119a057806336568abe146119595780633c1a88b01461191f578063418b207d1461174257806343ed3274146115e9578063476cb030146115ab5780634b19d4631461152457806358db61a8146114dd5780635c36901c146114a85780639138c99e1461145257806391cc00e91461140857806391d14854146113c0578063971cfbf114610c90578063a217fddf14610c76578063b19ffd1c14610c09578063b1c551ca14610bbd578063b778f86914610b5f578063b82644b714610af2578063c0abde45146108a0578063c206baa314610874578063cbdfc4e01461066d578063ccdf049314610568578063d046a7fa146104f1578063d2e03427146103f7578063d547741f146103bb578063d5ed579d14610393578063d6741a421461031e578063dd31611314610300578063e10a2ea4146102c6578063e44cc48214610243578063f33d2c00146101f95763f44a0780146101b3575f80fd5b346101f5575f3660031901126101f5576101f16040516101dd816101d6816121f8565b0382612340565b604051918291602083526020830190612182565b0390f35b5f80fd5b346101f55760203660031901126101f5576004356001600160401b0381116101f55761023561022e602092369060040161212f565b3691612502565b818151910120604051908152f35b346101f5576020806003193601126101f5576004356001600160a01b038116908190036101f5575f526009815260405f209060405190818184549182815201935f52815f20915f905b8282106102af576101f1856102a381890382612340565b60405191829182612361565b83548652948501946001938401939091019061028c565b346101f55760203660031901126101f55760ff6102e1612172565b165f526001602052602060ff600160405f200154166040519015158152f35b346101f55760203660031901126101f55761031c6004356127b3565b005b346101f55760203660031901126101f55760ff610339612172565b165f52600160205261038260405f20604051906103618261035a8184612278565b0383612340565b600260ff600183015416910154604051938493606085526060850190612182565b911515602084015260408301520390f35b346101f55760203660031901126101f55760206103b16004356126c9565b6040519015158152f35b346101f55760403660031901126101f55761031c6004356103da61215c565b90805f525f6020526103f2600160405f2001546123f2565b61248f565b346101f5575f3660031901126101f5575f80600454905b8181106104b1575061041f82612613565b9161042d6040519384612340565b80835261043c601f1991612613565b013660208401375f805b82811061045b57604051806101f18682612361565b8061047961046b6104849361264c565b919054600392831b1c6126c9565b610489575b5061262a565b610446565b6104928261264c565b9054911b1c6104aa6104a38561262a565b9487612638565b528561047e565b6104c96104bd8261264c565b90549060031b1c6126c9565b6104dc575b6104d79061262a565b61040e565b916104e96104d79161262a565b9290506104ce565b346101f5575f3660031901126101f557604051600780548083525f918252602080840193927fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892915b828210610551576101f1856102a381890382612340565b83548652948501946001938401939091019061053a565b346101f55760203660031901126101f5576004355f52600660205260405f2060018060a01b038082541690600183015416916040516105ae816101d68160028601612278565b604051916105ca836105c38160038501612278565b0384612340565b60ff604051936105e8856105e18160048701612278565b0386612340565b61064460058401549561063660068601549361062860086007890154980154986040519c8d9c8d5260208d015260408c61014091829101528c0190612182565b908a820360608c0152612182565b9088820360808a0152612182565b9460a087015281811660c087015260081c16151560e08501526101008401526101208301520390f35b346101f55760403660031901126101f557610686612172565b6001600160401b03906024358281116101f5576106a960ff91369060040161212f565b9190926106b461239b565b1692835f52600192602091848352600260405f20015461086257604051906106db82612325565b6106e6368685612502565b82528382018681526040830191428352885f5287865260405f20935180519182116107d85761071f8261071987546121c0565b8761255d565b8690601f83116001146107f75791806002959492610768945f926107ec575b50505f19600383901b1c1916908a1b1785555b5115158885019060ff801983541691151516179055565b5191015560025490600160401b8210156107d8576107af827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107d394016002556125a2565b81549060031b9060ff89831b921b19161790556040519383859485528401916125f3565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508c8061073e565b899291601f19831691875f52895f20925f5b8b82821061084c575050916107689593918560029998969410610834575b505050811b018555610751565b01515f1960f88460031b161c191690558c8080610827565b8385015186558e97909501949384019301610809565b60405163095faf7360e01b8152600490fd5b346101f55760603660031901126101f5576020610898604435602435600435612ad8565b604051908152f35b346101f5576003196080368201126101f557600435906001600160401b03908183116101f55760609083360301126101f5576024358181116101f5576108ea90369060040161212f565b9091610903604435946108fb61239b565b60040161273c565b9380151580610adc575b610aa6575b506040519260808401848110838211176107d857604052600193848152602092600384830192428452604081015f815261094d368988612502565b94606083019586528a5f5283885261097860405f2093511515849060ff801983541691151516179055565b518983015551600282015501915180519182116107d85781906109a58261099f86546121c0565b8661255d565b8590601f8311600114610a47575f92610a3c575b50505f19600383901b1c191690861b1790555b60045490600160401b8210156107d85785610a29610a13847fa3cbee87ba6def0cc8a2c5a3cf7a54f0209c6f939b07979dde08d447d6c3666d986107d3960160045561264c565b819391549060031b91821b915f19901b19161790565b90556040519383859485528401916125f3565b0151905088806109b9565b90889350601f19831691855f52875f20925f5b89828210610a905750508411610a78575b505050811b0190556109cc565b01515f1960f88460031b161c19169055888080610a6b565b8385015186558c97909501949384019301610a5a565b606435420190814211610ac8575f526003602052600260405f20015584610912565b634e487b7160e01b5f52601160045260245ffd5b50805f52600360205260ff60405f20541661090d565b346101f5576020806003193601126101f55760ff610b0e612172565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610b48576101f1856102a381890382612340565b835486529485019460019384019390910190610b31565b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600460405f200160405192838092612278565b60405163f6523b6560e01b8152600490fd5b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600360405f200160405192838092612278565b346101f55760203660031901126101f5576004355f52600360205260405f2060ff8154166101f16003600184015493610c5160028201549161035a6040518095819301612278565b6040519485941515855260208501526040840152608060608401526080830190612182565b346101f5575f3660031901126101f55760206040515f8152f35b346101f5575f3660031901126101f5576040518060029182549081835260208093019283855f528282805f20925f5b8780601f83011061123857610ddf95549184828210611225575b82821061120f575b8282106111f9575b8282106111e3575b8282106111ce575b8282106111b8575b8282106111a2575b82821061118c575b828210611176575b828210611160575b82821061114a575b828210611134575b82821061111e575b828210611108575b8282106110f2575b8282106110dc575b8282106110c6575b8282106110b0575b82821061109a575b828210611084575b82821061106e575b828210611058575b828210611042575b82821061102c575b828210611016575b828210611000575b828210610fea575b828210610fd4575b828210610fbe575b828210610fa8575b828210610f92575b5010610f84575b50905096939295960382612340565b610de883612613565b92610df66040519485612340565b808452601f199283610e0783612613565b015f5b818110610f525750505f5b828110610ed557505050604051936040850191604086525180925260609182860196905f5b818110610ebe57505050848603818601528351918287528187019082808560051b8a01019601945f985b858a10610e715788880389f35b909192939495968580828585600195030188528a5190604080610e9b84518b85528b850190612182565b938581015115158685015201519101529901950199019892919095949395610e64565b825160ff1689529783019791830191600101610e3a565b80610ee7610f489299989596996125a2565b9060ff918291549060031b1c165f528360019182885260405f209060405193610f0f85612325565b604051610f20816101d68187612278565b85528201541615158884015201546040820152610f3d8289612638565b5261047e8188612638565b9693929596610e15565b968094959897604051610f6481612325565b606081525f838201525f604082015282828a010152019794939697610e0a565b60f81c815201839089610dd0565b6001919460ff8560f01c16815201930184610dc9565b6001919460ff8560e81c16815201930184610dc1565b6001919460ff8560e01c16815201930184610db9565b6001919460ff8560d81c16815201930184610db1565b6001919460ff8560d01c16815201930184610da9565b6001919460ff8560c81c16815201930184610da1565b6001919460ff8560c01c16815201930184610d99565b6001919460ff8560b81c16815201930184610d91565b6001919460ff8560b01c16815201930184610d89565b6001919460ff8560a81c16815201930184610d81565b6001919460ff8560a01c16815201930184610d79565b6001919460ff8560981c16815201930184610d71565b6001919460ff8560901c16815201930184610d69565b6001919460ff8560881c16815201930184610d61565b6001919460ff8560801c16815201930184610d59565b6001919460ff8560781c16815201930184610d51565b6001919460ff8560701c16815201930184610d49565b6001919460ff8560681c16815201930184610d41565b6001919460ff8560601c16815201930184610d39565b6001919460ff8560581c16815201930184610d31565b6001919460ff8560501c16815201930184610d29565b6001919460ff8560481c16815201930184610d21565b6001919460ff8560401c16815201930184610d19565b6001919460ff8560381c16815201930184610d11565b6001919460ff8560301c16815201930184610d09565b6001919460ff8560281c16815201930184610d01565b6001919460ff85831c16815201930184610cf9565b6001919460ff8560181c16815201930184610cf1565b6001919460ff8560101c16815201930184610ce9565b6001919460ff8560081c16815201930184610ce1565b6001919460ff8516815201930184610cd9565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018491928491610cbf565b346101f55760403660031901126101f5576113d961215c565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f526006602052602060018060a01b03600160405f20015416604051908152f35b346101f55760203660031901126101f55760043561146e61239b565b805f52600360205260405f2060ff1981541690557f021cbdaf8e2ddc626ca016468398743e833c1d67b00106b10077757586679a205f80a2005b346101f55760203660031901126101f5576004355f526006602052602060ff600660405f20015460081c166040519015158152f35b346101f55760a03660031901126101f5576084356001600160401b0381116101f5576103b1611512602092369060040161212f565b906064356044356024356004356129fc565b346101f55760203660031901126101f55760ff61153f612172565b61154761239b565b16805f526001602052600260405f2001541561159a57805f526001602052600160405f200160ff1981541690557f30e2b43b128fac88e776d92f693d539845d727cdb1978017ddff47a2e5319a035f80a2005b604051629f97d960e31b8152600490fd5b346101f5576003196020368201126101f557600435906001600160401b0382116101f55760609082360301126101f55761089860209160040161273c565b346101f5576020806003193601126101f5576001600160401b036004358181116101f55761161b90369060040161212f565b9161162461239b565b82116107d85760059161163783546121c0565b601f811161170b575b505f92601f82116001146116a95781611673945f9161169e575b508260011b905f198460031b1c19161790553691612502565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661165a565b601f19821693815f52855f20905f5b878782106116f55750508361167396106116dc575b5050600182811b01905561022e565b8401355f19600385901b60f8161c1916905585806116cd565b60018394829394890135815501930191016116b8565b61173290845f52855f20601f8401861c810191878510611738575b601f01861c0190612547565b84611640565b9091508190611726565b346101f55760203660031901126101f5576004355f61012060405161176681612309565b82815282602082015260606040820152606080820152606060808201528260a08201528260c08201528260e0820152826101008201520152805f526006602052600760405f20015415610bab575f52600660205260405f206008604051916117cd83612309565b80546001600160a01b03908116845260018201541660208401526040516117fb816101d68160028601612278565b6040840152604051611814816101d68160038601612278565b606084015260405161182d816101d68160048601612278565b6080840152600581015460a084015260ff600682015481811660c0860152831c16151560e08401526007810154610100840152015461012082015260405180916020825260018060a01b03815116602083015260018060a01b03602082015116604083015260408101516101206118e66118b561014093846060880152610160870190612182565b6118d1606086015191601f1992838983030160808a0152612182565b906080860151908783030160a0880152612182565b9260a081015160c086015260ff60c08201511660e086015260e08101511515610100860152610100810151828601520151908301520390f35b346101f5575f3660031901126101f55760206040517fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf8152f35b346101f55760403660031901126101f55761197261215c565b336001600160a01b0382160361198e5761031c9060043561248f565b60405163334bd91960e11b8152600490fd5b346101f55760403660031901126101f55761031c6004356119bf61215c565b90805f525f6020526119d7600160405f2001546123f2565b612413565b346101f55760203660031901126101f5576004355f525f6020526020600160405f200154604051908152f35b346101f55760c03660031901126101f5576004356001600160401b0381116101f557611a3890369060040161212f565b6024356001600160401b0381116101f557611a5790369060040161212f565b90926044356001600160401b0381116101f557611a7890369060040161212f565b6064359591906001600160a01b03871687036101f5576001600160401b03608435116101f557611aad3660843560040161212f565b909360a4359660ff881688036101f557335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff16156120665760ff88165f52600160205260ff600160405f200154161561205457611b19368284612502565b6020815191012098895f526006602052600760405f20015461204257611b6797604091611b9e611b8d611b7a85519c8d95869563f7cf40d360e01b87526080600488015260848701916125f3565b84810360031901602486015287896125f3565b838103600319016044850152898b6125f3565b8281036003190160648401526121f8565b03816109005afa968715612037575f905f98611ff6575b5015611fb057611bc4876126c9565b15611f9f57611bfc611c1496611c089460209c6040519d8e91611be683612309565b3383526001600160a01b03169101523691612502565b60408b01523691612502565b60608801523691612502565b608085015260a084015260ff811660c0840152600160e084018190524261010085018190526101208501525f83815260066020908152604091829020865181546001600160a01b03199081166001600160a01b03928316178355928801519482018054909316941693909317905584015180516001600160401b0381116107d857806002840192611ca98261099f86546121c0565b602090601f8311600114611f3b575f92611f30575b50508160011b915f199060031b1c19161790555b606084015180516001600160401b0381116107d857806003840192611cfb8261099f86546121c0565b602090601f8311600114611ecc575f92611ec1575b50508160011b915f199060031b1c19161790555b60808401519384516001600160401b0381116107d857602095611d5782611d4e60048701546121c0565b6004870161255d565b8690601f8311600114611e5157918060089492610120945f92611e46575b50508160011b915f199060031b1c19161760048501555b60a081015160058501556006840160ff60c08301511681549061ff0060e08501511515871b169161ffff19161717905561010081015160078501550151910155600754825f526008845260405f2055611de48261267c565b335f5260098352611df88260405f206126a2565b60ff81165f52600a8352611e0f8260405f206126a2565b60ff60405191168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3604051908152f35b015190508980611d75565b90600485015f52875f20915f5b601f1985168110611eaa575092600894926001926101209583601f19811610611e92575b505050811b016004850155611d8c565b01515f1960f88460031b161c19169055898080611e82565b919289600181928685015181550194019201611e5e565b015190508780611d10565b5f858152602081209350601f198516905b818110611f185750908460019594939210611f00575b505050811b019055611d24565b01515f1960f88460031b161c19169055878080611ef3565b92936020600181928786015181550195019301611edd565b015190508780611cbe565b5f858152602081209350601f198516905b818110611f875750908460019594939210611f6f575b505050811b019055611cd2565b01515f1960f88460031b161c19169055878080611f62565b92936020600181928786015181550195019301611f4c565b60405162d85d7f60e71b8152600490fd5b604051630368f92d60e11b815260206004820152601f60248201527f4174746573746174696f6e20766572696669636174696f6e206661696c6564006044820152606490fd5b9750506040873d60401161202f575b8161201260409383612340565b810103126101f5576020612025886127a6565b970151968b611bb5565b3d9150612005565b6040513d5f823e3d90fd5b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201527fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf6024820152604490fd5b346101f55760203660031901126101f55761031c600435612922565b346101f5575f3660031901126101f55760206040516109008152f35b346101f55760203660031901126101f5576004359063ffffffff60e01b82168092036101f557602091637965db0b60e01b811490811561211e575b5015158152f35b6301ffc9a760e01b14905083612117565b9181601f840112156101f5578235916001600160401b0383116101f557602083818601950101116101f557565b602435906001600160a01b03821682036101f557565b6004359060ff821682036101f557565b91908251928382525f5b8481106121ac575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161218c565b90600182811c921680156121ee575b60208310146121da57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916121cf565b6005545f9291612207826121c0565b9081815260019283811690815f1461225d575060011461222657505050565b9092935060055f52602092835f2092845f945b8386106122495750505050010190565b805485870183015294019385908201612239565b91935050602093945060ff191683830152151560051b010190565b905f9291805491612288836121c0565b9182825260019384811690815f146122e657506001146122a9575b50505050565b90919394505f52602092835f2092845f945b8386106122d257505050500101905f8080806122a3565b8054858701830152940193859082016122bb565b9294505050602093945060ff191683830152151560051b0101905f8080806122a3565b61014081019081106001600160401b038211176107d857604052565b606081019081106001600160401b038211176107d857604052565b90601f801991011681019081106001600160401b038211176107d857604052565b602090816040818301928281528551809452019301915f5b828110612387575050505090565b835185529381019392810192600101612379565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156123d45750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156123d45750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f1461248a57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f1461248a5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b9291926001600160401b0382116107d8576040519161252b601f8201601f191660200184612340565b8294818452818301116101f5578281602093845f960137010152565b818110612552575050565b5f8155600101612547565b9190601f811161256c57505050565b612596925f5260205f20906020601f840160051c83019310612598575b601f0160051c0190612547565b565b9091508190612589565b906002548210156125df5760025f52600582901c7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b0381116107d85760051b60200190565b5f198114610ac85760010190565b80518210156125df5760209160051b010190565b6004548110156125df5760045f5260205f2001905f90565b6007548110156125df5760075f5260205f2001905f90565b600754600160401b8110156107d857610a1381600161269e9301600755612664565b9055565b805490600160401b8210156107d857600182018082558210156125df575f5260205f200155565b5f52600360205260405f2060ff81541615612705576002015480151590816126fa575b506126f657600190565b5f90565b90504210155f6126ec565b505f90565b903590601e19813603018212156101f557018035906001600160401b0382116101f5576020019181360383136101f557565b61276b6127a061274c838061270a565b60209491949161275e8382018261270a565b958260408894019061270a565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612340565b51902090565b519081151582036101f557565b5f8181526020600681526040908183206007810154156129115780546001600160a01b0316331415806128f5575b6128e45760068101805460ff8160081c16156128db5761ff0019169055426008918201558484528152818320546007545f1991908281019081116128c757808203612897575b505060075480156128835784939192837f784ce1077bc44f5b33e1724f6a1b9469423f16a15680c26d150787fb36349d179694600893019061286882612664565b909182549160031b1b191690556007558684525281205580a2565b634e487b7160e01b85526031600452602485fd5b6128a090612664565b90549060031b1c806128b4610a1384612664565b9055855260088352838520555f80612827565b634e487b7160e01b86526011600452602486fd5b50505050505050565b82516398b31e6f60e01b8152600490fd5b50838052838252828420338552825260ff8385205416156127e1565b825163f6523b6560e01b8152600490fd5b5f8181526006602052604090818120916007830154156129ec5782546001600160a01b0316331415806129ce575b6129be57600683019283549360ff8560081c166129b6576101007ff1107fa7ae6c5cdb1675a2191d02d30d03220d684e051e5c0632ca0458fe79749561ff0019161790556008429101556007549084835260086020528220556129b28361267c565b80a2565b505050505050565b516398b31e6f60e01b8152600490fd5b508180528160205280822033835260205260ff818320541615612950565b5163f6523b6560e01b8152600490fd5b949092915f9586526006602052604086209160ff600684015460081c1615612acf57612a31612a5894612a6f93602097612ad8565b906040519687958695633f1333cd60e11b8752606060048801526003606488019101612278565b9260248601526003198584030160448601526125f3565b03816109005afa918215612ac3578092612a8857505090565b9091506020823d8211612abb575b81612aa360209383612340565b81010312612ab85750612ab5906127a6565b90565b80fd5b3d9150612a96565b604051903d90823e3d90fd5b50505050505090565b9160405191602083019384526040830152606082015260608152608081018181106001600160401b038211176107d8576040525190209056fea2646970667358221220b43915f7eedd07d3c720e5ec23e1ec88df5e10dfaa5f60b76e9737aa7042660d64736f6c63430008140033", + "bytecode": "0x6080604052346200009c576107086002556200001b33620000a0565b5062000027336200011e565b505f8051602062003c5a8339815191525f81815260208190527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbec805490829055604051927fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff8380a4613a8d9081620001ad8239f35b5f80fd5b6001600160a01b03165f8181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff166200011a57818052816020526040822081835260205260408220600160ff1982541617905533915f8051602062003c3a8339815191528180a4600190565b5090565b6001600160a01b03165f8181527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb60205260408120549091905f8051602062003c5a8339815191529060ff16620001a757808352826020526040832082845260205260408320600160ff198254161790555f8051602062003c3a833981519152339380a4600190565b50509056fe60e0806040526004361015610012575f80fd5b5f3560e01c9081625ae7cf14612c8a5750806301ffc9a714612c3457806308c84e7014612c185780631788371714612b0e5780632068b3db14612a95578063248a9ca314612a695780632cbe755214612a325780632f2ff15d146129f657806336568abe146129af5780633c1a88b014612988578063418b207d1461292c57806342830ad81461276e57806343ed327414612617578063476cb030146125d95780634b6014da146124255780634c4c8e8b146121985780635766275a14611fe35780635c1090b214611fc257806363b86b8914611f5557806387267aca14611f385780638ecaf98c1461049d57806391d1485414611ef0578063927d3c9514611ebb578063971cfbf1146117bd5780639831ced51461177a578063987f940614610a1c578063a041ff51146108d1578063a217fddf146108b7578063a3bf31f11461088e578063abbf8f6a1461081d578063b82644b7146107b0578063cbdfc4e0146105d7578063ccdf0493146104b7578063cf79c8e11461049d578063d101bec414610409578063d547741f146103cb578063d6741a4214610375578063e10a2ea414610336578063e44cc482146102b3578063f33d2c0014610269578063f44a0780146102275763fec0de46146101e9575f80fd5b34610223575f3660031901126102235760206040517fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8152f35b5f80fd5b34610223575f366003190112610223576102656040516102518161024a8161309b565b0382612df1565b604051918291602083526020830190612eb3565b0390f35b34610223576020366003190112610223576004356001600160401b038111610223576102a561029e6020923690600401613034565b3691613286565b818151910120604051908152f35b3461022357602080600319360112610223576004356001600160a01b03811690819003610223575f52600b815260405f209060405190818184549182815201935f52815f20915f905b82821061031f576102658561031381890382612df1565b60405191829182613061565b8354865294850194600193840193909101906102fc565b3461022357602036600319011261022357602061036b610354612d46565b60ff165f526003602052600160405f200154151590565b6040519015158152f35b346102235760203660031901126102235760ff610390612d46565b165f5260036020526103c160405f2060016103aa82612e12565b910154604051928392604084526040840190612eb3565b9060208301520390f35b34610223576040366003190112610223576104076004356103ea612ef1565b90805f525f602052610402600160405f200154613927565b6139c4565b005b346102235760e036600319011261022357610422612d56565b61ffff604435818116810361022357606435928284168403610223576084359283168303610223576001600160401b039260a4358481116102235761046b903690600401613034565b9160c4359586116102235760209661048a610495973690600401613034565b969095600435613820565b604051908152f35b34610223575f366003190112610223576020604051818152f35b34610223576020366003190112610223576004355f908152600760205260409020805460018201546001600160a01b03908116929116906104fa60028201612e12565b60405190918161050d8160038401613117565b036105189083612df1565b604051918261052a8160048501613117565b036105359084612df1565b600582015492600683015490600784015492600885015494600901610559906131a8565b96604051998a998a5260208a01526101608060408b0152890161057b91612eb3565b88810360608a015261058c91612eb3565b878103608089015261059d91612eb3565b9360a087015260ff811660c087015260081c60ff16151560e086015261010085015261012084015282810361014084015261026591612f07565b34610223576040366003190112610223576105f0612d46565b6001600160401b036024358181116102235761061260ff913690600401613034565b91909361061d6138d0565b1692835f52602090600382526001938460405f20015461079e576040519061064482612d9e565b61064f368685613286565b8252838201428152875f526003855260405f20925191825190811161071d5787926106848261067e8754612d66565b8761324e565b8690601f831160011461073c575f919083610731575b50505f19600383901b1c191690831b1783555b5191015560045490600160401b82101561071d576106f4827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc9089661071894016004556132cb565b81549060031b9060ff89831b921b191617905560405193838594855284019161331c565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508a8061069a565b90601f19831691865f52885f20925f5b8a8282106107885750509084879594939210610770575b505050811b0183556106ad565b01515f1960f88460031b161c191690558a8080610763565b8385015186558d9890950194938401930161074c565b60405163095faf7360e01b8152600490fd5b34610223576020806003193601126102235760ff6107cc612d46565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610806576102658561031381890382612df1565b8354865294850194600193840193909101906107ef565b34610223576020366003190112610223576004356108396134d9565b50805f526007602052600760405f2001541561087c575f526007602052610265610868600960405f20016131a8565b604051918291602083526020830190612f07565b60405163f6523b6560e01b8152600490fd5b34610223576020366003190112610223576004355f526007602052602061036b60405f206137ba565b34610223575f3660031901126102235760206040515f8152f35b34610223576040366003190112610223576004356108ed612d56565b906108f66138d0565b60ff821691825f52600560209080825260405f20845f52825260ff60405f20541615610a0b57845f5280825260405f20845f52825260405f2060ff198154169055845f526008825260405f20928354805b6109735786867fd5a6741f8bbaee4cc3ea05953ea04c6a1863ce4c66a52da653dd0a315f84c2fb5f80a3005b5f1981018181116109b657610988908661339e565b90549060031b1c805f526007855260405f208785820154146109ca575b505080156109b6575f190180610947565b634e487b7160e01b5f52601160045260245ffd5b600601805461ff00191690556109e083826135b8565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a287806109a5565b60405162d85d7f60e71b8152600490fd5b346102235760e0366003190112610223576004356001600160401b03811161022357610a4c903690600401613034565b906024356001600160401b03811161022357610a6c903690600401613034565b90916044356001600160401b03811161022357610a8d903690600401613034565b60643594919290916001600160a01b0386168603610223576084356001600160401b03811161022357610ac4903690600401613034565b95909260ff60a4351660a43503610223576001600160401b0360c435116102235760e060c4353603600319011261022357335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff161561174f57610b4860a43560ff165f526003602052600160405f200154151590565b1561173d57610b58368385613286565b6020815191012098895f526007602052600760405f20015461172b57610ba691604091610bdd610bcc610bb98551968795869563f7cf40d360e01b875260806004880152608487019161331c565b848103600319016024860152888a61331c565b8381036003190160448501528a8c61331c565b82810360031901606484015261309b565b03816109005afa908115611658575f915f60c0526116e9575b501561169257610c0a60a43560c0516134b5565b610c126134d9565b50610c27608460c4350160c4356004016133b3565b9050158015611675575b61166357602061ffff610c48602460c435016134a6565b1603611663576020610c64608460c4350160c4356004016133b3565b90500361166357610cdb610c7c60c435600401613498565b89610c8b602460c435016134a6565b610c99604460c435016134a6565b90610ca8606460c435016134a6565b610cbc608460c4350160c4356004016133b3565b939092610cd360a460c4350160c4356004016133b3565b979096613820565b6020610cf060c480350160c4356004016133b3565b604051633f1333cd60e11b8152606060048201529384928392610d339291610d1c606486018a8c61331c565b92602486015260031985840301604486015261331c565b03816109005afa908115611658575f9161161e575b501561160c57610e6d610e7993610e8597610e39610d6a60c435600401613498565b610d78602460c435016134a6565b60a05260ff610e2b610d8e604460c435016134a6565b61ffff610e1e610da2606460c435016134a6565b82610db7608460c4350160c4356004016133b3565b919092610dce60a460c4350160c4356004016133b3565b969097610de460c480350160c4356004016133b3565b9b909c610df660405180608052612db9565b16608051528160a0511660206080510152166040608051015216606060805101523691613286565b6080805101523691613286565b60a060805101523691613286565b60c060805101524260e0608051015260405199610e558b612dd5565b338b526001600160a01b031660208b01523691613286565b60408801523691613286565b60608501523691613286565b608082015260c05160a082015260ff60a4351660c0820152600160e08201524261010082015242610120820152608051610140820152815f52600760205260405f209060018060a01b0381511660018060a01b03199081845416178355600183019060018060a01b036020840151169082541617905560408101519283516001600160401b03811161071d57610f2b81610f226002870154612d66565b6002870161324e565b6020601f82116001146115a15781929394955f92611596575b50508160011b915f199060031b1c19161760028401555b60608201519283516001600160401b03811161071d57610f8b81610f826003850154612d66565b6003850161324e565b6020601f821160011461152b5781929394955f92611520575b50508160011b915f199060031b1c19161760038201555b60808301519283516001600160401b03811161071d57610feb81610fe26004860154612d66565b6004860161324e565b6020601f82116001146114b55781929394955f926114aa575b50508160011b915f199060031b1c19161760048301555b60a081015160058301556006820160ff60c08301511681549061ff0060e0850151151560081b169161ffff19161717905561010081015160078301556101208101516008830155610140600983019101519060ff82511681549062ffff00602085015160081b1664ffff000000604086015160181b169166ffff0000000000606087015160281b169366ffffffffffffff191617171717905560808101519283516001600160401b03811161071d576110e4816110db600a870154612d66565b600a870161324e565b6020601f821160011461143f5781929394955f92611434575b50508160011b915f199060031b1c191617600a8401555b60a08201519283516001600160401b03811161071d576111448161113b600b850154612d66565b600b850161324e565b6020601f82116001146113c95781929394955f926113be575b50508160011b915f199060031b1c191617600b8201555b60c08301519283516001600160401b03811161071d576020946111a78261119e600c870154612d66565b600c870161324e565b8590601f831160011461134f579180600d949260e0945f92611344575b50508160011b915f199060031b1c191617600c8501555b015191015560ff60a435165f526008825260405f20546009835260405f20825f52835260405f205560ff60a435165f526008825261121c8160405f2061345c565b60ff60a435165f52600a82526112358160405f2061345c565b335f52600b82526112498160405f2061345c565b60405160ff60a435168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3807f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a60c06112ac60c435600401613498565b6112ba602460c435016134a6565b6112c8604460c435016134a6565b61ffff6112d9606460c435016134a6565b816112f161029e608460c4350160c4356004016133b3565b8b8151910120938161131061029e60a460c4350160c4356004016133b3565b8d81519101209660ff60405199168952168c880152166040860152166060840152608083015260a0820152a2604051908152f35b0151905088806111c4565b90600c85015f52865f20915f5b601f19851681106113a7575092600d949260019260e09583601f1981161061138f575b505050811b01600c8501556111db565b01515f1960f88460031b161c1916905588808061137f565b91928860018192868501518155019401920161135c565b01519050858061115d565b600b83015f5260205f20905f5b601f198416811061141c575060019394959683601f19811610611404575b505050811b01600b820155611174565b01515f1960f88460031b161c191690558580806113f4565b9091602060018192858b0151815501930191016113d6565b0151905085806110fd565b600a85015f5260205f20905f5b601f1984168110611492575060019394959683601f1981161061147a575b505050811b01600a840155611114565b01515f1960f88460031b161c1916905585808061146a565b9091602060018192858b01518155019301910161144c565b015190508580611004565b600484015f5260205f20905f5b601f1984168110611508575060019394959683601f198116106114f0575b505050811b01600483015561101b565b01515f1960f88460031b161c191690558580806114e0565b9091602060018192858b0151815501930191016114c2565b015190508580610fa4565b600383015f5260205f20905f5b601f198416811061157e575060019394959683601f19811610611566575b505050811b016003820155610fbb565b01515f1960f88460031b161c19169055858080611556565b9091602060018192858b015181550193019101611538565b015190508580610f44565b600285015f5260205f20905f5b601f19841681106115f4575060019394959683601f198116106115dc575b505050811b016002840155610f5b565b01515f1960f88460031b161c191690558580806115cc565b9091602060018192858b0151815501930191016115ae565b604051632d557a2f60e11b8152600490fd5b90506020813d602011611650575b8161163960209383612df1565b810103126102235761164a9061344f565b89610d48565b3d915061162c565b6040513d5f823e3d90fd5b6040516332f12a8d60e21b8152600490fd5b5061168a60a460c4350160c4356004016133b3565b905015610c31565b604051630368f92d60e11b815260206004820152602860248201527f4174746573746174696f6e20646f63756d656e7420766572696669636174696f6044820152671b8819985a5b195960c21b6064820152608490fd5b90506040813d604011611723575b8161170460409383612df1565b810103126102235760206117178261344f565b91015160c05289610bf6565b3d91506116f7565b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201525f80516020613a388339815191526024820152604490fd5b346102235760403660031901126102235760ff611795612d46565b165f52600560205260405f206024355f52602052602060ff60405f2054166040519015158152f35b34610223575f36600319011261022357604051806004549182815260208091018060045f528383805f20925f5b8880601f830110611d335761190895549184828210611d20575b828210611d0a575b828210611cf4575b828210611cde575b828210611cc9575b828210611cb3575b828210611c9d575b828210611c87575b828210611c71575b828210611c5b575b828210611c45575b828210611c2f575b828210611c19575b828210611c03575b828210611bed575b828210611bd7575b828210611bc1575b828210611bab575b828210611b95575b828210611b7f575b828210611b69575b828210611b53575b828210611b3d575b828210611b27575b828210611b11575b828210611afb575b828210611ae5575b828210611acf575b828210611ab9575b828210611aa3575b828210611a8d575b5010611a7f575b5090509592950384612df1565b6119118161333c565b9161191f6040519384612df1565b818352601f1991826119308261333c565b015f5b818110611a565750505f5b8181106119ef57505060405193604085019060408652518091526060850195905f5b8181106119d8575050508385038185015282519081865280860181808460051b8901019501935f975b8489106119965787870388f35b90919293949584808285856001950301875289519082806119c08451604085526040850190612eb3565b93015191015298019401980197919094939294611989565b825160ff1688529683019691830191600101611960565b8060ff611a01611a4e939996996132cb565b919054600392831b1c165f52845260405f20600160405191611a2283612d9e565b611a2b81612e12565b8352015485820152611a3d8288613361565b52611a488187613361565b50613353565b95929561193e565b8390604098959851611a6781612d9e565b606081525f8382015282828901015201969396611933565b60f81c8152018490886118fb565b6001919460ff8560f01c168152019301846118f4565b6001919460ff8560e81c168152019301846118ec565b6001919460ff8560e01c168152019301846118e4565b6001919460ff8560d81c168152019301846118dc565b6001919460ff8560d01c168152019301846118d4565b6001919460ff8560c81c168152019301846118cc565b6001919460ff8560c01c168152019301846118c4565b6001919460ff8560b81c168152019301846118bc565b6001919460ff8560b01c168152019301846118b4565b6001919460ff8560a81c168152019301846118ac565b6001919460ff8560a01c168152019301846118a4565b6001919460ff8560981c1681520193018461189c565b6001919460ff8560901c16815201930184611894565b6001919460ff8560881c1681520193018461188c565b6001919460ff8560801c16815201930184611884565b6001919460ff8560781c1681520193018461187c565b6001919460ff8560701c16815201930184611874565b6001919460ff8560681c1681520193018461186c565b6001919460ff8560601c16815201930184611864565b6001919460ff8560581c1681520193018461185c565b6001919460ff8560501c16815201930184611854565b6001919460ff8560481c1681520193018461184c565b6001919460ff8560401c16815201930184611844565b6001919460ff8560381c1681520193018461183c565b6001919460ff8560301c16815201930184611834565b6001919460ff8560281c1681520193018461182c565b6001919460ff85831c16815201930184611824565b6001919460ff8560181c1681520193018461181c565b6001919460ff8560101c16815201930184611814565b6001919460ff8560081c1681520193018461180c565b6001919460ff8516815201930184611804565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e0820152019301910185919285916117ea565b34610223576020366003190112610223576004355f526007602052602060ff600660405f20015460081c166040519015158152f35b3461022357604036600319011261022357611f09612ef1565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610223575f366003190112610223576020600254604051908152f35b34610223576020806003193601126102235760ff611f71612d46565b165f526008815260405f209060405190818184549182815201935f52815f20915f905b828210611fab576102658561031381890382612df1565b835486529485019460019384019390910190611f94565b3461022357602036600319011261022357611fdb6138d0565b600435600255005b34610223575f366003190112610223575f80600654905b818110612153575061200b8261333c565b916120196040519384612df1565b808352612028601f199161333c565b015f5b8181106121305750505f805b828210612087576040805160208082528651818301819052818801938301915f5b8281106120655784840385f35b85518051855282015160ff168483015294810194604090930192600101612058565b61209082613375565b509160ff6120ca81600180960154166120a884613375565b50549060ff165f52600560205260405f20905f5260205260ff60405f20541690565b6120e1575b506120db919250613353565b90612037565b6120db92612128916120f284613375565b50906120fd83613353565b966040519261210b84612d9e565b8054845201541660208201526121218289613361565b5286613361565b5082916120cf565b60209060405161213f81612d9e565b5f8152825f8183015282870101520161202b565b61217060ff600161216384613375565b500154166120a883613375565b612183575b61217e90613353565b611ffa565b9161219061217e91613353565b929050612175565b346102235760031960603682011261022357600435906001600160401b039081831161022357606090833603011261022357602435818111610223576121e2903690600401613034565b6044359360ff851694858103610223576121fe906103546138d0565b1561173d5761220f906004016133e5565b92845f526020906005825260405f20855f5282526001908160405f200154159060405190608082018281108282111761071d576040528382526002858301928a845260408101428152612263368a8c613286565b94606083019586528c5f526005895260405f208c5f52895260405f20925115159060ff61ff008554925160081b1692169061ffff1916171782555186820155019151805191821161071d5781906122c4826122be8654612d66565b8661324e565b8690601f83116001146123c6575f926123bb575b50505f19600383901b1c191690841b1790555b61232c575b506123277f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe9360405193838594855284019161331c565b0390a3005b926040519061233a82612d9e565b858252828201878152600654600160401b81101561071d5780876123619201600655613375565b9390936123a8577f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe966123279460ff925181550191511660ff1982541617905593506122f0565b634e487b7160e01b5f525f60045260245ffd5b015190508a806122d8565b90869350601f19831691855f52885f20925f5b8a82821061240f57505084116123f7575b505050811b0190556122eb565b01515f1960f88460031b161c191690558a80806123ea565b8385015186558a979095019493840193016123d9565b34610223576020806003193601126102235760ff612441612d46565b165f9081526008825260408120805490825b82811061258e57506124648361333c565b926124726040519485612df1565b808452612481601f199161333c565b01845f5b828110612578575050505f905f5b8381106124f7576040805187815286518189018190525f92600582901b8301810191898b01918b9085015b8287106124cb5785850386f35b9091929382806124e7600193603f198a82030186528851612f84565b96019201960195929190926124be565b8061250561252d928461339e565b9054600391821b1c5f5260079081895261252160405f206137ba565b612532575b5050613353565b612493565b61253c838661339e565b9054911b1c5f52875261257060405f209461255f61255982613353565b96613708565b612569828a613361565b5287613361565b508780612526565b6125806136ad565b828288010152018590612485565b612598818361339e565b90549060031b1c5f52600785526125b160405f206137ba565b6125c4575b6125bf90613353565b612453565b926125d16125bf91613353565b9390506125b6565b346102235760031960203682011261022357600435906001600160401b038211610223576060908236030112610223576104956020916004016133e5565b3461022357602080600319360112610223576001600160401b0360043581811161022357612649903690600401613034565b916126526138d0565b821161071d576001916126658354612d66565b601f8111612735575b505f92601f82116001146126d657816126a0945f916126cb575b505f19600384901b1c191682821b1790553691613286565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b905083013586612688565b601f19821693815f52855f20905f5b87878210612721575050836126a09610612708575b50508082811b01905561029e565b8401355f19600385901b60f8161c1916905585806126fa565b8683013584559284019291820191016126e5565b61275e90845f52855f20601f840160051c810191878510612764575b601f0160051c0190613238565b8461266e565b9091508190612751565b34610223576060366003190112610223576004356024356001600160401b03604435818111610223576127a5903690600401613034565b9290845f526020926007845260405f209460078601541561087c5760ff600687015460081c161561291a57428411612908576127e18442613391565b600254106128f65760405192858401908882528560408601526040855260608501938585109085111761071d578461285c88948694856040528351902090633f1333cd60e11b8652606060648501526128418c600360c487019101613117565b9160848501526063198483030160a4850152605f199461331c565b0301816109005afa9182156116585784905f936128bf575b505050156128ad577f3cf0bf7708060bf073b6ddb921eef0485b3eb9da828e0ed751fa6582ef337f5792600842910155604051908152a2005b6040516345576abb60e01b8152600490fd5b90809293503d83116128ef575b6128d68185612df1565b81010312610223576128e79061344f565b858381612874565b503d6128cc565b604051631cfc538360e11b8152600490fd5b604051630315ef0b60e21b8152600490fd5b60405163028b8e4960e61b8152600490fd5b34610223576020366003190112610223576004356129486136ad565b50805f526007602052600760405f2001541561087c575f52600760205261026561297460405f20613708565b604051918291602083526020830190612f84565b34610223575f3660031901126102235760206040515f80516020613a388339815191528152f35b34610223576040366003190112610223576129c8612ef1565b336001600160a01b038216036129e457610407906004356139c4565b60405163334bd91960e11b8152600490fd5b3461022357604036600319011261022357610407600435612a15612ef1565b90805f525f602052612a2d600160405f200154613927565b613948565b34610223576020366003190112610223576004355f52600760205261026561024a610251600360405f200160405192838092613117565b34610223576020366003190112610223576004355f525f6020526020600160405f200154604051908152f35b346102235760403660031901126102235760ff612ab0612d46565b165f52600560205260405f206024355f5260205260ff60405f20610265815491612ae1600260018301549201612e12565b906040519484818796161515865260081c1660208501526040840152608060608401526080830190612eb3565b34610223576020806003193601126102235760043590815f5260078152600760405f2001541561087c575f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149081612bec575b159081612be3575b50612bd157600790825f5252600660405f200180549060ff8260081c161561291a5761ff001982169055612bab9060ff16826135b8565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a2005b6040516398b31e6f60e01b8152600490fd5b90501583612b74565b90505f80516020613a388339815191525f525f825260405f20335f52825260ff60405f20541690612b6c565b34610223575f3660031901126102235760206040516109008152f35b346102235760203660031901126102235760043563ffffffff60e01b811680910361022357602090637965db0b60e01b8114908115612c79575b506040519015158152f35b6301ffc9a760e01b14905082612c6e565b34610223576020806003193601126102235760043591825f5260078252600760405f20015415612d3757505f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149182612d09575b50159081612d00575b50612bd15761040790613518565b90501582612cf2565b9091505f80516020613a388339815191525f525f815260405f2090335f525260ff60405f2054169083612ce9565b63f6523b6560e01b8152600490fd5b6004359060ff8216820361022357565b6024359060ff8216820361022357565b90600182811c92168015612d94575b6020831014612d8057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612d75565b604081019081106001600160401b0382111761071d57604052565b61010081019081106001600160401b0382111761071d57604052565b61016081019081106001600160401b0382111761071d57604052565b90601f801991011681019081106001600160401b0382111761071d57604052565b9060405191825f825492612e2584612d66565b9081845260019485811690815f14612e905750600114612e50575b5050612e4e92500383612df1565b565b909391505f52602090815f20935f915b818310612e78575050612e4e93508201015f80612e40565b85548884018501529485019487945091830191612e60565b915050612e4e94506020925060ff191682840152151560051b8201015f80612e40565b91908251928382525f5b848110612edd575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ebd565b602435906001600160a01b038216820361022357565b9060ff825116815261ffff806020840151166020830152806040840151166040830152606083015116606082015260e080612f7b612f69612f576080870151610100806080890152870190612eb3565b60a087015186820360a0880152612eb3565b60c086015185820360c0870152612eb3565b93015191015290565b906130319160018060a01b038082511683526020820151166020830152612fe4612fd2612fc06040840151610160806040880152860190612eb3565b60608401518582036060870152612eb3565b60808301518482036080860152612eb3565b9160a082015160a082015260ff60c08301511660c082015260e0820151151560e0820152610100808301519082015261012080830151908201526101408092015191818403910152612f07565b90565b9181601f84011215610223578235916001600160401b038311610223576020838186019501011161022357565b602090816040818301928281528551809452019301915f5b828110613087575050505090565b835185529381019392810192600101613079565b600180545f93926130ab82612d66565b808252918381169081156130fc57506001146130c657505050565b90929350815f52602092835f2092845f945b8386106130e85750505050010190565b8054858701830152940193859082016130d8565b91935050602093945060ff191683830152151560051b010190565b905f929180549161312783612d66565b9182825260019384811690815f146131855750600114613148575b50505050565b90919394505f52602092835f2092845f945b83861061317157505050500101905f808080613142565b80548587018301529401938590820161315a565b9294505050602093945060ff191683830152151560051b0101905f808080613142565b906040516131b581612db9565b60e060048294805460ff8116855261ffff90818160081c166020870152818160181c16604087015260281c1660608501526040516131fa8161024a8160018601613117565b60808501526040516132138161024a8160028601613117565b60a085015260405161322c8161024a8160038601613117565b60c08501520154910152565b818110613243575050565b5f8155600101613238565b9190601f811161325d57505050565b612e4e925f5260205f20906020601f840160051c8301931061276457601f0160051c0190613238565b9291926001600160401b03821161071d57604051916132af601f8201601f191660200184612df1565b829481845281830111610223578281602093845f960137010152565b906004548210156133085760045f52600582901c7f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b03811161071d5760051b60200190565b5f1981146109b65760010190565b80518210156133085760209160051b010190565b6006548110156133085760065f5260205f209060011b01905f90565b919082039182116109b657565b8054821015613308575f5260205f2001905f90565b903590601e198136030182121561022357018035906001600160401b0382116102235760200191813603831361022357565b6134146134496133f583806133b3565b602094919491613407838201826133b3565b95826040889401906133b3565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612df1565b51902090565b5190811515820361022357565b805490600160401b82101561071d578161347e9160016134949401815561339e565b819391549060031b91821b915f19901b19161790565b9055565b3560ff811681036102235790565b3561ffff811681036102235790565b9060ff165f52600560205260405f20905f5260205260ff60405f20541615610a0b57565b604051906134e682612db9565b5f60e08382815282602082015282604082015282606082015260606080820152606060a0820152606060c08201520152565b5f818152600760205260408120906006820180549060ff8260081c166135b15761010060057fcfe7e61b35fbf35c6b0d5c7ebe6feb619ab832dc476c4a02dbdc041fb92b5ca69501549261357060ff821680956134b5565b61ff001916179055808252600860205260408220546009602052604083208584526020526040832055815260086020526135ad836040832061345c565b80a2565b5050505050565b9060ff165f918183526020600981526040928385208386528252838520548186526008835284862054905f19918281019081116136995780820361364c575b5050818652600883528486208054801561363857820191613618838361339e565b909182549160031b1b191690555584526009815282842091845252812055565b634e487b7160e01b88526031600452602488fd5b61365f908489526008865287892061339e565b90549060031b1c838852600885528061367d61347e848a8c2061339e565b9055838852600985528688209088528452858720555f806135f7565b634e487b7160e01b88526011600452602488fd5b604051906136ba82612dd5565b815f80825280602083015260606040830152606080830152606060808301528060a08301528060c08301528060e0830152806101008301526101208201526101406137036134d9565b910152565b9060405161371581612dd5565b6101406137036009839560018060a01b03808254168652600182015416602086015261374360028201612e12565b604086015260405161375c8161024a8160038601613117565b60608601526040516137758161024a8160048601613117565b6080860152600581015460a086015260ff600682015481811660c088015260081c16151560e086015260078101546101008601526008810154610120860152016131a8565b600681015460ff8160081c161561381a576137d9600883015442613391565b6002541061381a5760ff600561380c930154911660ff165f52600560205260405f20905f5260205260ff60405f20541690565b1561381657600190565b5f90565b50505f90565b97946138389061384793979892989594953691613286565b60208151910120943691613286565b602081519101209360ff6040519660208801987fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8a52604089015216606087015261ffff928380921660808801521660a08601521660c084015260e08301526101009081830152815261012081018181106001600160401b0382111761071d5760405251902090565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156139095750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156139095750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f146139bf57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f146139bf5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a460019056feae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bfa2646970667358221220bbf959dc0fb482514b0b3d8bdf3c8e9cefce7fa8cee4cf0d4316552dbedb731a64736f6c634300081400332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf", + "deployedBytecode": "0x60e0806040526004361015610012575f80fd5b5f3560e01c9081625ae7cf14612c8a5750806301ffc9a714612c3457806308c84e7014612c185780631788371714612b0e5780632068b3db14612a95578063248a9ca314612a695780632cbe755214612a325780632f2ff15d146129f657806336568abe146129af5780633c1a88b014612988578063418b207d1461292c57806342830ad81461276e57806343ed327414612617578063476cb030146125d95780634b6014da146124255780634c4c8e8b146121985780635766275a14611fe35780635c1090b214611fc257806363b86b8914611f5557806387267aca14611f385780638ecaf98c1461049d57806391d1485414611ef0578063927d3c9514611ebb578063971cfbf1146117bd5780639831ced51461177a578063987f940614610a1c578063a041ff51146108d1578063a217fddf146108b7578063a3bf31f11461088e578063abbf8f6a1461081d578063b82644b7146107b0578063cbdfc4e0146105d7578063ccdf0493146104b7578063cf79c8e11461049d578063d101bec414610409578063d547741f146103cb578063d6741a4214610375578063e10a2ea414610336578063e44cc482146102b3578063f33d2c0014610269578063f44a0780146102275763fec0de46146101e9575f80fd5b34610223575f3660031901126102235760206040517fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8152f35b5f80fd5b34610223575f366003190112610223576102656040516102518161024a8161309b565b0382612df1565b604051918291602083526020830190612eb3565b0390f35b34610223576020366003190112610223576004356001600160401b038111610223576102a561029e6020923690600401613034565b3691613286565b818151910120604051908152f35b3461022357602080600319360112610223576004356001600160a01b03811690819003610223575f52600b815260405f209060405190818184549182815201935f52815f20915f905b82821061031f576102658561031381890382612df1565b60405191829182613061565b8354865294850194600193840193909101906102fc565b3461022357602036600319011261022357602061036b610354612d46565b60ff165f526003602052600160405f200154151590565b6040519015158152f35b346102235760203660031901126102235760ff610390612d46565b165f5260036020526103c160405f2060016103aa82612e12565b910154604051928392604084526040840190612eb3565b9060208301520390f35b34610223576040366003190112610223576104076004356103ea612ef1565b90805f525f602052610402600160405f200154613927565b6139c4565b005b346102235760e036600319011261022357610422612d56565b61ffff604435818116810361022357606435928284168403610223576084359283168303610223576001600160401b039260a4358481116102235761046b903690600401613034565b9160c4359586116102235760209661048a610495973690600401613034565b969095600435613820565b604051908152f35b34610223575f366003190112610223576020604051818152f35b34610223576020366003190112610223576004355f908152600760205260409020805460018201546001600160a01b03908116929116906104fa60028201612e12565b60405190918161050d8160038401613117565b036105189083612df1565b604051918261052a8160048501613117565b036105359084612df1565b600582015492600683015490600784015492600885015494600901610559906131a8565b96604051998a998a5260208a01526101608060408b0152890161057b91612eb3565b88810360608a015261058c91612eb3565b878103608089015261059d91612eb3565b9360a087015260ff811660c087015260081c60ff16151560e086015261010085015261012084015282810361014084015261026591612f07565b34610223576040366003190112610223576105f0612d46565b6001600160401b036024358181116102235761061260ff913690600401613034565b91909361061d6138d0565b1692835f52602090600382526001938460405f20015461079e576040519061064482612d9e565b61064f368685613286565b8252838201428152875f526003855260405f20925191825190811161071d5787926106848261067e8754612d66565b8761324e565b8690601f831160011461073c575f919083610731575b50505f19600383901b1c191690831b1783555b5191015560045490600160401b82101561071d576106f4827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc9089661071894016004556132cb565b81549060031b9060ff89831b921b191617905560405193838594855284019161331c565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508a8061069a565b90601f19831691865f52885f20925f5b8a8282106107885750509084879594939210610770575b505050811b0183556106ad565b01515f1960f88460031b161c191690558a8080610763565b8385015186558d9890950194938401930161074c565b60405163095faf7360e01b8152600490fd5b34610223576020806003193601126102235760ff6107cc612d46565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610806576102658561031381890382612df1565b8354865294850194600193840193909101906107ef565b34610223576020366003190112610223576004356108396134d9565b50805f526007602052600760405f2001541561087c575f526007602052610265610868600960405f20016131a8565b604051918291602083526020830190612f07565b60405163f6523b6560e01b8152600490fd5b34610223576020366003190112610223576004355f526007602052602061036b60405f206137ba565b34610223575f3660031901126102235760206040515f8152f35b34610223576040366003190112610223576004356108ed612d56565b906108f66138d0565b60ff821691825f52600560209080825260405f20845f52825260ff60405f20541615610a0b57845f5280825260405f20845f52825260405f2060ff198154169055845f526008825260405f20928354805b6109735786867fd5a6741f8bbaee4cc3ea05953ea04c6a1863ce4c66a52da653dd0a315f84c2fb5f80a3005b5f1981018181116109b657610988908661339e565b90549060031b1c805f526007855260405f208785820154146109ca575b505080156109b6575f190180610947565b634e487b7160e01b5f52601160045260245ffd5b600601805461ff00191690556109e083826135b8565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a287806109a5565b60405162d85d7f60e71b8152600490fd5b346102235760e0366003190112610223576004356001600160401b03811161022357610a4c903690600401613034565b906024356001600160401b03811161022357610a6c903690600401613034565b90916044356001600160401b03811161022357610a8d903690600401613034565b60643594919290916001600160a01b0386168603610223576084356001600160401b03811161022357610ac4903690600401613034565b95909260ff60a4351660a43503610223576001600160401b0360c435116102235760e060c4353603600319011261022357335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff161561174f57610b4860a43560ff165f526003602052600160405f200154151590565b1561173d57610b58368385613286565b6020815191012098895f526007602052600760405f20015461172b57610ba691604091610bdd610bcc610bb98551968795869563f7cf40d360e01b875260806004880152608487019161331c565b848103600319016024860152888a61331c565b8381036003190160448501528a8c61331c565b82810360031901606484015261309b565b03816109005afa908115611658575f915f60c0526116e9575b501561169257610c0a60a43560c0516134b5565b610c126134d9565b50610c27608460c4350160c4356004016133b3565b9050158015611675575b61166357602061ffff610c48602460c435016134a6565b1603611663576020610c64608460c4350160c4356004016133b3565b90500361166357610cdb610c7c60c435600401613498565b89610c8b602460c435016134a6565b610c99604460c435016134a6565b90610ca8606460c435016134a6565b610cbc608460c4350160c4356004016133b3565b939092610cd360a460c4350160c4356004016133b3565b979096613820565b6020610cf060c480350160c4356004016133b3565b604051633f1333cd60e11b8152606060048201529384928392610d339291610d1c606486018a8c61331c565b92602486015260031985840301604486015261331c565b03816109005afa908115611658575f9161161e575b501561160c57610e6d610e7993610e8597610e39610d6a60c435600401613498565b610d78602460c435016134a6565b60a05260ff610e2b610d8e604460c435016134a6565b61ffff610e1e610da2606460c435016134a6565b82610db7608460c4350160c4356004016133b3565b919092610dce60a460c4350160c4356004016133b3565b969097610de460c480350160c4356004016133b3565b9b909c610df660405180608052612db9565b16608051528160a0511660206080510152166040608051015216606060805101523691613286565b6080805101523691613286565b60a060805101523691613286565b60c060805101524260e0608051015260405199610e558b612dd5565b338b526001600160a01b031660208b01523691613286565b60408801523691613286565b60608501523691613286565b608082015260c05160a082015260ff60a4351660c0820152600160e08201524261010082015242610120820152608051610140820152815f52600760205260405f209060018060a01b0381511660018060a01b03199081845416178355600183019060018060a01b036020840151169082541617905560408101519283516001600160401b03811161071d57610f2b81610f226002870154612d66565b6002870161324e565b6020601f82116001146115a15781929394955f92611596575b50508160011b915f199060031b1c19161760028401555b60608201519283516001600160401b03811161071d57610f8b81610f826003850154612d66565b6003850161324e565b6020601f821160011461152b5781929394955f92611520575b50508160011b915f199060031b1c19161760038201555b60808301519283516001600160401b03811161071d57610feb81610fe26004860154612d66565b6004860161324e565b6020601f82116001146114b55781929394955f926114aa575b50508160011b915f199060031b1c19161760048301555b60a081015160058301556006820160ff60c08301511681549061ff0060e0850151151560081b169161ffff19161717905561010081015160078301556101208101516008830155610140600983019101519060ff82511681549062ffff00602085015160081b1664ffff000000604086015160181b169166ffff0000000000606087015160281b169366ffffffffffffff191617171717905560808101519283516001600160401b03811161071d576110e4816110db600a870154612d66565b600a870161324e565b6020601f821160011461143f5781929394955f92611434575b50508160011b915f199060031b1c191617600a8401555b60a08201519283516001600160401b03811161071d576111448161113b600b850154612d66565b600b850161324e565b6020601f82116001146113c95781929394955f926113be575b50508160011b915f199060031b1c191617600b8201555b60c08301519283516001600160401b03811161071d576020946111a78261119e600c870154612d66565b600c870161324e565b8590601f831160011461134f579180600d949260e0945f92611344575b50508160011b915f199060031b1c191617600c8501555b015191015560ff60a435165f526008825260405f20546009835260405f20825f52835260405f205560ff60a435165f526008825261121c8160405f2061345c565b60ff60a435165f52600a82526112358160405f2061345c565b335f52600b82526112498160405f2061345c565b60405160ff60a435168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3807f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a60c06112ac60c435600401613498565b6112ba602460c435016134a6565b6112c8604460c435016134a6565b61ffff6112d9606460c435016134a6565b816112f161029e608460c4350160c4356004016133b3565b8b8151910120938161131061029e60a460c4350160c4356004016133b3565b8d81519101209660ff60405199168952168c880152166040860152166060840152608083015260a0820152a2604051908152f35b0151905088806111c4565b90600c85015f52865f20915f5b601f19851681106113a7575092600d949260019260e09583601f1981161061138f575b505050811b01600c8501556111db565b01515f1960f88460031b161c1916905588808061137f565b91928860018192868501518155019401920161135c565b01519050858061115d565b600b83015f5260205f20905f5b601f198416811061141c575060019394959683601f19811610611404575b505050811b01600b820155611174565b01515f1960f88460031b161c191690558580806113f4565b9091602060018192858b0151815501930191016113d6565b0151905085806110fd565b600a85015f5260205f20905f5b601f1984168110611492575060019394959683601f1981161061147a575b505050811b01600a840155611114565b01515f1960f88460031b161c1916905585808061146a565b9091602060018192858b01518155019301910161144c565b015190508580611004565b600484015f5260205f20905f5b601f1984168110611508575060019394959683601f198116106114f0575b505050811b01600483015561101b565b01515f1960f88460031b161c191690558580806114e0565b9091602060018192858b0151815501930191016114c2565b015190508580610fa4565b600383015f5260205f20905f5b601f198416811061157e575060019394959683601f19811610611566575b505050811b016003820155610fbb565b01515f1960f88460031b161c19169055858080611556565b9091602060018192858b015181550193019101611538565b015190508580610f44565b600285015f5260205f20905f5b601f19841681106115f4575060019394959683601f198116106115dc575b505050811b016002840155610f5b565b01515f1960f88460031b161c191690558580806115cc565b9091602060018192858b0151815501930191016115ae565b604051632d557a2f60e11b8152600490fd5b90506020813d602011611650575b8161163960209383612df1565b810103126102235761164a9061344f565b89610d48565b3d915061162c565b6040513d5f823e3d90fd5b6040516332f12a8d60e21b8152600490fd5b5061168a60a460c4350160c4356004016133b3565b905015610c31565b604051630368f92d60e11b815260206004820152602860248201527f4174746573746174696f6e20646f63756d656e7420766572696669636174696f6044820152671b8819985a5b195960c21b6064820152608490fd5b90506040813d604011611723575b8161170460409383612df1565b810103126102235760206117178261344f565b91015160c05289610bf6565b3d91506116f7565b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201525f80516020613a388339815191526024820152604490fd5b346102235760403660031901126102235760ff611795612d46565b165f52600560205260405f206024355f52602052602060ff60405f2054166040519015158152f35b34610223575f36600319011261022357604051806004549182815260208091018060045f528383805f20925f5b8880601f830110611d335761190895549184828210611d20575b828210611d0a575b828210611cf4575b828210611cde575b828210611cc9575b828210611cb3575b828210611c9d575b828210611c87575b828210611c71575b828210611c5b575b828210611c45575b828210611c2f575b828210611c19575b828210611c03575b828210611bed575b828210611bd7575b828210611bc1575b828210611bab575b828210611b95575b828210611b7f575b828210611b69575b828210611b53575b828210611b3d575b828210611b27575b828210611b11575b828210611afb575b828210611ae5575b828210611acf575b828210611ab9575b828210611aa3575b828210611a8d575b5010611a7f575b5090509592950384612df1565b6119118161333c565b9161191f6040519384612df1565b818352601f1991826119308261333c565b015f5b818110611a565750505f5b8181106119ef57505060405193604085019060408652518091526060850195905f5b8181106119d8575050508385038185015282519081865280860181808460051b8901019501935f975b8489106119965787870388f35b90919293949584808285856001950301875289519082806119c08451604085526040850190612eb3565b93015191015298019401980197919094939294611989565b825160ff1688529683019691830191600101611960565b8060ff611a01611a4e939996996132cb565b919054600392831b1c165f52845260405f20600160405191611a2283612d9e565b611a2b81612e12565b8352015485820152611a3d8288613361565b52611a488187613361565b50613353565b95929561193e565b8390604098959851611a6781612d9e565b606081525f8382015282828901015201969396611933565b60f81c8152018490886118fb565b6001919460ff8560f01c168152019301846118f4565b6001919460ff8560e81c168152019301846118ec565b6001919460ff8560e01c168152019301846118e4565b6001919460ff8560d81c168152019301846118dc565b6001919460ff8560d01c168152019301846118d4565b6001919460ff8560c81c168152019301846118cc565b6001919460ff8560c01c168152019301846118c4565b6001919460ff8560b81c168152019301846118bc565b6001919460ff8560b01c168152019301846118b4565b6001919460ff8560a81c168152019301846118ac565b6001919460ff8560a01c168152019301846118a4565b6001919460ff8560981c1681520193018461189c565b6001919460ff8560901c16815201930184611894565b6001919460ff8560881c1681520193018461188c565b6001919460ff8560801c16815201930184611884565b6001919460ff8560781c1681520193018461187c565b6001919460ff8560701c16815201930184611874565b6001919460ff8560681c1681520193018461186c565b6001919460ff8560601c16815201930184611864565b6001919460ff8560581c1681520193018461185c565b6001919460ff8560501c16815201930184611854565b6001919460ff8560481c1681520193018461184c565b6001919460ff8560401c16815201930184611844565b6001919460ff8560381c1681520193018461183c565b6001919460ff8560301c16815201930184611834565b6001919460ff8560281c1681520193018461182c565b6001919460ff85831c16815201930184611824565b6001919460ff8560181c1681520193018461181c565b6001919460ff8560101c16815201930184611814565b6001919460ff8560081c1681520193018461180c565b6001919460ff8516815201930184611804565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e0820152019301910185919285916117ea565b34610223576020366003190112610223576004355f526007602052602060ff600660405f20015460081c166040519015158152f35b3461022357604036600319011261022357611f09612ef1565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610223575f366003190112610223576020600254604051908152f35b34610223576020806003193601126102235760ff611f71612d46565b165f526008815260405f209060405190818184549182815201935f52815f20915f905b828210611fab576102658561031381890382612df1565b835486529485019460019384019390910190611f94565b3461022357602036600319011261022357611fdb6138d0565b600435600255005b34610223575f366003190112610223575f80600654905b818110612153575061200b8261333c565b916120196040519384612df1565b808352612028601f199161333c565b015f5b8181106121305750505f805b828210612087576040805160208082528651818301819052818801938301915f5b8281106120655784840385f35b85518051855282015160ff168483015294810194604090930192600101612058565b61209082613375565b509160ff6120ca81600180960154166120a884613375565b50549060ff165f52600560205260405f20905f5260205260ff60405f20541690565b6120e1575b506120db919250613353565b90612037565b6120db92612128916120f284613375565b50906120fd83613353565b966040519261210b84612d9e565b8054845201541660208201526121218289613361565b5286613361565b5082916120cf565b60209060405161213f81612d9e565b5f8152825f8183015282870101520161202b565b61217060ff600161216384613375565b500154166120a883613375565b612183575b61217e90613353565b611ffa565b9161219061217e91613353565b929050612175565b346102235760031960603682011261022357600435906001600160401b039081831161022357606090833603011261022357602435818111610223576121e2903690600401613034565b6044359360ff851694858103610223576121fe906103546138d0565b1561173d5761220f906004016133e5565b92845f526020906005825260405f20855f5282526001908160405f200154159060405190608082018281108282111761071d576040528382526002858301928a845260408101428152612263368a8c613286565b94606083019586528c5f526005895260405f208c5f52895260405f20925115159060ff61ff008554925160081b1692169061ffff1916171782555186820155019151805191821161071d5781906122c4826122be8654612d66565b8661324e565b8690601f83116001146123c6575f926123bb575b50505f19600383901b1c191690841b1790555b61232c575b506123277f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe9360405193838594855284019161331c565b0390a3005b926040519061233a82612d9e565b858252828201878152600654600160401b81101561071d5780876123619201600655613375565b9390936123a8577f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe966123279460ff925181550191511660ff1982541617905593506122f0565b634e487b7160e01b5f525f60045260245ffd5b015190508a806122d8565b90869350601f19831691855f52885f20925f5b8a82821061240f57505084116123f7575b505050811b0190556122eb565b01515f1960f88460031b161c191690558a80806123ea565b8385015186558a979095019493840193016123d9565b34610223576020806003193601126102235760ff612441612d46565b165f9081526008825260408120805490825b82811061258e57506124648361333c565b926124726040519485612df1565b808452612481601f199161333c565b01845f5b828110612578575050505f905f5b8381106124f7576040805187815286518189018190525f92600582901b8301810191898b01918b9085015b8287106124cb5785850386f35b9091929382806124e7600193603f198a82030186528851612f84565b96019201960195929190926124be565b8061250561252d928461339e565b9054600391821b1c5f5260079081895261252160405f206137ba565b612532575b5050613353565b612493565b61253c838661339e565b9054911b1c5f52875261257060405f209461255f61255982613353565b96613708565b612569828a613361565b5287613361565b508780612526565b6125806136ad565b828288010152018590612485565b612598818361339e565b90549060031b1c5f52600785526125b160405f206137ba565b6125c4575b6125bf90613353565b612453565b926125d16125bf91613353565b9390506125b6565b346102235760031960203682011261022357600435906001600160401b038211610223576060908236030112610223576104956020916004016133e5565b3461022357602080600319360112610223576001600160401b0360043581811161022357612649903690600401613034565b916126526138d0565b821161071d576001916126658354612d66565b601f8111612735575b505f92601f82116001146126d657816126a0945f916126cb575b505f19600384901b1c191682821b1790553691613286565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b905083013586612688565b601f19821693815f52855f20905f5b87878210612721575050836126a09610612708575b50508082811b01905561029e565b8401355f19600385901b60f8161c1916905585806126fa565b8683013584559284019291820191016126e5565b61275e90845f52855f20601f840160051c810191878510612764575b601f0160051c0190613238565b8461266e565b9091508190612751565b34610223576060366003190112610223576004356024356001600160401b03604435818111610223576127a5903690600401613034565b9290845f526020926007845260405f209460078601541561087c5760ff600687015460081c161561291a57428411612908576127e18442613391565b600254106128f65760405192858401908882528560408601526040855260608501938585109085111761071d578461285c88948694856040528351902090633f1333cd60e11b8652606060648501526128418c600360c487019101613117565b9160848501526063198483030160a4850152605f199461331c565b0301816109005afa9182156116585784905f936128bf575b505050156128ad577f3cf0bf7708060bf073b6ddb921eef0485b3eb9da828e0ed751fa6582ef337f5792600842910155604051908152a2005b6040516345576abb60e01b8152600490fd5b90809293503d83116128ef575b6128d68185612df1565b81010312610223576128e79061344f565b858381612874565b503d6128cc565b604051631cfc538360e11b8152600490fd5b604051630315ef0b60e21b8152600490fd5b60405163028b8e4960e61b8152600490fd5b34610223576020366003190112610223576004356129486136ad565b50805f526007602052600760405f2001541561087c575f52600760205261026561297460405f20613708565b604051918291602083526020830190612f84565b34610223575f3660031901126102235760206040515f80516020613a388339815191528152f35b34610223576040366003190112610223576129c8612ef1565b336001600160a01b038216036129e457610407906004356139c4565b60405163334bd91960e11b8152600490fd5b3461022357604036600319011261022357610407600435612a15612ef1565b90805f525f602052612a2d600160405f200154613927565b613948565b34610223576020366003190112610223576004355f52600760205261026561024a610251600360405f200160405192838092613117565b34610223576020366003190112610223576004355f525f6020526020600160405f200154604051908152f35b346102235760403660031901126102235760ff612ab0612d46565b165f52600560205260405f206024355f5260205260ff60405f20610265815491612ae1600260018301549201612e12565b906040519484818796161515865260081c1660208501526040840152608060608401526080830190612eb3565b34610223576020806003193601126102235760043590815f5260078152600760405f2001541561087c575f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149081612bec575b159081612be3575b50612bd157600790825f5252600660405f200180549060ff8260081c161561291a5761ff001982169055612bab9060ff16826135b8565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a2005b6040516398b31e6f60e01b8152600490fd5b90501583612b74565b90505f80516020613a388339815191525f525f825260405f20335f52825260ff60405f20541690612b6c565b34610223575f3660031901126102235760206040516109008152f35b346102235760203660031901126102235760043563ffffffff60e01b811680910361022357602090637965db0b60e01b8114908115612c79575b506040519015158152f35b6301ffc9a760e01b14905082612c6e565b34610223576020806003193601126102235760043591825f5260078252600760405f20015415612d3757505f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149182612d09575b50159081612d00575b50612bd15761040790613518565b90501582612cf2565b9091505f80516020613a388339815191525f525f815260405f2090335f525260ff60405f2054169083612ce9565b63f6523b6560e01b8152600490fd5b6004359060ff8216820361022357565b6024359060ff8216820361022357565b90600182811c92168015612d94575b6020831014612d8057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612d75565b604081019081106001600160401b0382111761071d57604052565b61010081019081106001600160401b0382111761071d57604052565b61016081019081106001600160401b0382111761071d57604052565b90601f801991011681019081106001600160401b0382111761071d57604052565b9060405191825f825492612e2584612d66565b9081845260019485811690815f14612e905750600114612e50575b5050612e4e92500383612df1565b565b909391505f52602090815f20935f915b818310612e78575050612e4e93508201015f80612e40565b85548884018501529485019487945091830191612e60565b915050612e4e94506020925060ff191682840152151560051b8201015f80612e40565b91908251928382525f5b848110612edd575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ebd565b602435906001600160a01b038216820361022357565b9060ff825116815261ffff806020840151166020830152806040840151166040830152606083015116606082015260e080612f7b612f69612f576080870151610100806080890152870190612eb3565b60a087015186820360a0880152612eb3565b60c086015185820360c0870152612eb3565b93015191015290565b906130319160018060a01b038082511683526020820151166020830152612fe4612fd2612fc06040840151610160806040880152860190612eb3565b60608401518582036060870152612eb3565b60808301518482036080860152612eb3565b9160a082015160a082015260ff60c08301511660c082015260e0820151151560e0820152610100808301519082015261012080830151908201526101408092015191818403910152612f07565b90565b9181601f84011215610223578235916001600160401b038311610223576020838186019501011161022357565b602090816040818301928281528551809452019301915f5b828110613087575050505090565b835185529381019392810192600101613079565b600180545f93926130ab82612d66565b808252918381169081156130fc57506001146130c657505050565b90929350815f52602092835f2092845f945b8386106130e85750505050010190565b8054858701830152940193859082016130d8565b91935050602093945060ff191683830152151560051b010190565b905f929180549161312783612d66565b9182825260019384811690815f146131855750600114613148575b50505050565b90919394505f52602092835f2092845f945b83861061317157505050500101905f808080613142565b80548587018301529401938590820161315a565b9294505050602093945060ff191683830152151560051b0101905f808080613142565b906040516131b581612db9565b60e060048294805460ff8116855261ffff90818160081c166020870152818160181c16604087015260281c1660608501526040516131fa8161024a8160018601613117565b60808501526040516132138161024a8160028601613117565b60a085015260405161322c8161024a8160038601613117565b60c08501520154910152565b818110613243575050565b5f8155600101613238565b9190601f811161325d57505050565b612e4e925f5260205f20906020601f840160051c8301931061276457601f0160051c0190613238565b9291926001600160401b03821161071d57604051916132af601f8201601f191660200184612df1565b829481845281830111610223578281602093845f960137010152565b906004548210156133085760045f52600582901c7f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b03811161071d5760051b60200190565b5f1981146109b65760010190565b80518210156133085760209160051b010190565b6006548110156133085760065f5260205f209060011b01905f90565b919082039182116109b657565b8054821015613308575f5260205f2001905f90565b903590601e198136030182121561022357018035906001600160401b0382116102235760200191813603831361022357565b6134146134496133f583806133b3565b602094919491613407838201826133b3565b95826040889401906133b3565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612df1565b51902090565b5190811515820361022357565b805490600160401b82101561071d578161347e9160016134949401815561339e565b819391549060031b91821b915f19901b19161790565b9055565b3560ff811681036102235790565b3561ffff811681036102235790565b9060ff165f52600560205260405f20905f5260205260ff60405f20541615610a0b57565b604051906134e682612db9565b5f60e08382815282602082015282604082015282606082015260606080820152606060a0820152606060c08201520152565b5f818152600760205260408120906006820180549060ff8260081c166135b15761010060057fcfe7e61b35fbf35c6b0d5c7ebe6feb619ab832dc476c4a02dbdc041fb92b5ca69501549261357060ff821680956134b5565b61ff001916179055808252600860205260408220546009602052604083208584526020526040832055815260086020526135ad836040832061345c565b80a2565b5050505050565b9060ff165f918183526020600981526040928385208386528252838520548186526008835284862054905f19918281019081116136995780820361364c575b5050818652600883528486208054801561363857820191613618838361339e565b909182549160031b1b191690555584526009815282842091845252812055565b634e487b7160e01b88526031600452602488fd5b61365f908489526008865287892061339e565b90549060031b1c838852600885528061367d61347e848a8c2061339e565b9055838852600985528688209088528452858720555f806135f7565b634e487b7160e01b88526011600452602488fd5b604051906136ba82612dd5565b815f80825280602083015260606040830152606080830152606060808301528060a08301528060c08301528060e0830152806101008301526101208201526101406137036134d9565b910152565b9060405161371581612dd5565b6101406137036009839560018060a01b03808254168652600182015416602086015261374360028201612e12565b604086015260405161375c8161024a8160038601613117565b60608601526040516137758161024a8160048601613117565b6080860152600581015460a086015260ff600682015481811660c088015260081c16151560e086015260078101546101008601526008810154610120860152016131a8565b600681015460ff8160081c161561381a576137d9600883015442613391565b6002541061381a5760ff600561380c930154911660ff165f52600560205260405f20905f5260205260ff60405f20541690565b1561381657600190565b5f90565b50505f90565b97946138389061384793979892989594953691613286565b60208151910120943691613286565b602081519101209360ff6040519660208801987fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8a52604089015216606087015261ffff928380921660808801521660a08601521660c084015260e08301526101009081830152815261012081018181106001600160401b0382111761071d5760405251902090565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156139095750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156139095750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f146139bf57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f146139bf5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a460019056feae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bfa2646970667358221220bbf959dc0fb482514b0b3d8bdf3c8e9cefce7fa8cee4cf0d4316552dbedb731a64736f6c63430008140033", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/solidity/TEERegistry.sol", "buildInfoId": "solc-0_8_20-f8e15bef114e3b1966bfe954ab3aed85a6eb9ce8" -} \ No newline at end of file +} diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 1eecd123..f13a4706 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -37,7 +37,8 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; /// precompile (`ITEEVerifier`). /// b. Extracts PCR measurements and checks they match an admin-approved set. /// c. Binds the TEE's signing key and TLS certificate to the verified enclave identity. -/// d. Stores the TEE as **enabled** and indexes it by type and owner. +/// d. Stores the TEE's OHTTP/HPKE config as part of the canonical TEE record. +/// e. Stores the TEE as **enabled** and indexes it by type and owner. /// /// 3. **Heartbeat** — Each TEE periodically proves liveness by submitting a signed /// timestamp via `heartbeat`. The RSA-PSS signature is verified on-chain against the @@ -119,6 +120,31 @@ contract TEERegistry is AccessControl { uint256 addedAt; } + struct OHTTPConfig { + uint8 keyId; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + bytes publicKey; + bytes keyConfig; + bytes signature; + uint256 registeredAt; + } + + /// @notice Calldata-only payload for an enclave's signed OHTTP/HPKE config. + /// @dev Grouped into a struct so callers (and the on-chain register fn) pass + /// a single argument instead of seven, which keeps the register + /// function readable and eases stack pressure. + struct OHTTPConfigInput { + uint8 keyId; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + bytes publicKey; + bytes keyConfig; + bytes signature; + } + struct TEEInfo { address owner; address paymentAddress; @@ -130,10 +156,16 @@ contract TEERegistry is AccessControl { bool enabled; uint256 registeredAt; uint256 lastHeartbeatAt; + OHTTPConfig ohttpConfig; } // ============ Storage ============ + bytes32 public constant OHTTP_CONFIG_DOMAIN_SEPARATOR = + keccak256("OPENGRADIENT_TEE_OHTTP_CONFIG_V1"); + uint16 public constant KEM_ID_X25519_HKDF_SHA256 = 32; + uint256 public constant X25519_PUBLIC_KEY_SIZE = 32; + // AWS Root Certificate bytes public awsRootCertificate; @@ -176,6 +208,15 @@ contract TEERegistry is AccessControl { event TEEEnabled(bytes32 indexed teeId); event AWSCertificateUpdated(bytes32 indexed certHash); event HeartbeatReceived(bytes32 indexed teeId, uint256 timestamp); + event OHTTPConfigRegistered( + bytes32 indexed teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes32 publicKeyHash, + bytes32 keyConfigHash + ); // ============ Errors ============ @@ -193,6 +234,8 @@ contract TEERegistry is AccessControl { error HeartbeatSignatureInvalid(); error HeartbeatTimestampTooOld(); error HeartbeatTimestampInFuture(); + error OHTTPConfigInvalid(); + error OHTTPConfigSignatureInvalid(); // ============ Modifiers ============ @@ -341,7 +384,8 @@ contract TEERegistry is AccessControl { bytes calldata tlsCertificate, address paymentAddress, string calldata endpoint, - uint8 teeType + uint8 teeType, + OHTTPConfigInput calldata ohttp ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { // Validate TEE type if (!isValidTEEType(teeType)) revert InvalidTEEType(); @@ -362,6 +406,10 @@ contract TEERegistry is AccessControl { // Verify PCR is approved and matches the TEE type _requirePCRValidForTEE(pcrHash, teeType); + // Validate and verify the signed OHTTP config (kept in a helper to bound + // the stack usage of this function). + OHTTPConfig memory ohttpConfig = _buildOHTTPConfig(teeId, signingPublicKey, ohttp); + // Store TEE tees[teeId] = TEEInfo({ owner: msg.sender, @@ -373,7 +421,8 @@ contract TEERegistry is AccessControl { teeType: teeType, enabled: true, registeredAt: block.timestamp, - lastHeartbeatAt: block.timestamp + lastHeartbeatAt: block.timestamp, + ohttpConfig: ohttpConfig }); // Add to indexes @@ -383,6 +432,61 @@ contract TEERegistry is AccessControl { _teesByOwner[msg.sender].push(teeId); emit TEERegistered(teeId, msg.sender, teeType); + emit OHTTPConfigRegistered( + teeId, + ohttp.keyId, + ohttp.kemId, + ohttp.kdfId, + ohttp.aeadId, + keccak256(ohttp.publicKey), + keccak256(ohttp.keyConfig) + ); + } + + /// @notice Validate an OHTTP config payload and verify it is signed by the + /// enclave's attested signing key, returning the stored representation. + /// @dev Reverts with OHTTPConfigInvalid for malformed payloads and + /// OHTTPConfigSignatureInvalid when the RSA-PSS signature does not match. + function _buildOHTTPConfig( + bytes32 teeId, + bytes calldata signingPublicKey, + OHTTPConfigInput calldata ohttp + ) internal view returns (OHTTPConfig memory) { + if (ohttp.publicKey.length == 0 || ohttp.keyConfig.length == 0) { + revert OHTTPConfigInvalid(); + } + // Only X25519-HKDF-SHA256 is supported; reject any other KEM and enforce + // its fixed public key size. + if (ohttp.kemId != KEM_ID_X25519_HKDF_SHA256) { + revert OHTTPConfigInvalid(); + } + if (ohttp.publicKey.length != X25519_PUBLIC_KEY_SIZE) { + revert OHTTPConfigInvalid(); + } + + bytes32 configHash = computeOHTTPConfigHash( + teeId, + ohttp.keyId, + ohttp.kemId, + ohttp.kdfId, + ohttp.aeadId, + ohttp.publicKey, + ohttp.keyConfig + ); + if (!VERIFIER.verifyRSAPSS(signingPublicKey, configHash, ohttp.signature)) { + revert OHTTPConfigSignatureInvalid(); + } + + return OHTTPConfig({ + keyId: ohttp.keyId, + kemId: ohttp.kemId, + kdfId: ohttp.kdfId, + aeadId: ohttp.aeadId, + publicKey: ohttp.publicKey, + keyConfig: ohttp.keyConfig, + signature: ohttp.signature, + registeredAt: block.timestamp + }); } /// @notice Disable a TEE, removing it from the enabled list @@ -488,6 +592,11 @@ contract TEERegistry is AccessControl { return tees[teeId]; } + function getOHTTPConfig(bytes32 teeId) external view returns (OHTTPConfig memory) { + if (tees[teeId].registeredAt == 0) revert TEENotFound(); + return tees[teeId].ohttpConfig; + } + /// @notice Get TEE IDs that are currently enabled for a given type /// @dev Does NOT filter by heartbeat freshness. /// Use getActiveTEEs() for fully verified results. @@ -561,4 +670,27 @@ contract TEERegistry is AccessControl { function computeTEEId(bytes calldata publicKey) external pure returns (bytes32) { return keccak256(publicKey); } + + function computeOHTTPConfigHash( + bytes32 teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig + ) public pure returns (bytes32) { + return keccak256( + abi.encode( + OHTTP_CONFIG_DOMAIN_SEPARATOR, + teeId, + keyId, + kemId, + kdfId, + aeadId, + keccak256(ohttpPublicKey), + keccak256(ohttpKeyConfig) + ) + ); + } } diff --git a/scripts/integration/local_tee_workflow.go b/scripts/integration/local_tee_workflow.go index 420a49cc..a1b31dcb 100755 --- a/scripts/integration/local_tee_workflow.go +++ b/scripts/integration/local_tee_workflow.go @@ -87,7 +87,7 @@ var ( SEL_SET_AWS_ROOT_CERT = crypto.Keccak256([]byte("setAWSRootCertificate(bytes)"))[:4] // TEE Registration & Lifecycle - FIXED - SEL_REGISTER_TEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8)"))[:4] + SEL_REGISTER_TEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8,(uint8,uint16,uint16,uint16,bytes,bytes,bytes))"))[:4] SEL_DISABLE_TEE = crypto.Keccak256([]byte("disableTEE(bytes32)"))[:4] // FIXED: disable not deactivate SEL_ENABLE_TEE = crypto.Keccak256([]byte("enableTEE(bytes32)"))[:4] // FIXED: enable not activate @@ -127,6 +127,26 @@ type AttestationResponse struct { PublicKey string `json:"public_key"` } +type OHTTPConfig struct { + KeyID uint8 + KEMID uint16 + KDFID uint16 + AEADID uint16 + PublicKey []byte + KeyConfig []byte + Signature []byte +} + +type rawOHTTPConfig struct { + KeyID uint8 `json:"key_id"` + KEMID uint16 `json:"kem_id"` + KDFID uint16 `json:"kdf_id"` + AEADID uint16 `json:"aead_id"` + PublicKey string `json:"public_key"` + KeyConfig string `json:"key_config"` + Signature string `json:"signature"` +} + // PCRKey mirrors the contract struct type PCRKey struct { PCRHash [32]byte @@ -423,36 +443,52 @@ func main() { results.Add("TEE already registered", true, "") } else { endpoint := fmt.Sprintf("https://%s", ENCLAVE_HOST) - - fmt.Println(" 📦 Registration inputs:") - fmt.Printf(" attestation: %d bytes\n", len(attestationBytes)) - fmt.Printf(" signingKey: %d bytes (SHA256=%x)\n", len(signingPubKeyDER), sha256.Sum256(signingPubKeyDER)) - fmt.Printf(" tlsCert: %d bytes (SHA256=%x)\n", len(tlsCertDER), sha256.Sum256(tlsCertDER)) - fmt.Printf(" paymentAddr: %s\n", account) - fmt.Printf(" endpoint: %s\n", endpoint) - fmt.Printf(" teeType: 0\n") - - txHash, err := callRegisterTEE(attestationBytes, signingPubKeyDER, tlsCertDER, account, endpoint, 0) + ohttpConfig, err := fetchOHTTPConfig(ENCLAVE_HOST) if err != nil { - fmt.Printf(" ❌ Registration error: %v\n", err) - results.Add("Register TEE with attestation", false, err.Error()) + fmt.Printf(" ❌ OHTTP config error: %v\n", err) + results.Add("Fetch signed OHTTP config", false, err.Error()) } else { - fmt.Printf(" 📤 Tx hash: %s\n", txHash) - success := waitForTx(txHash) - results.Add("Register TEE with attestation", success, "") - if success { - registrationSuccess = true - registeredTEEId = expectedTeeId - - fmt.Printf(" ✅ Registration succeeded — verifying on-chain state...\n") - postActive, _ := callIsActive(registeredTEEId) - fmt.Printf(" isActive(teeId)=%v\n", postActive) + results.Add("Fetch signed OHTTP config", true, fmt.Sprintf("%d bytes key_config", len(ohttpConfig.KeyConfig))) + + fmt.Println(" 📦 Registration inputs:") + fmt.Printf(" attestation: %d bytes\n", len(attestationBytes)) + fmt.Printf(" signingKey: %d bytes (SHA256=%x)\n", len(signingPubKeyDER), sha256.Sum256(signingPubKeyDER)) + fmt.Printf(" tlsCert: %d bytes (SHA256=%x)\n", len(tlsCertDER), sha256.Sum256(tlsCertDER)) + fmt.Printf(" paymentAddr: %s\n", account) + fmt.Printf(" endpoint: %s\n", endpoint) + fmt.Printf(" teeType: 0\n") + fmt.Printf(" ohttp: key_id=%d kem=%d kdf=%d aead=%d public_key=%d bytes key_config=%d bytes signature=%d bytes\n", + ohttpConfig.KeyID, + ohttpConfig.KEMID, + ohttpConfig.KDFID, + ohttpConfig.AEADID, + len(ohttpConfig.PublicKey), + len(ohttpConfig.KeyConfig), + len(ohttpConfig.Signature), + ) + + txHash, err := callRegisterTEE(attestationBytes, signingPubKeyDER, tlsCertDER, account, endpoint, 0, *ohttpConfig) + if err != nil { + fmt.Printf(" ❌ Registration error: %v\n", err) + results.Add("Register TEE with attestation", false, err.Error()) } else { - fmt.Println(" ❌ Registration failed — dumping PCR state for diagnosis:") - failPCRs, _ := callGetActivePCRs() - fmt.Printf(" Active PCRs in contract (%d):\n", len(failPCRs)) - for i, p := range failPCRs { - fmt.Printf(" [%d] hash=%s teeType=%d\n", i, hex.EncodeToString(p.PCRHash[:]), p.TEEType) + fmt.Printf(" 📤 Tx hash: %s\n", txHash) + success := waitForTx(txHash) + results.Add("Register TEE with attestation", success, "") + if success { + registrationSuccess = true + registeredTEEId = expectedTeeId + + fmt.Printf(" ✅ Registration succeeded — verifying on-chain state...\n") + postActive, _ := callIsActive(registeredTEEId) + fmt.Printf(" isActive(teeId)=%v\n", postActive) + } else { + fmt.Println(" ❌ Registration failed — dumping PCR state for diagnosis:") + failPCRs, _ := callGetActivePCRs() + fmt.Printf(" Active PCRs in contract (%d):\n", len(failPCRs)) + for i, p := range failPCRs { + fmt.Printf(" [%d] hash=%s teeType=%d\n", i, hex.EncodeToString(p.PCRHash[:]), p.TEEType) + } } } } @@ -918,6 +954,59 @@ func fetchSigningPublicKey(host string) ([]byte, error) { return block.Bytes, nil } +func fetchOHTTPConfig(host string) (*OHTTPConfig, error) { + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} + client := &http.Client{Transport: tr, Timeout: 30 * time.Second} + + url := fmt.Sprintf("https://%s/v1/ohttp/config", host) + fmt.Printf(" 🔍 Fetching OHTTP config from: %s\n", url) + + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) + } + + var raw rawOHTTPConfig + if err := json.Unmarshal(body, &raw); err != nil { + return nil, fmt.Errorf("json unmarshal failed: %v", err) + } + + publicKey, err := hex.DecodeString(strings.TrimPrefix(raw.PublicKey, "0x")) + if err != nil { + return nil, fmt.Errorf("invalid OHTTP public_key hex: %v", err) + } + keyConfig, err := base64.StdEncoding.DecodeString(raw.KeyConfig) + if err != nil { + return nil, fmt.Errorf("invalid OHTTP key_config base64: %v", err) + } + signature, err := base64.StdEncoding.DecodeString(raw.Signature) + if err != nil { + return nil, fmt.Errorf("invalid OHTTP signature base64: %v", err) + } + if len(publicKey) == 0 || len(keyConfig) == 0 || len(signature) == 0 { + return nil, fmt.Errorf("OHTTP config missing public_key, key_config, or signature") + } + + return &OHTTPConfig{ + KeyID: raw.KeyID, + KEMID: raw.KEMID, + KDFID: raw.KDFID, + AEADID: raw.AEADID, + PublicKey: publicKey, + KeyConfig: keyConfig, + Signature: signature, + }, nil +} + func fetchTLSCertificate(host, port string) ([]byte, error) { addr := host + ":" + port fmt.Printf(" 🔌 Connecting to %s via TLS...\n", addr) @@ -1197,17 +1286,56 @@ func callSetAWSRootCert(certDER []byte) (string, error) { // CONTRACT CALLS - TEE Registration // ============================================================================ -func callRegisterTEE(attestation, signingKey, tlsCert []byte, paymentAddr, endpoint string, teeType uint8) (string, error) { +func callRegisterTEE(attestation, signingKey, tlsCert []byte, paymentAddr, endpoint string, teeType uint8, ohttp OHTTPConfig) (string, error) { bytesType, _ := abi.NewType("bytes", "", nil) addrType, _ := abi.NewType("address", "", nil) stringType, _ := abi.NewType("string", "", nil) uint8Type, _ := abi.NewType("uint8", "", nil) + // OHTTP config is passed as a single struct (tuple) argument on-chain. + ohttpType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "keyId", Type: "uint8"}, + {Name: "kemId", Type: "uint16"}, + {Name: "kdfId", Type: "uint16"}, + {Name: "aeadId", Type: "uint16"}, + {Name: "publicKey", Type: "bytes"}, + {Name: "keyConfig", Type: "bytes"}, + {Name: "signature", Type: "bytes"}, + }) + if err != nil { + return "", fmt.Errorf("failed to create OHTTP ABI type: %v", err) + } + args := abi.Arguments{ {Type: bytesType}, {Type: bytesType}, {Type: bytesType}, {Type: addrType}, {Type: stringType}, {Type: uint8Type}, - } - encoded, err := args.Pack(attestation, signingKey, tlsCert, common.HexToAddress(paymentAddr), endpoint, teeType) + {Type: ohttpType}, + } + encoded, err := args.Pack( + attestation, + signingKey, + tlsCert, + common.HexToAddress(paymentAddr), + endpoint, + teeType, + struct { + KeyId uint8 + KemId uint16 + KdfId uint16 + AeadId uint16 + PublicKey []byte + KeyConfig []byte + Signature []byte + }{ + KeyId: ohttp.KeyID, + KemId: ohttp.KEMID, + KdfId: ohttp.KDFID, + AeadId: ohttp.AEADID, + PublicKey: ohttp.PublicKey, + KeyConfig: ohttp.KeyConfig, + Signature: ohttp.Signature, + }, + ) if err != nil { return "", fmt.Errorf("abi pack failed: %v", err) } diff --git a/scripts/tee-mgmt-cli/Registration.md b/scripts/tee-mgmt-cli/Registration.md index 6a2817b4..8c2b6863 100644 --- a/scripts/tee-mgmt-cli/Registration.md +++ b/scripts/tee-mgmt-cli/Registration.md @@ -91,14 +91,16 @@ Verify PCR is approved: **What happens during registration:** -1. ✅ CLI generates a random 20-byte nonce -2. ✅ Fetches attestation document from `https://enclave_host/enclave/attestation?nonce=` -3. ✅ Fetches signing public key from `https://enclave_host/signing-key` -4. ✅ Fetches TLS certificate via TLS handshake to `enclave_host:443` -5. ✅ Computes expected TEE ID: `keccak256(signing_public_key)` -6. ✅ Submits transaction: `registerTEEWithAttestation(attestation, signingKey, tlsCert, paymentAddr, endpoint, teeType)` +1. ✅ Fetches signing public key from `https://enclave_host/signing-key` +2. ✅ Computes expected TEE ID: `keccak256(signing_public_key)` and checks it is not already registered +3. ✅ Generates a random 20-byte nonce and fetches the attestation document from `https://enclave_host/enclave/attestation?nonce=` +4. ✅ Fetches signed OHTTP config from `https://enclave_host/v1/ohttp/config` + - Includes `key_id`, `kem_id`, `kdf_id`, `aead_id`, `public_key`, `key_config`, and `signature` +5. ✅ Fetches TLS certificate via TLS handshake to `enclave_host:443` +6. ✅ Submits transaction: `registerTEEWithAttestation(attestation, signingKey, tlsCert, paymentAddr, endpoint, teeType, ohttpConfig...)` 7. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) -8. ✅ TEE is registered and **enabled** by default +8. ✅ Contract verifies the OHTTP config signature against the registered TEE signing key +9. ✅ TEE is registered and **enabled** by default ### Step 4: Verify Registration ```bash diff --git a/scripts/tee-mgmt-cli/cmd/tee.go b/scripts/tee-mgmt-cli/cmd/tee.go index 7c66518b..9d585029 100644 --- a/scripts/tee-mgmt-cli/cmd/tee.go +++ b/scripts/tee-mgmt-cli/cmd/tee.go @@ -116,6 +116,19 @@ var teeRegisterCmd = &cobra.Command{ fmt.Printf(" Payment: %s\n", paymentAddr) fmt.Printf(" Type: %d\n\n", teeType) + registry.Log("Fetching signing public key...") + signingKey, err := registry.FetchSigningPublicKey(enclaveHost) + if err != nil { + return fmt.Errorf("failed to fetch signing key: %w", err) + } + fmt.Printf(" Signing Key: %d bytes\n", len(signingKey)) + + expectedId := crypto.Keccak256Hash(signingKey) + if info, err := client.GetTEE(expectedId); err == nil && info != nil { + fmt.Printf("\nTEE already registered: 0x%s\n", hex.EncodeToString(expectedId[:])) + return nil + } + registry.Log("Fetching attestation document...") nonce := registry.GenerateNonce() attestDoc, err := registry.FetchAttestation(fmt.Sprintf("https://%s/enclave/attestation?nonce=%s", enclaveHost, nonce)) @@ -125,12 +138,21 @@ var teeRegisterCmd = &cobra.Command{ attestBytes, _ := registry.DecodeBase64(attestDoc) fmt.Printf(" Attestation: %d bytes\n", len(attestBytes)) - registry.Log("Fetching signing public key...") - signingKey, err := registry.FetchSigningPublicKey(enclaveHost) + registry.Log("Fetching signed OHTTP config...") + ohttpConfig, err := registry.FetchOHTTPConfig(enclaveHost) if err != nil { - return fmt.Errorf("failed to fetch signing key: %w", err) + return fmt.Errorf("failed to fetch signed OHTTP config: %w", err) } - fmt.Printf(" Signing Key: %d bytes\n", len(signingKey)) + fmt.Printf( + " OHTTP Config: key_id=%d kem=%d kdf=%d aead=%d public_key=%d bytes key_config=%d bytes signature=%d bytes\n", + ohttpConfig.KeyID, + ohttpConfig.KEMID, + ohttpConfig.KDFID, + ohttpConfig.AEADID, + len(ohttpConfig.PublicKey), + len(ohttpConfig.KeyConfig), + len(ohttpConfig.Signature), + ) registry.Log("Fetching TLS certificate...") tlsCert, err := registry.FetchTLSCertificate(enclaveHost, enclavePort) @@ -139,14 +161,17 @@ var teeRegisterCmd = &cobra.Command{ } fmt.Printf(" TLS Cert: %d bytes\n", len(tlsCert)) - expectedId := crypto.Keccak256Hash(signingKey) - if info, err := client.GetTEE(expectedId); err == nil && info != nil { - fmt.Printf("\nTEE already registered: 0x%s\n", hex.EncodeToString(expectedId[:])) - return nil - } - registry.Log("Sending registration transaction...") - txHash, err := client.RegisterTEE(account, attestBytes, signingKey, tlsCert, paymentAddr, endpoint, teeType) + txHash, err := client.RegisterTEE( + account, + attestBytes, + signingKey, + tlsCert, + paymentAddr, + endpoint, + teeType, + *ohttpConfig, + ) if err != nil { return fmt.Errorf("failed to register: %w", err) } diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 94c6a376..5756be2a 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -32,40 +32,41 @@ var ( // Method selectors var ( - selGrantRole = crypto.Keccak256([]byte("grantRole(bytes32,address)"))[:4] - selRevokeRole = crypto.Keccak256([]byte("revokeRole(bytes32,address)"))[:4] - selHasRole = crypto.Keccak256([]byte("hasRole(bytes32,address)"))[:4] - selAddTEEType = crypto.Keccak256([]byte("addTEEType(uint8,string)"))[:4] - selIsValidType = crypto.Keccak256([]byte("isValidTEEType(uint8)"))[:4] - selApprovePCR = crypto.Keccak256([]byte("approvePCR((bytes,bytes,bytes),string,uint8)"))[:4] - selRevokePCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint8)"))[:4] - selIsPCRApproved = crypto.Keccak256([]byte("isPCRApproved(uint8,bytes32)"))[:4] - selComputePCRHash = crypto.Keccak256([]byte("computePCRHash((bytes,bytes,bytes))"))[:4] - selGetApprovedPCRs = crypto.Keccak256([]byte("getApprovedPCRs()"))[:4] - selSetAWSRootCert = crypto.Keccak256([]byte("setAWSRootCertificate(bytes)"))[:4] - selRegisterTEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8)"))[:4] - selDisableTEE = crypto.Keccak256([]byte("disableTEE(bytes32)"))[:4] - selEnableTEE = crypto.Keccak256([]byte("enableTEE(bytes32)"))[:4] - selGetEnabledTEEs = crypto.Keccak256([]byte("getEnabledTEEs(uint8)"))[:4] - selGetTEE = crypto.Keccak256([]byte("getTEE(bytes32)"))[:4] - selIsTEEActive = crypto.Keccak256([]byte("isTEEActive(bytes32)"))[:4] - selSetHeartbeatMaxAge = crypto.Keccak256([]byte("setHeartbeatMaxAge(uint256)"))[:4] - selHeartbeatMaxAge = crypto.Keccak256([]byte("heartbeatMaxAge()"))[:4] + selGrantRole = crypto.Keccak256([]byte("grantRole(bytes32,address)"))[:4] + selRevokeRole = crypto.Keccak256([]byte("revokeRole(bytes32,address)"))[:4] + selHasRole = crypto.Keccak256([]byte("hasRole(bytes32,address)"))[:4] + selAddTEEType = crypto.Keccak256([]byte("addTEEType(uint8,string)"))[:4] + selIsValidType = crypto.Keccak256([]byte("isValidTEEType(uint8)"))[:4] + selApprovePCR = crypto.Keccak256([]byte("approvePCR((bytes,bytes,bytes),string,uint8)"))[:4] + selRevokePCR = crypto.Keccak256([]byte("revokePCR(bytes32,uint8)"))[:4] + selIsPCRApproved = crypto.Keccak256([]byte("isPCRApproved(uint8,bytes32)"))[:4] + selComputePCRHash = crypto.Keccak256([]byte("computePCRHash((bytes,bytes,bytes))"))[:4] + selGetApprovedPCRs = crypto.Keccak256([]byte("getApprovedPCRs()"))[:4] + selSetAWSRootCert = crypto.Keccak256([]byte("setAWSRootCertificate(bytes)"))[:4] + selRegisterTEE = crypto.Keccak256([]byte("registerTEEWithAttestation(bytes,bytes,bytes,address,string,uint8,(uint8,uint16,uint16,uint16,bytes,bytes,bytes))"))[:4] + selDisableTEE = crypto.Keccak256([]byte("disableTEE(bytes32)"))[:4] + selEnableTEE = crypto.Keccak256([]byte("enableTEE(bytes32)"))[:4] + selGetEnabledTEEs = crypto.Keccak256([]byte("getEnabledTEEs(uint8)"))[:4] + selGetTEE = crypto.Keccak256([]byte("getTEE(bytes32)"))[:4] + selIsTEEActive = crypto.Keccak256([]byte("isTEEActive(bytes32)"))[:4] + selSetHeartbeatMaxAge = crypto.Keccak256([]byte("setHeartbeatMaxAge(uint256)"))[:4] + selHeartbeatMaxAge = crypto.Keccak256([]byte("heartbeatMaxAge()"))[:4] ) // Structs type TEEInfo struct { - Owner common.Address - PaymentAddress common.Address - Endpoint string - PublicKey []byte - TLSCertificate []byte - PCRHash [32]byte - TEEType uint8 - IsEnabled bool - RegisteredAt time.Time + Owner common.Address + PaymentAddress common.Address + Endpoint string + PublicKey []byte + TLSCertificate []byte + PCRHash [32]byte + TEEType uint8 + IsEnabled bool + RegisteredAt time.Time LastHeartbeatAt time.Time + OHTTPConfig OHTTPConfig } type AttestationResponse struct { @@ -91,6 +92,27 @@ type MeasurementsFile struct { } `json:"Measurements"` } +type OHTTPConfig struct { + KeyID uint8 `json:"key_id"` + KEMID uint16 `json:"kem_id"` + KDFID uint16 `json:"kdf_id"` + AEADID uint16 `json:"aead_id"` + PublicKey []byte + KeyConfig []byte + Signature []byte + RegisteredAt time.Time +} + +type rawOHTTPConfig struct { + KeyID uint8 `json:"key_id"` + KEMID uint16 `json:"kem_id"` + KDFID uint16 `json:"kdf_id"` + AEADID uint16 `json:"aead_id"` + PublicKey string `json:"public_key"` + KeyConfig string `json:"key_config"` + Signature string `json:"signature"` +} + // Client type Client struct { @@ -149,6 +171,17 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { return nil, err } + ohttpComponents := []abi.ArgumentMarshaling{ + {Name: "keyId", Type: "uint8"}, + {Name: "kemId", Type: "uint16"}, + {Name: "kdfId", Type: "uint16"}, + {Name: "aeadId", Type: "uint16"}, + {Name: "publicKey", Type: "bytes"}, + {Name: "keyConfig", Type: "bytes"}, + {Name: "signature", Type: "bytes"}, + {Name: "registeredAt", Type: "uint256"}, + } + tupleType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ {Name: "owner", Type: "address"}, {Name: "paymentAddress", Type: "address"}, @@ -160,6 +193,7 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { {Name: "enabled", Type: "bool"}, {Name: "registeredAt", Type: "uint256"}, {Name: "lastHeartbeatAt", Type: "uint256"}, + {Name: "ohttpConfig", Type: "tuple", Components: ohttpComponents}, }) if err != nil { return nil, fmt.Errorf("failed to create ABI type: %v", err) @@ -178,40 +212,115 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { // The result is a struct (anonymous) containing the tuple fields s := values[0].(struct { - Owner common.Address `json:"owner"` - PaymentAddress common.Address `json:"paymentAddress"` - Endpoint string `json:"endpoint"` - PublicKey []byte `json:"publicKey"` - TlsCertificate []byte `json:"tlsCertificate"` - PcrHash [32]byte `json:"pcrHash"` - TeeType uint8 `json:"teeType"` - Enabled bool `json:"enabled"` - RegisteredAt *big.Int `json:"registeredAt"` - LastHeartbeatAt *big.Int `json:"lastHeartbeatAt"` + Owner common.Address `json:"owner"` + PaymentAddress common.Address `json:"paymentAddress"` + Endpoint string `json:"endpoint"` + PublicKey []byte `json:"publicKey"` + TlsCertificate []byte `json:"tlsCertificate"` + PcrHash [32]byte `json:"pcrHash"` + TeeType uint8 `json:"teeType"` + Enabled bool `json:"enabled"` + RegisteredAt *big.Int `json:"registeredAt"` + LastHeartbeatAt *big.Int `json:"lastHeartbeatAt"` + OhttpConfig struct { + KeyId uint8 `json:"keyId"` + KemId uint16 `json:"kemId"` + KdfId uint16 `json:"kdfId"` + AeadId uint16 `json:"aeadId"` + PublicKey []byte `json:"publicKey"` + KeyConfig []byte `json:"keyConfig"` + Signature []byte `json:"signature"` + RegisteredAt *big.Int `json:"registeredAt"` + } `json:"ohttpConfig"` }) return &TEEInfo{ - Owner: s.Owner, - PaymentAddress: s.PaymentAddress, - Endpoint: s.Endpoint, - PublicKey: s.PublicKey, - TLSCertificate: s.TlsCertificate, - PCRHash: s.PcrHash, - TEEType: s.TeeType, - IsEnabled: s.Enabled, - RegisteredAt: time.Unix(s.RegisteredAt.Int64(), 0), + Owner: s.Owner, + PaymentAddress: s.PaymentAddress, + Endpoint: s.Endpoint, + PublicKey: s.PublicKey, + TLSCertificate: s.TlsCertificate, + PCRHash: s.PcrHash, + TEEType: s.TeeType, + IsEnabled: s.Enabled, + RegisteredAt: time.Unix(s.RegisteredAt.Int64(), 0), LastHeartbeatAt: time.Unix(s.LastHeartbeatAt.Int64(), 0), + OHTTPConfig: OHTTPConfig{ + KeyID: s.OhttpConfig.KeyId, + KEMID: s.OhttpConfig.KemId, + KDFID: s.OhttpConfig.KdfId, + AEADID: s.OhttpConfig.AeadId, + PublicKey: s.OhttpConfig.PublicKey, + KeyConfig: s.OhttpConfig.KeyConfig, + Signature: s.OhttpConfig.Signature, + RegisteredAt: time.Unix(s.OhttpConfig.RegisteredAt.Int64(), 0), + }, }, nil } -func (c *Client) RegisterTEE(from string, attestation, signingKey, tlsCert []byte, paymentAddr, endpoint string, teeType uint8) (string, error) { +func (c *Client) RegisterTEE( + from string, + attestation, signingKey, tlsCert []byte, + paymentAddr, endpoint string, + teeType uint8, + ohttp OHTTPConfig, +) (string, error) { bytesT, _ := abi.NewType("bytes", "", nil) addrT, _ := abi.NewType("address", "", nil) strT, _ := abi.NewType("string", "", nil) u8T, _ := abi.NewType("uint8", "", nil) - args := abi.Arguments{{Type: bytesT}, {Type: bytesT}, {Type: bytesT}, {Type: addrT}, {Type: strT}, {Type: u8T}} - encoded, _ := args.Pack(attestation, signingKey, tlsCert, common.HexToAddress(paymentAddr), endpoint, teeType) + // OHTTP config is passed as a single struct (tuple) argument on-chain. + ohttpT, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "keyId", Type: "uint8"}, + {Name: "kemId", Type: "uint16"}, + {Name: "kdfId", Type: "uint16"}, + {Name: "aeadId", Type: "uint16"}, + {Name: "publicKey", Type: "bytes"}, + {Name: "keyConfig", Type: "bytes"}, + {Name: "signature", Type: "bytes"}, + }) + if err != nil { + return "", fmt.Errorf("failed to create OHTTP ABI type: %w", err) + } + + args := abi.Arguments{ + {Type: bytesT}, + {Type: bytesT}, + {Type: bytesT}, + {Type: addrT}, + {Type: strT}, + {Type: u8T}, + {Type: ohttpT}, + } + encoded, err := args.Pack( + attestation, + signingKey, + tlsCert, + common.HexToAddress(paymentAddr), + endpoint, + teeType, + struct { + KeyId uint8 + KemId uint16 + KdfId uint16 + AeadId uint16 + PublicKey []byte + KeyConfig []byte + Signature []byte + }{ + KeyId: ohttp.KeyID, + KemId: ohttp.KEMID, + KdfId: ohttp.KDFID, + AeadId: ohttp.AEADID, + PublicKey: ohttp.PublicKey, + KeyConfig: ohttp.KeyConfig, + Signature: ohttp.Signature, + }, + ) + if err != nil { + return "", fmt.Errorf("failed to encode registerTEE args: %w", err) + } return c.sendTx(from, append(selRegisterTEE, encoded...)) } @@ -223,7 +332,6 @@ func (c *Client) EnableTEE(from string, teeId [32]byte) (string, error) { return c.sendTx(from, encodeBytes32(selEnableTEE, teeId)) } - func (c *Client) IsTEEActive(teeId [32]byte) (bool, error) { result, err := c.ethCall(encodeBytes32(selIsTEEActive, teeId)) return len(result) >= 32 && result[31] == 1, err @@ -665,6 +773,52 @@ func FetchSigningPublicKey(host string) ([]byte, error) { return nil, fmt.Errorf("invalid key format") } +func FetchOHTTPConfig(host string) (*OHTTPConfig, error) { + client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, Timeout: 30 * time.Second} + resp, err := client.Get(fmt.Sprintf("https://%s/v1/ohttp/config", host)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("ohttp config request failed: %s: %s", resp.Status, strings.TrimSpace(string(body))) + } + + body, _ := io.ReadAll(resp.Body) + var raw rawOHTTPConfig + if err := json.Unmarshal(body, &raw); err != nil { + return nil, fmt.Errorf("failed to decode ohttp config: %w", err) + } + + publicKey, err := hex.DecodeString(strings.TrimPrefix(raw.PublicKey, "0x")) + if err != nil { + return nil, fmt.Errorf("invalid ohttp public key hex: %w", err) + } + keyConfig, err := base64.StdEncoding.DecodeString(raw.KeyConfig) + if err != nil { + return nil, fmt.Errorf("invalid ohttp key_config base64: %w", err) + } + signature, err := base64.StdEncoding.DecodeString(raw.Signature) + if err != nil { + return nil, fmt.Errorf("invalid ohttp signature base64: %w", err) + } + if len(publicKey) == 0 || len(keyConfig) == 0 || len(signature) == 0 { + return nil, fmt.Errorf("ohttp config is missing public_key, key_config, or signature") + } + + return &OHTTPConfig{ + KeyID: raw.KeyID, + KEMID: raw.KEMID, + KDFID: raw.KDFID, + AEADID: raw.AEADID, + PublicKey: publicKey, + KeyConfig: keyConfig, + Signature: signature, + }, nil +} + func FetchTLSCertificate(host, port string) ([]byte, error) { conn, err := tls.Dial("tcp", host+":"+port, &tls.Config{InsecureSkipVerify: true}) if err != nil { diff --git a/tests/jsonrpc/docker-compose.yml b/tests/jsonrpc/docker-compose.yml index 314b3747..a3fe5db3 100644 --- a/tests/jsonrpc/docker-compose.yml +++ b/tests/jsonrpc/docker-compose.yml @@ -55,7 +55,6 @@ services: --ws.port 8546 --ws.api eth,net,web3,personal,admin,debug,miner,txpool --ws.origins "*" - --allow-insecure-unlock --rpc.allow-unprotected-txs --nodiscover healthcheck: diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index c267b69b..3e96452f 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -36,7 +36,17 @@ contract MockTEERegistry is TEERegistry { teeType: teeType, enabled: false, registeredAt: block.timestamp, - lastHeartbeatAt: block.timestamp + lastHeartbeatAt: block.timestamp, + ohttpConfig: OHTTPConfig({ + keyId: 0, + kemId: 0, + kdfId: 0, + aeadId: 0, + publicKey: "", + keyConfig: "", + signature: "", + registeredAt: 0 + }) }); // Add to indexes (matching registerTEE behavior) @@ -45,4 +55,32 @@ contract MockTEERegistry is TEERegistry { emit TEERegistered(teeId, msg.sender, teeType); } + + /// @notice Exposes the internal OHTTP config validation for testing. + /// @dev Lets tests exercise the OHTTPConfigInvalid checks (which run before any + /// precompile call) without the attestation flow. The RSA-PSS verification + /// step still requires the TEE verifier precompile. + function validateOHTTPConfigForTesting( + bytes32 teeId, + bytes calldata signingPublicKey, + OHTTPConfigInput calldata ohttp + ) external view returns (OHTTPConfig memory) { + return _buildOHTTPConfig(teeId, signingPublicKey, ohttp); + } + + /// @notice Stores an OHTTP config on an existing TEE so getOHTTPConfig() retrieval + /// (and the ABI round-trip) can be tested without the precompile. + function setOHTTPConfigForTesting(bytes32 teeId, OHTTPConfigInput calldata ohttp) external { + if (tees[teeId].registeredAt == 0) revert TEENotFound(); + tees[teeId].ohttpConfig = OHTTPConfig({ + keyId: ohttp.keyId, + kemId: ohttp.kemId, + kdfId: ohttp.kdfId, + aeadId: ohttp.aeadId, + publicKey: ohttp.publicKey, + keyConfig: ohttp.keyConfig, + signature: ohttp.signature, + registeredAt: block.timestamp + }); + } } diff --git a/tests/solidity/suites/tee/contracts/TEETestHelper.sol b/tests/solidity/suites/tee/contracts/TEETestHelper.sol index 4d522911..59143999 100644 --- a/tests/solidity/suites/tee/contracts/TEETestHelper.sol +++ b/tests/solidity/suites/tee/contracts/TEETestHelper.sol @@ -84,7 +84,8 @@ contract TEETestHelper { bytes calldata tlsCertificate, address paymentAddress, string calldata endpoint, - uint8 teeType + uint8 teeType, + TEERegistry.OHTTPConfigInput calldata ohttp ) external returns (bytes32 teeId) { return registry.registerTEEWithAttestation( attestationDocument, @@ -92,7 +93,8 @@ contract TEETestHelper { tlsCertificate, paymentAddress, endpoint, - teeType + teeType, + ohttp ); } diff --git a/tests/solidity/suites/tee/package.json b/tests/solidity/suites/tee/package.json index c07f29a9..adc45869 100644 --- a/tests/solidity/suites/tee/package.json +++ b/tests/solidity/suites/tee/package.json @@ -8,7 +8,7 @@ "get-contracts": "mkdir -p ./contracts/cosmos && rsync -avm --exclude='testdata/' --include='*/' --include='*.sol' --exclude='*' ../../../../precompiles/ ./contracts/cosmos/ && cp ../../../../contracts/solidity/TEERegistry.sol ./contracts/cosmos/ && cp ../../../../contracts/solidity/TEEInferenceVerifier.sol ./contracts/cosmos/ && cp ../../../../contracts/solidity/InferenceSettlementRelay.sol ./contracts/cosmos/ && sed -i.bak 's|./precompiles/tee/ITEEVerifier.sol|./tee/ITEEVerifier.sol|g' ./contracts/cosmos/TEERegistry.sol && sed -i.bak 's|./precompiles/tee/ITEEVerifier.sol|./tee/ITEEVerifier.sol|g' ./contracts/cosmos/TEEInferenceVerifier.sol && rm -f ./contracts/cosmos/*.bak", "clean-contracts": "rm -rf ./contracts/cosmos/*", "test-ganache": "yarn truffle test", - "test-cosmos": "yarn get-contracts && yarn truffle test --network cosmos && yarn clean-contracts" + "test-cosmos": "yarn get-contracts && yarn truffle compile && yarn truffle test --network cosmos --compile-none && yarn clean-contracts" }, "devDependencies": { "truffle-assertions": "^0.9.2", diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index 43b5f236..57124d9b 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -3,11 +3,37 @@ const crypto = require('crypto') const truffleAssert = require('truffle-assertions') const TEERegistry = artifacts.require('TEERegistry') const TEETestHelper = artifacts.require('TEETestHelper') +const MockTEERegistry = artifacts.require('MockTEERegistry') contract('TEERegistry', function (accounts) { let owner, teeOperator, user1, user2 let registry, helper + function makeOHTTPConfig(overrides = {}) { + return { + keyId: 1, + kemId: 32, + kdfId: 1, + aeadId: 3, + publicKey: '0x' + Buffer.alloc(32, 0xA0).toString('hex'), + keyConfig: '0x' + Buffer.from('test-ohttp-key-config', 'utf8').toString('hex'), + signature: '0x' + Buffer.alloc(256, 0x5A).toString('hex'), + ...overrides + } + } + + function ohttpArgs(config = makeOHTTPConfig()) { + return [ + config.keyId, + config.kemId, + config.kdfId, + config.aeadId, + config.publicKey, + config.keyConfig, + config.signature + ] + } + before(async () => { [owner, teeOperator, user1, user2] = accounts @@ -283,6 +309,7 @@ contract('TEERegistry', function (accounts) { user1, 'https://tee.example.com', 1, + ohttpArgs(), { from: user1 } ) ) @@ -302,6 +329,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 99, // Invalid type + ohttpArgs(), { from: teeOperator } ) ) @@ -321,6 +349,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 1, + ohttpArgs(), { from: teeOperator } ) ) @@ -329,6 +358,166 @@ contract('TEERegistry', function (accounts) { }) }) + describe('OHTTP Config', function () { + it('should expose OHTTP constants', async function () { + const domain = await registry.OHTTP_CONFIG_DOMAIN_SEPARATOR() + const expectedDomain = web3.utils.keccak256('OPENGRADIENT_TEE_OHTTP_CONFIG_V1') + + expect(domain).to.equal(expectedDomain) + expect((await registry.KEM_ID_X25519_HKDF_SHA256()).toNumber()).to.equal(32) + expect((await registry.X25519_PUBLIC_KEY_SIZE()).toNumber()).to.equal(32) + + console.log('✓ OHTTP constants exposed correctly') + }) + + it('should compute OHTTP config hash correctly', async function () { + const teeId = web3.utils.keccak256('0x1234') + const config = makeOHTTPConfig() + const domain = await registry.OHTTP_CONFIG_DOMAIN_SEPARATOR() + const publicKeyHash = web3.utils.keccak256(config.publicKey) + const keyConfigHash = web3.utils.keccak256(config.keyConfig) + + const expectedHash = web3.utils.keccak256( + web3.eth.abi.encodeParameters( + ['bytes32', 'bytes32', 'uint8', 'uint16', 'uint16', 'uint16', 'bytes32', 'bytes32'], + [ + domain, + teeId, + config.keyId, + config.kemId, + config.kdfId, + config.aeadId, + publicKeyHash, + keyConfigHash + ] + ) + ) + + const computedHash = await registry.computeOHTTPConfigHash( + teeId, + config.keyId, + config.kemId, + config.kdfId, + config.aeadId, + config.publicKey, + config.keyConfig + ) + + expect(computedHash).to.equal(expectedHash) + + console.log('✓ OHTTP config hash computation correct') + }) + + it('should revert OHTTP config lookup for unknown TEE', async function () { + const nonExistentId = web3.utils.keccak256('0xBEEF') + + await truffleAssert.reverts( + registry.getOHTTPConfig(nonExistentId) + ) + + console.log('✓ Unknown TEE OHTTP config lookup reverts correctly') + }) + + // Validation and storage of OHTTP config happen after the attestation + // precompile call, so they cannot be reached through the real + // registerTEEWithAttestation on a precompile-less network. MockTEERegistry + // exposes the validation helper and a config setter so this logic is covered. + describe('Validation & storage (mock-backed)', function () { + const TEE_TYPE = 1 + const pcrs = { + pcr0: '0x' + Buffer.alloc(48, 0x11).toString('hex'), + pcr1: '0x' + Buffer.alloc(48, 0x22).toString('hex'), + pcr2: '0x' + Buffer.alloc(48, 0x33).toString('hex') + } + let mock, mockKey, mockTeeId + + before(async function () { + mock = await MockTEERegistry.new() + const TEE_OPERATOR_ROLE = await mock.TEE_OPERATOR() + await mock.grantRole(TEE_OPERATOR_ROLE, teeOperator) + await mock.addTEEType(TEE_TYPE, 'AWS Nitro') + await mock.approvePCR(pcrs, 'v1.0.0', TEE_TYPE) + + const keyPair = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'der' } + }) + mockKey = '0x' + keyPair.publicKey.toString('hex') + mockTeeId = await mock.computeTEEId(mockKey) + }) + + it('should reject an empty OHTTP public key', async function () { + // Reverts with the custom error OHTTPConfigInvalid; truffle-assertions + // can only see the bare revert, so we assert on that. + await truffleAssert.reverts( + mock.validateOHTTPConfigForTesting( + mockTeeId, + mockKey, + ohttpArgs(makeOHTTPConfig({ publicKey: '0x' })) + ) + ) + + console.log('✓ Empty OHTTP public key rejected') + }) + + it('should reject an empty OHTTP key config', async function () { + await truffleAssert.reverts( + mock.validateOHTTPConfigForTesting( + mockTeeId, + mockKey, + ohttpArgs(makeOHTTPConfig({ keyConfig: '0x' })) + ) + ) + + console.log('✓ Empty OHTTP key config rejected') + }) + + it('should reject an X25519 public key of the wrong size', async function () { + // kemId 32 == X25519_HKDF_SHA256 requires a 32-byte public key. + await truffleAssert.reverts( + mock.validateOHTTPConfigForTesting( + mockTeeId, + mockKey, + ohttpArgs(makeOHTTPConfig({ + kemId: 32, + publicKey: '0x' + Buffer.alloc(31, 0xA0).toString('hex') + })) + ) + ) + + console.log('✓ Mismatched X25519 public key size rejected') + }) + + it('should store and return the OHTTP config for a registered TEE', async function () { + const pcrHash = await mock.computePCRHash(pcrs) + await mock.registerTEEForTesting( + mockKey, + '0x' + Buffer.alloc(64, 0xCC).toString('hex'), + user1, + 'https://tee.example.com', + TEE_TYPE, + pcrHash, + { from: teeOperator } + ) + + const config = makeOHTTPConfig() + await mock.setOHTTPConfigForTesting(mockTeeId, ohttpArgs(config), { from: teeOperator }) + + const stored = await mock.getOHTTPConfig(mockTeeId) + expect(Number(stored.keyId)).to.equal(config.keyId) + expect(Number(stored.kemId)).to.equal(config.kemId) + expect(Number(stored.kdfId)).to.equal(config.kdfId) + expect(Number(stored.aeadId)).to.equal(config.aeadId) + expect(stored.publicKey).to.equal(config.publicKey) + expect(stored.keyConfig).to.equal(config.keyConfig) + expect(stored.signature).to.equal(config.signature) + expect(Number(stored.registeredAt)).to.be.greaterThan(0) + + console.log('✓ Stored OHTTP config retrieved correctly') + }) + }) + }) + describe('Query Functions', function () { it('should compute TEE ID correctly', async function () { const testKey = '0x0102030405' diff --git a/tests/solidity/suites/tee/truffle-config.js b/tests/solidity/suites/tee/truffle-config.js index 1413c442..fb9109b4 100644 --- a/tests/solidity/suites/tee/truffle-config.js +++ b/tests/solidity/suites/tee/truffle-config.js @@ -30,7 +30,8 @@ module.exports = { optimizer: { enabled: true, runs: 200 - } + }, + viaIR: true } } }