From 61d261f92157098aef5b6eb85a423cf3fed9a56f Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Fri, 22 May 2026 17:49:05 +0530 Subject: [PATCH 01/14] ohttp registry changes --- contracts/solidity/TEERegistry.sol | 2 +- contracts/solidity/TEERegistryV2.sol | 261 +++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 contracts/solidity/TEERegistryV2.sol diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 1eecd123..673a1f79 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -342,7 +342,7 @@ contract TEERegistry is AccessControl { address paymentAddress, string calldata endpoint, uint8 teeType - ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { + ) public virtual onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { // Validate TEE type if (!isValidTEEType(teeType)) revert InvalidTEEType(); diff --git a/contracts/solidity/TEERegistryV2.sol b/contracts/solidity/TEERegistryV2.sol new file mode 100644 index 00000000..c3d21833 --- /dev/null +++ b/contracts/solidity/TEERegistryV2.sol @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./TEERegistry.sol"; + +/// @title TEERegistryV2 - TEE registry with on-chain OHTTP config +/// @notice Extends TEERegistry with an attested-signing-key-backed OHTTP config +/// record. This avoids changing the TEE verifier precompile: the Nitro +/// attestation still binds the TEE signing key, and that signing key +/// signs the OHTTP/HPKE config before it is accepted on-chain. +contract TEERegistryV2 is TEERegistry { + 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; + + struct OHTTPConfig { + uint8 keyId; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + bytes publicKey; + bytes keyConfig; + uint256 registeredAt; + uint256 updatedAt; + } + + struct TEEOHTTPRecord { + bytes32 teeId; + TEEInfo tee; + OHTTPConfig ohttpConfig; + } + + mapping(bytes32 => OHTTPConfig) private _ohttpConfigs; + + event OHTTPConfigUpdated( + bytes32 indexed teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes32 publicKeyHash, + bytes32 keyConfigHash + ); + event OHTTPConfigCleared(bytes32 indexed teeId); + + error OHTTPConfigNotFound(); + error OHTTPConfigInvalid(); + error OHTTPConfigSignatureInvalid(); + + /// @notice Register a TEE using the original attestation flow, then attach + /// a signed OHTTP config in the same transaction. + /// @dev This keeps the precompile unchanged. The OHTTP config is verified + /// using verifyRSAPSS against the signing public key that was bound by + /// the Nitro attestation. + function registerTEEWithAttestationAndOHTTPConfig( + bytes calldata attestationDocument, + bytes calldata signingPublicKey, + bytes calldata tlsCertificate, + address paymentAddress, + string calldata endpoint, + uint8 teeType, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { + teeId = registerTEEWithAttestation( + attestationDocument, + signingPublicKey, + tlsCertificate, + paymentAddress, + endpoint, + teeType + ); + + _setOHTTPConfig( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig, + ohttpConfigSignature + ); + } + + /// @notice Set or rotate the OHTTP config for a registered TEE. + /// @dev The caller must own the TEE or be an admin, and the config must be + /// signed by the TEE signing key stored in the base registry. + function setOHTTPConfig( + bytes32 teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) external onlyTEEOwnerOrAdmin(teeId) { + _setOHTTPConfig( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig, + ohttpConfigSignature + ); + } + + function clearOHTTPConfig(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { + if (_ohttpConfigs[teeId].registeredAt == 0) revert OHTTPConfigNotFound(); + delete _ohttpConfigs[teeId]; + emit OHTTPConfigCleared(teeId); + } + + function getOHTTPConfig(bytes32 teeId) external view returns (OHTTPConfig memory) { + OHTTPConfig memory config = _ohttpConfigs[teeId]; + if (config.registeredAt == 0) revert OHTTPConfigNotFound(); + return config; + } + + function hasOHTTPConfig(bytes32 teeId) external view returns (bool) { + return _ohttpConfigs[teeId].registeredAt != 0; + } + + function getTEEWithOHTTPConfig( + bytes32 teeId + ) external view returns (TEEInfo memory tee, OHTTPConfig memory ohttpConfig) { + tee = tees[teeId]; + if (tee.registeredAt == 0) revert TEENotFound(); + + ohttpConfig = _ohttpConfigs[teeId]; + if (ohttpConfig.registeredAt == 0) revert OHTTPConfigNotFound(); + } + + function getActiveTEERecordsWithOHTTPConfig( + uint8 teeType + ) external view returns (TEEOHTTPRecord[] memory) { + bytes32[] memory teeIds = this.getEnabledTEEs(teeType); + uint256 count = 0; + + for (uint256 i = 0; i < teeIds.length; i++) { + if (_isActiveWithOHTTPConfig(teeIds[i])) count++; + } + + TEEOHTTPRecord[] memory result = new TEEOHTTPRecord[](count); + uint256 j = 0; + for (uint256 i = 0; i < teeIds.length; i++) { + bytes32 teeId = teeIds[i]; + if (_isActiveWithOHTTPConfig(teeId)) { + result[j++] = TEEOHTTPRecord({ + teeId: teeId, + tee: tees[teeId], + ohttpConfig: _ohttpConfigs[teeId] + }); + } + } + + return result; + } + + 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) + ) + ); + } + + function _setOHTTPConfig( + bytes32 teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) private { + TEEInfo storage tee = tees[teeId]; + if (tee.registeredAt == 0) revert TEENotFound(); + if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { + revert OHTTPConfigInvalid(); + } + if ( + kemId == KEM_ID_X25519_HKDF_SHA256 && + ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE + ) { + revert OHTTPConfigInvalid(); + } + + bytes32 configHash = computeOHTTPConfigHash( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig + ); + bool valid = VERIFIER.verifyRSAPSS(tee.publicKey, configHash, ohttpConfigSignature); + if (!valid) revert OHTTPConfigSignatureInvalid(); + + OHTTPConfig storage config = _ohttpConfigs[teeId]; + uint256 registeredAt = config.registeredAt == 0 ? block.timestamp : config.registeredAt; + + _ohttpConfigs[teeId] = OHTTPConfig({ + keyId: keyId, + kemId: kemId, + kdfId: kdfId, + aeadId: aeadId, + publicKey: ohttpPublicKey, + keyConfig: ohttpKeyConfig, + registeredAt: registeredAt, + updatedAt: block.timestamp + }); + + emit OHTTPConfigUpdated( + teeId, + keyId, + kemId, + kdfId, + aeadId, + keccak256(ohttpPublicKey), + keccak256(ohttpKeyConfig) + ); + } + + function _isActiveWithOHTTPConfig(bytes32 teeId) private view returns (bool) { + TEEInfo storage tee = tees[teeId]; + if (tee.registeredAt == 0) return false; + if (!tee.enabled) return false; + if (block.timestamp < tee.lastHeartbeatAt) return false; + if (block.timestamp - tee.lastHeartbeatAt > heartbeatMaxAge) return false; + if (!isPCRApproved(tee.teeType, tee.pcrHash)) return false; + if (_ohttpConfigs[teeId].registeredAt == 0) return false; + return true; + } +} From b81a7090a3a01d82edef71a5a43152d144226e2b Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Fri, 22 May 2026 18:44:25 +0530 Subject: [PATCH 02/14] registration client changes --- scripts/tee-mgmt-cli/cmd/tee.go | 45 ++++- scripts/tee-mgmt-cli/registry/client.go | 208 ++++++++++++++++++------ 2 files changed, 204 insertions(+), 49 deletions(-) diff --git a/scripts/tee-mgmt-cli/cmd/tee.go b/scripts/tee-mgmt-cli/cmd/tee.go index 7c66518b..32a8b45f 100644 --- a/scripts/tee-mgmt-cli/cmd/tee.go +++ b/scripts/tee-mgmt-cli/cmd/tee.go @@ -109,6 +109,7 @@ var teeRegisterCmd = &cobra.Command{ endpoint = fmt.Sprintf("https://%s", enclaveHost) } teeType, _ := cmd.Flags().GetUint8("tee-type") + skipOHTTPConfig, _ := cmd.Flags().GetBool("skip-ohttp-config") fmt.Println("=== Registering TEE ===") fmt.Printf(" Enclave: %s:%s\n", enclaveHost, enclavePort) @@ -132,6 +133,25 @@ var teeRegisterCmd = &cobra.Command{ } fmt.Printf(" Signing Key: %d bytes\n", len(signingKey)) + var ohttpConfig *registry.OHTTPConfig + if !skipOHTTPConfig { + registry.Log("Fetching signed OHTTP config...") + ohttpConfig, err = registry.FetchOHTTPConfig(enclaveHost) + if err != nil { + return fmt.Errorf("failed to fetch signed OHTTP config: %w", err) + } + 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) if err != nil { @@ -146,7 +166,29 @@ var teeRegisterCmd = &cobra.Command{ } registry.Log("Sending registration transaction...") - txHash, err := client.RegisterTEE(account, attestBytes, signingKey, tlsCert, paymentAddr, endpoint, teeType) + var txHash string + if ohttpConfig != nil { + txHash, err = client.RegisterTEEWithOHTTPConfig( + account, + attestBytes, + signingKey, + tlsCert, + paymentAddr, + endpoint, + teeType, + *ohttpConfig, + ) + } else { + txHash, err = client.RegisterTEE( + account, + attestBytes, + signingKey, + tlsCert, + paymentAddr, + endpoint, + teeType, + ) + } if err != nil { return fmt.Errorf("failed to register: %w", err) } @@ -268,6 +310,7 @@ func init() { teeRegisterCmd.Flags().String("payment-address", "", "Payment address for the TEE (defaults to sender)") teeRegisterCmd.Flags().String("endpoint", "", "Public endpoint URL for the TEE (defaults to https://)") teeRegisterCmd.Flags().Uint8("tee-type", 0, "TEE type ID (e.g. 0=LLMProxy, 1=Validator)") + teeRegisterCmd.Flags().Bool("skip-ohttp-config", false, "Use the legacy registration method without signed OHTTP config") teeRegisterCmd.MarkFlagRequired("enclave-host") teeActiveCmd.Flags().Uint8("tee-type", 0, "TEE type ID to list") diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 94c6a376..4eeff688 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -32,39 +32,40 @@ 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)"))[:4] + selRegisterTEEOHTTP = crypto.Keccak256([]byte("registerTEEWithAttestationAndOHTTPConfig(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 } @@ -91,6 +92,26 @@ 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 +} + +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 { @@ -178,28 +199,28 @@ 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"` }) 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), }, nil } @@ -215,6 +236,52 @@ func (c *Client) RegisterTEE(from string, attestation, signingKey, tlsCert []byt return c.sendTx(from, append(selRegisterTEE, encoded...)) } +func (c *Client) RegisterTEEWithOHTTPConfig( + 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) + u16T, _ := abi.NewType("uint16", "", nil) + + args := abi.Arguments{ + {Type: bytesT}, + {Type: bytesT}, + {Type: bytesT}, + {Type: addrT}, + {Type: strT}, + {Type: u8T}, + {Type: u8T}, + {Type: u16T}, + {Type: u16T}, + {Type: u16T}, + {Type: bytesT}, + {Type: bytesT}, + {Type: bytesT}, + } + encoded, _ := args.Pack( + attestation, + signingKey, + tlsCert, + common.HexToAddress(paymentAddr), + endpoint, + teeType, + ohttp.KeyID, + ohttp.KEMID, + ohttp.KDFID, + ohttp.AEADID, + ohttp.PublicKey, + ohttp.KeyConfig, + ohttp.Signature, + ) + return c.sendTx(from, append(selRegisterTEEOHTTP, encoded...)) +} + func (c *Client) DisableTEE(from string, teeId [32]byte) (string, error) { return c.sendTx(from, encodeBytes32(selDisableTEE, teeId)) } @@ -223,7 +290,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 +731,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 { From cc362233fbc43506b9a6dec6e563d171bee70b29 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Tue, 26 May 2026 21:05:18 +0530 Subject: [PATCH 03/14] removed v2 contract --- contracts/solidity/TEERegistry.sol | 197 +++++++++++++++++- contracts/solidity/TEERegistryV2.sol | 261 ------------------------ scripts/tee-mgmt-cli/Registration.md | 2 +- scripts/tee-mgmt-cli/cmd/tee.go | 66 +++--- scripts/tee-mgmt-cli/registry/client.go | 18 +- 5 files changed, 224 insertions(+), 320 deletions(-) delete mode 100644 contracts/solidity/TEERegistryV2.sol diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 673a1f79..68ee06cb 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -132,8 +132,29 @@ contract TEERegistry is AccessControl { uint256 lastHeartbeatAt; } + struct OHTTPConfig { + uint8 keyId; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + bytes publicKey; + bytes keyConfig; + uint256 registeredAt; + } + + struct TEEOHTTPRecord { + bytes32 teeId; + TEEInfo tee; + 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; @@ -166,6 +187,9 @@ contract TEERegistry is AccessControl { // TEEs by owner mapping(address => bytes32[]) internal _teesByOwner; + // OHTTP config by TEE id. Set once during TEE registration. + mapping(bytes32 => OHTTPConfig) private _ohttpConfigs; + // ============ Events ============ event TEETypeAdded(uint8 indexed typeId, string name); @@ -176,6 +200,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 +226,9 @@ contract TEERegistry is AccessControl { error HeartbeatSignatureInvalid(); error HeartbeatTimestampTooOld(); error HeartbeatTimestampInFuture(); + error OHTTPConfigNotFound(); + error OHTTPConfigInvalid(); + error OHTTPConfigSignatureInvalid(); // ============ Modifiers ============ @@ -336,13 +372,49 @@ contract TEERegistry is AccessControl { // ============ TEE Management ============ function registerTEEWithAttestation( + bytes calldata attestationDocument, + bytes calldata signingPublicKey, + bytes calldata tlsCertificate, + address paymentAddress, + string calldata endpoint, + uint8 teeType, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { + teeId = _registerTEEWithAttestation( + attestationDocument, + signingPublicKey, + tlsCertificate, + paymentAddress, + endpoint, + teeType + ); + + _setOHTTPConfig( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig, + ohttpConfigSignature + ); + } + + function _registerTEEWithAttestation( bytes calldata attestationDocument, bytes calldata signingPublicKey, bytes calldata tlsCertificate, address paymentAddress, string calldata endpoint, uint8 teeType - ) public virtual onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { + ) private returns (bytes32 teeId) { // Validate TEE type if (!isValidTEEType(teeType)) revert InvalidTEEType(); @@ -385,6 +457,61 @@ contract TEERegistry is AccessControl { emit TEERegistered(teeId, msg.sender, teeType); } + function _setOHTTPConfig( + bytes32 teeId, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) private { + TEEInfo storage tee = tees[teeId]; + if (tee.registeredAt == 0) revert TEENotFound(); + if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { + revert OHTTPConfigInvalid(); + } + if ( + kemId == KEM_ID_X25519_HKDF_SHA256 && + ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE + ) { + revert OHTTPConfigInvalid(); + } + + bytes32 configHash = computeOHTTPConfigHash( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig + ); + bool valid = VERIFIER.verifyRSAPSS(tee.publicKey, configHash, ohttpConfigSignature); + if (!valid) revert OHTTPConfigSignatureInvalid(); + + _ohttpConfigs[teeId] = OHTTPConfig({ + keyId: keyId, + kemId: kemId, + kdfId: kdfId, + aeadId: aeadId, + publicKey: ohttpPublicKey, + keyConfig: ohttpKeyConfig, + registeredAt: block.timestamp + }); + + emit OHTTPConfigRegistered( + teeId, + keyId, + kemId, + kdfId, + aeadId, + keccak256(ohttpPublicKey), + keccak256(ohttpKeyConfig) + ); + } + /// @notice Disable a TEE, removing it from the enabled list /// @dev Requires caller to be the TEE owner with TEE_OPERATOR role, or an admin /// @param teeId The TEE identifier to disable @@ -488,6 +615,26 @@ contract TEERegistry is AccessControl { return tees[teeId]; } + function getOHTTPConfig(bytes32 teeId) external view returns (OHTTPConfig memory) { + OHTTPConfig memory config = _ohttpConfigs[teeId]; + if (config.registeredAt == 0) revert OHTTPConfigNotFound(); + return config; + } + + function hasOHTTPConfig(bytes32 teeId) external view returns (bool) { + return _ohttpConfigs[teeId].registeredAt != 0; + } + + function getTEEWithOHTTPConfig( + bytes32 teeId + ) external view returns (TEEInfo memory tee, OHTTPConfig memory ohttpConfig) { + tee = tees[teeId]; + if (tee.registeredAt == 0) revert TEENotFound(); + + ohttpConfig = _ohttpConfigs[teeId]; + if (ohttpConfig.registeredAt == 0) revert OHTTPConfigNotFound(); + } + /// @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. @@ -519,6 +666,31 @@ contract TEERegistry is AccessControl { return result; } + function getActiveTEERecordsWithOHTTPConfig( + uint8 teeType + ) external view returns (TEEOHTTPRecord[] memory) { + bytes32[] storage list = _enabledTEEList[teeType]; + uint256 count = 0; + for (uint256 i = 0; i < list.length; i++) { + bytes32 teeId = list[i]; + if (_isTEEActive(tees[teeId]) && _ohttpConfigs[teeId].registeredAt != 0) count++; + } + + TEEOHTTPRecord[] memory result = new TEEOHTTPRecord[](count); + uint256 j = 0; + for (uint256 i = 0; i < list.length; i++) { + bytes32 teeId = list[i]; + if (_isTEEActive(tees[teeId]) && _ohttpConfigs[teeId].registeredAt != 0) { + result[j++] = TEEOHTTPRecord({ + teeId: teeId, + tee: tees[teeId], + ohttpConfig: _ohttpConfigs[teeId] + }); + } + } + return result; + } + /// @notice Get all TEE IDs (enabled and disabled) for a given type /// @param teeType The TEE type to query /// @return Array of TEE IDs @@ -561,4 +733,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/contracts/solidity/TEERegistryV2.sol b/contracts/solidity/TEERegistryV2.sol deleted file mode 100644 index c3d21833..00000000 --- a/contracts/solidity/TEERegistryV2.sol +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./TEERegistry.sol"; - -/// @title TEERegistryV2 - TEE registry with on-chain OHTTP config -/// @notice Extends TEERegistry with an attested-signing-key-backed OHTTP config -/// record. This avoids changing the TEE verifier precompile: the Nitro -/// attestation still binds the TEE signing key, and that signing key -/// signs the OHTTP/HPKE config before it is accepted on-chain. -contract TEERegistryV2 is TEERegistry { - 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; - - struct OHTTPConfig { - uint8 keyId; - uint16 kemId; - uint16 kdfId; - uint16 aeadId; - bytes publicKey; - bytes keyConfig; - uint256 registeredAt; - uint256 updatedAt; - } - - struct TEEOHTTPRecord { - bytes32 teeId; - TEEInfo tee; - OHTTPConfig ohttpConfig; - } - - mapping(bytes32 => OHTTPConfig) private _ohttpConfigs; - - event OHTTPConfigUpdated( - bytes32 indexed teeId, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes32 publicKeyHash, - bytes32 keyConfigHash - ); - event OHTTPConfigCleared(bytes32 indexed teeId); - - error OHTTPConfigNotFound(); - error OHTTPConfigInvalid(); - error OHTTPConfigSignatureInvalid(); - - /// @notice Register a TEE using the original attestation flow, then attach - /// a signed OHTTP config in the same transaction. - /// @dev This keeps the precompile unchanged. The OHTTP config is verified - /// using verifyRSAPSS against the signing public key that was bound by - /// the Nitro attestation. - function registerTEEWithAttestationAndOHTTPConfig( - bytes calldata attestationDocument, - bytes calldata signingPublicKey, - bytes calldata tlsCertificate, - address paymentAddress, - string calldata endpoint, - uint8 teeType, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature - ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { - teeId = registerTEEWithAttestation( - attestationDocument, - signingPublicKey, - tlsCertificate, - paymentAddress, - endpoint, - teeType - ); - - _setOHTTPConfig( - teeId, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig, - ohttpConfigSignature - ); - } - - /// @notice Set or rotate the OHTTP config for a registered TEE. - /// @dev The caller must own the TEE or be an admin, and the config must be - /// signed by the TEE signing key stored in the base registry. - function setOHTTPConfig( - bytes32 teeId, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature - ) external onlyTEEOwnerOrAdmin(teeId) { - _setOHTTPConfig( - teeId, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig, - ohttpConfigSignature - ); - } - - function clearOHTTPConfig(bytes32 teeId) external onlyTEEOwnerOrAdmin(teeId) { - if (_ohttpConfigs[teeId].registeredAt == 0) revert OHTTPConfigNotFound(); - delete _ohttpConfigs[teeId]; - emit OHTTPConfigCleared(teeId); - } - - function getOHTTPConfig(bytes32 teeId) external view returns (OHTTPConfig memory) { - OHTTPConfig memory config = _ohttpConfigs[teeId]; - if (config.registeredAt == 0) revert OHTTPConfigNotFound(); - return config; - } - - function hasOHTTPConfig(bytes32 teeId) external view returns (bool) { - return _ohttpConfigs[teeId].registeredAt != 0; - } - - function getTEEWithOHTTPConfig( - bytes32 teeId - ) external view returns (TEEInfo memory tee, OHTTPConfig memory ohttpConfig) { - tee = tees[teeId]; - if (tee.registeredAt == 0) revert TEENotFound(); - - ohttpConfig = _ohttpConfigs[teeId]; - if (ohttpConfig.registeredAt == 0) revert OHTTPConfigNotFound(); - } - - function getActiveTEERecordsWithOHTTPConfig( - uint8 teeType - ) external view returns (TEEOHTTPRecord[] memory) { - bytes32[] memory teeIds = this.getEnabledTEEs(teeType); - uint256 count = 0; - - for (uint256 i = 0; i < teeIds.length; i++) { - if (_isActiveWithOHTTPConfig(teeIds[i])) count++; - } - - TEEOHTTPRecord[] memory result = new TEEOHTTPRecord[](count); - uint256 j = 0; - for (uint256 i = 0; i < teeIds.length; i++) { - bytes32 teeId = teeIds[i]; - if (_isActiveWithOHTTPConfig(teeId)) { - result[j++] = TEEOHTTPRecord({ - teeId: teeId, - tee: tees[teeId], - ohttpConfig: _ohttpConfigs[teeId] - }); - } - } - - return result; - } - - 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) - ) - ); - } - - function _setOHTTPConfig( - bytes32 teeId, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature - ) private { - TEEInfo storage tee = tees[teeId]; - if (tee.registeredAt == 0) revert TEENotFound(); - if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { - revert OHTTPConfigInvalid(); - } - if ( - kemId == KEM_ID_X25519_HKDF_SHA256 && - ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE - ) { - revert OHTTPConfigInvalid(); - } - - bytes32 configHash = computeOHTTPConfigHash( - teeId, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig - ); - bool valid = VERIFIER.verifyRSAPSS(tee.publicKey, configHash, ohttpConfigSignature); - if (!valid) revert OHTTPConfigSignatureInvalid(); - - OHTTPConfig storage config = _ohttpConfigs[teeId]; - uint256 registeredAt = config.registeredAt == 0 ? block.timestamp : config.registeredAt; - - _ohttpConfigs[teeId] = OHTTPConfig({ - keyId: keyId, - kemId: kemId, - kdfId: kdfId, - aeadId: aeadId, - publicKey: ohttpPublicKey, - keyConfig: ohttpKeyConfig, - registeredAt: registeredAt, - updatedAt: block.timestamp - }); - - emit OHTTPConfigUpdated( - teeId, - keyId, - kemId, - kdfId, - aeadId, - keccak256(ohttpPublicKey), - keccak256(ohttpKeyConfig) - ); - } - - function _isActiveWithOHTTPConfig(bytes32 teeId) private view returns (bool) { - TEEInfo storage tee = tees[teeId]; - if (tee.registeredAt == 0) return false; - if (!tee.enabled) return false; - if (block.timestamp < tee.lastHeartbeatAt) return false; - if (block.timestamp - tee.lastHeartbeatAt > heartbeatMaxAge) return false; - if (!isPCRApproved(tee.teeType, tee.pcrHash)) return false; - if (_ohttpConfigs[teeId].registeredAt == 0) return false; - return true; - } -} diff --git a/scripts/tee-mgmt-cli/Registration.md b/scripts/tee-mgmt-cli/Registration.md index 6a2817b4..5074a1ad 100644 --- a/scripts/tee-mgmt-cli/Registration.md +++ b/scripts/tee-mgmt-cli/Registration.md @@ -96,7 +96,7 @@ Verify PCR is approved: 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)` +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 diff --git a/scripts/tee-mgmt-cli/cmd/tee.go b/scripts/tee-mgmt-cli/cmd/tee.go index 32a8b45f..4153b543 100644 --- a/scripts/tee-mgmt-cli/cmd/tee.go +++ b/scripts/tee-mgmt-cli/cmd/tee.go @@ -109,7 +109,6 @@ var teeRegisterCmd = &cobra.Command{ endpoint = fmt.Sprintf("https://%s", enclaveHost) } teeType, _ := cmd.Flags().GetUint8("tee-type") - skipOHTTPConfig, _ := cmd.Flags().GetBool("skip-ohttp-config") fmt.Println("=== Registering TEE ===") fmt.Printf(" Enclave: %s:%s\n", enclaveHost, enclavePort) @@ -133,24 +132,21 @@ var teeRegisterCmd = &cobra.Command{ } fmt.Printf(" Signing Key: %d bytes\n", len(signingKey)) - var ohttpConfig *registry.OHTTPConfig - if !skipOHTTPConfig { - registry.Log("Fetching signed OHTTP config...") - ohttpConfig, err = registry.FetchOHTTPConfig(enclaveHost) - if err != nil { - return fmt.Errorf("failed to fetch signed OHTTP config: %w", err) - } - 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 signed OHTTP config...") + ohttpConfig, err := registry.FetchOHTTPConfig(enclaveHost) + if err != nil { + return fmt.Errorf("failed to fetch signed OHTTP config: %w", err) } + 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) @@ -166,29 +162,16 @@ var teeRegisterCmd = &cobra.Command{ } registry.Log("Sending registration transaction...") - var txHash string - if ohttpConfig != nil { - txHash, err = client.RegisterTEEWithOHTTPConfig( - account, - attestBytes, - signingKey, - tlsCert, - paymentAddr, - endpoint, - teeType, - *ohttpConfig, - ) - } else { - 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) } @@ -310,7 +293,6 @@ func init() { teeRegisterCmd.Flags().String("payment-address", "", "Payment address for the TEE (defaults to sender)") teeRegisterCmd.Flags().String("endpoint", "", "Public endpoint URL for the TEE (defaults to https://)") teeRegisterCmd.Flags().Uint8("tee-type", 0, "TEE type ID (e.g. 0=LLMProxy, 1=Validator)") - teeRegisterCmd.Flags().Bool("skip-ohttp-config", false, "Use the legacy registration method without signed OHTTP config") teeRegisterCmd.MarkFlagRequired("enclave-host") teeActiveCmd.Flags().Uint8("tee-type", 0, "TEE type ID to list") diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 4eeff688..4e79a3b8 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -43,8 +43,7 @@ var ( 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] - selRegisterTEEOHTTP = crypto.Keccak256([]byte("registerTEEWithAttestationAndOHTTPConfig(bytes,bytes,bytes,address,string,uint8,uint8,uint16,uint16,uint16,bytes,bytes,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] @@ -225,18 +224,7 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { }, nil } -func (c *Client) RegisterTEE(from string, attestation, signingKey, tlsCert []byte, paymentAddr, endpoint string, teeType uint8) (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) - return c.sendTx(from, append(selRegisterTEE, encoded...)) -} - -func (c *Client) RegisterTEEWithOHTTPConfig( +func (c *Client) RegisterTEE( from string, attestation, signingKey, tlsCert []byte, paymentAddr, endpoint string, @@ -279,7 +267,7 @@ func (c *Client) RegisterTEEWithOHTTPConfig( ohttp.KeyConfig, ohttp.Signature, ) - return c.sendTx(from, append(selRegisterTEEOHTTP, encoded...)) + return c.sendTx(from, append(selRegisterTEE, encoded...)) } func (c *Client) DisableTEE(from string, teeId [32]byte) (string, error) { From e1282d19c706e7b2c9200c0a6bda554ceabe40af Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 00:13:07 +0530 Subject: [PATCH 04/14] contract test --- .../suites/tee/contracts/TEETestHelper.sol | 18 +++- tests/solidity/suites/tee/test/registry.js | 98 +++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/tests/solidity/suites/tee/contracts/TEETestHelper.sol b/tests/solidity/suites/tee/contracts/TEETestHelper.sol index 4d522911..e9946871 100644 --- a/tests/solidity/suites/tee/contracts/TEETestHelper.sol +++ b/tests/solidity/suites/tee/contracts/TEETestHelper.sol @@ -84,7 +84,14 @@ contract TEETestHelper { bytes calldata tlsCertificate, address paymentAddress, string calldata endpoint, - uint8 teeType + uint8 teeType, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature ) external returns (bytes32 teeId) { return registry.registerTEEWithAttestation( attestationDocument, @@ -92,7 +99,14 @@ contract TEETestHelper { tlsCertificate, paymentAddress, endpoint, - teeType + teeType, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig, + ohttpConfigSignature ); } diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index 43b5f236..7a15e76e 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -8,6 +8,31 @@ 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 +308,7 @@ contract('TEERegistry', function (accounts) { user1, 'https://tee.example.com', 1, + ...ohttpArgs(), { from: user1 } ) ) @@ -302,6 +328,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 99, // Invalid type + ...ohttpArgs(), { from: teeOperator } ) ) @@ -321,6 +348,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 1, + ...ohttpArgs(), { from: teeOperator } ) ) @@ -329,6 +357,76 @@ 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 report missing OHTTP config for unknown TEE', async function () { + const nonExistentId = web3.utils.keccak256('0xBEEF') + + expect(await registry.hasOHTTPConfig(nonExistentId)).to.be.false + + await truffleAssert.reverts( + registry.getOHTTPConfig(nonExistentId) + ) + + console.log('✓ Missing OHTTP config handled correctly') + }) + + it('should return empty active OHTTP records for unused type', async function () { + const records = await registry.getActiveTEERecordsWithOHTTPConfig(50) + expect(records.length).to.equal(0) + + console.log('✓ Empty OHTTP active records returned for unused type') + }) + }) + describe('Query Functions', function () { it('should compute TEE ID correctly', async function () { const testKey = '0x0102030405' From 3bfb55d0799907f117d00a91cae25e325f4f2ea6 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 16:15:30 +0530 Subject: [PATCH 05/14] contract cleanup --- contracts/solidity/TEERegistry.sol | 172 +++++------------- scripts/tee-mgmt-cli/registry/client.go | 50 ++++- .../suites/tee/contracts/MockTEERegistry.sol | 11 +- tests/solidity/suites/tee/test/registry.js | 13 +- 4 files changed, 98 insertions(+), 148 deletions(-) diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 68ee06cb..673e9ba2 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,16 @@ contract TEERegistry is AccessControl { uint256 addedAt; } + struct OHTTPConfig { + uint8 keyId; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + bytes publicKey; + bytes keyConfig; + uint256 registeredAt; + } + struct TEEInfo { address owner; address paymentAddress; @@ -130,21 +141,6 @@ contract TEERegistry is AccessControl { bool enabled; uint256 registeredAt; uint256 lastHeartbeatAt; - } - - struct OHTTPConfig { - uint8 keyId; - uint16 kemId; - uint16 kdfId; - uint16 aeadId; - bytes publicKey; - bytes keyConfig; - uint256 registeredAt; - } - - struct TEEOHTTPRecord { - bytes32 teeId; - TEEInfo tee; OHTTPConfig ohttpConfig; } @@ -187,9 +183,6 @@ contract TEERegistry is AccessControl { // TEEs by owner mapping(address => bytes32[]) internal _teesByOwner; - // OHTTP config by TEE id. Set once during TEE registration. - mapping(bytes32 => OHTTPConfig) private _ohttpConfigs; - // ============ Events ============ event TEETypeAdded(uint8 indexed typeId, string name); @@ -226,7 +219,6 @@ contract TEERegistry is AccessControl { error HeartbeatSignatureInvalid(); error HeartbeatTimestampTooOld(); error HeartbeatTimestampInFuture(); - error OHTTPConfigNotFound(); error OHTTPConfigInvalid(); error OHTTPConfigSignatureInvalid(); @@ -386,35 +378,6 @@ contract TEERegistry is AccessControl { bytes calldata ohttpKeyConfig, bytes calldata ohttpConfigSignature ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { - teeId = _registerTEEWithAttestation( - attestationDocument, - signingPublicKey, - tlsCertificate, - paymentAddress, - endpoint, - teeType - ); - - _setOHTTPConfig( - teeId, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig, - ohttpConfigSignature - ); - } - - function _registerTEEWithAttestation( - bytes calldata attestationDocument, - bytes calldata signingPublicKey, - bytes calldata tlsCertificate, - address paymentAddress, - string calldata endpoint, - uint8 teeType - ) private returns (bytes32 teeId) { // Validate TEE type if (!isValidTEEType(teeType)) revert InvalidTEEType(); @@ -434,41 +397,6 @@ contract TEERegistry is AccessControl { // Verify PCR is approved and matches the TEE type _requirePCRValidForTEE(pcrHash, teeType); - // Store TEE - tees[teeId] = TEEInfo({ - owner: msg.sender, - paymentAddress: paymentAddress, - endpoint: endpoint, - publicKey: signingPublicKey, - tlsCertificate: tlsCertificate, - pcrHash: pcrHash, - teeType: teeType, - enabled: true, - registeredAt: block.timestamp, - lastHeartbeatAt: block.timestamp - }); - - // Add to indexes - _enabledTEEIndex[teeType][teeId] = _enabledTEEList[teeType].length; - _enabledTEEList[teeType].push(teeId); - _teesByType[teeType].push(teeId); - _teesByOwner[msg.sender].push(teeId); - - emit TEERegistered(teeId, msg.sender, teeType); - } - - function _setOHTTPConfig( - bytes32 teeId, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature - ) private { - TEEInfo storage tee = tees[teeId]; - if (tee.registeredAt == 0) revert TEENotFound(); if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { revert OHTTPConfigInvalid(); } @@ -488,10 +416,14 @@ contract TEERegistry is AccessControl { ohttpPublicKey, ohttpKeyConfig ); - bool valid = VERIFIER.verifyRSAPSS(tee.publicKey, configHash, ohttpConfigSignature); - if (!valid) revert OHTTPConfigSignatureInvalid(); + bool validConfig = VERIFIER.verifyRSAPSS( + signingPublicKey, + configHash, + ohttpConfigSignature + ); + if (!validConfig) revert OHTTPConfigSignatureInvalid(); - _ohttpConfigs[teeId] = OHTTPConfig({ + OHTTPConfig memory ohttpConfig = OHTTPConfig({ keyId: keyId, kemId: kemId, kdfId: kdfId, @@ -501,6 +433,28 @@ contract TEERegistry is AccessControl { registeredAt: block.timestamp }); + // Store TEE + tees[teeId] = TEEInfo({ + owner: msg.sender, + paymentAddress: paymentAddress, + endpoint: endpoint, + publicKey: signingPublicKey, + tlsCertificate: tlsCertificate, + pcrHash: pcrHash, + teeType: teeType, + enabled: true, + registeredAt: block.timestamp, + lastHeartbeatAt: block.timestamp, + ohttpConfig: ohttpConfig + }); + + // Add to indexes + _enabledTEEIndex[teeType][teeId] = _enabledTEEList[teeType].length; + _enabledTEEList[teeType].push(teeId); + _teesByType[teeType].push(teeId); + _teesByOwner[msg.sender].push(teeId); + + emit TEERegistered(teeId, msg.sender, teeType); emit OHTTPConfigRegistered( teeId, keyId, @@ -616,23 +570,8 @@ contract TEERegistry is AccessControl { } function getOHTTPConfig(bytes32 teeId) external view returns (OHTTPConfig memory) { - OHTTPConfig memory config = _ohttpConfigs[teeId]; - if (config.registeredAt == 0) revert OHTTPConfigNotFound(); - return config; - } - - function hasOHTTPConfig(bytes32 teeId) external view returns (bool) { - return _ohttpConfigs[teeId].registeredAt != 0; - } - - function getTEEWithOHTTPConfig( - bytes32 teeId - ) external view returns (TEEInfo memory tee, OHTTPConfig memory ohttpConfig) { - tee = tees[teeId]; - if (tee.registeredAt == 0) revert TEENotFound(); - - ohttpConfig = _ohttpConfigs[teeId]; - if (ohttpConfig.registeredAt == 0) revert OHTTPConfigNotFound(); + if (tees[teeId].registeredAt == 0) revert TEENotFound(); + return tees[teeId].ohttpConfig; } /// @notice Get TEE IDs that are currently enabled for a given type @@ -666,31 +605,6 @@ contract TEERegistry is AccessControl { return result; } - function getActiveTEERecordsWithOHTTPConfig( - uint8 teeType - ) external view returns (TEEOHTTPRecord[] memory) { - bytes32[] storage list = _enabledTEEList[teeType]; - uint256 count = 0; - for (uint256 i = 0; i < list.length; i++) { - bytes32 teeId = list[i]; - if (_isTEEActive(tees[teeId]) && _ohttpConfigs[teeId].registeredAt != 0) count++; - } - - TEEOHTTPRecord[] memory result = new TEEOHTTPRecord[](count); - uint256 j = 0; - for (uint256 i = 0; i < list.length; i++) { - bytes32 teeId = list[i]; - if (_isTEEActive(tees[teeId]) && _ohttpConfigs[teeId].registeredAt != 0) { - result[j++] = TEEOHTTPRecord({ - teeId: teeId, - tee: tees[teeId], - ohttpConfig: _ohttpConfigs[teeId] - }); - } - } - return result; - } - /// @notice Get all TEE IDs (enabled and disabled) for a given type /// @param teeType The TEE type to query /// @return Array of TEE IDs diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 4e79a3b8..11ebf385 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -66,6 +66,7 @@ type TEEInfo struct { IsEnabled bool RegisteredAt time.Time LastHeartbeatAt time.Time + OHTTPConfig OHTTPConfig } type AttestationResponse struct { @@ -92,13 +93,14 @@ type MeasurementsFile struct { } 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 + 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 { @@ -169,6 +171,21 @@ 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: "registeredAt", Type: "uint256"}, + } + + _, err = abi.NewType("tuple", "", ohttpComponents) + if err != nil { + return nil, fmt.Errorf("failed to create OHTTP ABI type: %v", err) + } + tupleType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ {Name: "owner", Type: "address"}, {Name: "paymentAddress", Type: "address"}, @@ -180,6 +197,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) @@ -208,6 +226,15 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { 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"` + RegisteredAt *big.Int `json:"registeredAt"` + } `json:"ohttpConfig"` }) return &TEEInfo{ @@ -221,6 +248,15 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { 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, + RegisteredAt: time.Unix(s.OhttpConfig.RegisteredAt.Int64(), 0), + }, }, nil } diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index c267b69b..73fba74b 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -36,7 +36,16 @@ 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: "", + registeredAt: 0 + }) }); // Add to indexes (matching registerTEE behavior) diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index 7a15e76e..dc6e2f0b 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -407,23 +407,14 @@ contract('TEERegistry', function (accounts) { console.log('✓ OHTTP config hash computation correct') }) - it('should report missing OHTTP config for unknown TEE', async function () { + it('should revert OHTTP config lookup for unknown TEE', async function () { const nonExistentId = web3.utils.keccak256('0xBEEF') - expect(await registry.hasOHTTPConfig(nonExistentId)).to.be.false - await truffleAssert.reverts( registry.getOHTTPConfig(nonExistentId) ) - console.log('✓ Missing OHTTP config handled correctly') - }) - - it('should return empty active OHTTP records for unused type', async function () { - const records = await registry.getActiveTEERecordsWithOHTTPConfig(50) - expect(records.length).to.equal(0) - - console.log('✓ Empty OHTTP active records returned for unused type') + console.log('✓ Unknown TEE OHTTP config lookup reverts correctly') }) }) From be454b40b9e0fdbe3112b1c064d7af430603d318 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 19:48:47 +0530 Subject: [PATCH 06/14] via ir config --- tests/solidity/suites/tee/truffle-config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 } } } From bf9ad98d456f5356889e03d64ae9d52da019753e Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 20:42:22 +0530 Subject: [PATCH 07/14] remove insecure unlock flag --- tests/jsonrpc/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) 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: From 1f23e4dda4954f0c9e47409112203417066d9539 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 21:27:43 +0530 Subject: [PATCH 08/14] testing --- tests/solidity/suites/tee/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From c1800d86fb4f019dcfef1dbc73f68615b16e62d9 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 22:49:54 +0530 Subject: [PATCH 09/14] review updates --- contracts/solidity/TEERegistry.json | 706 +++++++++++++++--- contracts/solidity/TEERegistry.sol | 78 +- scripts/integration/local_tee_workflow.go | 166 +++- scripts/tee-mgmt-cli/Registration.md | 13 +- scripts/tee-mgmt-cli/cmd/tee.go | 26 +- .../suites/tee/contracts/MockTEERegistry.sol | 23 + tests/solidity/suites/tee/test/registry.js | 64 +- 7 files changed, 886 insertions(+), 190 deletions(-) diff --git a/contracts/solidity/TEERegistry.json b/contracts/solidity/TEERegistry.json index bdd2b66f..e7a55507 100644 --- a/contracts/solidity/TEERegistry.json +++ b/contracts/solidity/TEERegistry.json @@ -8,27 +8,6 @@ "stateMutability": "nonpayable", "type": "constructor" }, - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, { "inputs": [ { @@ -40,6 +19,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 +57,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 +113,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 +214,12 @@ "internalType": "bytes32", "name": "pcrHash", "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "teeType", + "type": "uint8" } ], "name": "PCRRevoked", @@ -237,7 +310,7 @@ "type": "bytes32" } ], - "name": "TEEEnabled", + "name": "TEEDisabled", "type": "event" }, { @@ -250,7 +323,7 @@ "type": "bytes32" } ], - "name": "TEEDisabled", + "name": "TEEEnabled", "type": "event" }, { @@ -310,6 +383,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 +436,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 +506,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 +523,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", @@ -552,13 +637,126 @@ "type": "function" }, { - "inputs": [], - "name": "getApprovedPCRs", + "inputs": [ + { + "internalType": "bytes32", + "name": "teeId", + "type": "bytes32" + } + ], + "name": "enableTEE", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + } + ], + "name": "getActiveTEEs", "outputs": [ { - "internalType": "bytes32[]", + "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": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" + } + ], + "internalType": "struct TEERegistry.TEEInfo[]", "name": "", - "type": "bytes32[]" + "type": "tuple[]" } ], "stateMutability": "view", @@ -566,12 +764,24 @@ }, { "inputs": [], - "name": "getActiveTEEs", + "name": "getApprovedPCRs", "outputs": [ { - "internalType": "bytes32[]", + "components": [ + { + "internalType": "bytes32", + "name": "pcrHash", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "teeType", + "type": "uint8" + } + ], + "internalType": "struct TEERegistry.PCRKey[]", "name": "", - "type": "bytes32[]" + "type": "tuple[]" } ], "stateMutability": "view", @@ -580,17 +790,17 @@ { "inputs": [ { - "internalType": "bytes32", - "name": "teeId", - "type": "bytes32" + "internalType": "uint8", + "name": "teeType", + "type": "uint8" } ], - "name": "getPaymentAddress", + "name": "getEnabledTEEs", "outputs": [ { - "internalType": "address", + "internalType": "bytes32[]", "name": "", - "type": "address" + "type": "bytes32[]" } ], "stateMutability": "view", @@ -604,12 +814,49 @@ "type": "bytes32" } ], - "name": "getTEEPublicKey", + "name": "getOHTTPConfig", "outputs": [ { - "internalType": "bytes", + "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": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", "name": "", - "type": "bytes" + "type": "tuple" } ], "stateMutability": "view", @@ -695,6 +942,48 @@ "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": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" } ], "internalType": "struct TEERegistry.TEEInfo", @@ -705,6 +994,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 +1085,39 @@ "inputs": [ { "internalType": "bytes32", - "name": "teeId", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" } ], - "name": "getTLSCertificate", + "name": "hasRole", "outputs": [ { - "internalType": "bytes", + "internalType": "bool", "name": "", - "type": "bytes" + "type": "bool" } ], "stateMutability": "view", @@ -796,34 +1127,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 +1191,7 @@ "type": "bytes32" } ], - "name": "isTEEEnabled", + "name": "isTEEActive", "outputs": [ { "internalType": "bool", @@ -857,11 +1206,11 @@ "inputs": [ { "internalType": "bytes32", - "name": "pcrHash", + "name": "teeId", "type": "bytes32" } ], - "name": "isPCRApproved", + "name": "isTEEEnabled", "outputs": [ { "internalType": "bool", @@ -891,6 +1240,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 +1310,41 @@ "internalType": "uint8", "name": "teeType", "type": "uint8" + }, + { + "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": "ohttpPublicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "ohttpKeyConfig", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "ohttpConfigSignature", + "type": "bytes" } ], "name": "registerTEEWithAttestation", @@ -944,7 +1367,7 @@ }, { "internalType": "address", - "name": "callerConfirmation", + "name": "account", "type": "address" } ], @@ -1002,6 +1425,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxAge", + "type": "uint256" + } + ], + "name": "setHeartbeatMaxAge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1104,17 +1540,59 @@ "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": "uint256", + "name": "registeredAt", + "type": "uint256" + } + ], + "internalType": "struct TEERegistry.OHTTPConfig", + "name": "ohttpConfig", + "type": "tuple" } ], "stateMutability": "view", "type": "function" } ], - "bytecode": "0x608060405234620000965762000015336200009a565b50620000213362000118565b505f8051602062002d0e8339815191525f81815260208190527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbec805490829055604051927fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff8380a4612b479081620001a78239f35b5f80fd5b6001600160a01b03165f8181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff166200011457818052816020526040822081835260205260408220600160ff1982541617905533915f8051602062002cee8339815191528180a4600190565b5090565b6001600160a01b03165f8181527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb60205260408120549091905f8051602062002d0e8339815191529060ff16620001a157808352826020526040832082845260205260408320600160ff198254161790555f8051602062002cee833981519152339380a4600190565b50509056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146120dc5750806308c84e70146120c0578063097775c6146120a457806315c9bdb414611a08578063248a9ca3146119dc5780632f2ff15d146119a057806336568abe146119595780633c1a88b01461191f578063418b207d1461174257806343ed3274146115e9578063476cb030146115ab5780634b19d4631461152457806358db61a8146114dd5780635c36901c146114a85780639138c99e1461145257806391cc00e91461140857806391d14854146113c0578063971cfbf114610c90578063a217fddf14610c76578063b19ffd1c14610c09578063b1c551ca14610bbd578063b778f86914610b5f578063b82644b714610af2578063c0abde45146108a0578063c206baa314610874578063cbdfc4e01461066d578063ccdf049314610568578063d046a7fa146104f1578063d2e03427146103f7578063d547741f146103bb578063d5ed579d14610393578063d6741a421461031e578063dd31611314610300578063e10a2ea4146102c6578063e44cc48214610243578063f33d2c00146101f95763f44a0780146101b3575f80fd5b346101f5575f3660031901126101f5576101f16040516101dd816101d6816121f8565b0382612340565b604051918291602083526020830190612182565b0390f35b5f80fd5b346101f55760203660031901126101f5576004356001600160401b0381116101f55761023561022e602092369060040161212f565b3691612502565b818151910120604051908152f35b346101f5576020806003193601126101f5576004356001600160a01b038116908190036101f5575f526009815260405f209060405190818184549182815201935f52815f20915f905b8282106102af576101f1856102a381890382612340565b60405191829182612361565b83548652948501946001938401939091019061028c565b346101f55760203660031901126101f55760ff6102e1612172565b165f526001602052602060ff600160405f200154166040519015158152f35b346101f55760203660031901126101f55761031c6004356127b3565b005b346101f55760203660031901126101f55760ff610339612172565b165f52600160205261038260405f20604051906103618261035a8184612278565b0383612340565b600260ff600183015416910154604051938493606085526060850190612182565b911515602084015260408301520390f35b346101f55760203660031901126101f55760206103b16004356126c9565b6040519015158152f35b346101f55760403660031901126101f55761031c6004356103da61215c565b90805f525f6020526103f2600160405f2001546123f2565b61248f565b346101f5575f3660031901126101f5575f80600454905b8181106104b1575061041f82612613565b9161042d6040519384612340565b80835261043c601f1991612613565b013660208401375f805b82811061045b57604051806101f18682612361565b8061047961046b6104849361264c565b919054600392831b1c6126c9565b610489575b5061262a565b610446565b6104928261264c565b9054911b1c6104aa6104a38561262a565b9487612638565b528561047e565b6104c96104bd8261264c565b90549060031b1c6126c9565b6104dc575b6104d79061262a565b61040e565b916104e96104d79161262a565b9290506104ce565b346101f5575f3660031901126101f557604051600780548083525f918252602080840193927fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892915b828210610551576101f1856102a381890382612340565b83548652948501946001938401939091019061053a565b346101f55760203660031901126101f5576004355f52600660205260405f2060018060a01b038082541690600183015416916040516105ae816101d68160028601612278565b604051916105ca836105c38160038501612278565b0384612340565b60ff604051936105e8856105e18160048701612278565b0386612340565b61064460058401549561063660068601549361062860086007890154980154986040519c8d9c8d5260208d015260408c61014091829101528c0190612182565b908a820360608c0152612182565b9088820360808a0152612182565b9460a087015281811660c087015260081c16151560e08501526101008401526101208301520390f35b346101f55760403660031901126101f557610686612172565b6001600160401b03906024358281116101f5576106a960ff91369060040161212f565b9190926106b461239b565b1692835f52600192602091848352600260405f20015461086257604051906106db82612325565b6106e6368685612502565b82528382018681526040830191428352885f5287865260405f20935180519182116107d85761071f8261071987546121c0565b8761255d565b8690601f83116001146107f75791806002959492610768945f926107ec575b50505f19600383901b1c1916908a1b1785555b5115158885019060ff801983541691151516179055565b5191015560025490600160401b8210156107d8576107af827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107d394016002556125a2565b81549060031b9060ff89831b921b19161790556040519383859485528401916125f3565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508c8061073e565b899291601f19831691875f52895f20925f5b8b82821061084c575050916107689593918560029998969410610834575b505050811b018555610751565b01515f1960f88460031b161c191690558c8080610827565b8385015186558e97909501949384019301610809565b60405163095faf7360e01b8152600490fd5b346101f55760603660031901126101f5576020610898604435602435600435612ad8565b604051908152f35b346101f5576003196080368201126101f557600435906001600160401b03908183116101f55760609083360301126101f5576024358181116101f5576108ea90369060040161212f565b9091610903604435946108fb61239b565b60040161273c565b9380151580610adc575b610aa6575b506040519260808401848110838211176107d857604052600193848152602092600384830192428452604081015f815261094d368988612502565b94606083019586528a5f5283885261097860405f2093511515849060ff801983541691151516179055565b518983015551600282015501915180519182116107d85781906109a58261099f86546121c0565b8661255d565b8590601f8311600114610a47575f92610a3c575b50505f19600383901b1c191690861b1790555b60045490600160401b8210156107d85785610a29610a13847fa3cbee87ba6def0cc8a2c5a3cf7a54f0209c6f939b07979dde08d447d6c3666d986107d3960160045561264c565b819391549060031b91821b915f19901b19161790565b90556040519383859485528401916125f3565b0151905088806109b9565b90889350601f19831691855f52875f20925f5b89828210610a905750508411610a78575b505050811b0190556109cc565b01515f1960f88460031b161c19169055888080610a6b565b8385015186558c97909501949384019301610a5a565b606435420190814211610ac8575f526003602052600260405f20015584610912565b634e487b7160e01b5f52601160045260245ffd5b50805f52600360205260ff60405f20541661090d565b346101f5576020806003193601126101f55760ff610b0e612172565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610b48576101f1856102a381890382612340565b835486529485019460019384019390910190610b31565b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600460405f200160405192838092612278565b60405163f6523b6560e01b8152600490fd5b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600360405f200160405192838092612278565b346101f55760203660031901126101f5576004355f52600360205260405f2060ff8154166101f16003600184015493610c5160028201549161035a6040518095819301612278565b6040519485941515855260208501526040840152608060608401526080830190612182565b346101f5575f3660031901126101f55760206040515f8152f35b346101f5575f3660031901126101f5576040518060029182549081835260208093019283855f528282805f20925f5b8780601f83011061123857610ddf95549184828210611225575b82821061120f575b8282106111f9575b8282106111e3575b8282106111ce575b8282106111b8575b8282106111a2575b82821061118c575b828210611176575b828210611160575b82821061114a575b828210611134575b82821061111e575b828210611108575b8282106110f2575b8282106110dc575b8282106110c6575b8282106110b0575b82821061109a575b828210611084575b82821061106e575b828210611058575b828210611042575b82821061102c575b828210611016575b828210611000575b828210610fea575b828210610fd4575b828210610fbe575b828210610fa8575b828210610f92575b5010610f84575b50905096939295960382612340565b610de883612613565b92610df66040519485612340565b808452601f199283610e0783612613565b015f5b818110610f525750505f5b828110610ed557505050604051936040850191604086525180925260609182860196905f5b818110610ebe57505050848603818601528351918287528187019082808560051b8a01019601945f985b858a10610e715788880389f35b909192939495968580828585600195030188528a5190604080610e9b84518b85528b850190612182565b938581015115158685015201519101529901950199019892919095949395610e64565b825160ff1689529783019791830191600101610e3a565b80610ee7610f489299989596996125a2565b9060ff918291549060031b1c165f528360019182885260405f209060405193610f0f85612325565b604051610f20816101d68187612278565b85528201541615158884015201546040820152610f3d8289612638565b5261047e8188612638565b9693929596610e15565b968094959897604051610f6481612325565b606081525f838201525f604082015282828a010152019794939697610e0a565b60f81c815201839089610dd0565b6001919460ff8560f01c16815201930184610dc9565b6001919460ff8560e81c16815201930184610dc1565b6001919460ff8560e01c16815201930184610db9565b6001919460ff8560d81c16815201930184610db1565b6001919460ff8560d01c16815201930184610da9565b6001919460ff8560c81c16815201930184610da1565b6001919460ff8560c01c16815201930184610d99565b6001919460ff8560b81c16815201930184610d91565b6001919460ff8560b01c16815201930184610d89565b6001919460ff8560a81c16815201930184610d81565b6001919460ff8560a01c16815201930184610d79565b6001919460ff8560981c16815201930184610d71565b6001919460ff8560901c16815201930184610d69565b6001919460ff8560881c16815201930184610d61565b6001919460ff8560801c16815201930184610d59565b6001919460ff8560781c16815201930184610d51565b6001919460ff8560701c16815201930184610d49565b6001919460ff8560681c16815201930184610d41565b6001919460ff8560601c16815201930184610d39565b6001919460ff8560581c16815201930184610d31565b6001919460ff8560501c16815201930184610d29565b6001919460ff8560481c16815201930184610d21565b6001919460ff8560401c16815201930184610d19565b6001919460ff8560381c16815201930184610d11565b6001919460ff8560301c16815201930184610d09565b6001919460ff8560281c16815201930184610d01565b6001919460ff85831c16815201930184610cf9565b6001919460ff8560181c16815201930184610cf1565b6001919460ff8560101c16815201930184610ce9565b6001919460ff8560081c16815201930184610ce1565b6001919460ff8516815201930184610cd9565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018491928491610cbf565b346101f55760403660031901126101f5576113d961215c565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f526006602052602060018060a01b03600160405f20015416604051908152f35b346101f55760203660031901126101f55760043561146e61239b565b805f52600360205260405f2060ff1981541690557f021cbdaf8e2ddc626ca016468398743e833c1d67b00106b10077757586679a205f80a2005b346101f55760203660031901126101f5576004355f526006602052602060ff600660405f20015460081c166040519015158152f35b346101f55760a03660031901126101f5576084356001600160401b0381116101f5576103b1611512602092369060040161212f565b906064356044356024356004356129fc565b346101f55760203660031901126101f55760ff61153f612172565b61154761239b565b16805f526001602052600260405f2001541561159a57805f526001602052600160405f200160ff1981541690557f30e2b43b128fac88e776d92f693d539845d727cdb1978017ddff47a2e5319a035f80a2005b604051629f97d960e31b8152600490fd5b346101f5576003196020368201126101f557600435906001600160401b0382116101f55760609082360301126101f55761089860209160040161273c565b346101f5576020806003193601126101f5576001600160401b036004358181116101f55761161b90369060040161212f565b9161162461239b565b82116107d85760059161163783546121c0565b601f811161170b575b505f92601f82116001146116a95781611673945f9161169e575b508260011b905f198460031b1c19161790553691612502565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661165a565b601f19821693815f52855f20905f5b878782106116f55750508361167396106116dc575b5050600182811b01905561022e565b8401355f19600385901b60f8161c1916905585806116cd565b60018394829394890135815501930191016116b8565b61173290845f52855f20601f8401861c810191878510611738575b601f01861c0190612547565b84611640565b9091508190611726565b346101f55760203660031901126101f5576004355f61012060405161176681612309565b82815282602082015260606040820152606080820152606060808201528260a08201528260c08201528260e0820152826101008201520152805f526006602052600760405f20015415610bab575f52600660205260405f206008604051916117cd83612309565b80546001600160a01b03908116845260018201541660208401526040516117fb816101d68160028601612278565b6040840152604051611814816101d68160038601612278565b606084015260405161182d816101d68160048601612278565b6080840152600581015460a084015260ff600682015481811660c0860152831c16151560e08401526007810154610100840152015461012082015260405180916020825260018060a01b03815116602083015260018060a01b03602082015116604083015260408101516101206118e66118b561014093846060880152610160870190612182565b6118d1606086015191601f1992838983030160808a0152612182565b906080860151908783030160a0880152612182565b9260a081015160c086015260ff60c08201511660e086015260e08101511515610100860152610100810151828601520151908301520390f35b346101f5575f3660031901126101f55760206040517fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf8152f35b346101f55760403660031901126101f55761197261215c565b336001600160a01b0382160361198e5761031c9060043561248f565b60405163334bd91960e11b8152600490fd5b346101f55760403660031901126101f55761031c6004356119bf61215c565b90805f525f6020526119d7600160405f2001546123f2565b612413565b346101f55760203660031901126101f5576004355f525f6020526020600160405f200154604051908152f35b346101f55760c03660031901126101f5576004356001600160401b0381116101f557611a3890369060040161212f565b6024356001600160401b0381116101f557611a5790369060040161212f565b90926044356001600160401b0381116101f557611a7890369060040161212f565b6064359591906001600160a01b03871687036101f5576001600160401b03608435116101f557611aad3660843560040161212f565b909360a4359660ff881688036101f557335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff16156120665760ff88165f52600160205260ff600160405f200154161561205457611b19368284612502565b6020815191012098895f526006602052600760405f20015461204257611b6797604091611b9e611b8d611b7a85519c8d95869563f7cf40d360e01b87526080600488015260848701916125f3565b84810360031901602486015287896125f3565b838103600319016044850152898b6125f3565b8281036003190160648401526121f8565b03816109005afa968715612037575f905f98611ff6575b5015611fb057611bc4876126c9565b15611f9f57611bfc611c1496611c089460209c6040519d8e91611be683612309565b3383526001600160a01b03169101523691612502565b60408b01523691612502565b60608801523691612502565b608085015260a084015260ff811660c0840152600160e084018190524261010085018190526101208501525f83815260066020908152604091829020865181546001600160a01b03199081166001600160a01b03928316178355928801519482018054909316941693909317905584015180516001600160401b0381116107d857806002840192611ca98261099f86546121c0565b602090601f8311600114611f3b575f92611f30575b50508160011b915f199060031b1c19161790555b606084015180516001600160401b0381116107d857806003840192611cfb8261099f86546121c0565b602090601f8311600114611ecc575f92611ec1575b50508160011b915f199060031b1c19161790555b60808401519384516001600160401b0381116107d857602095611d5782611d4e60048701546121c0565b6004870161255d565b8690601f8311600114611e5157918060089492610120945f92611e46575b50508160011b915f199060031b1c19161760048501555b60a081015160058501556006840160ff60c08301511681549061ff0060e08501511515871b169161ffff19161717905561010081015160078501550151910155600754825f526008845260405f2055611de48261267c565b335f5260098352611df88260405f206126a2565b60ff81165f52600a8352611e0f8260405f206126a2565b60ff60405191168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3604051908152f35b015190508980611d75565b90600485015f52875f20915f5b601f1985168110611eaa575092600894926001926101209583601f19811610611e92575b505050811b016004850155611d8c565b01515f1960f88460031b161c19169055898080611e82565b919289600181928685015181550194019201611e5e565b015190508780611d10565b5f858152602081209350601f198516905b818110611f185750908460019594939210611f00575b505050811b019055611d24565b01515f1960f88460031b161c19169055878080611ef3565b92936020600181928786015181550195019301611edd565b015190508780611cbe565b5f858152602081209350601f198516905b818110611f875750908460019594939210611f6f575b505050811b019055611cd2565b01515f1960f88460031b161c19169055878080611f62565b92936020600181928786015181550195019301611f4c565b60405162d85d7f60e71b8152600490fd5b604051630368f92d60e11b815260206004820152601f60248201527f4174746573746174696f6e20766572696669636174696f6e206661696c6564006044820152606490fd5b9750506040873d60401161202f575b8161201260409383612340565b810103126101f5576020612025886127a6565b970151968b611bb5565b3d9150612005565b6040513d5f823e3d90fd5b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201527fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf6024820152604490fd5b346101f55760203660031901126101f55761031c600435612922565b346101f5575f3660031901126101f55760206040516109008152f35b346101f55760203660031901126101f5576004359063ffffffff60e01b82168092036101f557602091637965db0b60e01b811490811561211e575b5015158152f35b6301ffc9a760e01b14905083612117565b9181601f840112156101f5578235916001600160401b0383116101f557602083818601950101116101f557565b602435906001600160a01b03821682036101f557565b6004359060ff821682036101f557565b91908251928382525f5b8481106121ac575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161218c565b90600182811c921680156121ee575b60208310146121da57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916121cf565b6005545f9291612207826121c0565b9081815260019283811690815f1461225d575060011461222657505050565b9092935060055f52602092835f2092845f945b8386106122495750505050010190565b805485870183015294019385908201612239565b91935050602093945060ff191683830152151560051b010190565b905f9291805491612288836121c0565b9182825260019384811690815f146122e657506001146122a9575b50505050565b90919394505f52602092835f2092845f945b8386106122d257505050500101905f8080806122a3565b8054858701830152940193859082016122bb565b9294505050602093945060ff191683830152151560051b0101905f8080806122a3565b61014081019081106001600160401b038211176107d857604052565b606081019081106001600160401b038211176107d857604052565b90601f801991011681019081106001600160401b038211176107d857604052565b602090816040818301928281528551809452019301915f5b828110612387575050505090565b835185529381019392810192600101612379565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156123d45750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156123d45750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f1461248a57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f1461248a5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b9291926001600160401b0382116107d8576040519161252b601f8201601f191660200184612340565b8294818452818301116101f5578281602093845f960137010152565b818110612552575050565b5f8155600101612547565b9190601f811161256c57505050565b612596925f5260205f20906020601f840160051c83019310612598575b601f0160051c0190612547565b565b9091508190612589565b906002548210156125df5760025f52600582901c7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b0381116107d85760051b60200190565b5f198114610ac85760010190565b80518210156125df5760209160051b010190565b6004548110156125df5760045f5260205f2001905f90565b6007548110156125df5760075f5260205f2001905f90565b600754600160401b8110156107d857610a1381600161269e9301600755612664565b9055565b805490600160401b8210156107d857600182018082558210156125df575f5260205f200155565b5f52600360205260405f2060ff81541615612705576002015480151590816126fa575b506126f657600190565b5f90565b90504210155f6126ec565b505f90565b903590601e19813603018212156101f557018035906001600160401b0382116101f5576020019181360383136101f557565b61276b6127a061274c838061270a565b60209491949161275e8382018261270a565b958260408894019061270a565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612340565b51902090565b519081151582036101f557565b5f8181526020600681526040908183206007810154156129115780546001600160a01b0316331415806128f5575b6128e45760068101805460ff8160081c16156128db5761ff0019169055426008918201558484528152818320546007545f1991908281019081116128c757808203612897575b505060075480156128835784939192837f784ce1077bc44f5b33e1724f6a1b9469423f16a15680c26d150787fb36349d179694600893019061286882612664565b909182549160031b1b191690556007558684525281205580a2565b634e487b7160e01b85526031600452602485fd5b6128a090612664565b90549060031b1c806128b4610a1384612664565b9055855260088352838520555f80612827565b634e487b7160e01b86526011600452602486fd5b50505050505050565b82516398b31e6f60e01b8152600490fd5b50838052838252828420338552825260ff8385205416156127e1565b825163f6523b6560e01b8152600490fd5b5f8181526006602052604090818120916007830154156129ec5782546001600160a01b0316331415806129ce575b6129be57600683019283549360ff8560081c166129b6576101007ff1107fa7ae6c5cdb1675a2191d02d30d03220d684e051e5c0632ca0458fe79749561ff0019161790556008429101556007549084835260086020528220556129b28361267c565b80a2565b505050505050565b516398b31e6f60e01b8152600490fd5b508180528160205280822033835260205260ff818320541615612950565b5163f6523b6560e01b8152600490fd5b949092915f9586526006602052604086209160ff600684015460081c1615612acf57612a31612a5894612a6f93602097612ad8565b906040519687958695633f1333cd60e11b8752606060048801526003606488019101612278565b9260248601526003198584030160448601526125f3565b03816109005afa918215612ac3578092612a8857505090565b9091506020823d8211612abb575b81612aa360209383612340565b81010312612ab85750612ab5906127a6565b90565b80fd5b3d9150612a96565b604051903d90823e3d90fd5b50505050505090565b9160405191602083019384526040830152606082015260608152608081018181106001600160401b038211176107d8576040525190209056fea2646970667358221220b43915f7eedd07d3c720e5ec23e1ec88df5e10dfaa5f60b76e9737aa7042660d64736f6c634300081400332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf", - "deployedBytecode": "0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146120dc5750806308c84e70146120c0578063097775c6146120a457806315c9bdb414611a08578063248a9ca3146119dc5780632f2ff15d146119a057806336568abe146119595780633c1a88b01461191f578063418b207d1461174257806343ed3274146115e9578063476cb030146115ab5780634b19d4631461152457806358db61a8146114dd5780635c36901c146114a85780639138c99e1461145257806391cc00e91461140857806391d14854146113c0578063971cfbf114610c90578063a217fddf14610c76578063b19ffd1c14610c09578063b1c551ca14610bbd578063b778f86914610b5f578063b82644b714610af2578063c0abde45146108a0578063c206baa314610874578063cbdfc4e01461066d578063ccdf049314610568578063d046a7fa146104f1578063d2e03427146103f7578063d547741f146103bb578063d5ed579d14610393578063d6741a421461031e578063dd31611314610300578063e10a2ea4146102c6578063e44cc48214610243578063f33d2c00146101f95763f44a0780146101b3575f80fd5b346101f5575f3660031901126101f5576101f16040516101dd816101d6816121f8565b0382612340565b604051918291602083526020830190612182565b0390f35b5f80fd5b346101f55760203660031901126101f5576004356001600160401b0381116101f55761023561022e602092369060040161212f565b3691612502565b818151910120604051908152f35b346101f5576020806003193601126101f5576004356001600160a01b038116908190036101f5575f526009815260405f209060405190818184549182815201935f52815f20915f905b8282106102af576101f1856102a381890382612340565b60405191829182612361565b83548652948501946001938401939091019061028c565b346101f55760203660031901126101f55760ff6102e1612172565b165f526001602052602060ff600160405f200154166040519015158152f35b346101f55760203660031901126101f55761031c6004356127b3565b005b346101f55760203660031901126101f55760ff610339612172565b165f52600160205261038260405f20604051906103618261035a8184612278565b0383612340565b600260ff600183015416910154604051938493606085526060850190612182565b911515602084015260408301520390f35b346101f55760203660031901126101f55760206103b16004356126c9565b6040519015158152f35b346101f55760403660031901126101f55761031c6004356103da61215c565b90805f525f6020526103f2600160405f2001546123f2565b61248f565b346101f5575f3660031901126101f5575f80600454905b8181106104b1575061041f82612613565b9161042d6040519384612340565b80835261043c601f1991612613565b013660208401375f805b82811061045b57604051806101f18682612361565b8061047961046b6104849361264c565b919054600392831b1c6126c9565b610489575b5061262a565b610446565b6104928261264c565b9054911b1c6104aa6104a38561262a565b9487612638565b528561047e565b6104c96104bd8261264c565b90549060031b1c6126c9565b6104dc575b6104d79061262a565b61040e565b916104e96104d79161262a565b9290506104ce565b346101f5575f3660031901126101f557604051600780548083525f918252602080840193927fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68892915b828210610551576101f1856102a381890382612340565b83548652948501946001938401939091019061053a565b346101f55760203660031901126101f5576004355f52600660205260405f2060018060a01b038082541690600183015416916040516105ae816101d68160028601612278565b604051916105ca836105c38160038501612278565b0384612340565b60ff604051936105e8856105e18160048701612278565b0386612340565b61064460058401549561063660068601549361062860086007890154980154986040519c8d9c8d5260208d015260408c61014091829101528c0190612182565b908a820360608c0152612182565b9088820360808a0152612182565b9460a087015281811660c087015260081c16151560e08501526101008401526101208301520390f35b346101f55760403660031901126101f557610686612172565b6001600160401b03906024358281116101f5576106a960ff91369060040161212f565b9190926106b461239b565b1692835f52600192602091848352600260405f20015461086257604051906106db82612325565b6106e6368685612502565b82528382018681526040830191428352885f5287865260405f20935180519182116107d85761071f8261071987546121c0565b8761255d565b8690601f83116001146107f75791806002959492610768945f926107ec575b50505f19600383901b1c1916908a1b1785555b5115158885019060ff801983541691151516179055565b5191015560025490600160401b8210156107d8576107af827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107d394016002556125a2565b81549060031b9060ff89831b921b19161790556040519383859485528401916125f3565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508c8061073e565b899291601f19831691875f52895f20925f5b8b82821061084c575050916107689593918560029998969410610834575b505050811b018555610751565b01515f1960f88460031b161c191690558c8080610827565b8385015186558e97909501949384019301610809565b60405163095faf7360e01b8152600490fd5b346101f55760603660031901126101f5576020610898604435602435600435612ad8565b604051908152f35b346101f5576003196080368201126101f557600435906001600160401b03908183116101f55760609083360301126101f5576024358181116101f5576108ea90369060040161212f565b9091610903604435946108fb61239b565b60040161273c565b9380151580610adc575b610aa6575b506040519260808401848110838211176107d857604052600193848152602092600384830192428452604081015f815261094d368988612502565b94606083019586528a5f5283885261097860405f2093511515849060ff801983541691151516179055565b518983015551600282015501915180519182116107d85781906109a58261099f86546121c0565b8661255d565b8590601f8311600114610a47575f92610a3c575b50505f19600383901b1c191690861b1790555b60045490600160401b8210156107d85785610a29610a13847fa3cbee87ba6def0cc8a2c5a3cf7a54f0209c6f939b07979dde08d447d6c3666d986107d3960160045561264c565b819391549060031b91821b915f19901b19161790565b90556040519383859485528401916125f3565b0151905088806109b9565b90889350601f19831691855f52875f20925f5b89828210610a905750508411610a78575b505050811b0190556109cc565b01515f1960f88460031b161c19169055888080610a6b565b8385015186558c97909501949384019301610a5a565b606435420190814211610ac8575f526003602052600260405f20015584610912565b634e487b7160e01b5f52601160045260245ffd5b50805f52600360205260ff60405f20541661090d565b346101f5576020806003193601126101f55760ff610b0e612172565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610b48576101f1856102a381890382612340565b835486529485019460019384019390910190610b31565b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600460405f200160405192838092612278565b60405163f6523b6560e01b8152600490fd5b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f5260066020526101f16101d66101dd600360405f200160405192838092612278565b346101f55760203660031901126101f5576004355f52600360205260405f2060ff8154166101f16003600184015493610c5160028201549161035a6040518095819301612278565b6040519485941515855260208501526040840152608060608401526080830190612182565b346101f5575f3660031901126101f55760206040515f8152f35b346101f5575f3660031901126101f5576040518060029182549081835260208093019283855f528282805f20925f5b8780601f83011061123857610ddf95549184828210611225575b82821061120f575b8282106111f9575b8282106111e3575b8282106111ce575b8282106111b8575b8282106111a2575b82821061118c575b828210611176575b828210611160575b82821061114a575b828210611134575b82821061111e575b828210611108575b8282106110f2575b8282106110dc575b8282106110c6575b8282106110b0575b82821061109a575b828210611084575b82821061106e575b828210611058575b828210611042575b82821061102c575b828210611016575b828210611000575b828210610fea575b828210610fd4575b828210610fbe575b828210610fa8575b828210610f92575b5010610f84575b50905096939295960382612340565b610de883612613565b92610df66040519485612340565b808452601f199283610e0783612613565b015f5b818110610f525750505f5b828110610ed557505050604051936040850191604086525180925260609182860196905f5b818110610ebe57505050848603818601528351918287528187019082808560051b8a01019601945f985b858a10610e715788880389f35b909192939495968580828585600195030188528a5190604080610e9b84518b85528b850190612182565b938581015115158685015201519101529901950199019892919095949395610e64565b825160ff1689529783019791830191600101610e3a565b80610ee7610f489299989596996125a2565b9060ff918291549060031b1c165f528360019182885260405f209060405193610f0f85612325565b604051610f20816101d68187612278565b85528201541615158884015201546040820152610f3d8289612638565b5261047e8188612638565b9693929596610e15565b968094959897604051610f6481612325565b606081525f838201525f604082015282828a010152019794939697610e0a565b60f81c815201839089610dd0565b6001919460ff8560f01c16815201930184610dc9565b6001919460ff8560e81c16815201930184610dc1565b6001919460ff8560e01c16815201930184610db9565b6001919460ff8560d81c16815201930184610db1565b6001919460ff8560d01c16815201930184610da9565b6001919460ff8560c81c16815201930184610da1565b6001919460ff8560c01c16815201930184610d99565b6001919460ff8560b81c16815201930184610d91565b6001919460ff8560b01c16815201930184610d89565b6001919460ff8560a81c16815201930184610d81565b6001919460ff8560a01c16815201930184610d79565b6001919460ff8560981c16815201930184610d71565b6001919460ff8560901c16815201930184610d69565b6001919460ff8560881c16815201930184610d61565b6001919460ff8560801c16815201930184610d59565b6001919460ff8560781c16815201930184610d51565b6001919460ff8560701c16815201930184610d49565b6001919460ff8560681c16815201930184610d41565b6001919460ff8560601c16815201930184610d39565b6001919460ff8560581c16815201930184610d31565b6001919460ff8560501c16815201930184610d29565b6001919460ff8560481c16815201930184610d21565b6001919460ff8560401c16815201930184610d19565b6001919460ff8560381c16815201930184610d11565b6001919460ff8560301c16815201930184610d09565b6001919460ff8560281c16815201930184610d01565b6001919460ff85831c16815201930184610cf9565b6001919460ff8560181c16815201930184610cf1565b6001919460ff8560101c16815201930184610ce9565b6001919460ff8560081c16815201930184610ce1565b6001919460ff8516815201930184610cd9565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018491928491610cbf565b346101f55760403660031901126101f5576113d961215c565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101f55760203660031901126101f557600435805f526006602052600760405f20015415610bab575f526006602052602060018060a01b03600160405f20015416604051908152f35b346101f55760203660031901126101f55760043561146e61239b565b805f52600360205260405f2060ff1981541690557f021cbdaf8e2ddc626ca016468398743e833c1d67b00106b10077757586679a205f80a2005b346101f55760203660031901126101f5576004355f526006602052602060ff600660405f20015460081c166040519015158152f35b346101f55760a03660031901126101f5576084356001600160401b0381116101f5576103b1611512602092369060040161212f565b906064356044356024356004356129fc565b346101f55760203660031901126101f55760ff61153f612172565b61154761239b565b16805f526001602052600260405f2001541561159a57805f526001602052600160405f200160ff1981541690557f30e2b43b128fac88e776d92f693d539845d727cdb1978017ddff47a2e5319a035f80a2005b604051629f97d960e31b8152600490fd5b346101f5576003196020368201126101f557600435906001600160401b0382116101f55760609082360301126101f55761089860209160040161273c565b346101f5576020806003193601126101f5576001600160401b036004358181116101f55761161b90369060040161212f565b9161162461239b565b82116107d85760059161163783546121c0565b601f811161170b575b505f92601f82116001146116a95781611673945f9161169e575b508260011b905f198460031b1c19161790553691612502565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661165a565b601f19821693815f52855f20905f5b878782106116f55750508361167396106116dc575b5050600182811b01905561022e565b8401355f19600385901b60f8161c1916905585806116cd565b60018394829394890135815501930191016116b8565b61173290845f52855f20601f8401861c810191878510611738575b601f01861c0190612547565b84611640565b9091508190611726565b346101f55760203660031901126101f5576004355f61012060405161176681612309565b82815282602082015260606040820152606080820152606060808201528260a08201528260c08201528260e0820152826101008201520152805f526006602052600760405f20015415610bab575f52600660205260405f206008604051916117cd83612309565b80546001600160a01b03908116845260018201541660208401526040516117fb816101d68160028601612278565b6040840152604051611814816101d68160038601612278565b606084015260405161182d816101d68160048601612278565b6080840152600581015460a084015260ff600682015481811660c0860152831c16151560e08401526007810154610100840152015461012082015260405180916020825260018060a01b03815116602083015260018060a01b03602082015116604083015260408101516101206118e66118b561014093846060880152610160870190612182565b6118d1606086015191601f1992838983030160808a0152612182565b906080860151908783030160a0880152612182565b9260a081015160c086015260ff60c08201511660e086015260e08101511515610100860152610100810151828601520151908301520390f35b346101f5575f3660031901126101f55760206040517fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf8152f35b346101f55760403660031901126101f55761197261215c565b336001600160a01b0382160361198e5761031c9060043561248f565b60405163334bd91960e11b8152600490fd5b346101f55760403660031901126101f55761031c6004356119bf61215c565b90805f525f6020526119d7600160405f2001546123f2565b612413565b346101f55760203660031901126101f5576004355f525f6020526020600160405f200154604051908152f35b346101f55760c03660031901126101f5576004356001600160401b0381116101f557611a3890369060040161212f565b6024356001600160401b0381116101f557611a5790369060040161212f565b90926044356001600160401b0381116101f557611a7890369060040161212f565b6064359591906001600160a01b03871687036101f5576001600160401b03608435116101f557611aad3660843560040161212f565b909360a4359660ff881688036101f557335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff16156120665760ff88165f52600160205260ff600160405f200154161561205457611b19368284612502565b6020815191012098895f526006602052600760405f20015461204257611b6797604091611b9e611b8d611b7a85519c8d95869563f7cf40d360e01b87526080600488015260848701916125f3565b84810360031901602486015287896125f3565b838103600319016044850152898b6125f3565b8281036003190160648401526121f8565b03816109005afa968715612037575f905f98611ff6575b5015611fb057611bc4876126c9565b15611f9f57611bfc611c1496611c089460209c6040519d8e91611be683612309565b3383526001600160a01b03169101523691612502565b60408b01523691612502565b60608801523691612502565b608085015260a084015260ff811660c0840152600160e084018190524261010085018190526101208501525f83815260066020908152604091829020865181546001600160a01b03199081166001600160a01b03928316178355928801519482018054909316941693909317905584015180516001600160401b0381116107d857806002840192611ca98261099f86546121c0565b602090601f8311600114611f3b575f92611f30575b50508160011b915f199060031b1c19161790555b606084015180516001600160401b0381116107d857806003840192611cfb8261099f86546121c0565b602090601f8311600114611ecc575f92611ec1575b50508160011b915f199060031b1c19161790555b60808401519384516001600160401b0381116107d857602095611d5782611d4e60048701546121c0565b6004870161255d565b8690601f8311600114611e5157918060089492610120945f92611e46575b50508160011b915f199060031b1c19161760048501555b60a081015160058501556006840160ff60c08301511681549061ff0060e08501511515871b169161ffff19161717905561010081015160078501550151910155600754825f526008845260405f2055611de48261267c565b335f5260098352611df88260405f206126a2565b60ff81165f52600a8352611e0f8260405f206126a2565b60ff60405191168152817f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec14931843393a3604051908152f35b015190508980611d75565b90600485015f52875f20915f5b601f1985168110611eaa575092600894926001926101209583601f19811610611e92575b505050811b016004850155611d8c565b01515f1960f88460031b161c19169055898080611e82565b919289600181928685015181550194019201611e5e565b015190508780611d10565b5f858152602081209350601f198516905b818110611f185750908460019594939210611f00575b505050811b019055611d24565b01515f1960f88460031b161c19169055878080611ef3565b92936020600181928786015181550195019301611edd565b015190508780611cbe565b5f858152602081209350601f198516905b818110611f875750908460019594939210611f6f575b505050811b019055611cd2565b01515f1960f88460031b161c19169055878080611f62565b92936020600181928786015181550195019301611f4c565b60405162d85d7f60e71b8152600490fd5b604051630368f92d60e11b815260206004820152601f60248201527f4174746573746174696f6e20766572696669636174696f6e206661696c6564006044820152606490fd5b9750506040873d60401161202f575b8161201260409383612340565b810103126101f5576020612025886127a6565b970151968b611bb5565b3d9150612005565b6040513d5f823e3d90fd5b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b60405163e2517d3f60e01b81523360048201527fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf6024820152604490fd5b346101f55760203660031901126101f55761031c600435612922565b346101f5575f3660031901126101f55760206040516109008152f35b346101f55760203660031901126101f5576004359063ffffffff60e01b82168092036101f557602091637965db0b60e01b811490811561211e575b5015158152f35b6301ffc9a760e01b14905083612117565b9181601f840112156101f5578235916001600160401b0383116101f557602083818601950101116101f557565b602435906001600160a01b03821682036101f557565b6004359060ff821682036101f557565b91908251928382525f5b8481106121ac575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520161218c565b90600182811c921680156121ee575b60208310146121da57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916121cf565b6005545f9291612207826121c0565b9081815260019283811690815f1461225d575060011461222657505050565b9092935060055f52602092835f2092845f945b8386106122495750505050010190565b805485870183015294019385908201612239565b91935050602093945060ff191683830152151560051b010190565b905f9291805491612288836121c0565b9182825260019384811690815f146122e657506001146122a9575b50505050565b90919394505f52602092835f2092845f945b8386106122d257505050500101905f8080806122a3565b8054858701830152940193859082016122bb565b9294505050602093945060ff191683830152151560051b0101905f8080806122a3565b61014081019081106001600160401b038211176107d857604052565b606081019081106001600160401b038211176107d857604052565b90601f801991011681019081106001600160401b038211176107d857604052565b602090816040818301928281528551809452019301915f5b828110612387575050505090565b835185529381019392810192600101612379565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156123d45750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f525f60205260405f20335f5260205260ff60405f205416156123d45750565b905f9180835282602052604083209160018060a01b03169182845260205260ff604084205416155f1461248a57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054165f1461248a5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b9291926001600160401b0382116107d8576040519161252b601f8201601f191660200184612340565b8294818452818301116101f5578281602093845f960137010152565b818110612552575050565b5f8155600101612547565b9190601f811161256c57505050565b612596925f5260205f20906020601f840160051c83019310612598575b601f0160051c0190612547565b565b9091508190612589565b906002548210156125df5760025f52600582901c7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0191601f1690565b634e487b7160e01b5f52603260045260245ffd5b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b0381116107d85760051b60200190565b5f198114610ac85760010190565b80518210156125df5760209160051b010190565b6004548110156125df5760045f5260205f2001905f90565b6007548110156125df5760075f5260205f2001905f90565b600754600160401b8110156107d857610a1381600161269e9301600755612664565b9055565b805490600160401b8210156107d857600182018082558210156125df575f5260205f200155565b5f52600360205260405f2060ff81541615612705576002015480151590816126fa575b506126f657600190565b5f90565b90504210155f6126ec565b505f90565b903590601e19813603018212156101f557018035906001600160401b0382116101f5576020019181360383136101f557565b61276b6127a061274c838061270a565b60209491949161275e8382018261270a565b958260408894019061270a565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612340565b51902090565b519081151582036101f557565b5f8181526020600681526040908183206007810154156129115780546001600160a01b0316331415806128f5575b6128e45760068101805460ff8160081c16156128db5761ff0019169055426008918201558484528152818320546007545f1991908281019081116128c757808203612897575b505060075480156128835784939192837f784ce1077bc44f5b33e1724f6a1b9469423f16a15680c26d150787fb36349d179694600893019061286882612664565b909182549160031b1b191690556007558684525281205580a2565b634e487b7160e01b85526031600452602485fd5b6128a090612664565b90549060031b1c806128b4610a1384612664565b9055855260088352838520555f80612827565b634e487b7160e01b86526011600452602486fd5b50505050505050565b82516398b31e6f60e01b8152600490fd5b50838052838252828420338552825260ff8385205416156127e1565b825163f6523b6560e01b8152600490fd5b5f8181526006602052604090818120916007830154156129ec5782546001600160a01b0316331415806129ce575b6129be57600683019283549360ff8560081c166129b6576101007ff1107fa7ae6c5cdb1675a2191d02d30d03220d684e051e5c0632ca0458fe79749561ff0019161790556008429101556007549084835260086020528220556129b28361267c565b80a2565b505050505050565b516398b31e6f60e01b8152600490fd5b508180528160205280822033835260205260ff818320541615612950565b5163f6523b6560e01b8152600490fd5b949092915f9586526006602052604086209160ff600684015460081c1615612acf57612a31612a5894612a6f93602097612ad8565b906040519687958695633f1333cd60e11b8752606060048801526003606488019101612278565b9260248601526003198584030160448601526125f3565b03816109005afa918215612ac3578092612a8857505090565b9091506020823d8211612abb575b81612aa360209383612340565b81010312612ab85750612ab5906127a6565b90565b80fd5b3d9150612a96565b604051903d90823e3d90fd5b50505050505090565b9160405191602083019384526040830152606082015260608152608081018181106001600160401b038211176107d8576040525190209056fea2646970667358221220b43915f7eedd07d3c720e5ec23e1ec88df5e10dfaa5f60b76e9737aa7042660d64736f6c63430008140033", + "bytecode": "0x60806040818152346200011e576107086002555f908180526001602091838352808420338552835260ff818520541615620000e8575b837fae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bf93848252818152828220338352815260ff838320541615620000b2575b848252528320019082825492557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff8380a4613d6c9081620001238239f35b84825281815282822033835281528282208460ff198254161790553333865f8051602062003e8f8339815191528580a462000074565b83805283835280842033855283528084208260ff198254161790553333855f8051602062003e8f8339815191528180a462000035565b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c9081625ae7cf14612d115750806301ffc9a714612cbb57806308c84e7014612c9f5780631788371714612b955780632068b3db14612b1c578063248a9ca314612af05780632cbe755214612ab95780632f2ff15d14612a1457806336568abe146129825780633c1a88b01461295b578063418b207d146128ff57806342830ad81461274157806343ed3274146125ea578063476cb030146125ac5780634b6014da146123f85780634c4c8e8b146121715780635766275a14611fbc5780635c1090b214611f9b57806363b86b8914611f2e57806387267aca14611f115780638ecaf98c1461049d57806391d1485414611ec9578063927d3c9514611e94578063939ea4b81461115d578063971cfbf114610a5f5780639831ced514610a1c578063a041ff51146108d1578063a217fddf146108b7578063a3bf31f11461088e578063abbf8f6a1461081d578063b82644b7146107b0578063cbdfc4e0146105d7578063ccdf0493146104b7578063cf79c8e11461049d578063d101bec414610409578063d547741f146103cb578063d6741a4214610375578063e10a2ea414610336578063e44cc482146102b3578063f33d2c0014610269578063f44a0780146102275763fec0de46146101e9575f80fd5b34610223575f3660031901126102235760206040517fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8152f35b5f80fd5b34610223575f366003190112610223576102656040516102518161024a8161312f565b0382612e92565b604051918291602083526020830190612f75565b0390f35b34610223576020366003190112610223576004356001600160401b038111610223576102a561029e60209236906004016130c8565b3691613704565b818151910120604051908152f35b3461022357602080600319360112610223576004356001600160a01b03811690819003610223575f52600b815260405f209060405190818184549182815201935f52815f20915f905b82821061031f576102658561031381890382612e92565b604051918291826130f5565b8354865294850194600193840193909101906102fc565b3461022357602036600319011261022357602061036b610354612dcd565b60ff165f526003602052600160405f200154151590565b6040519015158152f35b346102235760203660031901126102235760ff610390612dcd565b165f5260036020526103c160405f2060016103aa82612eb3565b910154604051928392604084526040840190612f75565b9060208301520390f35b34610223576040366003190112610223576104076004356103ea612f9a565b90805f525f602052610402600160405f20015461347e565b61357c565b005b346102235760e036600319011261022357610422612ddd565b61ffff604435818116810361022357606435928284168403610223576084359283168303610223576001600160401b039260a4358481116102235761046b9036906004016130c8565b9160c4359586116102235760209661048a6104959736906004016130c8565b969095600435613c66565b604051908152f35b34610223575f366003190112610223576020604051818152f35b34610223576020366003190112610223576004355f908152600760205260409020805460018201546001600160a01b03908116929116906104fa60028201612eb3565b60405190918161050d81600384016131ab565b036105189083612e92565b604051918261052a81600485016131ab565b036105359084612e92565b6005820154926006830154906007840154926008850154946009016105599061323c565b96604051998a998a5260208a01526101608060408b0152890161057b91612f75565b88810360608a015261058c91612f75565b878103608089015261059d91612f75565b9360a087015260ff811660c087015260081c60ff16151560e086015261010085015261012084015282810361014084015261026591612fb0565b34610223576040366003190112610223576105f0612dcd565b6001600160401b036024358181116102235761061260ff9136906004016130c8565b91909361061d6132b3565b1692835f52602090600382526001938460405f20015461079e576040519061064482612e25565b61064f368685613704565b8252838201428152875f526003855260405f20925191825190811161071d5787926106848261067e8754612ded565b876136cc565b8690601f831160011461073c575f919083610731575b50505f19600383901b1c191690831b1783555b5191015560045490600160401b82101561071d576106f4827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107189401600455613749565b81549060031b9060ff89831b921b1916179055604051938385948552840191613786565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508a8061069a565b90601f19831691865f52885f20925f5b8a8282106107885750509084879594939210610770575b505050811b0183556106ad565b01515f1960f88460031b161c191690558a8080610763565b8385015186558d9890950194938401930161074c565b60405163095faf7360e01b8152600490fd5b34610223576020806003193601126102235760ff6107cc612dcd565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610806576102658561031381890382612e92565b8354865294850194600193840193909101906107ef565b3461022357602036600319011261022357600435610839613abb565b50805f526007602052600760405f2001541561087c575f526007602052610265610868600960405f200161323c565b604051918291602083526020830190612fb0565b60405163f6523b6560e01b8152600490fd5b34610223576020366003190112610223576004355f526007602052602061036b60405f20613c00565b34610223575f3660031901126102235760206040515f8152f35b34610223576040366003190112610223576004356108ed612ddd565b906108f66132b3565b60ff821691825f52600590602082815260405f20845f52815260ff60405f20541615610a0b57845f5282815260405f20845f52815260405f2060ff198154169055845f526008815260405f20928354805b6109735786867fd5a6741f8bbaee4cc3ea05953ea04c6a1863ce4c66a52da653dd0a315f84c2fb5f80a3005b5f198101908082116109f75761098c6109b09287613808565b90549060031b1c805f526007855260405f208885820154146109b6575b5050613600565b80610947565b600601805461ff00191690556109cc86826139c6565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a288806109a9565b634e487b7160e01b5f52601160045260245ffd5b60405162d85d7f60e71b8152600490fd5b346102235760403660031901126102235760ff610a37612dcd565b165f52600560205260405f206024355f52602052602060ff60405f2054166040519015158152f35b34610223575f36600319011261022357604051806004549182815260208091018060045f528383805f20925f5b8880601f830110610fd557610baa95549184828210610fc2575b828210610fac575b828210610f96575b828210610f80575b828210610f6b575b828210610f55575b828210610f3f575b828210610f29575b828210610f13575b828210610efd575b828210610ee7575b828210610ed1575b828210610ebb575b828210610ea5575b828210610e8f575b828210610e79575b828210610e63575b828210610e4d575b828210610e37575b828210610e21575b828210610e0b575b828210610df5575b828210610ddf575b828210610dc9575b828210610db3575b828210610d9d575b828210610d87575b828210610d71575b828210610d5b575b828210610d45575b828210610d2f575b5010610d21575b5090509592950384612e92565b610bb3816137a6565b91610bc16040519384612e92565b818352601f199182610bd2826137a6565b015f5b818110610cf85750505f5b818110610c9157505060405193604085019060408652518091526060850195905f5b818110610c7a575050508385038185015282519081865280860181808460051b8901019501935f975b848910610c385787870388f35b9091929394958480828585600195030187528951908280610c628451604085526040850190612f75565b93015191015298019401980197919094939294610c2b565b825160ff1688529683019691830191600101610c02565b8060ff610ca3610cf093999699613749565b919054600392831b1c165f52845260405f20600160405191610cc483612e25565b610ccd81612eb3565b8352015485820152610cdf82886137cb565b52610cea81876137cb565b506137bd565b959295610be0565b8390604098959851610d0981612e25565b606081525f8382015282828901015201969396610bd5565b60f81c815201849088610b9d565b6001919460ff8560f01c16815201930184610b96565b6001919460ff8560e81c16815201930184610b8e565b6001919460ff8560e01c16815201930184610b86565b6001919460ff8560d81c16815201930184610b7e565b6001919460ff8560d01c16815201930184610b76565b6001919460ff8560c81c16815201930184610b6e565b6001919460ff8560c01c16815201930184610b66565b6001919460ff8560b81c16815201930184610b5e565b6001919460ff8560b01c16815201930184610b56565b6001919460ff8560a81c16815201930184610b4e565b6001919460ff8560a01c16815201930184610b46565b6001919460ff8560981c16815201930184610b3e565b6001919460ff8560901c16815201930184610b36565b6001919460ff8560881c16815201930184610b2e565b6001919460ff8560801c16815201930184610b26565b6001919460ff8560781c16815201930184610b1e565b6001919460ff8560701c16815201930184610b16565b6001919460ff8560681c16815201930184610b0e565b6001919460ff8560601c16815201930184610b06565b6001919460ff8560581c16815201930184610afe565b6001919460ff8560501c16815201930184610af6565b6001919460ff8560481c16815201930184610aee565b6001919460ff8560401c16815201930184610ae6565b6001919460ff8560381c16815201930184610ade565b6001919460ff8560301c16815201930184610ad6565b6001919460ff8560281c16815201930184610ace565b6001919460ff85831c16815201930184610ac6565b6001919460ff8560181c16815201930184610abe565b6001919460ff8560101c16815201930184610ab6565b6001919460ff8560081c16815201930184610aae565b6001919460ff8516815201930184610aa6565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018591928591610a8c565b34610223576101a0366003190112610223576004356001600160401b0381116102235761118e9036906004016130c8565b6024356001600160401b038111610223576111ad9036906004016130c8565b6044929192356001600160401b038111610223576111cf9036906004016130c8565b606435959194916001600160a01b0387168703610223576084356001600160401b038111610223576112059036906004016130c8565b91909660ff60a4351660a435036102235760ff60c4351660c435036102235761ffff60e4351660e435036102235761ffff610104351661010435036102235761ffff61012435166101243503610223576001600160401b0361014435116102235761127636610144356004016130c8565b9490956001600160401b0361016435116102235761129a36610164356004016130c8565b98909a6001600160401b036101843511610223576112be36610184356004016130c8565b335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff1615611cf35761131160a43560ff165f526003602052600160405f200154151590565b15611ce157611321368587613704565b602081519101209c8d5f526007602052600760405f200154611ccf5761136f986040916113a661139561138285519d8e95869563f7cf40d360e01b8752608060048801526084870191613786565b8481036003190160248601528a8c613786565b8381036003190160448501528b8d613786565b82810360031901606484015261312f565b03816109005afa978815611c07575f905f99611c8e575b5015611c37576113cf60a43589613902565b6113d7613abb565b5089158015611c2f575b611c1257602061ffff60e435161480611c24575b611c125760209061141e8f8f908f918f8f916101243590610104359060e4359060c43590613c66565b926114606040519485938493633f1333cd60e11b855260606004860152611449606486018b8d613786565b926024860152600319858403016044860152613786565b03816109005afa908115611c07575f91611bcd575b5015611bbb576040519661148888612e40565b60ff60c43516885261ffff60e435811660208a015261010435811660408a0152610124351660608901526114bd368a8c613704565b60808901526114cd368c8f613704565b60a08901524260c0890152604051809e6114e682612e5b565b3382526001600160a01b031660209091015261150491369190613704565b60408d0152369061151492613704565b60608b0152369061152492613704565b608089015260a088015260ff60a4351660c0880152600160e088018190524261010089018190526101208901526101408801919091525f85815260076020908152604091829020895181546001600160a01b03199081166001600160a01b03928316178355928b0151948201805490931694169390931790558701518051906001600160401b03821161071d5781906115cd826115c46002870154612ded565b600287016136cc565b602090601f8311600114611b4d575f92611b42575b50508160011b915f199060031b1c19161760028201555b60608701518051906001600160401b03821161071d57819061162b826116226003870154612ded565b600387016136cc565b602090601f8311600114611ad4575f92611ac9575b50508160011b915f199060031b1c19161760038201555b60808701518051906001600160401b03821161071d578190611689826116806004870154612ded565b600487016136cc565b602090601f8311600114611a5b575f92611a50575b50508160011b915f199060031b1c19161760048201555b60a087015160058201556006810160ff60c08901511681549061ff0060e08b0151151560081b169161ffff19161717905561010087015160078201556101208701516008820155610140600982019701519660ff88511681549062ffff0060208b015160081b1664ffff00000060408c015160181b169166ffff000000000060608d015160281b169366ffffffffffffff1916171717179055608087015180516001600160401b03811161071d5780600a84019261177d826117778654612ded565b866136cc565b602090601f83116001146119ec575f926119e1575b50508160011b915f199060031b1c19161790555b60a08701519687516001600160401b03811161071d576020986117d9826117d0600b870154612ded565b600b87016136cc565b8990601f831160011461194457946118ef94600c60c08b9c96866118e197839c977f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a9e9c5f92611939575b50508160011b915f199060031b1c191617600b8501555b015191015560ff60a435165f5260088b5260405f205460098c5260405f208a5f528c5260405f205560ff60a435165f5260088b5261187c8960405f206138c6565b60ff60a435165f52600a8b526118958960405f206138c6565b335f52600b8b526118a98960405f206138c6565b60405160ff60a435168152897f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec149318d3393a33691613704565b888151910120923691613704565b8681519101206040519160ff60c43516835261ffff60e435168884015261ffff6101043516604084015261ffff61012435166060840152608083015260a0820152a2604051908152f35b015190505f80611824565b90600b85015f528a5f20915f5b601f19851681106119ca575094600c60c08b9c96600187839c977f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a9e9c976118ef9c6118e19b601f198116106119b2575b505050811b01600b85015561183b565b01515f1960f88460031b161c191690555f80806119a2565b91928c600181928685015181550194019201611951565b015190508a80611792565b5f858152602081209350601f198516905b818110611a385750908460019594939210611a20575b505050811b0190556117a6565b01515f1960f88460031b161c191690558a8080611a13565b929360206001819287860151815501950193016119fd565b01519050898061169e565b9250600484015f5260205f20905f935b601f1984168510611aae576001945083601f19811610611a96575b505050811b0160048201556116b5565b01515f1960f88460031b161c19169055898080611a86565b81810151835560209485019460019093019290910190611a6b565b015190508980611640565b9250600384015f5260205f20905f935b601f1984168510611b27576001945083601f19811610611b0f575b505050811b016003820155611657565b01515f1960f88460031b161c19169055898080611aff565b81810151835560209485019460019093019290910190611ae4565b0151905089806115e2565b9250600284015f5260205f20905f935b601f1984168510611ba0576001945083601f19811610611b88575b505050811b0160028201556115f9565b01515f1960f88460031b161c19169055898080611b78565b81810151835560209485019460019093019290910190611b5d565b604051632d557a2f60e11b8152600490fd5b90506020813d602011611bff575b81611be860209383612e92565b8101031261022357611bf9906138b9565b8e611475565b3d9150611bdb565b6040513d5f823e3d90fd5b6040516332f12a8d60e21b8152600490fd5b5060208a14156113f5565b508b156113e1565b604051630368f92d60e11b815260206004820152602860248201527f4174746573746174696f6e20646f63756d656e7420766572696669636174696f6044820152671b8819985a5b195960c21b6064820152608490fd5b9850506040883d604011611cc7575b81611caa60409383612e92565b81010312610223576020611cbd896138b9565b980151975f6113bd565b3d9150611c9d565b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b611cfc3361360c565b604051905f80516020613d17833981519152611d1783612e77565b6042835260208301906060368337835115611e8057603082538351600190811015611e8057607860218601536041905b808211611e3c575050611df8576048611dc592611dd492611df49560405195869376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b6020860152611d9c815180926020603789019101612f54565b8401917001034b99036b4b9b9b4b733903937b6329607d1b603784015251809386840190612f54565b01036028810184520182612e92565b60405162461bcd60e51b8152602060048201529182916024830190612f75565b0390fd5b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f8116906010821015611e8057611e7a916f181899199a1a9b1b9c1cb0b131b232b360811b901a611e7085896135ef565b5360041c92613600565b90611d47565b634e487b7160e01b5f52603260045260245ffd5b34610223576020366003190112610223576004355f526007602052602060ff600660405f20015460081c166040519015158152f35b3461022357604036600319011261022357611ee2612f9a565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610223575f366003190112610223576020600254604051908152f35b34610223576020806003193601126102235760ff611f4a612dcd565b165f526008815260405f209060405190818184549182815201935f52815f20915f905b828210611f84576102658561031381890382612e92565b835486529485019460019384019390910190611f6d565b3461022357602036600319011261022357611fb46132b3565b600435600255005b34610223575f366003190112610223575f80600654905b81811061212c5750611fe4826137a6565b91611ff26040519384612e92565b808352612001601f19916137a6565b015f5b8181106121095750505f805b828210612060576040805160208082528651818301819052818801938301915f5b82811061203e5784840385f35b85518051855282015160ff168483015294810194604090930192600101612031565b612069826137df565b509160ff6120a38160018096015416612081846137df565b50549060ff165f52600560205260405f20905f5260205260ff60405f20541690565b6120ba575b506120b49192506137bd565b90612010565b6120b492612101916120cb846137df565b50906120d6836137bd565b96604051926120e484612e25565b8054845201541660208201526120fa82896137cb565b52866137cb565b5082916120a8565b60209060405161211881612e25565b5f8152825f81830152828701015201612004565b61214960ff600161213c846137df565b50015416612081836137df565b61215c575b612157906137bd565b611fd3565b91612169612157916137bd565b92905061214e565b346102235760031960603682011261022357600435906001600160401b039081831161022357606090833603011261022357602435818111610223576121bb9036906004016130c8565b6044359360ff851694858103610223576121d7906103546132b3565b15611ce1576121e89060040161384f565b92845f526020906005825260405f20855f5282526001908160405f200154159060405190608082018281108282111761071d576040528382526002858301928a84526040810142815261223c368a8c613704565b94606083019586528c5f526005895260405f208c5f52895260405f20925115159060ff61ff008554925160081b1692169061ffff1916171782555186820155019151805191821161071d578190612297826117778654612ded565b8690601f8311600114612399575f9261238e575b50505f19600383901b1c191690841b1790555b6122ff575b506122fa7f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe93604051938385948552840191613786565b0390a3005b926040519061230d82612e25565b858252828201878152600654600160401b81101561071d57808761233492016006556137df565b93909361237b577f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe966122fa9460ff925181550191511660ff1982541617905593506122c3565b634e487b7160e01b5f525f60045260245ffd5b015190508a806122ab565b90869350601f19831691855f52885f20925f5b8a8282106123e257505084116123ca575b505050811b0190556122be565b01515f1960f88460031b161c191690558a80806123bd565b8385015186558a979095019493840193016123ac565b34610223576020806003193601126102235760ff612414612dcd565b165f9081526008825260408120805490825b8281106125615750612437836137a6565b926124456040519485612e92565b808452612454601f19916137a6565b01845f5b82811061254b575050505f905f5b8381106124ca576040805187815286518189018190525f92600582901b8301810191898b01918b9085015b82871061249e5785850386f35b9091929382806124ba600193603f198a82030186528851613018565b9601920196019592919092612491565b806124d86125009284613808565b9054600391821b1c5f526007908189526124f460405f20613c00565b612505575b50506137bd565b612466565b61250f8386613808565b9054911b1c5f52875261254360405f209461253261252c826137bd565b96613b4e565b61253c828a6137cb565b52876137cb565b5087806124f9565b612553613af3565b828288010152018590612458565b61256b8183613808565b90549060031b1c5f526007855261258460405f20613c00565b612597575b612592906137bd565b612426565b926125a4612592916137bd565b939050612589565b346102235760031960203682011261022357600435906001600160401b0382116102235760609082360301126102235761049560209160040161384f565b3461022357602080600319360112610223576001600160401b036004358181116102235761261c9036906004016130c8565b916126256132b3565b821161071d576001916126388354612ded565b601f8111612708575b505f92601f82116001146126a95781612673945f9161269e575b505f19600384901b1c191682821b1790553691613704565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661265b565b601f19821693815f52855f20905f5b878782106126f45750508361267396106126db575b50508082811b01905561029e565b8401355f19600385901b60f8161c1916905585806126cd565b8683013584559284019291820191016126b8565b61273190845f52855f20601f840160051c810191878510612737575b601f0160051c01906136b6565b84612641565b9091508190612724565b34610223576060366003190112610223576004356024356001600160401b03604435818111610223576127789036906004016130c8565b9290845f526020926007845260405f209460078601541561087c5760ff600687015460081c16156128ed574284116128db576127b484426137fb565b600254106128c95760405192858401908882528560408601526040855260608501938585109085111761071d578461282f88948694856040528351902090633f1333cd60e11b8652606060648501526128148c600360c4870191016131ab565b9160848501526063198483030160a4850152605f1994613786565b0301816109005afa918215611c075784905f93612892575b50505015612880577f3cf0bf7708060bf073b6ddb921eef0485b3eb9da828e0ed751fa6582ef337f5792600842910155604051908152a2005b6040516345576abb60e01b8152600490fd5b90809293503d83116128c2575b6128a98185612e92565b81010312610223576128ba906138b9565b858381612847565b503d61289f565b604051631cfc538360e11b8152600490fd5b604051630315ef0b60e21b8152600490fd5b60405163028b8e4960e61b8152600490fd5b346102235760203660031901126102235760043561291b613af3565b50805f526007602052600760405f2001541561087c575f52600760205261026561294760405f20613b4e565b604051918291602083526020830190613018565b34610223575f3660031901126102235760206040515f80516020613d178339815191528152f35b346102235760403660031901126102235761299b612f9a565b336001600160a01b038216036129b7576104079060043561357c565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b3461022357604036600319011261022357600435612a30612f9a565b815f525f602052612a47600160405f20015461347e565b815f525f60205260405f209060018060a01b031690815f5260205260ff60405f20541615612a7157005b815f525f60205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b34610223576020366003190112610223576004355f52600760205261026561024a610251600360405f2001604051928380926131ab565b34610223576020366003190112610223576004355f525f6020526020600160405f200154604051908152f35b346102235760403660031901126102235760ff612b37612dcd565b165f52600560205260405f206024355f5260205260ff60405f20610265815491612b68600260018301549201612eb3565b906040519484818796161515865260081c1660208501526040840152608060608401526080830190612f75565b34610223576020806003193601126102235760043590815f5260078152600760405f2001541561087c575f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149081612c73575b159081612c6a575b50612c5857600790825f5252600660405f200180549060ff8260081c16156128ed5761ff001982169055612c329060ff16826139c6565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a2005b6040516398b31e6f60e01b8152600490fd5b90501583612bfb565b90505f80516020613d178339815191525f525f825260405f20335f52825260ff60405f20541690612bf3565b34610223575f3660031901126102235760206040516109008152f35b346102235760203660031901126102235760043563ffffffff60e01b811680910361022357602090637965db0b60e01b8114908115612d00575b506040519015158152f35b6301ffc9a760e01b14905082612cf5565b34610223576020806003193601126102235760043591825f5260078252600760405f20015415612dbe57505f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149182612d90575b50159081612d87575b50612c585761040790613926565b90501582612d79565b9091505f80516020613d178339815191525f525f815260405f2090335f525260ff60405f2054169083612d70565b63f6523b6560e01b8152600490fd5b6004359060ff8216820361022357565b6024359060ff8216820361022357565b90600182811c92168015612e1b575b6020831014612e0757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612dfc565b604081019081106001600160401b0382111761071d57604052565b60e081019081106001600160401b0382111761071d57604052565b61016081019081106001600160401b0382111761071d57604052565b608081019081106001600160401b0382111761071d57604052565b90601f801991011681019081106001600160401b0382111761071d57604052565b9060405191825f825492612ec684612ded565b9081845260019485811690815f14612f315750600114612ef1575b5050612eef92500383612e92565b565b909391505f52602090815f20935f915b818310612f19575050612eef93508201015f80612ee1565b85548884018501529485019487945091830191612f01565b915050612eef94506020925060ff191682840152151560051b8201015f80612ee1565b5f5b838110612f655750505f910152565b8181015183820152602001612f56565b90602091612f8e81518092818552858086019101612f54565b601f01601f1916010190565b602435906001600160a01b038216820361022357565b9060ff825116815261ffff806020840151166020830152806040840151166040830152606083015116606082015260c08061300f612ffd608086015160e0608087015260e0860190612f75565b60a086015185820360a0870152612f75565b93015191015290565b906130c59160018060a01b0380825116835260208201511660208301526130786130666130546040840151610160806040880152860190612f75565b60608401518582036060870152612f75565b60808301518482036080860152612f75565b9160a082015160a082015260ff60c08301511660c082015260e0820151151560e0820152610100808301519082015261012080830151908201526101408092015191818403910152612fb0565b90565b9181601f84011215610223578235916001600160401b038311610223576020838186019501011161022357565b602090816040818301928281528551809452019301915f5b82811061311b575050505090565b83518552938101939281019260010161310d565b600180545f939261313f82612ded565b80825291838116908115613190575060011461315a57505050565b90929350815f52602092835f2092845f945b83861061317c5750505050010190565b80548587018301529401938590820161316c565b91935050602093945060ff191683830152151560051b010190565b905f92918054916131bb83612ded565b9182825260019384811690815f1461321957506001146131dc575b50505050565b90919394505f52602092835f2092845f945b83861061320557505050500101905f8080806131d6565b8054858701830152940193859082016131ee565b9294505050602093945060ff191683830152151560051b0101905f8080806131d6565b9060405161324981612e40565b60c060038294805460ff8116855261ffff90818160081c166020870152818160181c16604087015260281c16606085015260405161328e8161024a81600186016131ab565b60808501526040516132a78161024a81600286016131ab565b60a08501520154910152565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602090815260408083205490929060ff16156132f457505050565b6132fd3361360c565b908084519061330b82612e77565b6042825284820192606036853782511561346a576030845382516001908110156134565790607860218501536041915b8083116133fc575050506133ba576048611df493869361339e9361338f985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611d9c815180928c603789019101612f54565b01036028810187520185612e92565b5192839262461bcd60e51b845260048401526024830190612f75565b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f811660108110156134425761343b91906f181899199a1a9b1b9c1cb0b131b232b360811b901a61343186886135ef565b5360041c93613600565b919061333b565b634e487b7160e01b84526032600452602484fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b81526032600452602490fd5b5f90808252602090828252604092838120338252835260ff8482205416156134a65750505050565b6134af3361360c565b918451906134bc82612e77565b6042825284820192606036853782511561346a576030845382516001908110156134565790607860218501536041915b808311613540575050506133ba576048611df493869361339e9361338f985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611d9c815180928c603789019101612f54565b909192600f811660108110156134425761357591906f181899199a1a9b1b9c1cb0b131b232b360811b901a61343186886135ef565b91906134ec565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054166135ab57505050565b80835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b908151811015611e80570160200190565b80156109f7575f190190565b60405190606082018281106001600160401b0382111761071d57604052602a8252602082016040368237825115611e8057603090538151600190811015611e8057607860218401536029905b808211613668575050611df85790565b9091600f81169060108210156136a25761369c916f181899199a1a9b1b9c1cb0b131b232b360811b901a611e7085876135ef565b90613658565b60245f634e487b7160e01b81526032600452fd5b8181106136c1575050565b5f81556001016136b6565b9190601f81116136db57505050565b612eef925f5260205f20906020601f840160051c8301931061273757601f0160051c01906136b6565b9291926001600160401b03821161071d576040519161372d601f8201601f191660200184612e92565b829481845281830111610223578281602093845f960137010152565b90600454821015611e805760045f52600582901c7f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0191601f1690565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b03811161071d5760051b60200190565b5f1981146109f75760010190565b8051821015611e805760209160051b010190565b600654811015611e805760065f5260205f209060011b01905f90565b919082039182116109f757565b8054821015611e80575f5260205f2001905f90565b903590601e198136030182121561022357018035906001600160401b0382116102235760200191813603831361022357565b61387e6138b361385f838061381d565b6020949194916138718382018261381d565b958260408894019061381d565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612e92565b51902090565b5190811515820361022357565b805490600160401b82101561071d57816138e89160016138fe94018155613808565b819391549060031b91821b915f19901b19161790565b9055565b9060ff165f52600560205260405f20905f5260205260ff60405f20541615610a0b57565b5f818152600760205260408120906006820180549060ff8260081c166139bf5761010060057fcfe7e61b35fbf35c6b0d5c7ebe6feb619ab832dc476c4a02dbdc041fb92b5ca69501549261397e60ff82168095613902565b61ff001916179055808252600860205260408220546009602052604083208584526020526040832055815260086020526139bb83604083206138c6565b80a2565b5050505050565b9060ff165f918183526020600981526040928385208386528252838520548186526008835284862054905f1991828101908111613aa757808203613a5a575b50508186526008835284862080548015613a4657820191613a268383613808565b909182549160031b1b191690555584526009815282842091845252812055565b634e487b7160e01b88526031600452602488fd5b613a6d9084895260088652878920613808565b90549060031b1c8388526008855280613a8b6138e8848a8c20613808565b9055838852600985528688209088528452858720555f80613a05565b634e487b7160e01b88526011600452602488fd5b60405190613ac882612e40565b5f60c08382815282602082015282604082015282606082015260606080820152606060a08201520152565b60405190613b0082612e5b565b815f80825280602083015260606040830152606080830152606060808301528060a08301528060c08301528060e083015280610100830152610120820152610140613b49613abb565b910152565b90604051613b5b81612e5b565b610140613b496009839560018060a01b038082541686526001820154166020860152613b8960028201612eb3565b6040860152604051613ba28161024a81600386016131ab565b6060860152604051613bbb8161024a81600486016131ab565b6080860152600581015460a086015260ff600682015481811660c088015260081c16151560e0860152600781015461010086015260088101546101208601520161323c565b600681015460ff8160081c1615613c6057613c1f6008830154426137fb565b60025410613c605760ff6005613c52930154911660ff165f52600560205260405f20905f5260205260ff60405f20541690565b15613c5c57600190565b5f90565b50505f90565b9794613c7e90613c8d93979892989594953691613704565b60208151910120943691613704565b602081519101209360ff6040519660208801987fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8a52604089015216606087015261ffff928380921660808801521660a08601521660c084015260e08301526101009081830152815261012081018181106001600160401b0382111761071d576040525190209056feae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bfa26469706673582212201024887846314102994802fb5a067cc08f43511b91364cd9088696fbdd19dc4d64736f6c634300081400332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "deployedBytecode": "0x6080806040526004361015610012575f80fd5b5f3560e01c9081625ae7cf14612d115750806301ffc9a714612cbb57806308c84e7014612c9f5780631788371714612b955780632068b3db14612b1c578063248a9ca314612af05780632cbe755214612ab95780632f2ff15d14612a1457806336568abe146129825780633c1a88b01461295b578063418b207d146128ff57806342830ad81461274157806343ed3274146125ea578063476cb030146125ac5780634b6014da146123f85780634c4c8e8b146121715780635766275a14611fbc5780635c1090b214611f9b57806363b86b8914611f2e57806387267aca14611f115780638ecaf98c1461049d57806391d1485414611ec9578063927d3c9514611e94578063939ea4b81461115d578063971cfbf114610a5f5780639831ced514610a1c578063a041ff51146108d1578063a217fddf146108b7578063a3bf31f11461088e578063abbf8f6a1461081d578063b82644b7146107b0578063cbdfc4e0146105d7578063ccdf0493146104b7578063cf79c8e11461049d578063d101bec414610409578063d547741f146103cb578063d6741a4214610375578063e10a2ea414610336578063e44cc482146102b3578063f33d2c0014610269578063f44a0780146102275763fec0de46146101e9575f80fd5b34610223575f3660031901126102235760206040517fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8152f35b5f80fd5b34610223575f366003190112610223576102656040516102518161024a8161312f565b0382612e92565b604051918291602083526020830190612f75565b0390f35b34610223576020366003190112610223576004356001600160401b038111610223576102a561029e60209236906004016130c8565b3691613704565b818151910120604051908152f35b3461022357602080600319360112610223576004356001600160a01b03811690819003610223575f52600b815260405f209060405190818184549182815201935f52815f20915f905b82821061031f576102658561031381890382612e92565b604051918291826130f5565b8354865294850194600193840193909101906102fc565b3461022357602036600319011261022357602061036b610354612dcd565b60ff165f526003602052600160405f200154151590565b6040519015158152f35b346102235760203660031901126102235760ff610390612dcd565b165f5260036020526103c160405f2060016103aa82612eb3565b910154604051928392604084526040840190612f75565b9060208301520390f35b34610223576040366003190112610223576104076004356103ea612f9a565b90805f525f602052610402600160405f20015461347e565b61357c565b005b346102235760e036600319011261022357610422612ddd565b61ffff604435818116810361022357606435928284168403610223576084359283168303610223576001600160401b039260a4358481116102235761046b9036906004016130c8565b9160c4359586116102235760209661048a6104959736906004016130c8565b969095600435613c66565b604051908152f35b34610223575f366003190112610223576020604051818152f35b34610223576020366003190112610223576004355f908152600760205260409020805460018201546001600160a01b03908116929116906104fa60028201612eb3565b60405190918161050d81600384016131ab565b036105189083612e92565b604051918261052a81600485016131ab565b036105359084612e92565b6005820154926006830154906007840154926008850154946009016105599061323c565b96604051998a998a5260208a01526101608060408b0152890161057b91612f75565b88810360608a015261058c91612f75565b878103608089015261059d91612f75565b9360a087015260ff811660c087015260081c60ff16151560e086015261010085015261012084015282810361014084015261026591612fb0565b34610223576040366003190112610223576105f0612dcd565b6001600160401b036024358181116102235761061260ff9136906004016130c8565b91909361061d6132b3565b1692835f52602090600382526001938460405f20015461079e576040519061064482612e25565b61064f368685613704565b8252838201428152875f526003855260405f20925191825190811161071d5787926106848261067e8754612ded565b876136cc565b8690601f831160011461073c575f919083610731575b50505f19600383901b1c191690831b1783555b5191015560045490600160401b82101561071d576106f4827fed0161664cf318441e585cccad9fa9fc770298b648a49635dda84ac6f0adc908966107189401600455613749565b81549060031b9060ff89831b921b1916179055604051938385948552840191613786565b0390a2005b634e487b7160e01b5f52604160045260245ffd5b015190508a8061069a565b90601f19831691865f52885f20925f5b8a8282106107885750509084879594939210610770575b505050811b0183556106ad565b01515f1960f88460031b161c191690558a8080610763565b8385015186558d9890950194938401930161074c565b60405163095faf7360e01b8152600490fd5b34610223576020806003193601126102235760ff6107cc612dcd565b165f52600a815260405f209060405190818184549182815201935f52815f20915f905b828210610806576102658561031381890382612e92565b8354865294850194600193840193909101906107ef565b3461022357602036600319011261022357600435610839613abb565b50805f526007602052600760405f2001541561087c575f526007602052610265610868600960405f200161323c565b604051918291602083526020830190612fb0565b60405163f6523b6560e01b8152600490fd5b34610223576020366003190112610223576004355f526007602052602061036b60405f20613c00565b34610223575f3660031901126102235760206040515f8152f35b34610223576040366003190112610223576004356108ed612ddd565b906108f66132b3565b60ff821691825f52600590602082815260405f20845f52815260ff60405f20541615610a0b57845f5282815260405f20845f52815260405f2060ff198154169055845f526008815260405f20928354805b6109735786867fd5a6741f8bbaee4cc3ea05953ea04c6a1863ce4c66a52da653dd0a315f84c2fb5f80a3005b5f198101908082116109f75761098c6109b09287613808565b90549060031b1c805f526007855260405f208885820154146109b6575b5050613600565b80610947565b600601805461ff00191690556109cc86826139c6565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a288806109a9565b634e487b7160e01b5f52601160045260245ffd5b60405162d85d7f60e71b8152600490fd5b346102235760403660031901126102235760ff610a37612dcd565b165f52600560205260405f206024355f52602052602060ff60405f2054166040519015158152f35b34610223575f36600319011261022357604051806004549182815260208091018060045f528383805f20925f5b8880601f830110610fd557610baa95549184828210610fc2575b828210610fac575b828210610f96575b828210610f80575b828210610f6b575b828210610f55575b828210610f3f575b828210610f29575b828210610f13575b828210610efd575b828210610ee7575b828210610ed1575b828210610ebb575b828210610ea5575b828210610e8f575b828210610e79575b828210610e63575b828210610e4d575b828210610e37575b828210610e21575b828210610e0b575b828210610df5575b828210610ddf575b828210610dc9575b828210610db3575b828210610d9d575b828210610d87575b828210610d71575b828210610d5b575b828210610d45575b828210610d2f575b5010610d21575b5090509592950384612e92565b610bb3816137a6565b91610bc16040519384612e92565b818352601f199182610bd2826137a6565b015f5b818110610cf85750505f5b818110610c9157505060405193604085019060408652518091526060850195905f5b818110610c7a575050508385038185015282519081865280860181808460051b8901019501935f975b848910610c385787870388f35b9091929394958480828585600195030187528951908280610c628451604085526040850190612f75565b93015191015298019401980197919094939294610c2b565b825160ff1688529683019691830191600101610c02565b8060ff610ca3610cf093999699613749565b919054600392831b1c165f52845260405f20600160405191610cc483612e25565b610ccd81612eb3565b8352015485820152610cdf82886137cb565b52610cea81876137cb565b506137bd565b959295610be0565b8390604098959851610d0981612e25565b606081525f8382015282828901015201969396610bd5565b60f81c815201849088610b9d565b6001919460ff8560f01c16815201930184610b96565b6001919460ff8560e81c16815201930184610b8e565b6001919460ff8560e01c16815201930184610b86565b6001919460ff8560d81c16815201930184610b7e565b6001919460ff8560d01c16815201930184610b76565b6001919460ff8560c81c16815201930184610b6e565b6001919460ff8560c01c16815201930184610b66565b6001919460ff8560b81c16815201930184610b5e565b6001919460ff8560b01c16815201930184610b56565b6001919460ff8560a81c16815201930184610b4e565b6001919460ff8560a01c16815201930184610b46565b6001919460ff8560981c16815201930184610b3e565b6001919460ff8560901c16815201930184610b36565b6001919460ff8560881c16815201930184610b2e565b6001919460ff8560801c16815201930184610b26565b6001919460ff8560781c16815201930184610b1e565b6001919460ff8560701c16815201930184610b16565b6001919460ff8560681c16815201930184610b0e565b6001919460ff8560601c16815201930184610b06565b6001919460ff8560581c16815201930184610afe565b6001919460ff8560501c16815201930184610af6565b6001919460ff8560481c16815201930184610aee565b6001919460ff8560401c16815201930184610ae6565b6001919460ff8560381c16815201930184610ade565b6001919460ff8560301c16815201930184610ad6565b6001919460ff8560281c16815201930184610ace565b6001919460ff85831c16815201930184610ac6565b6001919460ff8560181c16815201930184610abe565b6001919460ff8560101c16815201930184610ab6565b6001919460ff8560081c16815201930184610aae565b6001919460ff8516815201930184610aa6565b509250610400600191855460ff8082168352808260081c1686840152808260101c16604084015280828482826060828260181c168185015282828d82826080921c1681880152828260a095828260281c16878b015260c099838360301c168b820152838360381c1660e0820152838360401c16610100820152838360481c16610120820152838360501c16610140820152610160848460581c169101521c166101808d0152828260681c166101a08d0152828260701c166101c08d0152828260781c166101e08d01521c166102008a0152828260881c166102208a0152828260901c166102408a0152828260981c166102608a01521c16610280870152828260a81c166102a0870152828260b01c166102c0870152828260b81c166102e08701521c16610300840152808260c81c16610320840152808260d01c16610340840152808260d81c16610360840152808260e01c16610380840152808260e81c166103a08401528160f01c166103c083015260f81c6103e082015201930191018591928591610a8c565b34610223576101a0366003190112610223576004356001600160401b0381116102235761118e9036906004016130c8565b6024356001600160401b038111610223576111ad9036906004016130c8565b6044929192356001600160401b038111610223576111cf9036906004016130c8565b606435959194916001600160a01b0387168703610223576084356001600160401b038111610223576112059036906004016130c8565b91909660ff60a4351660a435036102235760ff60c4351660c435036102235761ffff60e4351660e435036102235761ffff610104351661010435036102235761ffff61012435166101243503610223576001600160401b0361014435116102235761127636610144356004016130c8565b9490956001600160401b0361016435116102235761129a36610164356004016130c8565b98909a6001600160401b036101843511610223576112be36610184356004016130c8565b335f9081527f04824ce33f8c803f181be3cbd8c64c21a81bc533fe6d4b921df175b0eaf5cbeb602052604090205460ff1615611cf35761131160a43560ff165f526003602052600160405f200154151590565b15611ce157611321368587613704565b602081519101209c8d5f526007602052600760405f200154611ccf5761136f986040916113a661139561138285519d8e95869563f7cf40d360e01b8752608060048801526084870191613786565b8481036003190160248601528a8c613786565b8381036003190160448501528b8d613786565b82810360031901606484015261312f565b03816109005afa978815611c07575f905f99611c8e575b5015611c37576113cf60a43589613902565b6113d7613abb565b5089158015611c2f575b611c1257602061ffff60e435161480611c24575b611c125760209061141e8f8f908f918f8f916101243590610104359060e4359060c43590613c66565b926114606040519485938493633f1333cd60e11b855260606004860152611449606486018b8d613786565b926024860152600319858403016044860152613786565b03816109005afa908115611c07575f91611bcd575b5015611bbb576040519661148888612e40565b60ff60c43516885261ffff60e435811660208a015261010435811660408a0152610124351660608901526114bd368a8c613704565b60808901526114cd368c8f613704565b60a08901524260c0890152604051809e6114e682612e5b565b3382526001600160a01b031660209091015261150491369190613704565b60408d0152369061151492613704565b60608b0152369061152492613704565b608089015260a088015260ff60a4351660c0880152600160e088018190524261010089018190526101208901526101408801919091525f85815260076020908152604091829020895181546001600160a01b03199081166001600160a01b03928316178355928b0151948201805490931694169390931790558701518051906001600160401b03821161071d5781906115cd826115c46002870154612ded565b600287016136cc565b602090601f8311600114611b4d575f92611b42575b50508160011b915f199060031b1c19161760028201555b60608701518051906001600160401b03821161071d57819061162b826116226003870154612ded565b600387016136cc565b602090601f8311600114611ad4575f92611ac9575b50508160011b915f199060031b1c19161760038201555b60808701518051906001600160401b03821161071d578190611689826116806004870154612ded565b600487016136cc565b602090601f8311600114611a5b575f92611a50575b50508160011b915f199060031b1c19161760048201555b60a087015160058201556006810160ff60c08901511681549061ff0060e08b0151151560081b169161ffff19161717905561010087015160078201556101208701516008820155610140600982019701519660ff88511681549062ffff0060208b015160081b1664ffff00000060408c015160181b169166ffff000000000060608d015160281b169366ffffffffffffff1916171717179055608087015180516001600160401b03811161071d5780600a84019261177d826117778654612ded565b866136cc565b602090601f83116001146119ec575f926119e1575b50508160011b915f199060031b1c19161790555b60a08701519687516001600160401b03811161071d576020986117d9826117d0600b870154612ded565b600b87016136cc565b8990601f831160011461194457946118ef94600c60c08b9c96866118e197839c977f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a9e9c5f92611939575b50508160011b915f199060031b1c191617600b8501555b015191015560ff60a435165f5260088b5260405f205460098c5260405f208a5f528c5260405f205560ff60a435165f5260088b5261187c8960405f206138c6565b60ff60a435165f52600a8b526118958960405f206138c6565b335f52600b8b526118a98960405f206138c6565b60405160ff60a435168152897f97d0af70a4d2c616e9c1093d5f57c8b31b3a7d824f1d335e5d5416402ec149318d3393a33691613704565b888151910120923691613704565b8681519101206040519160ff60c43516835261ffff60e435168884015261ffff6101043516604084015261ffff61012435166060840152608083015260a0820152a2604051908152f35b015190505f80611824565b90600b85015f528a5f20915f5b601f19851681106119ca575094600c60c08b9c96600187839c977f6ac759b77c43a1cbd77ccfc37dc0558964e2c451be81d5f7211f237ba1b9f07a9e9c976118ef9c6118e19b601f198116106119b2575b505050811b01600b85015561183b565b01515f1960f88460031b161c191690555f80806119a2565b91928c600181928685015181550194019201611951565b015190508a80611792565b5f858152602081209350601f198516905b818110611a385750908460019594939210611a20575b505050811b0190556117a6565b01515f1960f88460031b161c191690558a8080611a13565b929360206001819287860151815501950193016119fd565b01519050898061169e565b9250600484015f5260205f20905f935b601f1984168510611aae576001945083601f19811610611a96575b505050811b0160048201556116b5565b01515f1960f88460031b161c19169055898080611a86565b81810151835560209485019460019093019290910190611a6b565b015190508980611640565b9250600384015f5260205f20905f935b601f1984168510611b27576001945083601f19811610611b0f575b505050811b016003820155611657565b01515f1960f88460031b161c19169055898080611aff565b81810151835560209485019460019093019290910190611ae4565b0151905089806115e2565b9250600284015f5260205f20905f935b601f1984168510611ba0576001945083601f19811610611b88575b505050811b0160028201556115f9565b01515f1960f88460031b161c19169055898080611b78565b81810151835560209485019460019093019290910190611b5d565b604051632d557a2f60e11b8152600490fd5b90506020813d602011611bff575b81611be860209383612e92565b8101031261022357611bf9906138b9565b8e611475565b3d9150611bdb565b6040513d5f823e3d90fd5b6040516332f12a8d60e21b8152600490fd5b5060208a14156113f5565b508b156113e1565b604051630368f92d60e11b815260206004820152602860248201527f4174746573746174696f6e20646f63756d656e7420766572696669636174696f6044820152671b8819985a5b195960c21b6064820152608490fd5b9850506040883d604011611cc7575b81611caa60409383612e92565b81010312610223576020611cbd896138b9565b980151975f6113bd565b3d9150611c9d565b604051631346e43960e01b8152600490fd5b604051635d672da760e01b8152600490fd5b611cfc3361360c565b604051905f80516020613d17833981519152611d1783612e77565b6042835260208301906060368337835115611e8057603082538351600190811015611e8057607860218601536041905b808211611e3c575050611df8576048611dc592611dd492611df49560405195869376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b6020860152611d9c815180926020603789019101612f54565b8401917001034b99036b4b9b9b4b733903937b6329607d1b603784015251809386840190612f54565b01036028810184520182612e92565b60405162461bcd60e51b8152602060048201529182916024830190612f75565b0390fd5b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f8116906010821015611e8057611e7a916f181899199a1a9b1b9c1cb0b131b232b360811b901a611e7085896135ef565b5360041c92613600565b90611d47565b634e487b7160e01b5f52603260045260245ffd5b34610223576020366003190112610223576004355f526007602052602060ff600660405f20015460081c166040519015158152f35b3461022357604036600319011261022357611ee2612f9a565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b34610223575f366003190112610223576020600254604051908152f35b34610223576020806003193601126102235760ff611f4a612dcd565b165f526008815260405f209060405190818184549182815201935f52815f20915f905b828210611f84576102658561031381890382612e92565b835486529485019460019384019390910190611f6d565b3461022357602036600319011261022357611fb46132b3565b600435600255005b34610223575f366003190112610223575f80600654905b81811061212c5750611fe4826137a6565b91611ff26040519384612e92565b808352612001601f19916137a6565b015f5b8181106121095750505f805b828210612060576040805160208082528651818301819052818801938301915f5b82811061203e5784840385f35b85518051855282015160ff168483015294810194604090930192600101612031565b612069826137df565b509160ff6120a38160018096015416612081846137df565b50549060ff165f52600560205260405f20905f5260205260ff60405f20541690565b6120ba575b506120b49192506137bd565b90612010565b6120b492612101916120cb846137df565b50906120d6836137bd565b96604051926120e484612e25565b8054845201541660208201526120fa82896137cb565b52866137cb565b5082916120a8565b60209060405161211881612e25565b5f8152825f81830152828701015201612004565b61214960ff600161213c846137df565b50015416612081836137df565b61215c575b612157906137bd565b611fd3565b91612169612157916137bd565b92905061214e565b346102235760031960603682011261022357600435906001600160401b039081831161022357606090833603011261022357602435818111610223576121bb9036906004016130c8565b6044359360ff851694858103610223576121d7906103546132b3565b15611ce1576121e89060040161384f565b92845f526020906005825260405f20855f5282526001908160405f200154159060405190608082018281108282111761071d576040528382526002858301928a84526040810142815261223c368a8c613704565b94606083019586528c5f526005895260405f208c5f52895260405f20925115159060ff61ff008554925160081b1692169061ffff1916171782555186820155019151805191821161071d578190612297826117778654612ded565b8690601f8311600114612399575f9261238e575b50505f19600383901b1c191690841b1790555b6122ff575b506122fa7f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe93604051938385948552840191613786565b0390a3005b926040519061230d82612e25565b858252828201878152600654600160401b81101561071d57808761233492016006556137df565b93909361237b577f98c6d69cba0181c4e436357d22ac56e02ec472c03f04df0fbf89d1bb614af2fe966122fa9460ff925181550191511660ff1982541617905593506122c3565b634e487b7160e01b5f525f60045260245ffd5b015190508a806122ab565b90869350601f19831691855f52885f20925f5b8a8282106123e257505084116123ca575b505050811b0190556122be565b01515f1960f88460031b161c191690558a80806123bd565b8385015186558a979095019493840193016123ac565b34610223576020806003193601126102235760ff612414612dcd565b165f9081526008825260408120805490825b8281106125615750612437836137a6565b926124456040519485612e92565b808452612454601f19916137a6565b01845f5b82811061254b575050505f905f5b8381106124ca576040805187815286518189018190525f92600582901b8301810191898b01918b9085015b82871061249e5785850386f35b9091929382806124ba600193603f198a82030186528851613018565b9601920196019592919092612491565b806124d86125009284613808565b9054600391821b1c5f526007908189526124f460405f20613c00565b612505575b50506137bd565b612466565b61250f8386613808565b9054911b1c5f52875261254360405f209461253261252c826137bd565b96613b4e565b61253c828a6137cb565b52876137cb565b5087806124f9565b612553613af3565b828288010152018590612458565b61256b8183613808565b90549060031b1c5f526007855261258460405f20613c00565b612597575b612592906137bd565b612426565b926125a4612592916137bd565b939050612589565b346102235760031960203682011261022357600435906001600160401b0382116102235760609082360301126102235761049560209160040161384f565b3461022357602080600319360112610223576001600160401b036004358181116102235761261c9036906004016130c8565b916126256132b3565b821161071d576001916126388354612ded565b601f8111612708575b505f92601f82116001146126a95781612673945f9161269e575b505f19600384901b1c191682821b1790553691613704565b80519101207f378c4547bd8b346276ae1db9e298d984a43bd8788ad795e7a141ba069045f4cb5f80a2005b90508301358661265b565b601f19821693815f52855f20905f5b878782106126f45750508361267396106126db575b50508082811b01905561029e565b8401355f19600385901b60f8161c1916905585806126cd565b8683013584559284019291820191016126b8565b61273190845f52855f20601f840160051c810191878510612737575b601f0160051c01906136b6565b84612641565b9091508190612724565b34610223576060366003190112610223576004356024356001600160401b03604435818111610223576127789036906004016130c8565b9290845f526020926007845260405f209460078601541561087c5760ff600687015460081c16156128ed574284116128db576127b484426137fb565b600254106128c95760405192858401908882528560408601526040855260608501938585109085111761071d578461282f88948694856040528351902090633f1333cd60e11b8652606060648501526128148c600360c4870191016131ab565b9160848501526063198483030160a4850152605f1994613786565b0301816109005afa918215611c075784905f93612892575b50505015612880577f3cf0bf7708060bf073b6ddb921eef0485b3eb9da828e0ed751fa6582ef337f5792600842910155604051908152a2005b6040516345576abb60e01b8152600490fd5b90809293503d83116128c2575b6128a98185612e92565b81010312610223576128ba906138b9565b858381612847565b503d61289f565b604051631cfc538360e11b8152600490fd5b604051630315ef0b60e21b8152600490fd5b60405163028b8e4960e61b8152600490fd5b346102235760203660031901126102235760043561291b613af3565b50805f526007602052600760405f2001541561087c575f52600760205261026561294760405f20613b4e565b604051918291602083526020830190613018565b34610223575f3660031901126102235760206040515f80516020613d178339815191528152f35b346102235760403660031901126102235761299b612f9a565b336001600160a01b038216036129b7576104079060043561357c565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b3461022357604036600319011261022357600435612a30612f9a565b815f525f602052612a47600160405f20015461347e565b815f525f60205260405f209060018060a01b031690815f5260205260ff60405f20541615612a7157005b815f525f60205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b34610223576020366003190112610223576004355f52600760205261026561024a610251600360405f2001604051928380926131ab565b34610223576020366003190112610223576004355f525f6020526020600160405f200154604051908152f35b346102235760403660031901126102235760ff612b37612dcd565b165f52600560205260405f206024355f5260205260ff60405f20610265815491612b68600260018301549201612eb3565b906040519484818796161515865260081c1660208501526040840152608060608401526080830190612f75565b34610223576020806003193601126102235760043590815f5260078152600760405f2001541561087c575f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149081612c73575b159081612c6a575b50612c5857600790825f5252600660405f200180549060ff8260081c16156128ed5761ff001982169055612c329060ff16826139c6565b7f01c235337ed5406ee8cebb3f299d60f237a2837430f8a14fbc81f94ce50236b65f80a2005b6040516398b31e6f60e01b8152600490fd5b90501583612bfb565b90505f80516020613d178339815191525f525f825260405f20335f52825260ff60405f20541690612bf3565b34610223575f3660031901126102235760206040516109008152f35b346102235760203660031901126102235760043563ffffffff60e01b811680910361022357602090637965db0b60e01b8114908115612d00575b506040519015158152f35b6301ffc9a760e01b14905082612cf5565b34610223576020806003193601126102235760043591825f5260078252600760405f20015415612dbe57505f80525f815260405f20335f52815260ff60405f205416825f52600782523360018060a01b0360405f205416149182612d90575b50159081612d87575b50612c585761040790613926565b90501582612d79565b9091505f80516020613d178339815191525f525f815260405f2090335f525260ff60405f2054169083612d70565b63f6523b6560e01b8152600490fd5b6004359060ff8216820361022357565b6024359060ff8216820361022357565b90600182811c92168015612e1b575b6020831014612e0757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612dfc565b604081019081106001600160401b0382111761071d57604052565b60e081019081106001600160401b0382111761071d57604052565b61016081019081106001600160401b0382111761071d57604052565b608081019081106001600160401b0382111761071d57604052565b90601f801991011681019081106001600160401b0382111761071d57604052565b9060405191825f825492612ec684612ded565b9081845260019485811690815f14612f315750600114612ef1575b5050612eef92500383612e92565b565b909391505f52602090815f20935f915b818310612f19575050612eef93508201015f80612ee1565b85548884018501529485019487945091830191612f01565b915050612eef94506020925060ff191682840152151560051b8201015f80612ee1565b5f5b838110612f655750505f910152565b8181015183820152602001612f56565b90602091612f8e81518092818552858086019101612f54565b601f01601f1916010190565b602435906001600160a01b038216820361022357565b9060ff825116815261ffff806020840151166020830152806040840151166040830152606083015116606082015260c08061300f612ffd608086015160e0608087015260e0860190612f75565b60a086015185820360a0870152612f75565b93015191015290565b906130c59160018060a01b0380825116835260208201511660208301526130786130666130546040840151610160806040880152860190612f75565b60608401518582036060870152612f75565b60808301518482036080860152612f75565b9160a082015160a082015260ff60c08301511660c082015260e0820151151560e0820152610100808301519082015261012080830151908201526101408092015191818403910152612fb0565b90565b9181601f84011215610223578235916001600160401b038311610223576020838186019501011161022357565b602090816040818301928281528551809452019301915f5b82811061311b575050505090565b83518552938101939281019260010161310d565b600180545f939261313f82612ded565b80825291838116908115613190575060011461315a57505050565b90929350815f52602092835f2092845f945b83861061317c5750505050010190565b80548587018301529401938590820161316c565b91935050602093945060ff191683830152151560051b010190565b905f92918054916131bb83612ded565b9182825260019384811690815f1461321957506001146131dc575b50505050565b90919394505f52602092835f2092845f945b83861061320557505050500101905f8080806131d6565b8054858701830152940193859082016131ee565b9294505050602093945060ff191683830152151560051b0101905f8080806131d6565b9060405161324981612e40565b60c060038294805460ff8116855261ffff90818160081c166020870152818160181c16604087015260281c16606085015260405161328e8161024a81600186016131ab565b60808501526040516132a78161024a81600286016131ab565b60a08501520154910152565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602090815260408083205490929060ff16156132f457505050565b6132fd3361360c565b908084519061330b82612e77565b6042825284820192606036853782511561346a576030845382516001908110156134565790607860218501536041915b8083116133fc575050506133ba576048611df493869361339e9361338f985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611d9c815180928c603789019101612f54565b01036028810187520185612e92565b5192839262461bcd60e51b845260048401526024830190612f75565b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f811660108110156134425761343b91906f181899199a1a9b1b9c1cb0b131b232b360811b901a61343186886135ef565b5360041c93613600565b919061333b565b634e487b7160e01b84526032600452602484fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b81526032600452602490fd5b5f90808252602090828252604092838120338252835260ff8482205416156134a65750505050565b6134af3361360c565b918451906134bc82612e77565b6042825284820192606036853782511561346a576030845382516001908110156134565790607860218501536041915b808311613540575050506133ba576048611df493869361339e9361338f985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611d9c815180928c603789019101612f54565b909192600f811660108110156134425761357591906f181899199a1a9b1b9c1cb0b131b232b360811b901a61343186886135ef565b91906134ec565b905f9180835282602052604083209160018060a01b03169182845260205260ff6040842054166135ab57505050565b80835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b908151811015611e80570160200190565b80156109f7575f190190565b60405190606082018281106001600160401b0382111761071d57604052602a8252602082016040368237825115611e8057603090538151600190811015611e8057607860218401536029905b808211613668575050611df85790565b9091600f81169060108210156136a25761369c916f181899199a1a9b1b9c1cb0b131b232b360811b901a611e7085876135ef565b90613658565b60245f634e487b7160e01b81526032600452fd5b8181106136c1575050565b5f81556001016136b6565b9190601f81116136db57505050565b612eef925f5260205f20906020601f840160051c8301931061273757601f0160051c01906136b6565b9291926001600160401b03821161071d576040519161372d601f8201601f191660200184612e92565b829481845281830111610223578281602093845f960137010152565b90600454821015611e805760045f52600582901c7f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0191601f1690565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160401b03811161071d5760051b60200190565b5f1981146109f75760010190565b8051821015611e805760209160051b010190565b600654811015611e805760065f5260205f209060011b01905f90565b919082039182116109f757565b8054821015611e80575f5260205f2001905f90565b903590601e198136030182121561022357018035906001600160401b0382116102235760200191813603831361022357565b61387e6138b361385f838061381d565b6020949194916138718382018261381d565b958260408894019061381d565b9283919260405198868a978989019c8d378701918883015f81523701918583015f815237015f83820152038084520182612e92565b51902090565b5190811515820361022357565b805490600160401b82101561071d57816138e89160016138fe94018155613808565b819391549060031b91821b915f19901b19161790565b9055565b9060ff165f52600560205260405f20905f5260205260ff60405f20541615610a0b57565b5f818152600760205260408120906006820180549060ff8260081c166139bf5761010060057fcfe7e61b35fbf35c6b0d5c7ebe6feb619ab832dc476c4a02dbdc041fb92b5ca69501549261397e60ff82168095613902565b61ff001916179055808252600860205260408220546009602052604083208584526020526040832055815260086020526139bb83604083206138c6565b80a2565b5050505050565b9060ff165f918183526020600981526040928385208386528252838520548186526008835284862054905f1991828101908111613aa757808203613a5a575b50508186526008835284862080548015613a4657820191613a268383613808565b909182549160031b1b191690555584526009815282842091845252812055565b634e487b7160e01b88526031600452602488fd5b613a6d9084895260088652878920613808565b90549060031b1c8388526008855280613a8b6138e8848a8c20613808565b9055838852600985528688209088528452858720555f80613a05565b634e487b7160e01b88526011600452602488fd5b60405190613ac882612e40565b5f60c08382815282602082015282604082015282606082015260606080820152606060a08201520152565b60405190613b0082612e5b565b815f80825280602083015260606040830152606080830152606060808301528060a08301528060c08301528060e083015280610100830152610120820152610140613b49613abb565b910152565b90604051613b5b81612e5b565b610140613b496009839560018060a01b038082541686526001820154166020860152613b8960028201612eb3565b6040860152604051613ba28161024a81600386016131ab565b6060860152604051613bbb8161024a81600486016131ab565b6080860152600581015460a086015260ff600682015481811660c088015260081c16151560e0860152600781015461010086015260088101546101208601520161323c565b600681015460ff8160081c1615613c6057613c1f6008830154426137fb565b60025410613c605760ff6005613c52930154911660ff165f52600560205260405f20905f5260205260ff60405f20541690565b15613c5c57600190565b5f90565b50505f90565b9794613c7e90613c8d93979892989594953691613704565b60208151910120943691613704565b602081519101209360ff6040519660208801987fff122683e0feb4f7d9e972e601b45f228210d60754c0f594e28eff64ca7bde9b8a52604089015216606087015261ffff928380921660808801521660a08601521660c084015260e08301526101009081830152815261012081018181106001600160401b0382111761071d576040525190209056feae5084c516dacf3f1a818c437d702c28b8d2088455f592a7ea799413e3b6f1bfa26469706673582212201024887846314102994802fb5a067cc08f43511b91364cd9088696fbdd19dc4d64736f6c63430008140033", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/solidity/TEERegistry.sol", - "buildInfoId": "solc-0_8_20-f8e15bef114e3b1966bfe954ab3aed85a6eb9ce8" + "buildInfoId": "solc-0_8_20-f03555f6acfb70fe84c226dfb8014812a96087b2" } \ No newline at end of file diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 673e9ba2..6792d9d4 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -397,41 +397,17 @@ contract TEERegistry is AccessControl { // Verify PCR is approved and matches the TEE type _requirePCRValidForTEE(pcrHash, teeType); - if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { - revert OHTTPConfigInvalid(); - } - if ( - kemId == KEM_ID_X25519_HKDF_SHA256 && - ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE - ) { - revert OHTTPConfigInvalid(); - } - - bytes32 configHash = computeOHTTPConfigHash( + OHTTPConfig memory ohttpConfig = _validateOHTTPConfig( teeId, + signingPublicKey, keyId, kemId, kdfId, aeadId, ohttpPublicKey, - ohttpKeyConfig - ); - bool validConfig = VERIFIER.verifyRSAPSS( - signingPublicKey, - configHash, + ohttpKeyConfig, ohttpConfigSignature ); - if (!validConfig) revert OHTTPConfigSignatureInvalid(); - - OHTTPConfig memory ohttpConfig = OHTTPConfig({ - keyId: keyId, - kemId: kemId, - kdfId: kdfId, - aeadId: aeadId, - publicKey: ohttpPublicKey, - keyConfig: ohttpKeyConfig, - registeredAt: block.timestamp - }); // Store TEE tees[teeId] = TEEInfo({ @@ -670,4 +646,52 @@ contract TEERegistry is AccessControl { ) ); } + + function _validateOHTTPConfig( + bytes32 teeId, + bytes calldata signingPublicKey, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) internal view returns (OHTTPConfig memory) { + if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { + revert OHTTPConfigInvalid(); + } + if ( + kemId == KEM_ID_X25519_HKDF_SHA256 && + ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE + ) { + revert OHTTPConfigInvalid(); + } + + bytes32 configHash = computeOHTTPConfigHash( + teeId, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig + ); + bool validConfig = VERIFIER.verifyRSAPSS( + signingPublicKey, + configHash, + ohttpConfigSignature + ); + if (!validConfig) revert OHTTPConfigSignatureInvalid(); + + return OHTTPConfig({ + keyId: keyId, + kemId: kemId, + kdfId: kdfId, + aeadId: aeadId, + publicKey: ohttpPublicKey, + keyConfig: ohttpKeyConfig, + registeredAt: block.timestamp + }); + } } diff --git a/scripts/integration/local_tee_workflow.go b/scripts/integration/local_tee_workflow.go index 420a49cc..07bf98a8 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,34 @@ 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) + uint16Type, _ := abi.NewType("uint16", "", nil) args := abi.Arguments{ {Type: bytesType}, {Type: bytesType}, {Type: bytesType}, {Type: addrType}, {Type: stringType}, {Type: uint8Type}, + {Type: uint8Type}, {Type: uint16Type}, {Type: uint16Type}, {Type: uint16Type}, + {Type: bytesType}, {Type: bytesType}, {Type: bytesType}, } - encoded, err := args.Pack(attestation, signingKey, tlsCert, common.HexToAddress(paymentAddr), endpoint, teeType) + encoded, err := args.Pack( + attestation, + signingKey, + tlsCert, + common.HexToAddress(paymentAddr), + endpoint, + teeType, + ohttp.KeyID, + ohttp.KEMID, + ohttp.KDFID, + ohttp.AEADID, + ohttp.PublicKey, + ohttp.KeyConfig, + 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 5074a1ad..3bb01682 100644 --- a/scripts/tee-mgmt-cli/Registration.md +++ b/scripts/tee-mgmt-cli/Registration.md @@ -94,11 +94,14 @@ Verify PCR is approved: 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, ohttpConfig...)` -7. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) -8. ✅ TEE is registered and **enabled** by default +4. ✅ Computes expected TEE ID: `keccak256(signing_public_key)` +5. ✅ 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` +6. ✅ Fetches TLS certificate via TLS handshake to `enclave_host:443` +7. ✅ Submits transaction: `registerTEEWithAttestation(attestation, signingKey, tlsCert, paymentAddr, endpoint, teeType, ohttpConfig...)` +8. ✅ Contract verifies the OHTTP config signature against the registered TEE signing key +9. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) +10. ✅ 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 4153b543..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,13 +138,6 @@ 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) - if err != nil { - return fmt.Errorf("failed to fetch signing key: %w", err) - } - fmt.Printf(" Signing Key: %d bytes\n", len(signingKey)) - registry.Log("Fetching signed OHTTP config...") ohttpConfig, err := registry.FetchOHTTPConfig(enclaveHost) if err != nil { @@ -155,12 +161,6 @@ 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, diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index 73fba74b..c74877ce 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -54,4 +54,27 @@ contract MockTEERegistry is TEERegistry { emit TEERegistered(teeId, msg.sender, teeType); } + + function validateOHTTPConfigForTesting( + bytes calldata signingPublicKey, + uint8 keyId, + uint16 kemId, + uint16 kdfId, + uint16 aeadId, + bytes calldata ohttpPublicKey, + bytes calldata ohttpKeyConfig, + bytes calldata ohttpConfigSignature + ) external view returns (OHTTPConfig memory) { + return _validateOHTTPConfig( + keccak256(signingPublicKey), + signingPublicKey, + keyId, + kemId, + kdfId, + aeadId, + ohttpPublicKey, + ohttpKeyConfig, + ohttpConfigSignature + ); + } } diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index dc6e2f0b..f1c8e2a0 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -3,10 +3,11 @@ 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 + let registry, helper, mockRegistry function makeOHTTPConfig(overrides = {}) { return { @@ -33,16 +34,29 @@ contract('TEERegistry', function (accounts) { ] } + function makeSigningPublicKey() { + const { publicKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { + type: 'spki', + format: 'der' + } + }) + return '0x' + publicKey.toString('hex') + } + before(async () => { [owner, teeOperator, user1, user2] = accounts // Deploy TEERegistry registry = await TEERegistry.new() + mockRegistry = await MockTEERegistry.new() // Deploy test helper helper = await TEETestHelper.new(registry.address) console.log('TEERegistry deployed at:', registry.address) + console.log('MockTEERegistry deployed at:', mockRegistry.address) console.log('TEETestHelper deployed at:', helper.address) // Grant TEE_OPERATOR role to teeOperator account @@ -416,6 +430,54 @@ contract('TEERegistry', function (accounts) { console.log('✓ Unknown TEE OHTTP config lookup reverts correctly') }) + + it('should reject empty OHTTP public key or key config', async function () { + const signingPublicKey = makeSigningPublicKey() + + await truffleAssert.reverts( + mockRegistry.validateOHTTPConfigForTesting( + signingPublicKey, + ...ohttpArgs(makeOHTTPConfig({ publicKey: '0x' })) + ) + ) + + await truffleAssert.reverts( + mockRegistry.validateOHTTPConfigForTesting( + signingPublicKey, + ...ohttpArgs(makeOHTTPConfig({ keyConfig: '0x' })) + ) + ) + + console.log('✓ Empty OHTTP fields rejected') + }) + + it('should reject wrong X25519 OHTTP public key length', async function () { + const signingPublicKey = makeSigningPublicKey() + + await truffleAssert.reverts( + mockRegistry.validateOHTTPConfigForTesting( + signingPublicKey, + ...ohttpArgs(makeOHTTPConfig({ + publicKey: '0x' + Buffer.alloc(31, 0xA0).toString('hex') + })) + ) + ) + + console.log('✓ Wrong OHTTP X25519 public key length rejected') + }) + + it('should reject invalid OHTTP config signature', async function () { + const signingPublicKey = makeSigningPublicKey() + + await truffleAssert.reverts( + mockRegistry.validateOHTTPConfigForTesting( + signingPublicKey, + ...ohttpArgs(makeOHTTPConfig()) + ) + ) + + console.log('✓ Invalid OHTTP config signature rejected') + }) }) describe('Query Functions', function () { From dd7dca9802794e132cdea6c72a5a5915846c07cd Mon Sep 17 00:00:00 2001 From: "balogh.adam@icloud.com" Date: Wed, 27 May 2026 13:23:39 -0400 Subject: [PATCH 10/14] simplifications --- contracts/solidity/TEERegistry.sol | 115 ++++++++++-------- scripts/tee-mgmt-cli/registry/client.go | 59 +++++---- .../suites/tee/contracts/MockTEERegistry.sol | 27 ++++ .../suites/tee/contracts/TEETestHelper.sol | 16 +-- tests/solidity/suites/tee/test/registry.js | 105 +++++++++++++++- 5 files changed, 235 insertions(+), 87 deletions(-) diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 673e9ba2..03a65489 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -130,6 +130,20 @@ contract TEERegistry is AccessControl { 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; @@ -370,13 +384,7 @@ contract TEERegistry is AccessControl { address paymentAddress, string calldata endpoint, uint8 teeType, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature + OHTTPConfigInput calldata ohttp ) external onlyRole(TEE_OPERATOR) returns (bytes32 teeId) { // Validate TEE type if (!isValidTEEType(teeType)) revert InvalidTEEType(); @@ -397,41 +405,9 @@ contract TEERegistry is AccessControl { // Verify PCR is approved and matches the TEE type _requirePCRValidForTEE(pcrHash, teeType); - if (ohttpPublicKey.length == 0 || ohttpKeyConfig.length == 0) { - revert OHTTPConfigInvalid(); - } - if ( - kemId == KEM_ID_X25519_HKDF_SHA256 && - ohttpPublicKey.length != X25519_PUBLIC_KEY_SIZE - ) { - revert OHTTPConfigInvalid(); - } - - bytes32 configHash = computeOHTTPConfigHash( - teeId, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig - ); - bool validConfig = VERIFIER.verifyRSAPSS( - signingPublicKey, - configHash, - ohttpConfigSignature - ); - if (!validConfig) revert OHTTPConfigSignatureInvalid(); - - OHTTPConfig memory ohttpConfig = OHTTPConfig({ - keyId: keyId, - kemId: kemId, - kdfId: kdfId, - aeadId: aeadId, - publicKey: ohttpPublicKey, - keyConfig: ohttpKeyConfig, - registeredAt: block.timestamp - }); + // 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({ @@ -457,13 +433,56 @@ contract TEERegistry is AccessControl { emit TEERegistered(teeId, msg.sender, teeType); emit OHTTPConfigRegistered( teeId, - keyId, - kemId, - kdfId, - aeadId, - keccak256(ohttpPublicKey), - keccak256(ohttpKeyConfig) + 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(); + } + if ( + ohttp.kemId == KEM_ID_X25519_HKDF_SHA256 && + 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, + registeredAt: block.timestamp + }); } /// @notice Disable a TEE, removing it from the enabled list diff --git a/scripts/tee-mgmt-cli/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 11ebf385..55f33cf2 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -43,7 +43,7 @@ var ( 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] + 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] @@ -181,11 +181,6 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { {Name: "registeredAt", Type: "uint256"}, } - _, err = abi.NewType("tuple", "", ohttpComponents) - if err != nil { - return nil, fmt.Errorf("failed to create OHTTP ABI type: %v", err) - } - tupleType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ {Name: "owner", Type: "address"}, {Name: "paymentAddress", Type: "address"}, @@ -271,7 +266,20 @@ func (c *Client) RegisterTEE( addrT, _ := abi.NewType("address", "", nil) strT, _ := abi.NewType("string", "", nil) u8T, _ := abi.NewType("uint8", "", nil) - u16T, _ := abi.NewType("uint16", "", nil) + + // 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}, @@ -280,29 +288,36 @@ func (c *Client) RegisterTEE( {Type: addrT}, {Type: strT}, {Type: u8T}, - {Type: u8T}, - {Type: u16T}, - {Type: u16T}, - {Type: u16T}, - {Type: bytesT}, - {Type: bytesT}, - {Type: bytesT}, + {Type: ohttpT}, } - encoded, _ := args.Pack( + encoded, err := args.Pack( attestation, signingKey, tlsCert, common.HexToAddress(paymentAddr), endpoint, teeType, - ohttp.KeyID, - ohttp.KEMID, - ohttp.KDFID, - ohttp.AEADID, - ohttp.PublicKey, - ohttp.KeyConfig, - ohttp.Signature, + 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...)) } diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index 73fba74b..f7691f28 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -54,4 +54,31 @@ 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, + registeredAt: block.timestamp + }); + } } diff --git a/tests/solidity/suites/tee/contracts/TEETestHelper.sol b/tests/solidity/suites/tee/contracts/TEETestHelper.sol index e9946871..59143999 100644 --- a/tests/solidity/suites/tee/contracts/TEETestHelper.sol +++ b/tests/solidity/suites/tee/contracts/TEETestHelper.sol @@ -85,13 +85,7 @@ contract TEETestHelper { address paymentAddress, string calldata endpoint, uint8 teeType, - uint8 keyId, - uint16 kemId, - uint16 kdfId, - uint16 aeadId, - bytes calldata ohttpPublicKey, - bytes calldata ohttpKeyConfig, - bytes calldata ohttpConfigSignature + TEERegistry.OHTTPConfigInput calldata ohttp ) external returns (bytes32 teeId) { return registry.registerTEEWithAttestation( attestationDocument, @@ -100,13 +94,7 @@ contract TEETestHelper { paymentAddress, endpoint, teeType, - keyId, - kemId, - kdfId, - aeadId, - ohttpPublicKey, - ohttpKeyConfig, - ohttpConfigSignature + ohttp ); } diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index dc6e2f0b..c55ebf23 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -3,6 +3,7 @@ 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 @@ -308,7 +309,7 @@ contract('TEERegistry', function (accounts) { user1, 'https://tee.example.com', 1, - ...ohttpArgs(), + ohttpArgs(), { from: user1 } ) ) @@ -328,7 +329,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 99, // Invalid type - ...ohttpArgs(), + ohttpArgs(), { from: teeOperator } ) ) @@ -348,7 +349,7 @@ contract('TEERegistry', function (accounts) { teeOperator, 'https://tee.example.com', 1, - ...ohttpArgs(), + ohttpArgs(), { from: teeOperator } ) ) @@ -416,6 +417,104 @@ contract('TEERegistry', function (accounts) { 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(Number(stored.registeredAt)).to.be.greaterThan(0) + + console.log('✓ Stored OHTTP config retrieved correctly') + }) + }) }) describe('Query Functions', function () { From b77ec168cc15690a19db277d86e2325d44d2acd4 Mon Sep 17 00:00:00 2001 From: Aniket Dixit Date: Wed, 27 May 2026 23:08:17 +0530 Subject: [PATCH 11/14] review updates --- scripts/tee-mgmt-cli/Registration.md | 4 +- tests/solidity/suites/tee/test/registry.js | 70 +++++++++++++++++++++- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/scripts/tee-mgmt-cli/Registration.md b/scripts/tee-mgmt-cli/Registration.md index 3bb01682..9d3e4ff0 100644 --- a/scripts/tee-mgmt-cli/Registration.md +++ b/scripts/tee-mgmt-cli/Registration.md @@ -99,8 +99,8 @@ Verify PCR is approved: - Includes `key_id`, `kem_id`, `kdf_id`, `aead_id`, `public_key`, `key_config`, and `signature` 6. ✅ Fetches TLS certificate via TLS handshake to `enclave_host:443` 7. ✅ Submits transaction: `registerTEEWithAttestation(attestation, signingKey, tlsCert, paymentAddr, endpoint, teeType, ohttpConfig...)` -8. ✅ Contract verifies the OHTTP config signature against the registered TEE signing key -9. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) +8. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) +9. ✅ Contract verifies the OHTTP config signature against the registered TEE signing key 10. ✅ TEE is registered and **enabled** by default ### Step 4: Verify Registration diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index f1c8e2a0..369b3d40 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -34,15 +34,55 @@ contract('TEERegistry', function (accounts) { ] } - function makeSigningPublicKey() { - const { publicKey } = crypto.generateKeyPairSync('rsa', { + function makeSigningKeyPair() { + const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'der' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem' } }) - return '0x' + publicKey.toString('hex') + return { + publicKey: '0x' + publicKey.toString('hex'), + privateKey + } + } + + function makeSigningPublicKey() { + return makeSigningKeyPair().publicKey + } + + function signHash(privateKey, hash) { + const signer = crypto.createSign('SHA256') + signer.update(Buffer.from(hash.slice(2), 'hex')) + signer.end() + + return '0x' + signer.sign({ + key: privateKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST + }).toString('hex') + } + + async function signOHTTPConfig(signingPublicKey, privateKey, config) { + const teeId = await registry.computeTEEId(signingPublicKey) + const configHash = await registry.computeOHTTPConfigHash( + teeId, + config.keyId, + config.kemId, + config.kdfId, + config.aeadId, + config.publicKey, + config.keyConfig + ) + return { + ...config, + signature: signHash(privateKey, configHash) + } } before(async () => { @@ -431,6 +471,30 @@ contract('TEERegistry', function (accounts) { console.log('✓ Unknown TEE OHTTP config lookup reverts correctly') }) + it('should accept a signed OHTTP config', async function () { + const { publicKey: signingPublicKey, privateKey } = makeSigningKeyPair() + const config = await signOHTTPConfig( + signingPublicKey, + privateKey, + makeOHTTPConfig() + ) + + const result = await mockRegistry.validateOHTTPConfigForTesting( + signingPublicKey, + ...ohttpArgs(config) + ) + + expect(result.keyId.toNumber()).to.equal(config.keyId) + expect(result.kemId.toNumber()).to.equal(config.kemId) + expect(result.kdfId.toNumber()).to.equal(config.kdfId) + expect(result.aeadId.toNumber()).to.equal(config.aeadId) + expect(result.publicKey).to.equal(config.publicKey) + expect(result.keyConfig).to.equal(config.keyConfig) + expect(result.registeredAt.toNumber()).to.be.greaterThan(0) + + console.log('✓ Signed OHTTP config accepted') + }) + it('should reject empty OHTTP public key or key config', async function () { const signingPublicKey = makeSigningPublicKey() From 5dc0ff779c503e718c6051f36959d7297c5f8994 Mon Sep 17 00:00:00 2001 From: "balogh.adam@icloud.com" Date: Wed, 27 May 2026 13:58:53 -0400 Subject: [PATCH 12/14] store signature --- contracts/solidity/TEERegistry.sol | 12 +++-- scripts/integration/local_tee_workflow.go | 44 ++++++++++++++----- scripts/tee-mgmt-cli/registry/client.go | 3 ++ .../suites/tee/contracts/MockTEERegistry.sol | 2 + tests/solidity/suites/tee/test/registry.js | 1 + 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/contracts/solidity/TEERegistry.sol b/contracts/solidity/TEERegistry.sol index 03a65489..f13a4706 100644 --- a/contracts/solidity/TEERegistry.sol +++ b/contracts/solidity/TEERegistry.sol @@ -127,6 +127,7 @@ contract TEERegistry is AccessControl { uint16 aeadId; bytes publicKey; bytes keyConfig; + bytes signature; uint256 registeredAt; } @@ -454,10 +455,12 @@ contract TEERegistry is AccessControl { if (ohttp.publicKey.length == 0 || ohttp.keyConfig.length == 0) { revert OHTTPConfigInvalid(); } - if ( - ohttp.kemId == KEM_ID_X25519_HKDF_SHA256 && - ohttp.publicKey.length != X25519_PUBLIC_KEY_SIZE - ) { + // 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(); } @@ -481,6 +484,7 @@ contract TEERegistry is AccessControl { aeadId: ohttp.aeadId, publicKey: ohttp.publicKey, keyConfig: ohttp.keyConfig, + signature: ohttp.signature, registeredAt: block.timestamp }); } diff --git a/scripts/integration/local_tee_workflow.go b/scripts/integration/local_tee_workflow.go index 07bf98a8..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,uint8,uint16,uint16,uint16,bytes,bytes,bytes)"))[: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 @@ -1291,13 +1291,25 @@ func callRegisterTEE(attestation, signingKey, tlsCert []byte, paymentAddr, endpo addrType, _ := abi.NewType("address", "", nil) stringType, _ := abi.NewType("string", "", nil) uint8Type, _ := abi.NewType("uint8", "", nil) - uint16Type, _ := abi.NewType("uint16", "", 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}, - {Type: uint8Type}, {Type: uint16Type}, {Type: uint16Type}, {Type: uint16Type}, - {Type: bytesType}, {Type: bytesType}, {Type: bytesType}, + {Type: ohttpType}, } encoded, err := args.Pack( attestation, @@ -1306,13 +1318,23 @@ func callRegisterTEE(attestation, signingKey, tlsCert []byte, paymentAddr, endpo common.HexToAddress(paymentAddr), endpoint, teeType, - ohttp.KeyID, - ohttp.KEMID, - ohttp.KDFID, - ohttp.AEADID, - ohttp.PublicKey, - ohttp.KeyConfig, - ohttp.Signature, + 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/registry/client.go b/scripts/tee-mgmt-cli/registry/client.go index 55f33cf2..5756be2a 100644 --- a/scripts/tee-mgmt-cli/registry/client.go +++ b/scripts/tee-mgmt-cli/registry/client.go @@ -178,6 +178,7 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { {Name: "aeadId", Type: "uint16"}, {Name: "publicKey", Type: "bytes"}, {Name: "keyConfig", Type: "bytes"}, + {Name: "signature", Type: "bytes"}, {Name: "registeredAt", Type: "uint256"}, } @@ -228,6 +229,7 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { AeadId uint16 `json:"aeadId"` PublicKey []byte `json:"publicKey"` KeyConfig []byte `json:"keyConfig"` + Signature []byte `json:"signature"` RegisteredAt *big.Int `json:"registeredAt"` } `json:"ohttpConfig"` }) @@ -250,6 +252,7 @@ func (c *Client) GetTEE(teeId [32]byte) (*TEEInfo, error) { 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 diff --git a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol index f7691f28..3e96452f 100644 --- a/tests/solidity/suites/tee/contracts/MockTEERegistry.sol +++ b/tests/solidity/suites/tee/contracts/MockTEERegistry.sol @@ -44,6 +44,7 @@ contract MockTEERegistry is TEERegistry { aeadId: 0, publicKey: "", keyConfig: "", + signature: "", registeredAt: 0 }) }); @@ -78,6 +79,7 @@ contract MockTEERegistry is TEERegistry { aeadId: ohttp.aeadId, publicKey: ohttp.publicKey, keyConfig: ohttp.keyConfig, + signature: ohttp.signature, registeredAt: block.timestamp }); } diff --git a/tests/solidity/suites/tee/test/registry.js b/tests/solidity/suites/tee/test/registry.js index c55ebf23..57124d9b 100644 --- a/tests/solidity/suites/tee/test/registry.js +++ b/tests/solidity/suites/tee/test/registry.js @@ -510,6 +510,7 @@ contract('TEERegistry', function (accounts) { 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') From 8f4270b69c55adaf8d7d86fc9d6582bb1c0ee6ff Mon Sep 17 00:00:00 2001 From: "balogh.adam@icloud.com" Date: Wed, 27 May 2026 14:22:51 -0400 Subject: [PATCH 13/14] readme --- scripts/tee-mgmt-cli/Registration.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scripts/tee-mgmt-cli/Registration.md b/scripts/tee-mgmt-cli/Registration.md index 9d3e4ff0..8c2b6863 100644 --- a/scripts/tee-mgmt-cli/Registration.md +++ b/scripts/tee-mgmt-cli/Registration.md @@ -91,17 +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. ✅ Computes expected TEE ID: `keccak256(signing_public_key)` -5. ✅ Fetches signed OHTTP config from `https://enclave_host/v1/ohttp/config` +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` -6. ✅ Fetches TLS certificate via TLS handshake to `enclave_host:443` -7. ✅ Submits transaction: `registerTEEWithAttestation(attestation, signingKey, tlsCert, paymentAddr, endpoint, teeType, ohttpConfig...)` -8. ✅ Contract verifies attestation via precompile (checks AWS signature, PCR approval) -9. ✅ Contract verifies the OHTTP config signature against the registered TEE signing key -10. ✅ TEE is registered and **enabled** by default +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. ✅ 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 From 039ec01698acf71166654c834abdb2711db81a8c Mon Sep 17 00:00:00 2001 From: "balogh.adam@icloud.com" Date: Wed, 27 May 2026 14:27:56 -0400 Subject: [PATCH 14/14] update abi --- contracts/solidity/TEERegistry.json | 832 +++++++++++++++++++++++----- 1 file changed, 679 insertions(+), 153 deletions(-) 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 +}