KR20230046291A - 연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체 - Google Patents

연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체 Download PDF

Info

Publication number
KR20230046291A
KR20230046291A KR1020237000934A KR20237000934A KR20230046291A KR 20230046291 A KR20230046291 A KR 20230046291A KR 1020237000934 A KR1020237000934 A KR 1020237000934A KR 20237000934 A KR20237000934 A KR 20237000934A KR 20230046291 A KR20230046291 A KR 20230046291A
Authority
KR
South Korea
Prior art keywords
param
bytes32
address
authority
value
Prior art date
Application number
KR1020237000934A
Other languages
English (en)
Inventor
죠지 도니
Original Assignee
시커런시 인크
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by 시커런시 인크 filed Critical 시커런시 인크
Publication of KR20230046291A publication Critical patent/KR20230046291A/ko

Links

Images

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F21/00Security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F21/60Protecting data
    • G06F21/604Tools and structures for managing or administering access control systems
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/30Payment architectures, schemes or protocols characterised by the use of specific devices or networks
    • G06Q20/36Payment architectures, schemes or protocols characterised by the use of specific devices or networks using electronic wallets or electronic money safes
    • G06Q20/367Payment architectures, schemes or protocols characterised by the use of specific devices or networks using electronic wallets or electronic money safes involving electronic purses or money safes
    • G06Q20/3674Payment architectures, schemes or protocols characterised by the use of specific devices or networks using electronic wallets or electronic money safes involving electronic purses or money safes involving authentication
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F16/00Information retrieval; Database structures therefor; File system structures therefor
    • G06F16/20Information retrieval; Database structures therefor; File system structures therefor of structured data, e.g. relational data
    • G06F16/27Replication, distribution or synchronisation of data between databases or within a distributed database system; Distributed database system architectures therefor
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F21/00Security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F21/60Protecting data
    • G06F21/62Protecting access to data via a platform, e.g. using keys or access control rules
    • G06F21/6218Protecting access to data via a platform, e.g. using keys or access control rules to a system of files or objects, e.g. local or distributed file system or database
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F21/00Security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F21/60Protecting data
    • G06F21/62Protecting access to data via a platform, e.g. using keys or access control rules
    • G06F21/6218Protecting access to data via a platform, e.g. using keys or access control rules to a system of files or objects, e.g. local or distributed file system or database
    • G06F21/6245Protecting personal data, e.g. for financial or medical purposes
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/38Payment protocols; Details thereof
    • G06Q20/382Payment protocols; Details thereof insuring higher security of transaction
    • G06Q20/3821Electronic credentials
    • G06Q20/38215Use of certificates or encrypted proofs of transaction rights
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/38Payment protocols; Details thereof
    • G06Q20/382Payment protocols; Details thereof insuring higher security of transaction
    • G06Q20/3825Use of electronic signatures
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/38Payment protocols; Details thereof
    • G06Q20/382Payment protocols; Details thereof insuring higher security of transaction
    • G06Q20/3829Payment protocols; Details thereof insuring higher security of transaction involving key management
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/38Payment protocols; Details thereof
    • G06Q20/389Keeping log of transactions for guaranteeing non-repudiation of a transaction
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06QINFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
    • G06Q20/00Payment architectures, schemes or protocols
    • G06Q20/38Payment protocols; Details thereof
    • G06Q20/40Authorisation, e.g. identification of payer or payee, verification of customer or shop credentials; Review and approval of payers, e.g. check credit lines or negative lists
    • G06Q20/401Transaction verification
    • G06Q20/4016Transaction verification involving fraud or risk level assessment in transaction processing
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L63/00Network architectures or network communication protocols for network security
    • H04L63/12Applying verification of the received information
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L9/00Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols
    • H04L9/08Key distribution or management, e.g. generation, sharing or updating, of cryptographic keys or passwords
    • H04L9/0894Escrow, recovery or storing of secret information, e.g. secret key escrow or cryptographic key storage
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L9/00Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols
    • H04L9/32Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols including means for verifying the identity or authority of a user of the system or for message authentication, e.g. authorization, entity authentication, data integrity or data verification, non-repudiation, key authentication or verification of credentials
    • H04L9/3236Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols including means for verifying the identity or authority of a user of the system or for message authentication, e.g. authorization, entity authentication, data integrity or data verification, non-repudiation, key authentication or verification of credentials using cryptographic hash functions
    • H04L9/3239Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols including means for verifying the identity or authority of a user of the system or for message authentication, e.g. authorization, entity authentication, data integrity or data verification, non-repudiation, key authentication or verification of credentials using cryptographic hash functions involving non-keyed hash functions, e.g. modification detection codes [MDCs], MD5, SHA or RIPEMD
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L9/00Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols
    • H04L9/32Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols including means for verifying the identity or authority of a user of the system or for message authentication, e.g. authorization, entity authentication, data integrity or data verification, non-repudiation, key authentication or verification of credentials
    • H04L9/3247Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols including means for verifying the identity or authority of a user of the system or for message authentication, e.g. authorization, entity authentication, data integrity or data verification, non-repudiation, key authentication or verification of credentials involving digital signatures
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F2221/00Indexing scheme relating to security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F2221/21Indexing scheme relating to G06F21/00 and subgroups addressing additional information or applications relating to security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F2221/2141Access rights, e.g. capability lists, access control lists, access tables, access matrices
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F2221/00Indexing scheme relating to security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F2221/21Indexing scheme relating to G06F21/00 and subgroups addressing additional information or applications relating to security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F2221/2145Inheriting rights or properties, e.g., propagation of permissions or restrictions within a hierarchy
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L2209/00Additional information or applications relating to cryptographic mechanisms or cryptographic arrangements for secret or secure communication H04L9/00
    • H04L2209/56Financial cryptography, e.g. electronic payment or e-cash
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L2463/00Additional details relating to network architectures or network communication protocols for network security covered by H04L63/00
    • H04L2463/102Additional details relating to network architectures or network communication protocols for network security covered by H04L63/00 applying security measure for e-commerce
    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04LTRANSMISSION OF DIGITAL INFORMATION, e.g. TELEGRAPHIC COMMUNICATION
    • H04L9/00Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols
    • H04L9/50Cryptographic mechanisms or cryptographic arrangements for secret or secure communications; Network security protocols using hash chains, e.g. blockchains or hash trees

Abstract

컴퓨터 네트워크 상에서 안전한 피어 투 피어(peer-to-peer) 데이터 전송을 위한 방법 및 장치가 개시되며, 상기 방법은 분산 원장 플랫폼을 포함하는 분산형 컴퓨팅 시스템에 의하여 달성된다. 루트 권한이 정의도고 다층(multilevel) 방식으로 월렛에 위임되어, 이에 따라 루트 권한과 연관된 월렛을 사이버 위험으로부터 분리한다.

Description

연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체
본 출원은 2020년 6월 10일에 출원된 미국 가출원 제63/037,034호에 대한 우선권을 주장하며, 그 개시 전체는 참고로서 본 출원에 포함된다.
본 발명은 가치 보호 및 트랜잭션 복구를 가능하게 하기 위해 분산형 컴퓨터 네트워크에서 보안 및 트랜잭션 유연성을 증가시키는 암호화 키 관리를 위한 시스템, 데이터 구조 및 프로세스에 관한 것이다.
본 특허 문서의 개시 내용 중 일부는 저작권 보호를 받는 자료를 포함한다. 저작권 소유자는 특허청 특허 파일 또는 기록에 나타난 대로의 특허 문서 또는 특허 개시 내용의 팩스 복제에 대해 이의를 제기하지 않으나, 그 외에는 모든 저작권을 보유한다.
블록체인 프로토콜 기반 네트워크와 같은 분산형 네트워크(분산 원장 기술(Distributed Ledger Technology, DLT)이라고도 함)는 당사자 간의 무신뢰(trustless) 트랜잭션을 위한 강력한 기준(baseline)을 제공한다. 기술로서의 분산형 네트워크는, 1)합의 불변성과 2)분산 권한이라는, 금융 생태계를 위한 두 가지 강력한 도구(enabler)를 제공한다. 합의 불변성에 의해, 모든 당사자는 트랜잭션이 적절하게 기록되었으며 추후에 변경될 수 없음을 확신할 수 있다. 분산된 권한은 행위자가 은행 및 정부 기관과 같은 중개자를 필요로 하지 않고 자신의 권한을 실행할 수 있도록 한다.
이러한 도구는 강력하지만, 복잡한 금융 생태계에서 상거래에 대한 모든 요구 사항을 충족시키기에는 그 자체로는 충분하지 않다. 널리 사용되기 위해서는, 가치의 공개 및 표현, 그룹 컨텍스트(context)에서의 묵시적 허가, 및 엔티티(entity) 간의 협업에 내재된 신뢰 및 권한 요소를 무신뢰 트랜잭션 컴퓨터 네트워킹 인프라가 통합하도록 보강되어야 한다. 예를 들어 기본적인 과제를 고려해 본다. 비트코인과 같은 무신뢰 블록체인 증권은 무기명 증권(bearer instrument)이다. 비트코인이 포함된 공개 주소에 대한 비밀을 소유한 당사자만이 이 가치에 대한 소유권(이 경우 전송 권한)을 행사할 수 있다. 이러한 모델의 주요 이점은 중개자 없이 어떤 규모의 트랜잭션이라도 수행할 수 있는 능력으로, 이러한 트랜잭션이 "검열 없는" 상태임을 보장한다. 그러나 이러한 이점에는 상당한 위험이 따른다. 분실 또는 유용(misappropriated)의 경우 상환 청구할 수 없다. "월렛(wallet)"(분산 원장의 상태 변경에 사용되는 공개/개인 키 조합)의 키가 분실되거나 도난당하면 가치 복구를 위한 메커니즘이 없다.
무기명 증서가 "힘이 곧 정의이다", 즉 "가질 수 있다면, 소유한 것"이라는 조건을 만든다는 점은 지난 역사가 보여주었다. 사람들이 현금을 휴대해야만 하는 장소에서 절도가 흔한 점을 고려하면, 현금(지폐)이 그러한 예이다. 블록체인 트랜잭션의 비가역성으로 인해, 당국이 도난당한 가치를 회수하는 것은 어렵거나 심지어 불가능할 수 있다. 결과적으로, 비트코인은 공격자가 피해자의 데이터를 암호화하고 공격자와 연결된 월렛으로 암호 화폐를 전송해야만 암호 해독 키를 제공하는 "랜섬웨어"와 같은 특정 강탈 방법에 선호되는 통화이다. 유용이 발생하였을 때 상환 청구를 할 수 없는 것은 당연히 공정한 금융 생태계의 핵심 가치와는 대립된다. 이에 따라, 블록체인 가치의 소유자는 종종 자신의 가치를 보호하기 위해 보관자(custodian)에 의지한다. 소유자가 중개자에게 의지한다면, 해당 시스템은 많은 블록체인 옹호자들이 대체하고자 하는 중앙 집중식 시스템과 근본적으로 다르지 않다.
본 발명은, 1)핵심 권한 회복을 위하여 사이버 위협을 완화하거나 제거하는 메커니즘("계층적 키 관리"); 및 2)중앙 권한 없이 권한을 설정, 전달, 및 시행(enforce)하는 프레임워크("연맹 권한 관리")를 활용하여 선행 기술의 한계를 극복하는 분산형 컴퓨터 네트워크를 제공하는 것을 그 목적으로 한다.
앞서 언급된 한계는, 분산형 네트워크의 기술적 특징인 합의, 불변성, 및 분산된 권한을 유지하면서도, 큐레이션 권한(예: 불법 트랜잭션을 철회하거나 손실된 가치를 복구하는 메커니즘 등)를 정의하고 행사하는 방법에 대한 필요성을 제기한다. 무신뢰 시스템이 신뢰성을 포함하도록 확장하기 위해서는, 독립적 엔티티가 권한, 정책, 및 계약을 정의하고, 공유하고, 그를 통해 거래할 수 있도록 하는 계층화된 권한 통제 구조(rights governance structure)가 필요하다. 본 명세서의 개시는, 권한이 강제로, 절도에 의해, 또는 사고로 인해 소멸되는 것을 방지하도록 설계된 네트워크 아키텍처 및 데이터 모델을 설명한다. 개시된 구현은, 사이버 위협으로부터 보호하고 유용에 대한 구제를 제공하면서도 참가자가 중개자 없이 트랜잭션을 수행할 수 있도록 하며, 이에 따라 분산 네트워크의 이점을 보존할 수 있다. 개시된 구현은 다음 2가지의 근본적인 기술 혁신을 활용한다. 1)핵심 권한 회복을 위하여 사이버 위협을 완화하거나 제거하는 메커니즘("계층적 키 관리"); 및 2)중앙 권한 없이 권한을 설정, 전달, 및 시행(enforce)하는 프레임워크("연맹 권한 관리"). 이러한 두 가지 혁신은 서로 연결되어 선행 기술의 한계를 극복하는 분산형 컴퓨터 네트워크를 제공할 수 있다.
"토큰"으로 알려진 블록체인 기반 자산에 대한 무기명 증권 문제는, 전송 에이전트(Transfer Agent), 즉 불법적이거나 잘못 발생한 트랜잭션에 대하여 트랜잭션을 철회하거나 가치를 "환수(claw-back)"할 수 있는 권한을 가진 당사자를 도입하여 해결할 수 있다. 특정 자산에 대하여 이러한 권한을 결정하기 위한 메커니즘이 제공되어야 한다. 이해관계자(stakeholder)는 전송 에이전트 권한이 부여된 당사자가 신뢰할 수 있고 자격이 있음(예: 법적 권한이 있고, 권한이 오용될 경우 책임을 질 수 있으며, 책임에 대하여 완전히 인식하고 있음)을 알 수 있어야 한다. 또한, 전송 에이전트가 취약해지거나(compromised), 지급 불능이 되거나, 무능할 가능성도 고려되어야 한다.
토큰 전송에 관한 이러한 각각의 문제에 대한 기술적 해결책을 추구함에 있어서, 모든 컨텍스트에 걸쳐 광범위하게 적용될 수 있는 일반적인 권한에 대한 보다 깊은 접근법이 개발되어야 한다. 중앙 집중식 또는 연합된(federated) 모델을 통하지 않고, 당사자들이 연대(coalition, 자발적으로 권한을 정의하고 적용하는 데 동의하는 둘 이상의 당사자 그룹)를 형성할 수 있도록 하는 기술적 접근법인 연맹 권한 관리(confederated rights management)가 여기에 개시된다. 연맹 권한 관리는, 연대가 자유롭게 형성되고 해산되도록 허용하는 동시에 블록체인의 분산 구조의 이점을 보호한다.
"연맹(confederated)" 관리 모델과 "연합(federated)" 관리 모델 사이의 차이점을 이해하는 것이 중요하다. 연합 권한 모델에서, 당사자들은 주권(sovereignty), 즉 권한을 정의하고 지정할 수 있는 능력이 새로 생성된 엔티티에게 있는 합의에 참여한다. 반면에, 연맹 모델은 당사자들이 권한의 정의나 지정에 대하여 자유롭게 동의하거나 반대할 수 있는 연대적 접근 방식이 특징이다. 연맹 모델에서 서로 효과적으로 거래하기 위해서는, 구성원은 권한이라는 것이 무엇을 의미하고 권한이 어떻게 전달되는지에 동의하기만 하면 된다. 당사자는 공통의 정의 및 권한 제어에 자유롭게 참여할 수 있으며, 제어 모델에 동의하지 않거나 탈퇴하는 것 또한 자유롭다.
블록체인 네트워크는 단순한 연맹 권한 모델의 예이다. 이러한 네트워크에 인코딩된 기본 권한은, 트랜잭션에 서명할 수 있는 권한, 즉, 네트워크에서 상태 변경을 승인할 수 있는 권한이다. 참여함으로써 구성원은 권한이 표현되고 전달되는 기본 구조(네트워크 거버넌스)에 적어도 암묵적으로 동의한다. 그러나 협업에는 신뢰가 필요하며 이를 위해서는 무신뢰 분산 원장 시스템 위에 구축된 프레임워크가 필요하다. 개시된 구현에 따른 연맹 권한 모델은 연합(federation), 즉 대가 없이는 종료될 수 없는 연대 당사자들 간 구속력 있는 합의를 지원하지만, 의무화하지는 않는다. 그러나 지속적이고 규모가 큰 권한을 개발하려면 연대가 상호 이익을 달성하기 위한 연대 합의(coalition agreements)를 신속하게 형성하고 해체할 수 있는 메커니즘이 필요하다.
개시된 구현의 연맹 권한 프레임워크에서, 권한 또는 신뢰성의 지정은 상대적일 수 있다. 참가자는 특정 권한, 정책, 계약, 또는 객체(object)가 참가자에게 이익이 되는 방식으로 관리되는지를 판단할 수 있다. 이를 위해서는 속성(attributes), 정책, 계약, 엔티티(개인 또는 그룹), 및 객체를 연대가 정의할 수 있는 기술 프로토콜 및 데이터 모델이 필요하다.
전송 에이전트 권한의 구현으로 돌아가서, 개시된 구현을 사용하여 연맹 권한 관리 접근법을 적용할 수 있다. 암호화 토큰 또는 기타 디지털 토큰의 컨텍스트에서, 전송 에이전트 권한은 책임 있는 당사자가 적용할 수 있다. 토큰을 생성할 때 당사자는 기본적으로 토큰의 컨텍스트 내에서 권한을 관리할 권한이 있는 발행자로 설정된다. 자격을 갖춘 당사자만이 발행자 권한을 얻을 수 있다(발행자 자격 취득 프로세스는 그 자체의 연맹 모델에서 구현될 수 있다). 발행자는 권한을 관리할 권한을 토큰 보유자(holder)에게 양도할 수 있으며, 토큰 보유자는 합의 투표 또는 주권 토큰 모델을 통해 권한을 행사할 수 있다. 관리자 권한 보유자는, (역시 연맹 모델을 통해) 권한을 보유할 자격을 취득한 후보자 목록에서 전송 에이전트를 지정(assign)할 수 있다. 만약 취약해지거나 비효율적인 경우, 대리인 역할은 관리자 권한을 보유한 당사자가 재지정할 수 있다.
관리자 권한(토큰에 대한 컨텍스트에서 이것은 "루트(root)" 권한임)을 보유한 당사자가 취약해진 경우, 루트 권한 챌린지(challenge)를 해결하려면 루트 권한의 훼손(compromise) 또는 상실로부터 보호하기 위한 혁신적인 기술 메커니즘이 필요하다. 이러한 해결책은 보호관리의 역설(Custody Paradox)을 해결해야 한다. 즉, 모든 네트워크 활동은 사이버 위험 및 운영상의 위험을 가지지만, 활동의 빈도가 줄어들면 기능의 유용성이 제한된다는 사실이다. 다시 말해서, 유일하게 안전한 권한은 사용되지 않는 권한이라는 것이다. 사이버 위험을 가늠하는 것은 거의 불가능하기 때문에, 이러한 보호관리의 역설은 블록체인 산업의 안전을 보장하기 어렵게 만들었다. 사이버 위험을 완화하기 위한 주요 해결책은 서명 절차를 통해 권한을 행사하는 것으로, 이는 트랜잭션 승인 시 안전한 위치에서 함께 참여하는 독립적인 엔티티가 관여되는 고보증(high assurance) 프로세스이다. 그러나 이 프로세스는 사이버 위험을 완화하도록 설계됨에 따라 느리고 비용이 많이 든다. 프로세스를 더 빠르게 만들면 위험이 발생한다. 이러한 문제는, "핫"(빠름/위험함) 월렛과 "콜드"(느림/비용이 많이 듦) 월렛 사이에 중간 지점이 없음에 따라 블록체인의 유용성을 제한하였다.
본 개시된 구현에서는 위임(delegation)을 통해 이러한 역설을 해결하기 위한 "계층화된 월렛(layered wallets)"의 개념이 사용된다. 이러한 위임을 통해, 루트 월렛은 비활성 상태를 유지하면서, 트랜잭션 서명에 수반되는 사이버 위험을 감수할 수 있는 월렛에 (필요한 경우 반복적으로) 권한을 위임할 수 있다. 위임된 권한이 훼손되거나 오용되는 경우 권한은 회수될 수 있다. 신속하고 가역적인(reversible) 트랜잭션을 위한 핫 월렛을 통해 권한을 표현할 수 있고, 훼손된 경우 콜드 월렛을 통해 권한을 행사하여 복구하거나 철회할 수 있으므로, 계층화는 원하는 모델을 제공한다. 콜드 월렛에서 권한을 행사하기 위한 유일한 요구 사항은, 훼손이 발생할 경우 위임된 권한을 복구하는 것으로, 이는 드물게 일어날 것으로 예상된다. 만약 복구가 빈번하다면, 루트가 사용될 가능성이 낮도록 위임 모델이 필요한 만큼 자주 반복될 수 있다. 이 모델은 루트가 최고 보증 수준에서 저장되는 것을 가정한다. 해당 구조가 사용을 방지할 수 있으므로, 사이버 위험이 완화되어 트랜잭션 참여자의 위험 노출이 제한된다.
본 발명의 일 양태는, 분산 원장 플랫폼을 포함하는 분산형 컴퓨팅 네트워크에서, 상태 변경으로 나타내어지는 데이터 트랜잭션의 보안 승인을 위한 방법으로서, 상기 방법은, 분산형 컴퓨팅 네트워크에서 실행되는 스마트 계약 코드(smart contract code)로 표현되는 루트 권한(루트 권한은 특정 기능의 실행을 허용함으로써 불변 권한(immutable right)를 생성함)을 생성하는 단계; 특정 기능 중 적어도 하나를 허용하기 위한 권한을 루트 권한으로부터 분산형 네트워크 상의 월렛으로 위임하여, 월렛이 상기 기능 중 적어도 하나에 대응하는 트랜잭션에 서명할 권한을 갖도록 위임된 권한을 생성하는 단계(여기서 위임된 권한은 루트 권한에 의해 철회되거나 재지정될 수 있음); 권한 위임을 나타내는 데이터 구조를 위임 레지스트리에 기록하는 단계; 및 월렛으로 트랜잭션에 서명하여 월렛 권한을 행사함으로써 분산형 컴퓨팅 네트워크에서 트랜잭션을 승인하는 단계; 를 포함한다.
본 발명은, 1)핵심 권한 회복을 위하여 사이버 위협을 완화하거나 제거하는 메커니즘("계층적 키 관리"); 및 2)중앙 권한 없이 권한을 설정, 전달, 및 시행(enforce)하는 프레임워크("연맹 권한 관리")를 활용하여 선행 기술의 한계를 극복하는 분산형 컴퓨터 네트워크를 제공하는 효과가 있다.
도 1은, 개시된 구현의 루트 권한 및 위임 구조를 설명하기 위해 본 명세서의 후속 도면에서 사용되는 기호의 예이다.
도 2는, 개시된 구현에 따른 계층화된 위임 패턴의 개략도이다.
도 3은, 개시된 구현에 따른 계층화된 월렛 관리에 기반한 작업 시퀀스(sequence of operations)의 예의 개략도이다.
도 4(4a 및 4b)는, 개시된 구현에 따른 연맹 권한 관리 시스템의 완전한 구현을 위한 스마트 계약 아키텍처의 개략도이다.
도 5는, 개시된 구현에 따른 권한 객체(500)의 예의 개략도이다.
도 6은, 개시된 구현에 따른 정책 객체의 개략도이다.
도 7은, 개시된 구현에 따른 스마트 계약 객체의 개략도이다.
도 8은, 개시된 구현에 따른 토큰 객체의 개략도이다.
도 9는, 개시된 구현에 따른 자산 객체의 개략도이다.
공개된 구현은 "암호화폐 월렛" 및 "스마트 계약"을 활용한다. 암호화폐 월렛 또는 "월렛"은 암호화폐 트랜잭션을 위한 공개 및/또는 개인 키를 저장하는 장치, 물리적 매체, 프로그램, 또는 서비스이다. 월렛은, 알고리즘 크기에 따라 길이가 달라지는 이론적 숫자 또는 난수(random number) 생성을 통해 생성할 수 있다. 이 숫자는 이어서 암호 화폐 암호화 알고리즘(cryptographic algorithm) 요건 중 특정 요건을 사용하여 개인 키로 변환된다. 그런 다음, 필요한 암호화 알고리즘 요건을 사용하여 개인 키로부터 공개 키가 생성된다. 개인 키는, 소유자가 암호 화폐에 접근하고 암호 화폐를 전송하는 데 사용되며 소유자에게만 공개되는 반면, 공개 키는 암호 화폐를 받기 위해 제3자에게 공유된다. "스마트 계약"은 블록체인과 같은 분산형 네트워크에서 적어도 부분적으로 저장되고 실행되는 실행 가능한(executable) 코드이다. 스마트 계약에서, 월렛 소유자(즉, 트랜잭션에 서명하는 데 필요한 비밀을 소유한 당사자)가 기능을 수행(즉, 네트워크의 분산 원장에 상태 변경을 유발)할 수 있는 권한은 코드에 의하여 결정될 수 있다. 코드는 분산 원장에 불변적으로 기록될 수 있는 데이터를 기반으로 기능 수행 권한을 평가할 수 있다. 예를 들어, 당사자는 하나의 분산 원장 주소(월렛)에서 다른 곳으로 토큰을 보내려고 할 수 있다(예: 이더리움 원장에서 ERC-20 "TransferFrom" 기능을 실행함에 따라 영향을 받는 상태 변경). 서명된 트랜잭션을 이더리움 원장에 게시할 때, 서명자의 기능 실행 권한은, 간단한 예를 들면 전송을 수행할 수 있는 주소 목록(화이트리스트)을 참조함에 따라 스마트 계약 코드에 의해 결정될 수 있다. 이 목록은 분산 원장에 저장되고 트랜잭션 승인 당사자가 관리하는 데이터로, 당업계의 종사자들이 잘 이해하는 표준 관행이다. 트랜잭션이 서명되면, 스마트 계약은 서명자의 공개 주소와 권한을 정의한 데이터 간의 일치를 기반으로 서명자가 승인된 당사자임을 검증할 수 있다.
승인의 다른 일 예는 토큰 발행, 즉 토큰의 새로운 유닛(unit)을 생성할 수 있는 권한에 관한 것이다. 이 권한을 보유한 당사자는, 토큰 보유자 가치의 바람직하지 않은 희석을 방지하기 위해 승인된 조건에서만 권한을 행사할 책임을 모든 토큰 보유자에 대하여 가지므로, 이는 특히 민감한 권한이다. 토큰 생성자(Token Creator, 즉, 토큰의 스마트 계약을 배포(deploy)한 트랜잭션에 서명한 당사자)는 더 많은 토큰을 발행할 수 있는 권한이 부여될 수 있다. 생성 시에 토큰 스마트 계약은 토큰 생성자의 공개 주소를 기록하는 데이터를 저장할 수 있다. 발행(Issue) 기능이 호출되면 스마트 계약은 서명자가 생성자의 주소인지 여부를 평가할 수 있으며, 서명자가 생성자의 주소라면 트랜잭션을 진행하도록 허가할 수 있다. 아래 표 및 유사 코드(pseudocode)는 이러한 기능을 예시한다.
Figure pct00001
이 기본적인 유사 코드는 역할 기반 시스템(Role Based Systems)에서 통상적이다. 분산형 네트워크가 제공하는 불변성의 힘과 분산된 권한을 가지고, 이 간단한 시퀀스는 중앙 관리자 없이 권한을 관리할 수 있는 상당한 유연성을 제공한다. 그러나 이러한 힘은 권한과 관련된 주소가 분실되거나 훼손된 경우 복구할 수 없는 손실(이 경우 가치의 희석)을 초래할 수 있다. 생성자 권한과 연결된 월렛의 비밀이 분실되거나 도난당하면, 어느 당사자도 필요한 기능을 수행할 수 없게 되어 잠재적으로 스마트 계약과 연관된 가치를 쓸모없게 만들 수 있다. 생성자가 권한을 행사(트랜잭션 서명)할 때마다 사이버 공격으로 인한 키 훼손의 가능성이 있다. 이렇게 중요한 "시스템" 권한은 여기에서 "루트(Root)" 권한"으로 지칭된다. 서명 권한의 분산화를 희생하지 않으면서도 통제력 상실 가능성을 완화함으로써 루트 권한의 복구 불가능한(unrecoverable) 특성을 보호하기 위해서는 혁신적인 기술 메커니즘이 필요하다.
개시된 구현에서, 루트 권한은, 스마트 계약 생성 시에 또는 다른 방식으로 설정할 수 있는 불변 데이터로서 저장된 값을 기반으로 지정되는 명명된 권한이다. 앞서 설명된 바와 같이, 스마트 계약은 그 생성자를 저장하고, 생성자 권한에 의하여 보호되는 기능을 승인하기 위해 이러한 속성을 참조할 수 있다. 루트 권한은 변경할 수 없으므로, 이 권한과 연결된 월렛이 훼손될 경우 복구할 수 없는 가치 손실이 발생할 조건이 만들어진다. 생성하는 월렛(creating wallet)이 훼손되지 않도록 보호하기 위해 스마트 계약은 아래에 설명된 IDelegableRights 인터페이스 및 스마트 계약을 구현할 수 있다. 이 인터페이스를 사용하면 루트 권한을 위임할 수 있으므로 "버리는(throw away)" 월렛을 사용하여 권한을 행사할 수 있고, 훼손된 경우 해당 월렛은 폐기 및/또는 교체될 수 있다.
별도의 스마트 계약이 루트 권한 보호 전용으로 사용될 수 있다. DelegatesRegistry는, 표 또는 기타 데이터베이스로 저장된 데이터 구조 집합으로서, 루트 권한이 한 번 이상 위임될 수 있도록 하여 위험 관리를 위한 "계층화된" 월렛 구조를 생성한다. DelegatesRegistry에 대한 기본 데이터 구조의 예는 아래와 같다.
Contract Right Parent Delegate
0xaabbccdd Creator 0x1234 0x5678
이 데이터 구조에서, Contract(계약)는 루트 권한이 실행되고 루트 주소가 기록되는 스마트 계약을 나타내며, Right(권한)는 해당 계약에 의해 보호되는 권한이며, Parent(부모)는 권한을 위임하는 주소, Delegate(수임자)는 해당 권한이 위임되는 주소이다. 표시된 데이터에서, 계약 0xaabbccdd에 대한 생성자 권한은 루트 월렛 0x1234에 의해 수임자 0x5678에 지정되었다.
IDelegableRights 스마트 계약의 발행(Issue) 기능이 생성자(Creater)에 의해서만 실행될 수 있도록 보호되는 예를 가정해 본다. 발행 기능이 호출되면 스마트 계약은 먼저 서명자가 생성자인지 확인한다. 그렇지 않은 경우 계약은 DelegatesRegistry를 확인하여 IDelegableRights 계약에 대한 생성자 권한이 위임되었는지, 그리고 서명자가 수임자인지(맞을 경우 true를 반환함)를 확인한다.
위에서 알 수 있듯이, DelegatesRegistry(위임 레지스트리)는 위임 기록을 데이터 구조로 데이터베이스에 저장한다. 이 레지스트리는 IDelegableRight 인터페이스를 구현하는 스마트 계약(여기서는 "실행(Execution)" 계약이라고 지칭함)에 의해 운영된다. 모든 위임 기록은 기록을 생성한 스마트 계약으로 표시되어, 해당 스마트 계약이 다른 스마트 계약의 권한 위임에 영향을 미치지 않도록 한다. 예를 들어, IDelegableRight 인터페이스는 Delegate(right, wallet); Replace(right, wallet, newWallet); 및 Subsume(right, wallet)의 3가지 호출을 지원할 수 있다.
도 1은, 루트 권한(Root Right) 및 위임 구조를 나타내기 위해 본 명세서 후속 도면에서 사용되는 기호를 예시한다. 루트 월렛(Root Wallet)의 위임 가능한 권한은 102에 나타내고, 기능 월렛(functional wallet)은 104에 나타내었다. IDelegableRight 인터페이스를 구현하는 스마트 계약(SmartContracts)은, DelegatesRegistry 데이터베이스를 사용하여 루트 권한이 한 번 이상 위임될 수 있도록 하여, 위험 관리를 위한 계층화된 월렛(LayeredWallet) 구조를 생성한다.
권한을 위임하기 위해, 권한을 보유한 당사자는 실행 계약(Executing Contract, 권한에 의해 보호되는 특정 기능을 실행하는 특정 스마트 계약)을 통해 IDelegableRight.Delegate 기능에 서명한다. 실행 계약은 호출자가 위임하고자 하는 권한을 보유하는지 확인한 다음, DelegatesRegistry 데이터베이스에서의 기록을 통해 권한을 위임하기 위해 DelegatesRegistry를 호출한다(생성자 권한을 위임하도록 사용된 위 샘플 데이터의 경우, 이는 Delegate('Creator', 0x5678)에 해당하고, 여기서 0x1234는 0xaabbccdd 실행 계약을 통해 생성자(Creator)로서 트랜잭션에 서명하였다). 본 예에서, 권한 위임은 아래의 표 항목 데이터 구조로서 기록될 수 있다.
DelegateRegistry Entry: Right='Creator'; Parent=0x1234 [W];
Delegate=0x5678 [W]; Contract=0xaabbccdd [SC]
이러한 지정을 통해 수임자 월렛(Delegate Wallet)은 이제 실행 계약의 컨텍스트에서 부모(parent)의 권한을 갖게 된다. 위임은 기능을 수행하는 기능 월렛을 루트 월렛과 분리한다. 수임자가 대신 트랜잭션에 서명함에 따라, 위임은 루트 월렛 사용과 연관된 사이버 위험으로부터 루트 월렛을 보호한다. 두 월렛을 모두 훼손시키는 공격은 개시된 모델의 효과를 약화시킬 수 있으므로, 위임 모델의 강력함은 수임자 월렛의 개인 키의 독립적인 보관(custody)에 달려 있다.
수임자 월렛이 훼손된 경우, 루트 월렛은 수임자 월렛을 교체하여 제어를 복원할 수 있다. 또한 루트 월렛은 아래에서 자세히 설명하는 바와 같이 수임자 월렛을 포괄(subsume)하여 해당 권한을 되찾을 수 있다. 수임자 월렛의 권한을 제거하기 위해, 권한을 가진 당사자는 실행 계약으로부터 IDelegableRight.Subsume 기능을 호출한다. 예를 들어, 앞의 예에서 지정된 권한을 제거하려면, Subsume('Creator', 0x5678) 기능이 실행되어 표에서 해당 행을 제거하고, 0x5678이 실행 계약의 생성자를 대신하여 이 권한을 실행하지 못하도록 한다.
루트 월렛의 훼손을 방지하기 위하여 여러 계층의 위임이 적용될 수 있으며, 이는 예를 들어 수임자 교체가 자주 발생하는 경우에 적용될 수 있다. 루트 월렛 훼손의 사이버 위험을 완화하기 위한 한 가지 전략으로, 루트 권한은 단 두 번만 사용되는데, 스마트 계약을 생성할 때 한 번, 그리고 해당 권한을 실행하기 위해 및/또는 권한을 추가로 위임하기 위해 사용되는 월렛에 권한을 위임할 때 두 번째로 사용된다. 위임된 권한에 대하여 유의할 만한 훼손 위험이 있다면, 위임 복구는 감독자 월렛(supervisor wallet, 즉, 위임 체인(chain)에서 더 높은 위치에 있는 월렛)에 의한 트랜잭션 서명을 수반하므로 권한은 두 번째, 세 번째, 또는 그 이상 위임될 수 있다. 루트 월렛에 의한 사이버 위험이 발생하지 않게 하기 위해서는, 루트 월렛 권한이 복구에 활용되지 않도록 하여 루트 월렛을 사이버 위험으로부터 보호할 수 있도록 필요한 계층 수만큼 여러 번 위임이 이루어져야 한다.
도 2는 계층화된 위임 패턴을 예시한다. 202에서, 루트 월렛은 기능 월렛에 권한을 위임하였다. 루트는 추후에 기능 월렛을 교체(Replace)하거나 위임을 포괄(철회)할 수 있다. 204에서, 202에서 위임을 받은 월렛은 다시 권한을 위임하여, 기능적 권한을 행사하는 데 사용되는 다른 월렛에 대하여 "감독자"의 역할을 한다. 206에서, 감독자 월렛은 그 역할을 위임하여 루트를 보호하는 추가 계층을 생성할 수 있다. 월렛은 체인/계층상에서 더 아래에 있는 모든 월렛을 대체할 수 있다. 물론, 모든 활동은 DelegatesRegistry에 기록된다.
도 3은, 계층화된 월렛 관리에 기반한 작업 시퀀스의 예를 예시한다. 도 3의 각 행은 권한 위임의 상태를 나타낸다. 1번에서, 루트(Root)는 시스템 속성에 대한 생성자(Creator) 권한을 부모 감독자 월렛(Parent Supervisory Wallet, "WarmRoot")에 위임하여 DelegateRegistry에 다음 항목을 생성한다.
Right=Creator; Parent=Root[W]; Delegate=WarmRoot[W];
Contract=AttributeRegistry[SC]
2번에서는, WarmRoot가 위임받은 감독자 월렛(Delegated Supervisory Wallet, "HotRoot")에 생성자 권한을 위임하여 DelegateRegistry에 다음 항목을 생성한다.
Right=Creator; Parent=WarmRoot[W]; Delegate=HotRoot[W];
Contract=AttributeRegistry[SC]
3번에서는, 루트 교체 작업이 호출되어 WarmRoot를 OthRoot로 교체하고 DelegateRegistry에 다음 항목을 생성한다.
Right=Creator; Parent=Root[W]; Delegate=OthRoot[W];
Contract=AttributeRegistry[SC]
DelegateRegistry Auto-Update: Right=Creator; Parent=OthRoot[W];
Delegate=WarmerRoot[W]; Contract=AttributeRegistry[SC]
4번에서는, 권한이 부모 감독자 월렛에 의해 포괄된다.
DelegateRegistry Remove: Right=Creator; Parent=Root[W];
Delegate=OthRoot[W]; Contract=AttributeRegistry[SC]
Delegate Registry Auto-Update: Right=Creator; Parent=Root[W];
Delegate=WarmerRoot[W]; Contract=AttributeRegistry[SC]
개시된 구현에서, 루트 권한의 사이버 위험을 관리하는 데 계층적 월렛 관리가 사용된다. 이 메커니즘은 아래에 설명된 방식으로 연맹 권한 관리(Confederated Rights Management)에 활용되며, 손실 또는 훼손으로부터 보호되는 기본 권한으로부터 많은 당사자의 이익을 나타내는 복잡한 권한이 구축될 수 있다.
금융 생태계는, 독립적인 법인 엔티티, 엔티티의 연대, 및 개인 간의 많은 상호 작용에 의존한다. 많은 독립적 행위자와 규제 체제가 있는 글로벌 금융 생태계에서, 권한은 중앙 집중적으로 제어되지 않으며 제어될 수도 없다. 당사자들은 법인, 그룹, 또는 기타 공동의 기업으로 함께 뭉칠 수 있다. 이러한 당사자들은, 보통 자발적으로, 중앙 권한이 없는 느슨한 연대(coalition)인 연맹(confederation)으로 더 연결되어 상거래에 참여할 수 있다. 연합 내에서 신뢰할 수 있는 정보 교환을 확장하기 위해 참가자는 종종 통신 방법, 공통 데이터 형식, 및 참가자의 권한에 대하여 합의한다.
효과적인 상거래는 엔티티가 권한을 정의 및 관리하고, 이러한 권한을 대규모로 결합, 공유, 또는 시행할 수 있도록 하는 프레임워크를 필요로 한다. 글로벌 상거래에서 발생하는 다양한 엔티티 유형과 다양한 거래를 지원하도록 설계된 시스템은 유연하고, 효율적으로 표현되며, 안전하고(권한의 무단 확대가 없음), 가역적이며, 분산되어야 한다. 블록체인의 핵심 이점인 분산 권한은, 글로벌 상거래를 위한 분산 권한 프레임워크를 구성할 수 있는 기준을 제공한다.
개시된 구현은, 자기 기술적(self-describing) 데이터 모델을 갖는 연맹 권한 프레임워크를 제공하며, 여기서 속성(attributes)으로 표현될 수 있는 권한 및 다른 것*을 정의할 수 있는 권한은 자기 기술적이며, 권한의 초기 행사로부터 선별된(curated) 방식으로 나타날 수 있다. 이러한 프레임워크는, 권한을 정책 생성(즉, 권한 및 정책을 생성하고 관리할 수 있는 권한을 포함하여, 활동을 통제하는 여러 권한의 표현)으로 확장하는 데 사용할 수 있다. 그로부터, 계약(코드 또는 기타 합의)을 원하는 권한 및 인증으로 배포하고 관리하여 필요한 보안 수준으로 다양한 트랜잭션을 활성화하고 자동화할 수 있다. 마지막으로, 활동은 권한의 프레임워크 내에서 생성 및 관리되는 토큰과 같은 가치 유닛에 연결될 수 있다. 토큰 거래는 분산 프레임워크 내에서 생성된 권한, 정책, 및 계약에 의해 통제(govern)될 수 있다. 단일 권한에서 시작하여 연맹 권한 및 복잡한 생태계의 통제를 가능하게 하는 자기 기술적 권한 프레임워크를 구축하는 방법이 아래에 설명된다.
개시된 구현에 따른 권한의 연맹 관리를 위한 데이터 모델은, ContextRegistry(컨텍스트 레지스트리), AttributeRegistry(속성 레지스트리), 및 AttestationRegistry(증명 레지스트리)의 3가지 표/데이터베이스를 포함할 수 있다. 컨텍스트(Context)는 하나 이상의 속성(Attribute)에 대한 제어 범위를 정의한다. 기본적으로, 컨텍스트가 생성되면 생성자는 컨텍스트를 업데이트하고 컨텍스트 내에서 속성 및 증명(attestation)을 생성 및 관리할 수 있는 모든 권한을 보유한다. 이러한 권한은, 아래에 설명된 대로, 증명을 사용하여, 또는 속성(정책 및/또는 계약)으로부터 파생된 기타 방법을 사용하여 다른 당사자에게 지정될 수 있다. ContextRegistry를 구현하기 위한 코드의 예는 여기에 첨부된 코드 부록에서 찾을 수 있다. ContextRegistry 기록의 예시적인 데이터 구조는 다음과 같다.
Id
Name Creator SelfSovereign ParentId
0xabcd
Core 0x1234 false [Null]
여기서,
Id는 컨텍스트의 고유 식별자이고,
Name(명칭)은 컨텍스트를 설명하는 명칭이고,
CreatorId는 컨텍스트를 생성한 월렛의 주소 혹은 다른 ID이며,
SelfSovereign은 시스템 권한으로 컨텍스트를 오버라이드(override)할 수 없음을 나타내고,
ParentID는 컨텍스트가 다른 컨텍스트로부터 상속되는(inherited) 경우 사용된다.
AttributeRegistry를 구현하기 위한 예시 코드는 첨부된 코드 부록에서 찾을 수 있다. AttributeRegistry 기록의 예시적인 데이터 구조는 다음과 같다.
Id
Name ContextId DataType ParentId SourceId
0xa1b1
System 0xabcd (Core) Boolean [Null] [Internal]
여기서,
Id는 속성의 고유 식별자이고,
Name(명칭)은 속성을 설명하는 명칭이고,
ContextId는 속성이 접근되고 관리되는 컨텍스트이고,
DataType은 AttestationRegistry 또는 기타 소스(source)에서 속성에 지정된 값의 데이터 유형이고(DataType 값의 목록은 코드 부록에서 찾을 수 있음).
ParentId는 속성 간의 상속(inheritance)을 정의하는 데 사용되며,
SourceId는 속성과 관련된 증명에 대한 데이터를 찾을 수 있는 위치를 정의한다(소스는 외부의 제3자 오라클 및/또는 스마트 계약 또는 기타 데이터 소스일 수 있지만, 본 명세서에 개시된 모든 예는 소스가 컨텍스트에 의해 제어되는 읽기 및 쓰기 규칙을 가진 내부 데이터 저장소인 AttestationRegistry라고 가정한다).
개시된 구현에 따라 AttestationRegistry를 구현하기 위한 예시 코드는 코드 부록의 PropertyMetadata 부분에서 찾을 수 있다. AttestationRegistry 기록의 예시적인 데이터 구조는 다음과 같다.
Id
AttributeID Key Value Creator Expiration
0x12ef 0xa1b1
(시스템)
0x5678
(이 경우에는
월렛)
true 0x1234 [Null]
여기서,
Id는 증명의 고유 식별자이고,
AttributeId는 증명의 형식과 목적을 정의하는 속성이고,
Key(키)는 값이 지정된 아이템이고(키는 둘 이상의 아이템을 값과 연결하는 복합 키일 수 있음),
Value(값)은 증명에 지정된 값이고,
Creator(생성자)는 증명 값을 설정한 당사자를 식별하며,
Expiration(만료)는 증명이 만료되는 시간을 나타낸다.
이러한 데이터 구조를 사용하여, 아래에서 더 자세히 설명되는 바와 같이 컨텍스트 생성자가 역할을 나타내는 속성을 생성하는 것이 가능하다. 역할은 증명을 통해 월렛에 지정될 수 있다. 생성자는, 컨텍스트, 속성, 및 증명을 관리하는 데 사용되는 특정 기능에 속성(역할)을 지정할 수 있고, 이에 따라 관리 구조를 관리하는 관리 구조, 즉, 그룹이 공동으로 권한을 형성하고 관리하도록 사용될 수 있는 자기 기술적이고 자체적으로 관리되는(self-managing) 시스템을 생성할 수 있다.
자기 기술적 권한 프레임워크를 구축하기 위해, 시스템의 핵심 요소를 생성하고 운영하도록 위임받은 당사자인 루트 권한(Root authority)은 프레임워크를 인스턴스화(instantiate)하기 위해 ContextRegistry, AttributeRegistry, 및 AttestationRegistry를 포함하는 스마트 계약 인프라를 배포한다. 분산 원장에 스마트 계약을 배포하는 것은 당업자에게 잘 알려진 통상적인 관행이므로 본 명세서에서는 자세히 설명하지 않는다. 루트 권한은, 기본 권한의 재설정이 필요한 특별한 이벤트가 없는 한 시스템 시작 시에만 제정될 것으로 기대된다. SelfSovereign 값이 true로 설정된 컨텍스트가 생성되면, 루트(또는 지정된 시스템 권한)는 컨텍스트를 수정할 권한이 없으므로, 루트가 생성하는 연맹으로부터 신뢰받을 필요가 없다.
스마트 계약 배포 후 루트의 첫 번째 활동은 ContextRegistry에서 '코어(Core)' 컨텍스트를 생성하기 위한 트랜잭션에 서명하는 것이다. 레지스트리 내에서 지정된 형식으로 데이터를 설정하는 트랜잭션 구조는 당업자에게 잘 알려진 일반적인 관행이므로 본 명세서에서는 자세히 설명하지 않는다. 특정 기능 서명들은 서로 다를 수 있다. 이어서 루트는 코어 컨텍스트에서 '시스템(System)' 속성(역할)을 생성하기 위한 트랜잭션에 서명한다. 다음으로 루트는 AttestationRegistry에서 키(0x5678)와 함께 값('true')을 시스템 속성에 지정하는 기록을 생성하기 위한 트랜잭션에 서명하여, 키에 대응하는 주소를 가진 월렛에 시스템 역할을 지정한다.
이와 동일한 시퀀스를 사용하여, 시스템 계정은 큐레이터 역할을 하는 코어 권한 부여자를 지정하여, 알려지고 자격을 갖춘 엔티티만이 시스템에서 활동을 수행하도록 한다. 컨텍스트 내에서 지정된 권한을 포함하는 월렛만이 컨텍스트를 수정할 수 있으므로, 시스템은 권한의 확대 또는 엔티티 교차 승인(cross entity authorization, 소속되지 않은(unaffiliated) 엔티티가 다른 엔티티에 권한을 지정하는 것)을 방지하도록 설계되었다.
도 4는, 연맹 권한 관리 시스템의 완전한 구현을 위한 스마트 계약 아키텍처를 예시한다. 아키텍처는, 프레임워크를 배포하는 당사자에게 지정된 루트 권한 및 루트 권한에서 파생된 권한을 생성하고 관리하는 데 사용되는 일련의 레지스트리로 구성된다. AttributeRegistry를 사용하면 인프라를 통제하는 데 사용할 수 있는 권한을 포함하여 모든 역할 또는 권한을 생성하고 관리할 수 있다. 아래에서는 연맹 권한 관리를 이용할 수 있도록 인프라를 설정(configuration)하는 프로세스를 설명한다. 설정 프로세스의 각 단계에 대하여, 앞서 개시된 바와 같은 레지스트리와 코드 부록에 속성이 기록된다. 초기에, 루트(Root), 웜 루트(Warm Root), 및 시스템(System) 기능을 수행하기 위해 사용될 월렛이 별도의 키 관리와 함께 1번에 존재한다. 2번에서, 루트는 인프라 스마트 계약(코드는 부록에 첨부)을 배포하고 생성자(Creator)로 기록된다. 3번에서, 루트는 웜 루트에 위임하여, 앞서 위임 흐름에 대하여 설명된 대로 생태계 생성자 권한과 책임(루트에 의해 제거될 수 있음)을 지정한다. 4번에서, 루트는 시스템 속성을 생성하고 시스템 역할을 수행하는 월렛에 대한 증명을 지정한다. 이 단계에 대한 ContextRegistry, AttributeRegistry, 및 AttestationRegistry의 데이터 항목은 상기 표 및 예로 나타내었다. 5번에서, 시스템은 CoreEligibility(코어 적격성) 속성(TokenRoleEligible, AssetRoleEligible 등)을 생성한다. 이 단계의 데이터 항목은 이전 단계에서 구축된 것과 동일한 패턴을 따르며 추가 도시되지 않는다. 이어서 6번에서 시스템은 CoreAuthorizer(코어 권한 부여자) 속성을 생성하고, 역할을 수행하는 각 월렛에 대한 증명을 만든다. 7번에서, (보통 추후에) 권한 부여자(Authorizers)는 각 속성에 대하여 원하는 월렛에 각각의 CoreEligibility 속성 에이전트(Attribute Agent) 권한을 지정한다. 8번에서, 적격의 속성 생성자(Eligible Attribute Creators)는 필요한 대로 다른 역할을 통제할 수 있는 새로운 속성을 생성할 수 있다. 9번에서, 적격의 생성자는 자기주권(self-sovereign) 토큰, 자산, 계약, 및 정책을 생성할 수 있으며 이러한 엔티티에 대한 권한을 추가로 지정할 수 있다.
기본적인 활동 중 하나는 속성(권한)을 생성하는 능력이다. 승인된 권한 생성자는 권한을 생성하고 관리할 수 있다. 이를 통해 권한을 가진 모든 엔티티가 자체 생태계를 생성하고 관리할 수 있다. 이러한 프레임워크는 엔티티 간의 상호작용 및 소속되지 않은 엔티티 간의 집단 활동(collective action)을 가능하게 한다. 설정 시퀀스의 일부로 생성될 데이터 구조를 도시하기 위해 약칭 표기(shorthand notation)가 사용된다.
도 5는, 개시된 구현에 따른 권한 객체(500)의 예를 도시한다. 생성자(코어 속성 증명)는 ContextRegistry의 항목을 통해 컨텍스트를 생성할 수 있다. 기본적으로, ContextRegistry에는 생성자가 AttributeRoleEligible 작성자 증명(역할)을 가져야 한다는 정책이 있다. 코어 속성 증명(Core Attribute Attestation)의 유효성을 검사하기 위해 AttributeRegistry에 아래와 같이 기록될 수 있다.
Attribute=AttributeRoleEligible; Key=Creator[W]; Value=Creator;
Source=AttributeRoleAuthorizer[W]; Contract=ContextRegistry[SC]
컨텍스트 생성자는 ContextRegistry에 컨텍스트 항목과 함께 기록된다. 이 역할은 아래에 설명된 대로 위임될 수 있다. 속성 생성 시, 관리자(Manager) 역할은 생성자에 의해 자체 지정된다. 본 구현에서는 속성 당 하나의 관리자만이 존재할 수 있다. AttributeRegistry에는 아래와 같이 기록된다.
Attribute=AttributeRole; Key=Attribute[ID], Creator[W]; Value=Manager;
Source=Creator[W]; Contract=AttributeRegistry
컨텍스트 생성 시, 관리자 역할은 아래의 데이터 구조를 통해 생성자에 의해 자체 지정된다.
Attribute=ContextRole; Key=Context[ID], Creator[W]; Value=Manager;
Source=Creator[W]; Contract=AttributeRegistry
컨텍스트 관리자는 컨텍스트 내에서 하나 이상의 속성을 생성할 수 있다.
속성 생성 시, 운영자(Operator) 역할은 생성자인 컨텍스트 관리자에 의해 자체 지정되며, AttributeRegistry에는 아래와 같이 기록된다.
Attribute=Attribute; Key=Context[ID], Creator[W]; Value=Operator;
Source=Creator[W]; Contract=AttributeRegistry
ContextRegistry 생성자는 아래의 규칙을 시행하는 정책(Policy)을 지정할 수 있다.
- 컨텍스트 당 하나의 관리자만이 존재할 수 있다. 생성자(또는 수임자)에 의해 관리자 역할이 설정되었다면, 생성자(또는 수임자)는 관리자를 재지정할 수 있다.
- 컨텍스트가 자기주권적이지 않다면(이는 기본값일 수 있음), 관리자는 시스템에 의해 설정될 수 있다. 관리자는 AttributeRoles(Operator, Agent)를 추가하거나 제거할 수 있다.
관리자는 AtributeRegistry에 아래와 같이 기록함으로써 속성에 대한 운영자(0에서 다수)를 추가하거나 제거할 수 있다.
Attribute=AttributeRole; Key=Attribute[ID], Operator[W]; Value=Operator;
Source=Manager[W]; Contract=AttributeRegistry
관리자는 AttributeRegistry에 아래와 같이 기록함으로써 속성에 대한 에이전트(0에서 다수)를 추가하거나 제거할 수 있다.
Attribute=AttributeRole; Key=Attribute[ID], Agent[W]; Value=Agent;
Source=Manager[W]; Contract=AttributeRegistry
기본적으로(AttestationRegistry 설정에서 지정됨), AttestationRegistry는 속성 운영자(Attribute Operators)가 속성에 대한 임의의 증명을 추가, 업데이트, 또는 제거할 수 있다는 정책을 가진다. 속성 에이전트(Attribute Agent)는 자신이 소스인 속성만을 업데이트하거나 제거할 수 있다. 에이전트 또는 운영자는 속성에 대한 증명(AttributeRegistry의 값)을 설정할 수 있다. 일부 속성은 AttestationRegistry라고 지칭되는, 값에 대한 내부 저장소를 사용한다. 다른 속성은 다양한 소스로부터의 외부 데이터로 향한다. 특수한 유형의 속성은 자체 증명(self-attestation)을 허용하는 속성이다. 즉, 영향을 받는 당사자에 의하여 값이 설정될 수 있는 속성으로, 예를 들어 아래에 대해 설정된 것으로 특정된다.
*[W]=월렛 주소, [SC]=스마트 계약 주소, [ID]=속성 식별자
유사한 방식으로, 다른 활동은 참가자 및 금융 트랜잭션의 다른 임의의 양상을 인코딩하는 데 사용될 수 있는 시스템의 기본 개체(정책, 계약, 엔티티, 및 가치 유닛)를 생성한다. 정책(Policies)은 규칙의 조합으로, 규칙은 하나 이상의 속성을 다른 데이터와 대비하여 평가하는 스테이트먼트(statement)이며, 규칙은 스마트 계약 트랜잭션을 관리하는 데 사용된다. 규칙의 구조 및 기능은 2018년 9월 26일에 출원된 미국 출원 16/143,058에서 설명되며, 그 개시 내용은 본 명세서에 참조에 의해 포함된다. 정책은 속성으로부터 파생되며, 컨텍스트, 속성, 및 증명의 생성 및 관리에 적용할 수 있는 규칙을 이루도록 특성을 구성하는 데 사용할 수 있다. 이는 본 개시된 데이터 구조가 그 자체를 통제하고 연맹을 조정(align)하는 데 사용되는 임의의 관리 구조를 생성하도록 하는 데 사용될 수 있는 또 다른 방법이다.
도 6은, 생성자(코어 속성 증명)에 의해 생성된 정책 객체(600)를 예시한다. 기본적으로(예를 들어, 설정 시 지정됨), PolicyRegistry는 생성자가 PolicyRoleEligible 생성자 증명(역할)을 가져야 한다는 정책을 가지며, 이는 예를 들어 아래와 같은 방식으로 표현된다.
Attribute=PolicyRoleEligible; Key=Creator[W]; Value=Creator;
Source=PolicyRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
생성자는 AttributeRegistry에 정책 항목과 함께 기록된다. 이 역할은 위임될 수 있다. 정책 객체 생성 시, 관리자 역할은 아래의 기록을 통해 생성자에 의해 자체 지정된다.
Attribute=PolicyRole; Key=Policy[ID], Creator[W]; Value=Manager;
Source=Creator[W]; Contract=PolicyRegistry[SC]
PolicyRegistry는 아래의 규칙을 시행하는 정책(생성 시 또는 생성 후 지정됨)을 가질 수 있다.
- 정책 당 하나의 관리자만이 존재할 수 있다.
- 생성자(또는 수임자)에 의하여 관리자 역할이 설정되었다면, 생성자(또는 수임자)는 관리자를 재지정할 수 있다.
- 정책이 자기주권적이지 않다면(이는 기본값일 수 있음), 관리자는 시스템에 의하여 설정될 수 있다.
- 관리자는 PolicyRoles(Operator, Certifier)를 추가하거나 제거할 수 있다.
정책 객체 생성 시, 운영자(Operator) 및 인증자(Certifier) 역할은 아래의 기록을 통해 작성자에 의해 자체 지정된다.
Attribute=PolicyRole; Key=Policy[ID], Creator[W]; Value=Operator;
Source=Creator[W]; Contract=PolicyRegistry[SC];
Attribute=PolicyRole; Key=Policy[ID], Certifier[W]; Value=Certifier;
Source=Creator[W]; Contract=PolicyRegistry[SC];
관리자는 아래를 기록함으로써 3번에서 하나 이상의 당사자를 운영자로 지정할 수 있다.
Attribute=PolicyRole; Key=Policy[ID], Operator[W]; Value=Operator;
Source=Manager[W]; Contract=PolicyRegistry[SC];
정책 당 하나의 인증자만이 있을 수 있다. 인증자가 생성자 이외의 당사자인 경우, 인증자는 PolicyRoleEligible 인증자 증명(역할)을 가지고 있어야 한다. 인증자는 역할이 적용되기 전에 지정을 확인해야 한다. 아래의 데이터 구조는 이를 특정한다.
Attribute=PolicyRoleEligible; Key=Certifier[W]; Value=Certifier;
Source=PolicyRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
Attribute=PolicyRole; Key=Policy[ID], Certifier[W]; Value=Certifier;
Source=Manager[W]; Contract=PolicyRegistry[SC];
*[W]=월렛 주소, [SC]=스마트 계약 주소, [ID]=레지스트리 식별자
전술한 바와 같이, 스마트 계약은 분산 원장에서 로직(logic)을 실행하는 데 사용된다. 본 명세서에 개시된 권한 관리 시스템을 포함하여, 설정된 시스템의 거동(behavior)을 변경하기 위해 새로운 스마트 계약을 배포할 수 있다. 권한 관리 시스템은, 아래에 설명된 대로 권한 관리 시스템 업그레이드를 수행할 권한을 통제하는 데 사용할 수 있다.
도 7에 도시된 바와 같이, 개발자(Developer, 핵심 속성 증명)는 SmartContract 객체(700)를 생성할 수 있다. 기본적으로(설정 시 지정됨), ContractRegistry는 개발자가 아래 데이터 구조에 의해 특정되는 ContractRoleEligible 개발자 증명(역할)을 가져야 한다는 정책을 갖는다.
Attribute=ContractRoleEligible; Key=Developer[W]; Value=Developer;
Source=ContractRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
개발자는 레지스트리에 계약 항목과 함께 기록된다. 이 역할은 위임될 수 있다. 계약 객체 생성 시, 관리자 역할은 아래의 데이터 구조를 통해 개발자에 의해 자체 지정된다.
Attribute=ContractRole; Key=TokenID[SC], Developer[W]; Value=Manager;
Source=Developer[W]; Contract=ContractRegistry[SC]
ContractRegistry는 아래의 규칙을 시행하는 정책(생성 시 또는 생성 후 지정됨)을 가질 수 있다.
- 계약 당 하나의 관리자만이 존재할 수 있다.
- 관리자 역할이 개발자(또는 수임자)에 의해 설정되었다면, 개발자(또는 수임자)는 관리자를 재지정할 수 있다.
- 계약이 자기주권적이지 않다면(이는 기본값일 수 있음), 관리자는 시스템에 의해 설정될 수 있다. 관리자는 ContractRoles(Operator, Certifier)를 추가하거나 제거할 수 있다.
계약 객체 생성 시, 운영자 및 인증자 역할은 아래의 데이터 구조를 통해 개발자에 의해 자체 지정된다.
Attribute=ContractRole; Key=ContractID[SC], Developer[W];
Value=Operator; Source=Developer[W]; Contract=ContractRegistry[SC]
Attribute=ContractRole; Key=ContractID[SC], Certifier[W];
Value=Certifier; Source=Developer[W]; Contract=ContractRegistry[SC];
관리자는 아래 데이터 구조를 통해 하나 이상의 당사자를 운영자로 지정할 수 있다.
Attribute=ContractRole; Key=ContractID[SC], Operator[W]; Value=Operator;
Source=Manager[W]; Contract=ContractRegistry[SC];
원한다면, 생성자는 계약 당 하나의 인증자만이 존재하도록 속성 관리 시스템을 제한하는 정책을 배포할 수 있다. 인증자가 개발자 이외의 당사자인 경우, 인증자는 ContractRoleEligible 인증자 증명(역할)을 가져야 한다. 인증자는 역할이 적용되기 전에 지정을 확인해야 한다.
Attribute=ContractRoleEligible; Key=Certifier[W]; Value=Certifier;
Source=ContractRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
Attestation => Attribute=ContractRole; Key=ContractID[SC], Certifier[W];
Value=Certifier; Source=Manager[W]; Contract=ContractRegistry[SC];
*[W]=월렛 주소, [SC]=스마트 계약 주소
토큰 레지스트리
도 8에 예시된 바와 같이, 발행자(Issuer, 코어 속성 증명)는 토큰의 소유권 및/또는 권한을 정의하는 스마트 계약(800)을 통해 토큰을 생성할 수 있다. 기본적으로(설정 시 지정됨), 토큰 레지스트리는 발행자가 아래 데이터 구조를 통해 특정되는 TokenRoleEligible 발행자 증명(역할)을 가져야 한다는 정책을 갖는다.
Validate Attestation => Attribute=TokenRoleEligible; Key=Issuer[W];
Value=Issuer; Source=TokenRoleAuthorizer[W];
Contract=AttributeRegistry[SC]
발행자는 레지스트리에 토큰 항목과 함께 기록된다. 이 역할은 위임될 수 있다. 토큰 생성 시, 관리자 역할은 아래 데이터 구조를 통해 발행자에 의해 자체 지정된다.
Attribute=TokenRole; Key=TokenID[SC], Issuer[W]; Value=Manager;
Source=Issuer[W]; Contract=TokenRegistry[SC]
TokenRegistry는 아래의 규칙을 시행하는 정책(생성 시 지정됨)을 갖는다.
- 토큰 당 하나의 관리자만이 존재할 수 있다.
- 관리자 역할이 발행자에 의해 설정되었다면, 발행자는 관리자를 재지정할 수 있다. 토큰이 자기주권적이지 않다면(기본값), 관리자는 시스템에 의해 설정될 수 있다.
- 자기주권 토큰의 경우 관리자는 토큰 보유자(tokenholders)의 선출(election)에 의해 설정될 수 있다(선출은 별도로 설명됨).
- 관리자는 TokenRoles(Operator,TransferAgnet)를 추가하거나 제거할 수 있다.
토큰 생성 시, 운영자 및 에이전트(Agent) 역할은 아래 데이터 구조를 통해 발행자에 의해 자체 지정된다.
Attribute=TokenRole; Key=TokenID[SC], Issuer[W]; Value=Operator;
Source=Issuer[W]; Contract=TokenRegistry[SC];
Attribute=TokenRole; Key=TokenID[SC], Issuer[W]; Value=Agent;
Source=Issuer[W]; Contract=TokenRegistry[SC];
관리자는 아래 데이터 구조를 통해 하나 이상의 당사자를 운영자로 지정할 수 있다.
Attribute=TokenRole; Key=TokenID[SC], Operator[W]; Value=Operator;
Source=Manager[W]; Contract=TokenRegistry[SC];
본 구현에서, 토큰당 전송 에이전트(TransferAgent)는 하나만 존재할 수 있다. 에이전트가 발행자 이외의 당사자인 경우, 에이전트는 TokenRoleEligible 에이전트 증명(역할)을 가져야 한다. 에이전트는 아래 데이터 구조를 통해 역할이 적용되기 전에 지정을 확인해야 한다.
Attribute=TokenRoleEligible; Key=Agent[W]; Value=Agent;
Source=TokenRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
Attribute=TokenRole; Key=TokenID[SC], Agent[W]; Value=Agent;
Source=Manager[W]; Contract=TokenRegistry[SC];
*[W]=월렛 주소, [SC]=스마트 계약 주소
도 9에 예시된 바와 같이, 자산 생성자(코어 속성 증명)는 자산(Asset) 객체(900)를 생성할 수 있다. 기본적으로(설정 시 또는 설정 후 지정됨), AssetRegistry는 생성자가 아래 표현된 바와 같은 AssetRoleEligible 생성자 증명(역할)을 가져야 한다는 정책을 갖는다.
Attribute=AssetRoleEligible; Key=Creator[W]; Value=Creator;
Source=AssetRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
생성자는 AssetRegistry에 자산 항목과 함께 기록된다. 이 역할은 위임될 수 있다. 자산 생성 시, 관리자 역할은 아래 데이터 구조를 통해 생성자에 의해 자체 지정된다.
Attribute=AssetRole; Key=Asset[ID], Creator[W]; Value=Manager;
Source=Creator[W]; Contract=AssetRegistry[SC]
AssetRegistry는 아래의 규칙을 시행하는 정책(생성 시 또는 생성 후 지정됨)을 갖는다.
- 자산 당 하나의 관리자만이 존재할 수 있다.
- 관리자 역할이 생성자(또는 수임자)에 의해 설정되었다면, 생성자(또는 수임자)는 관리자를 재지정할 수 있다.
- 자산이 자기주권적이지 않다면(기본값), 관리자는 시스템에 의해 설정될 수 있다. 관리자는 AssetRoles(Operator, Certifier)를 추가하거나 제거할 수 있다.
자산 생성 시, 운영자 및 인증자 역할은 아래와 같이 생성자에 의해 자체 지정된다.
Attestation => Attribute=AssetRole; Key=Asset[ID], Creator[W];
Value=Operator; Source=Creator[W]; Contract=AssetRegistry[SC];
Attestation => Attribute=AssetRole; Key=Asset[ID], Certifier[W];
Value=Certifier; Source=Creator[W]; Contract=AssetRegistry[SC];
관리자는 아래 데이터 구조를 통해 하나 이상의 당사자를 운영자로 지정할 수 있다.
Attribute=AssetRole; Key=Asset[ID], Operator[W]; Value=Operator;
Source=Manager[W]; Contract=AssetRegistry[SC];
원한다면, 생성자는 계약 당 하나의 인증자만이 존재하도록 속성 관리 시스템을 제한하는 정책을 배포할 수 있다. 인증자가 개발자 이외의 당사자인 경우, 인증자는 ContractRoleEligible 인증자 증명(역할)을 가져야 한다. 인증자는 역할이 적용되기 전에 지정을 확인해야 한다.
Attribute=AssetRoleEligible; Key=Certifier[W]; Value=Certifier;
Source=AssetRoleAuthorizer[W]; Contract=AttributeRegistry[SC]
Attribute=AssetRole; Key=Asset[ID], Certifier[W]; Value=Certifier;
Source=Manager[W]; Contract=AssetRegistry[SC];
*[W]=월렛 주소, [SC]=스마트 계약 주소, [ID]=레지스트리 식별자
개시된 구현의 프레임워크는, 특정 토큰에 대한 전송 에이전트 권한의 결정을 규제하는 분산형(자체 형성) 정책을 허용하여, 전송 에이전트 권한을 가진 당사자가 자격을 갖추고 있고(법적 권한을 가지며 책임을 완전히 인지하고 있음) 신뢰할 수 있음을 이해관계자에게 보장한다. 토큰 발행자(Token Issuer) 또는 전송 에이전트(Transfer Agent)와 같은 주요 기능을 수행할 당사자를 인증하는 코어 권한 부여자(core authorizers)를 지정하기 위해, 생태계 이해관계자에 의해 속성이 생성되고 관리될 수 있다. 특정 토큰에 대한 이해관계자는, 전송 에이전트 역할을 보유하도록 인증된 당사자에게 전송 에이전트 권한을 지정하거나 재지정할 수 있다.
그러나, 자신의 권한 및 자신이 소유한 가치에 영향을 미치는 당사자의 권한을 이해하고자 하는 일반 참가자에게 개방형(open-ended) 프레임워크의 힘은 지나치게 강력할 수 있다. 권한이 명료하고 접근 가능하지 않으면, 사용자는 예상하거나 이해하지 못한 방식으로 자신의 권한이 영향을 받는 트랜잭션에 무심코 참여할 수도 있다. 상기 프레임워크는, 예를 들어 통상적인 투자 패턴을 제공하는 권한을 묶은 패키지와 같은 전략(Strategies)을 포함한다. 이는 참가자가 자신이 선호하고 편한 권한 패키지를 찾을 수 있도록 하는 간단한 모델을 제공한다. 예를 들어, 권한 패키지는 자기주권적이고, 관리될 수 있다.
주어진 컴퓨팅 장치는 컴퓨터 프로그램 모듈을 실행하도록 구성된 하나 이상의 프로세서를 포함할 수 있다. 컴퓨터 프로그램 모듈은, 주어진 컴퓨팅 플랫폼과 연관된 사용자 또는 전문가가 시스템 및/또는 외부 리소스와 인터페이스(interface)할 수 있도록 구성될 수 있다. 비제한적인 예로서, 주어진 컴퓨팅 플랫폼은, 서버, 데스크탑 컴퓨터, 랩탑 컴퓨터, 핸드헬드(hand-held) 컴퓨터, 태블릿 컴퓨팅 플랫폼, 스마트폰, 게임 콘솔, 및/또는 기타 컴퓨팅 플랫폼 중 하나 이상을 포함할 수 있다.
다양한 데이터 및 코드는, 정보를 전자적으로 저장하는 비일시적(non-transitory) 저장 매체를 포함할 수 있는 전자 저장 장치에 저장될 수 있다. 전자 저장 장치의 전자 저장 매체는, 컴퓨팅 장치와 일체로 제공되는(즉, 실질적으로 제거 불가능한) 시스템 저장 장치 및/또는, 예를 들면 포트(예: USB 포트, 파이어와이어 포트 등) 또는 드라이브(예: 디스크 드라이브 등)를 통하여 컴퓨팅 장치에 제거 가능하게 연결되는 제거 가능한 저장 장치를 포함할 수 있다. 전자 저장 장치는, 광학적으로 판독 가능한 저장 매체(예: 광학 디스크 등), 자기적으로 판독 가능한 저장 매체(예: 자기 테이프, 자기 하드 드라이브, 플로피 드라이브 등), 전하 기반 저장 매체(예: EEPROM, RAM 등), 반도체 저장 매체(예: 플래시 드라이브 등), 및/또는 기타 전자적으로 판독 가능한 저장 매체 중 하나 이상을 포함할 수 있다.
컴퓨팅 장치의 프로세서는 정보 처리 능력을 제공하도록 구성될 수 있으며, 디지털 프로세서, 아날로그 프로세서, 정보를 처리하도록 설계된 디지털 회로, 정보를 처리하도록 설계된 아날로그 회로, 상태 기계, 및/또는 정보를 전자적으로 처리하기 위한 기타 메커니즘 중 하나 이상을 포함할 수 있다. 본 명세서에서 사용된 "모듈"이라는 용어는, 상기 모듈에 주어진 기능을 수행하는 임의의 구성 요소 또는 구성 요소 집합을 지칭할 수 있다. 이는 프로세서 판독 가능 인스트럭션 실행 중의 하나 이상의 물리적 프로세서, 프로세서 판독 가능 인스트럭션, 회로, 하드웨어, 저장 매체, 또는 임의의 기타 구성 요소를 포함할 수 있다.
본 기술은 현재 본 기술의 가장 실용적이고 바람직한 것으로 간주되는 구현에 기반하여 예시의 목적으로 상세하게 설명되었으나, 그러한 세부사항은 오로지 예시의 목적을 위한 것이며 본 기술은 개시된 구현에 한정되지 않고, 오히려 첨부된 청구범위의 사상 및 범위 내에서 수정 및 상응하는 배열을 포함하도록 의도된다. 예를 들어, 본 기술은 가능한 범위 내에서 어느 구현의 하나 이상의 특징이 다른 어느 구현의 하나 이상의 특징과 조합될 수 있음을 고려한다는 점이 이해되어야 한다.
구현 및 예시가 도시되고 설명되었지만, 본 발명은 본 명세서에 개시된 정확한 구조 및 구성요소에만 한정되지 않음이 이해되어야 한다. 첨부된 청구범위에 정의된 본 발명의 사상 및 범위를 벗어나지 않고, 본 명세서에 개시된 방법 및 장치의 배열, 작동, 및 세부 사항에 대하여 다양한 수정, 변경, 및 변형이 이루어질 수 있다.
코드 부록
pragma solidity >=0.8.0 <0.9.0;
/**
* @title Bytes Helper
* @dev Different operations with bytes
*/
library BytesHelper {
/**
* @notice Get pointer to bytes array
* @param bts Bytes array
*/
function _getPointer(bytes memory bts) internal pure returns (uint) {
uint ptr;
assembly {
ptr := bts
}
return ptr;
}
// MARK: - Bytes32 conversions
/**
* @notice Cut first 32 bytes
* @param data Bytes array
*/
function _bytesToBytes32(bytes memory data) internal pure returns (bytes32 result) {
assembly {
result := mload(add(data, 0x20))
}
}
/**
* @notice Convert address type to the bytes type
* @param addr Address to convert
*/
function _addressToBytes32(address addr) internal pure returns (bytes32 bts){
return bytes32(uint(uint160(addr)) << 96);
}
/**
* @notice Change bytes to upper case
* @param data Bytes
*/
function _bytes32toUpper(bytes32 data) internal pure returns (bytes32 bts) {
bytes32 pointer;
assembly {
pointer := mload(0x40)
}
bytes memory baseBytes = new bytes(32);
for (uint i = 0; i < 32; i++) {
bytes1 b1 = data[i];
if (b1 >= 0x61 && b1 <= 0x7A) {
b1 = bytes1(uint8(b1)-32);
}
baseBytes[i] = b1;
}
assembly { bts := mload(add(pointer,0x20)) }
}
/**
* @notice Bytes32 value to address
* @param value Value to be converted to address
*/
function _bytes32ToAddress(bytes32 value) internal pure returns (address) {
return address(uint160(uint(value) >> 96));
}
/**
* @notice Extract 32 bytes from the bytes array by provided offset
* @param input Input bytes
* @param offset Offset from which will be extracted 32 bytes
*/
function _extractBytes32(bytes memory input, uint offset) internal pure returns (bytes32 result) {
require(
offset + 32 <= input.length,
"_extractBytes32: Wrong offset"
);
assembly {
result := mload(add(add(0x20, input), offset))
}
}
/**
* @notice Calculates length without empty bytes
* @param x Value
*/
function _countPureLengthForBytes32(bytes32 x) internal pure returns (uint) {
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
bytes1 char = bytes1(bytes32(uint(x) << j * 8));
if (char != 0) {
charCount++;
}
}
return charCount;
}
/**
* @notice removes empty bytes from the 32 bytes value
* @param x Value to be converted
* @return trimmed value bytes
*/
function _trimBytes32(bytes32 x) internal pure returns (bytes memory) {
bytes memory bytesArray = new bytes(32);
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
bytes1 char = bytes1(bytes32(uint(x) << j * 8));
if (char != 0) {
bytesArray[charCount] = char;
charCount++;
}
}
bytes memory bytesTrimmed = new bytes(charCount);
for (uint j = 0; j < charCount; j++) {
bytesTrimmed[j] = bytesArray[j];
}
return bytesTrimmed;
}
// MARK: - Address conversions
/**
* @notice Convert address type to the bytes type
* @param addr Address to convert
*/
function _addressToBytesPacked(address addr) internal pure returns (bytes memory bts){
assembly {
let m := mload(0x40)
mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, addr))
mstore(0x40, add(m, 52))
bts := m
}
}
/**
* @notice bytes value to address
* @param value Value to be converted to address
*/
function _bytesToAddress(bytes memory value) internal pure returns (address) {
return address(uint160(uint(_bytesToBytes32(value)) >> 96));
}
// MARK: - Uint256 conversions
/**
* @notice Cut first 32 bytes and converts it to uint
* @param data Bytes array
*/
function _bytesToUint(bytes memory data) internal pure returns (uint result) {
assembly {
result := mload(add(data, 0x20))
}
}
// MARK: - Boolean conversion
/**
* @notice Converts boolean to bytes
* @param value Boolean value
*/
function _boolToBytes(bool value) internal pure returns (bytes[] memory) {
bytes[] memory bvalue = new bytes[](1);
bvalue[0] = abi.encodePacked(value);
return bvalue;
}
// MARK: - Uint8 conversions
/**
* @notice Converts bytes to uint8
* @param data Bytes array
* @param start Start index
*/
function _bytesToUint8(bytes memory data, uint start) internal pure returns (uint8 result) {
require(data.length >= (start + 1), "_bytesToUint8: Wrong start");
assembly {
result := mload(add(add(data, 0x1), start))
}
}
/**
* @notice Converts bytes to uint8 unsafely
* @param data Bytes array
*/
function _bytesToUint8UNSAFE(bytes memory data) internal pure returns (uint8 result) {
assembly {
result := mload(add(data, 0x1))
}
}
// MARK: - Int256 conversions
/**
* @notice Cut first 32 bytes and converts it to int
* @param data Bytes array
*/
function _bytesToInt(bytes memory data) internal pure returns (int result) {
assembly {
result := mload(add(data, 0x20))
}
}
// MARK: - Slices
/**
* @notice Returns slice from bytes with fixed length
* @param data Bytes array
* @param start Start index
* @param length Slice length
*/
function _getSlice(
bytes memory data,
uint start,
uint length
)
internal
pure
returns (bytes memory result)
{
require(data.length >= (start + length), "_getSlice: Wrong start or length");
assembly {
switch iszero(length)
case 0 {
result := mload(0x40)
let lengthmod := and(length, 31)
let mc := add(add(result, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, length)
for {
let cc := add(add(add(data, lengthmod), mul(0x20, iszero(lengthmod))), start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(result, length)
mstore(0x40, and(add(mc, 31), not(31)))
}
default {
result := mload(0x40)
mstore(0x40, add(result, 0x20))
}
}
}
/**
* @notice Returns slice of bytes32 array from range
* @param data Bytes32 array
* @param start Start index
* @param end End index
*/
function _getSliceBytes32(
bytes32[] memory data,
uint start,
uint end
)
internal
pure
returns (bytes32[] memory)
{
bytes32[] memory result = new bytes32[](end - start);
for(uint i = 0; i < end - start; i++){
result[i] = data[i + start];
}
return result;
}
/**
* @notice Returns slice from range
* @param data Bytes array
* @param start Start index
* @param end End index
*/
function _getSlice(
bytes[] memory data,
uint start,
uint end
)
internal
pure
returns (bytes[] memory)
{
bytes[] memory result = new bytes[](end - start);
for(uint i = 0; i < end - start; i++){
result[i] = data[i + start];
}
return result;
}
// MARK: - ASCII conversions
/**
* @notice Converts 32 bytes to uint
* @param data Bytes array
*/
function _ASCIIBytesToUint(bytes memory data) internal pure returns (uint result) {
require(
data.length <= 32,
"_ASCIIBytesToUint: Overflow"
);
for (uint i = 0; i < data.length; i++) {
uint char = uint(uint8(data[i]));
require(
char >= 48 && char <= 57,
"_ASCIIBytesToUint: Wrong char"
);
result = result * 10 + (char - 48);
}
return result;
}
/**
* @notice Original Copyright (c) 2015-2016 Oraclize SRL
* @notice Original Copyright (c) 2016 Oraclize LTD
* @notice Modified Copyright (c) 2020 SECURRENCY INC.
* @dev Converts an unsigned integer to its bytes representation
* @notice https://github.com/provable-things/ethereum-api/blob/master/oraclizeAPI_0.5.sol#L1045
* @param num The number to be converted
* @return Bytes representation of the number
*/
function _uintToASCIIBytes(uint num) internal pure returns (bytes memory) {
uint _i = num;
if (_i == 0) {
return "0";
}
uint j = _i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
while (_i != 0) {
bstr[len - 1] = bytes1(uint8(48 + _i % 10));
_i /= 10;
len--;
}
return bstr;
}
/**
* @notice Modified version from https://gitter.im/ethereum/solidity?at=56b085b5eaf741c118d65198
* @notice In the original there is no necessary conversion in ASCII. Also added leading 0x
*/
function _addressToASCIIBytes(address addr) internal pure returns (bytes memory) {
bytes memory res = new bytes(40);
for (uint i = 0; i < 20; i++) {
uint8 symbol = uint8(uint(uint160(addr)) >> 8 * (19 - i));
(bytes1 high, bytes1 low) = _uint8ConvertToAscii(symbol);
res[2 * i] = high;
res[2 * i + 1] = low;
}
return abi.encodePacked("0x", res);
}
/**
* @notice Converts bytes32 array to ASCII Bytes
*/
function _bytes32ArrayToASCIIBytes(bytes32[] memory input) internal pure returns (bytes memory) {
bytes memory result;
uint length = input.length;
if (length > 0) {
result = abi.encodePacked(_bytes32ToASCIIBytes(input[0]));
for (uint i = 1; i < length; i++) {
result = abi.encodePacked(
result,
", ",
_bytes32ToASCIIBytes(input[i])
);
}
}
return result;
}
/**
* @notice Modified version from https://gitter.im/ethereum/solidity?at=56b085b5eaf741c118d65198
* @notice In the original there is no necessary conversion in ASCII. Also added leading 0x
*/
function _bytes32ToASCIIBytes(bytes32 input) internal pure returns (bytes memory) {
bytes memory res = new bytes(64);
for (uint i = 0; i < 32; i++) {
uint8 symbol = uint8(uint(input) >> 8 * (31 - i));
(bytes1 high, bytes1 low) = _uint8ConvertToAscii(symbol);
res[2 * i] = high;
res[2 * i + 1] = low;
}
return abi.encodePacked("0x", res);
}
// MARK: - Strings
/**
* @notice takes an array of strings and a separator
* @notice and merge all strings into a single string
* @param strArray, array containing all the strings to be concatenated
* @param separator, separator to place between each concatenation
* @return res string merged with separators
*/
function _append(string[] memory strArray, string memory separator) internal pure returns (string memory res) {
uint length = strArray.length;
if(length > 0) {
for(uint i = 0; i < length; i++) {
if(i == 0) {
res = string(abi.encodePacked(res, strArray[i]));
} else {
res = string(abi.encodePacked(res, separator, strArray[i]));
}
}
} else {
res = "";
}
}
/**
* @notice takes an array of bytes and a separator
* @notice and merge all bytes into a single string
* @param bytesArray, array containing all the bytes to be concatenated
* @param separator, separator to place between each concatenation
* @return res string merged with separators
*/
function _append(bytes[] memory bytesArray, string memory separator) internal pure returns (string memory res) {
uint length = bytesArray.length;
if(length > 0) {
for(uint i = 0; i < length; i++) {
if(i == 0){
res = string(abi.encodePacked(res, string(bytesArray[i])));
} else {
res = string(abi.encodePacked(res, separator, string(bytesArray[i])));
}
}
} else {
res = "";
}
}
// MARK: - Bytes[] values conversions
/**
* @notice Converts bytes[] value to uint[]
* @param valuesArray Value to convert
* @return uint[] value
*/
function _toUint(bytes[] memory valuesArray) internal pure returns (uint[] memory) {
uint length = valuesArray.length;
uint[] memory result = new uint[](length);
for (uint i = 0; i < length; i++) {
result[i] = _bytesToUint(valuesArray[i]);
}
return result;
}
/**
* @notice Converts bytes[] value to int[]
* @param valuesArray Value to convert
* @return int[] value
*/
function _toInt(bytes[] memory valuesArray) internal pure returns (int[] memory) {
uint length = valuesArray.length;
int[] memory result = new int[](length);
for (uint i = 0; i < length; i++) {
result[i] = _bytesToInt(valuesArray[i]);
}
return result;
}
/**
* @notice Converts bytes[] value to bool[]
* @param valuesArray Value to convert
* @return bool[] value
*/
function _toBoolean(bytes[] memory valuesArray) internal pure returns (bool[] memory) {
uint length = valuesArray.length;
bool[] memory result = new bool[](length);
for (uint i = 0; i < length; i++) {
require(
valuesArray[i].length == 1,
"_toBoolean: Wrong values length"
);
result[i] = (valuesArray[i][0] == bytes1(0x01)) ? true : false;
}
return result;
}
/**
* @notice Converts bytes[] value to bytes32[]
* @param valuesArray Value to convert
* @return bytes32[] value
*/
function _toBytes32(bytes[] memory valuesArray) internal pure returns (bytes32[] memory) {
uint length = valuesArray.length;
bytes32[] memory result = new bytes32[](length);
for (uint i = 0; i < length; i++) {
result[i] = _bytesToBytes32(valuesArray[i]);
}
return result;
}
/**
* @notice Converts bytes[] value to string[]
* @param valuesArray Value to convert
* @return string[] value
*/
function _toString(bytes[] memory valuesArray) internal pure returns (string[] memory) {
uint length = valuesArray.length;
string[] memory result = new string[](length);
for (uint i = 0; i < length; i++) {
result[i] = string(valuesArray[i]);
}
return result;
}
// MARK: - Comparison
/**
* @return bool - true if values are equal
* @param expectedValue Value expected
* @param gotValue Value got
*/
function _isEqual(bytes[] memory expectedValue, bytes[] memory gotValue) internal pure returns (bool) {
return keccak256(abi.encode(expectedValue)) == keccak256(abi.encode(gotValue));
}
// MARK: - Privates
/**
* @notice Converts symbol to acsii
*/
function _uint8ConvertToAscii(uint8 symbol) private pure returns (bytes1 high, bytes1 low) {
uint8 h = symbol / 16;
uint8 l = symbol - 16 * h;
high = h < 10 ? bytes1(h + 0x30) : bytes1(h + 0x57);
low = l < 10 ? bytes1(l + 0x30) : bytes1(l + 0x57);
}
}
// Stored package structure
struct Package {
address packageAddress;
string version;
}
// Stored component structure
struct StoredComponent {
bytes32 componentId;
uint currentPackageId;
}
// Stored component structure
struct StoredPackages {
Package[] packages;
mapping(address => uint) packagesIndexes;
}
// Struct for butch update
struct BatchUpdateDetails {
address updatePackage;
string version;
}
// Verification status enum
enum VerificationStatus {
Pending,
Done
}
/**
* @title Compliance Oracle property keys
*/
contract ComplianceOraclePropertyKeys {
bytes32 constant WALLET = keccak256("wallet");
bytes32 constant DESTINATION_WALLET = keccak256("destination.wallet");
bytes32 constant SPENDER = keccak256("spender.wallet");
bytes32 constant AMOUNT = keccak256("tx.details.amount");
bytes32 constant TOKEN = keccak256("token");
bytes32 constant SMART_CONTRACT = keccak256("smart.contract");
bytes32 constant PARTITION = keccak256("partition");
bytes32 constant ACTION = keccak256("action");
bytes32 constant SUB_ID = keccak256("sub.id");
bytes32 constant TARGET_SMART_CONTRACT = keccak256("traget.smart.contract");
}
/**
* @title Default Compliance Oracle
*/
contract ComplianceOracleDefaults {
string constant internal CONTEXT_MANAGER = "Context manager";
bytes32 constant internal CONTEXT_MANAGER_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, CONTEXT_MANAGER));
string constant internal SOURCE_MANAGER = "Source manager";
bytes32 constant internal SOURCE_MANAGER_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, SOURCE_MANAGER));
string constant internal PROPERTY_MANAGER = "Property manager";
bytes32 constant internal PROPERTY_MANAGER_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, PROPERTY_MANAGER));
string constant internal DEFAULT_CONTEXT = "Securrency";
bytes32 constant internal DEFAULT_CONTEXT_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT));
string constant internal DEFAULT_SOURCE = "Internal storage";
bytes32 constant internal DEFAULT_SOURCE_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, DEFAULT_SOURCE));
string constant internal ROOT_L1_ROLE = "RootL1";
bytes32 constant internal ROOT_L1_ROLE_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, ROOT_L1_ROLE));
string constant internal ROOT_L2_ROLE = "RootL2";
bytes32 constant internal ROOT_L2_ROLE_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, ROOT_L2_ROLE));
string constant internal SYSTEM_ROLE = "System";
bytes32 constant internal SYSTEM_ROLE_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, SYSTEM_ROLE));
string constant internal DEFAULT_COMPONENT_MANAGER_ROLE = "Default component manager";
bytes32 constant internal DEFAULT_COMPONENT_MANAGER_ROLE_ID = keccak256(abi.encodePacked(DEFAULT_CONTEXT_ID, DEFAULT_COMPONENT_MANAGER_ROLE));
}
/**
* @notice Operation types
*/
enum OperationType {
Create,
Update,
Disable,
Enable,
Remove
}
/**
* @notice Source types
*/
enum SourceType {
Storage,
External,
Policy,
Direct
}
/**
* @notice Data types
*/
enum DataType {
None,
Uint,
Int,
String,
Boolean,
Bytes32
}
/**
* @notice Policy result
*/
enum PolicyResult {
Pending,
True,
False
}
/**
* @notice Context metadata input
*/
struct ContextInput {
// [required] context name
string name;
// [optional] additional description of the action
string description;
// [optional] parent context identifier
bytes32 parentId;
}
/**
* @notice Context metadata
*/
struct ContextMetadata {
// [required] context name
string name;
// [optional] parent context identifier
bytes32 parentId;
// [optional] marks if the context is default or not
bool isDefault;
}
/**
* @notice Context enumerable
*/
struct ContextEnumerable {
// [auto increment] shows the context depth in its inheritance tree
uint depth;
// [auto increment] shows how many entities are used this context as a parent
uint childrenTotal;
// [auto increment] shows how many properties are added for this context
uint propertiesTotal;
// [auto increment] shows how many sources are added for this context
uint sourcesTotal;
// [automatically] last modification timestamp
uint modifiedAt;
// [optional] marks if the context is disabled or not
bool isDisabled;
}
/**
* @notice Source creation and update input
*/
struct SourceInput {
// [required] source name
string name;
// [required] context id
bytes32 contextId;
// [required] source type
SourceType sourceType;
// [optional] source contract address
// - for Attestation source type
address source;
// [optional] policy bytecode for policy contract deployment
// - for Policy source type
bytes policyBytecode;
// [optional] source url
// - for Api source type
string url;
// [optional] json format
// - for Api source type
string jsonFormat;
// [optional] additional description of the action
string description;
}
/**
* @notice Source metadata
*/
struct SourceMetadata {
// [required] source name
string name;
// [required] context id
bytes32 contextId;
// [required] source type
SourceType sourceType;
// [optional] policy bytecode hash
// - for Policy source type
bytes32 policyBytecodeHash;
// [optional] source contract address - both
// - for Attestation source type
address source;
// [optional] policy address
// - for Policy source type
address policyAddress;
// [optional] marks if the source is default or not
bool isDefault;
// [optional] source url
// - for Api source type
string url;
// [optional] json format
// - for Api source type
string jsonFormat;
}
/**
* @notice Source enumberable data
*/
struct SourceEnumerable {
// [auto increment] shows how many properties are added for this source
uint propertiesTotal;
// [automatically] last modification timestamp
uint modifiedAt;
// [optional] marks if the source is disabled or not
bool isDisabled;
}
/**
* @notice Property metadata input
*/
struct PropertyInput {
// [required] property name
string name;
// [required] context id
bytes32 contextId;
// [required] data source identifier
bytes32 sourceId;
// [required] data type
DataType dataType;
// [optional] parent property id
bytes32 parentId;
// [optional] external identifier (unique for context, not for storage)
bytes32 externalId;
// [optional] marks if the property is constant or not
// - it will allow to create properties with unchangable value
bool isConstant;
// [optional] additional description of the action
string description;
}
/**
* @notice Property metadata
*/
struct PropertyMetadata {
// [required] property name
string name;
// [required] context id
bytes32 contextId;
// [required] data source identifier
bytes32 sourceId;
// [required] data type
DataType dataType;
// [optional] parent property id
bytes32 parentId;
// [optional] external identifier (unique for context, not for storage)
bytes32 externalId;
// [optional] marks if the property is constant or not
// - it will allow to create properties with unchangable value
bool isConstant;
// [optional] marks if the property is default or not
bool isDefault;
}
/**
* @notice Property enumberable data
*/
struct PropertyEnumerable {
// [auto increment] shows how many entities are used this property as a parent
uint childrenTotal;
// [auto increment] shows how many values are added for this property
uint valuesTotal;
// [automatically] last modification timestamp
uint modifiedAt;
// [optional] marks if the property is disabled or not
bool isDisabled;
}
/**
* @notice Property accessability data
*/
struct PropertyAccessible {
// [optional] keys that will be used for values searching:
// source wallet, destination wallet, token, etc.
bytes32[] keys;
// [optional] expiration time for the property attestation
uint expirationTime;
// [optional] can configure behavior when someone wants to refuse attestation
address onRefuse;
}
/**
* @notice Internal values struct
*/
struct Value {
bytes[] value;
uint timestamp;
}
/**
* @notice Struct of external call
*/
struct ExternalCall {
// External call property identifier
bytes32 propertyId;
// External calls session id - for external calls in policies
bytes32 sessionId;
// Callback address for external call
address callbackAddress;
// Flag - true when result hasn't been obtained yet
bool pending;
// External call result
string result;
}
/**
* @notice Struct of external calls session
*/
struct ExternalCallsSession {
// Policy property identifier
bytes32 policyPropertyId;
// Increases when first policy evaluated, decreases when external
// calls are obtained, then increases again during policy reprocessing
uint externalCallsCounter;
// Total number of external calls in policy (including subpolicies)
uint externalCallsTotal;
// By order of evaluating in policy
bytes32[] externalCallsIds;
// External calls session callback address
address callbackAddress;
// When last external call result obtained - policy starts reprocessing
bool isReprocessing;
}
/**
* @notice Struct to store values in storage in batch
*/
struct BatchSetValueDetails {
bytes[] value;
bytes32 propertyId;
bytes32[] keysValues;
}
/**
* @notice Struct to delete values from storage in batch
*/
struct BatchDeleteValueDetails {
bytes32 propertyId;
bytes32[] keysValues;
}
// Permission structure - property id and its extpected value
struct Permission {
bytes32 propertyId;
bytes[] expectedValue;
}
/*
ORACLIZE_API
Copyright (c) 2015-2016 Oraclize SRL
Copyright (c) 2016 Oraclize LTD
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
abstract contract OraclizeAddrResolverI {
function getAddress() public virtual returns (address _address);
}
/*
ORACLIZE_API
Copyright (c) 2015-2016 Oraclize SRL
Copyright (c) 2016 Oraclize LTD
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
abstract contract OraclizeI {
address public cbAddress;
function setProofType(bytes1 _proofType) external virtual;
function setCustomGasPrice(uint _gasPrice) external virtual;
function getPrice(string memory _datasource) public virtual returns (uint _dsprice);
function randomDS_getSessionPubKeyHash() external view virtual returns (bytes32 _sessionKeyHash);
function getPrice(string memory _datasource, uint _gasLimit) public virtual returns (uint _dsprice);
function queryN(uint _timestamp, string memory _datasource, bytes memory _argN) public payable virtual returns (bytes32 _id);
function query(uint _timestamp, string calldata _datasource, string calldata _arg) external payable virtual returns (bytes32 _id);
function query2(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) public payable virtual returns (bytes32 _id);
function query_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg, uint _gasLimit) external payable virtual returns (bytes32 _id);
function queryN_withGasLimit(uint _timestamp, string calldata _datasource, bytes calldata _argN, uint _gasLimit) external payable virtual returns (bytes32 _id);
function query2_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg1, string calldata _arg2, uint _gasLimit) external payable virtual returns (bytes32 _id);
}
/**
* @title Interface of the Rules Engine events
*/
interface IRulesEngineEvents {
/**
* @notice Write info to the log when action handler was changed
*/
event ActionHandlerChanged(
address indexed smartContract,
bytes32 indexed action,
address handler
);
/**
* @notice Write info to the log when action permission properrty was changed
*/
event ActionPermissionPropertyChanged(
address indexed smartContract,
bytes32 indexed action,
bytes32 indexed subId,
bytes32 propertyId
);
}
/**
* @title Interface of the Rules Engine actions permissions
*/
interface IRulesEngineActionsPermissions {
/**
* @notice Set address of the action handler
* @param smartContract Smart contract address
* @param action Action
* @param subId Addition identifier (may be empty)
* @param permission Permission to set
*/
function setActionPermission(
address smartContract,
bytes32 action,
bytes32 subId,
Permission calldata permission
)
external;
/**
* @notice Set permission for multiple actions for the sender
* @param actionsList List of the actions
* @param subId Sub identifier
* @param permission Permission
*/
function setMultipleActionsPermission(
bytes32[] calldata actionsList,
bytes32 subId,
Permission calldata permission
)
external;
/**
* @return action permission
* @param smartContract Smart contract address
* @param action Action to be handled
*/
function getActionPermission(
address smartContract,
bytes32 action,
bytes32 subId
)
external
view
returns (Permission memory);
/**
* @return permission - action permission
* @return exists - flag - true if action permission exists
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
*/
function safeGetActionPermission(
address smartContract,
bytes32 action,
bytes32 subId
)
external
view
returns (Permission memory permission, bool exists);
/**
* @notice Calculates permission verification price
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
* @param gasPrice Gas price
* @return permission verification price
*/
function getPermissionVerificationPrice(
address smartContract,
bytes32 action,
bytes32 subId,
uint gasPrice
)
external
view
returns (uint);
/**
* @notice Returns external calls total number for permission
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
* @return permission external calls total number
*/
function getPermissionExternalCallsTotal(
address smartContract,
bytes32 action,
bytes32 subId
)
external
view
returns (uint);
}
/**
* @title Compliance oracle storage batch setters interface
*/
interface IComplianceOracleStorageBatchSetters {
/**
* @notice Stores Properties values if it is the first time in batch
* @param valuesDetails Values by properties with keys to be stored
*/
function batchCreateValues(BatchSetValueDetails[] calldata valuesDetails) external;
/**
* @notice Updates stored Properties values in batch
* @param valuesDetails Values by properties with keys to be stored
*/
function batchUpdateValues(BatchSetValueDetails[] calldata valuesDetails) external;
/**
* @notice Removes stored Properties values in batch
* @param valuesDetails Values by properties with keys to be deleted
*/
function batchRemoveValues(BatchDeleteValueDetails[] calldata valuesDetails) external;
}
/**
* @title Compliance oracle storage roles setters interface
*/
interface IComplianceOracleStorageRolesSetters {
/**
* @notice Creates Role value
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
*/
function setRole(
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] calldata additionalKeysValues
)
external;
/**
* @notice Updates Role value
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be updated
* @param additionalKeysValues Additional property keys values
*/
function updateRole(
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] calldata additionalKeysValues
)
external;
/**
* @notice Removes stored Role value
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be removed
* @param additionalKeysValues Additional property keys values
*/
function removeRole(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] calldata additionalKeysValues
)
external;
/**
* @notice Sender removes role from herself, initiating onRefuse action
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param additionalKeysValues Additional property keys values
*/
function refuseFromRole(
bytes32 propertyId,
bytes32 subId,
bytes32[] calldata additionalKeysValues
)
external;
}
/**
* @title Compliance oracle storage setters interface
*/
interface IComplianceOracleStorageSetters {
/**
* @notice Stores Property value if it is the first time
* @param value Value to be stored
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function createValue(
bytes[] calldata value,
bytes32 propertyId,
bytes32[] calldata propertyKeysValues
)
external;
/**
* @notice Updates stored Property value
* @param value Value to be stored
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function updateValue(
bytes[] calldata value,
bytes32 propertyId,
bytes32[] calldata propertyKeysValues
)
external;
/**
* @notice Removes stored Property value
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function removeValue(
bytes32 propertyId,
bytes32[] calldata propertyKeysValues
)
external;
}
/**
* @title Compliance oracle storage getters interface
*/
interface IComplianceOracleValuesGetters {
/**
* @notice Requests attestation property
* @notice if property has external data source
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function requestValue(
bytes32 propertyId,
bytes32[] calldata inputKeys,
bytes32[] calldata inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
external
payable
returns (
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
);
/**
* @notice Accepts callback from the oracles
* @param id Request identifier
* @param result External call responce
*/
function __callback(bytes32 id, string calldata result) external;
/**
* @notice Returns Property value
* @notice if property has a storage or direct data source
* @param propertyId [required] Property unique id
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function getValue(
bytes32 propertyId,
bytes32[] calldata inputKeys,
bytes32[] calldata inputKeysValues
)
external
view
returns (
DataType valueDataType,
Value memory value
);
/**
* @notice Returns Storage Property value
* @notice if property has a storage source
* @param propertyId [required] Property unique id
* @param keysValues [optional] Already calculated keys values
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function getStorageValue(
bytes32 propertyId,
bytes32[] calldata keysValues
)
external
view
returns (
DataType valueDataType,
Value memory value
);
/**
* @notice Returns Role value
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
* @return value Value of the property
*/
function getRole(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] calldata additionalKeysValues
)
external
view
returns (Value memory);
}
/**
* @title Compliance oracle constants
*/
contract ComplianceOracleConstants {
// Component id
bytes32 constant internal COMPONENT_ID = keccak256("ComplianceOracle");
// Max possible depth level
uint constant internal MAX_DEPTH = 4;
// Oraclize request gas limit
uint constant internal ORACLIZE_GAS_LIMIT = 20000;
// Oraclize network id
uint8 constant internal networkID_auto = 0;
// Oraclize request data source
string constant internal ORACLIZE_DATA_SOURCE = "URL";
// Oraclize url prefix
bytes constant internal _jsonPrefix = "json(";
}
/**
* @title Compliance oracle storage
*/
contract ComplianceOracleStorage is ComplianceOracleConstants {
// Last external calls session
uint internal _lastExternalCallsSession = 0;
// Current create context request identifier
uint internal _currentCreateContextRequestId = 0;
// oraclize
OraclizeI internal _oraclize;
// oraclize address resolver
OraclizeAddrResolverI internal _oraclizeAddressResolver;
// Updates repository address
address internal _updatesRepositoryAddress;
// Rules engine address
address internal _rulesEngineAddress;
// Oraclize network name
string internal oraclize_network_name;
// Context requests
mapping(uint => bytes32) internal _createContextsRequests;
// Properties contexts metadata
mapping(bytes32 => ContextMetadata) internal _contextsMetadata;
// Properties contexts enumerable data
mapping(bytes32 => ContextEnumerable) internal _contextsEnumerable;
// Properties sources metadata
mapping(bytes32 => SourceMetadata) internal _sourcesMetadata;
// Properties sources enumerable data
mapping(bytes32 => SourceEnumerable) internal _sourcesEnumerable;
// Properties metadatas
mapping(bytes32 => PropertyMetadata) internal _propertiesMetadata;
// Properties enumerable data
mapping(bytes32 => PropertyEnumerable) internal _propertiesEnumerable;
// Properties accessible data
mapping(bytes32 => PropertyAccessible) internal _propertiesAccessible;
// Properties identifiers by external identifiers in context
mapping(bytes32 => mapping(bytes32 => bytes32)) internal _propertiesIdsByExternal;
// Properties values by keys
mapping(bytes32 => Value) internal _propertiesValues;
// Policy sessions
mapping(bytes32 => ExternalCallsSession) internal _externalCallsSessions;
// External calls
mapping(bytes32 => ExternalCall) internal _externalCalls;
// Oraclize args
mapping(bytes32 => bytes32) internal _oraclize_randomDS_args;
// Oraclize
mapping(bytes32 => bool) internal _oraclize_randomDS_sessionKeysHashVerified;
// Addresses of the methods implementations
mapping(bytes4 => address) internal _methodsImplementations;
}
/**
* @title Compliance Oracle protected verifications common methods
*/
contract ComplianceOracleProtectedVerifications is ComplianceOracleStorage {
// Define libraries
using CommonBasePermissionAction for *;
using CommonRulesEngine for *;
// prefix for the signature of the protected method
bytes private constant PERMISSION = "permission*";
/**
* @notice Verify permission
* @param subId Additional identifier for verification
*/
modifier verifyPermission(bytes32 subId) {
_verifyPermission(
msg.sender,
address(0x00),
address(0x00),
subId,
keccak256(abi.encodePacked(PERMISSION, msg.sig))
);
_;
}
/**
* @notice Verify permission internal method with unsafe handler for defaults
* @param subId Additional identifier for verification
*/
function _verifyPermission(bytes32 subId) internal {
_verifyPermission(
msg.sender,
address(0x00),
address(0x00),
subId,
keccak256(abi.encodePacked(PERMISSION, msg.sig))
);
}
/**
* @notice Verify permission internal method with unsafe handler for defaults
* @param subId Additional identifier for verification
*/
function _verifyPermission(
address sender,
address targetAddress,
address destinationAddress,
bytes32 subId,
bytes32 sig
)
internal
{
address actionHandler = _rulesEngineAddress._tryGetActionHandler(
address(this),
sig,
subId
);
actionHandler._tryVerify(
sender,
destinationAddress,
address(this),
targetAddress,
sig,
subId,
new bytes(0)
);
}
}
/**
* @title Compliance Oracle storage protected verifications
*/
contract ComplianceOracleStorageSettersProtectedVerifications is
ComplianceOraclePropertyKeys,
ComplianceOracleProtectedVerifications
{
// Define libraries
using BytesHelper for *;
using CommonBasePermissionAction for *;
using CommonRulesEngine for *;
// prefix for the signature of the protected method
bytes constant PERMISSION = "permission*";
// 2nd prefix for the signature of the protected method that requires
// destination value address to be set
bytes constant DESTINATION = "destination*";
/**
* @notice Verify permissions for sender and destination
* @param method Requested method
* @param propertyId property identifier
* @param propertyKeysValues Property keys values
*/
modifier verifyValueActionPermissions(
bytes4 method,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
) {
bytes32[] memory keys = _propertiesAccessible[propertyId].keys;
(
address target,
address destination,
bytes32 subId
) = _tryGetSubKeysValues(keys, propertyKeysValues);
subId = subId == bytes32(0x00)
? propertyId
: keccak256(abi.encodePacked(propertyId, subId));
// Verify permission for sender
bytes32 sig = keccak256(abi.encodePacked(PERMISSION, method));
_verifyPermissionSafe(
msg.sender,
target,
address(0x00),
subId,
sig
);
// Verify permission for destination
if (destination != address(0x00)) {
sig = keccak256(abi.encodePacked(PERMISSION, DESTINATION, method));
_verifyPermission(
msg.sender,
target,
destination,
subId,
sig
);
}
_;
}
/**
* @notice Verify permission with safe action handler
* @param sender Transaction sender address
* @param targetAddress Target smart contract address
* @param destinationAddress Permission destination address
* @param subId Additional identifier for verification
* @param sig Action signature
*/
function _verifyPermissionSafe(
address sender,
address targetAddress,
address destinationAddress,
bytes32 subId,
bytes32 sig
)
internal
{
address actionHandler = _rulesEngineAddress._tryGetSafeActionHandler(
address(this),
sig,
subId
);
actionHandler._tryVerify(
sender,
destinationAddress,
address(this),
targetAddress,
sig,
subId,
new bytes(0)
);
}
/**
* @param propertyKeys Property keys
* @param propertyKeysValues Property keys values
* @return target and destination and subId from values
*/
function _tryGetSubKeysValues(
bytes32[] memory propertyKeys,
bytes32[] memory propertyKeysValues
)
internal
pure
returns (
address target,
address destination,
bytes32 subId
)
{
uint keysLength = propertyKeys.length;
require(
keysLength <= propertyKeysValues.length,
"_tryGetSubKeysValues: Not enough values for keys"
);
for (uint i = 0; i < keysLength; i++) {
if (propertyKeys[i] == bytes32(0x00)) break;
if (propertyKeys[i] == TARGET_SMART_CONTRACT) {
target = propertyKeysValues[i]._bytes32ToAddress();
if (
destination != address(0x00)
&& subId != bytes32(0x0)
) {
break;
}
}
if (propertyKeys[i] == DESTINATION_WALLET) {
destination = propertyKeysValues[i]._bytes32ToAddress();
if (
target != address(0x00)
&& subId != bytes32(0x0)
) {
break;
}
}
if (propertyKeys[i] == SUB_ID) {
subId = propertyKeysValues[i];
if (
destination != address(0x00)
&& target != address(0x0)
) {
break;
}
}
}
return (
target,
destination,
subId
);
}
}
/**
* @title Compliance oracle events interface
*/
interface IComplianceOracleEvents {
/**
* @notice Write info about context creation approved
*
* @param requestId Request unique identifier
*/
event CreateContextApproved(uint requestId);
/**
* @notice Write info about context creation accepted
*
* @param requestId Request unique identifier
*/
event CreateContextAccepted(uint requestId);
/**
* @notice Write info about context updates
*
* @param contextId Context unique identifier
* @param parentId Parent property unique identifier
* @param operationType Update context operation type (Created/Updated/Disabled)
* @param description Update description
*/
event ContextUpdated(
bytes32 indexed contextId,
bytes32 indexed parentId,
OperationType operationType,
string description
);
/**
* @notice Write info about source updates
*
* @param sourceId Source unique identifier
* @param contextId Context identifier
* @param operationType Update source operation type (Created/Updated/Disabled)
* @param description Update description
*/
event SourceUpdated(
bytes32 indexed sourceId,
bytes32 indexed contextId,
OperationType operationType,
string description
);
/**
* @notice Write info about property updates
*
* @param propertyId Property unique identifier
* @param contextId Context identifier
* @param parentId Parent property unique identifier
* @param operationType Update property operation type (Created/Updated/Disabled)
* @param description Update description
*/
event PropertyUpdated(
bytes32 indexed propertyId,
bytes32 indexed contextId,
bytes32 indexed parentId,
OperationType operationType,
string description
);
/**
* @notice Write info about property values updates
*
* @param propertyId Property unique id
* @param valueKey Internal storage value key related to policy with keys
* @param operationType Update property values operation type (Created/Updated/Removed)
*/
event PropertyValuesUpdated(
bytes32 indexed propertyId,
bytes32 indexed valueKey,
OperationType operationType
);
/**
* @notice Write info to the log with callback responce
* @param callbackAddress Address where result will be send
* @param callId Request identifier
* @param result External call responce
*/
event CallbackResponce(
address indexed callbackAddress,
bytes32 indexed callId,
string result
);
/**
* @notice Write info to the log with information about value in callback
* @param valueDataType Data type of the property value
* @param value Value of the property
* @param errorCode Error code for policy type
* @param requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
event ValueCallback(
DataType valueDataType,
Value value,
bytes32 errorCode,
bytes32 indexed requestId
);
}
/**
* @title Compliance Oracle permissions setup common methods
*/
contract ComplianceOraclePermissionsSetupCommons is
IComplianceOracleEvents,
ComplianceOracleStorage,
ComplianceOracleDefaults
{
// Define libraries
using ComplianceOraclePropertyCommons for *;
using CommonRulesEngine for *;
using BytesHelper for *;
// prefix for the signature of the protected method
bytes internal constant PERMISSION = "permission*";
/**
* @notice Setups permissions
* @param propertyId Property identifier
* @param subId Sub identifier
* @param value Value expected
* @param methods Methods for permissions list
*/
function _setPermissions(
bytes32 propertyId,
bytes32 subId,
bytes[] memory value,
bytes32[] memory methods
)
internal
{
Permission memory permission = Permission({
propertyId: propertyId,
expectedValue: value
});
_rulesEngineAddress._trySetMultipleActionsPermission(
methods,
subId,
permission
);
}
/**
* @notice Stores Property value by addintional identifier
* @param propertyId Property identifier
* @param subId Additional identifier for property value
* @param manager Property manager address
* @param value Value to be stored
*/
function _setValueBySubId(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes[] memory value
)
internal
{
bytes32[] memory propertyKeysValues = new bytes32[](2);
propertyKeysValues[0] = subId;
propertyKeysValues[1] = manager._addressToBytes32();
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
_propertiesValues[valueKey]._verifyPropertyHasNoValue();
_propertiesValues[valueKey].value = value;
_propertiesValues[valueKey].timestamp = block.timestamp;
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Update
);
}
}
/**
* @title Compliance Oracle context permissions common methods
*/
contract ComplianceOracleContextPermissionsCommons is ComplianceOraclePermissionsSetupCommons {
/**
* @notice Setups context
* @param contextId Context identifier
* @param manager Context manager address
*/
function _setupContext(
bytes32 contextId,
address manager
)
internal
{
bytes[] memory value = new bytes[](1);
value[0] = hex"01";
// Set context manager role
_setValueBySubId(
CONTEXT_MANAGER_ID,
contextId,
manager,
value
);
// Set context manager property manager role
bytes32 subId = keccak256(abi.encodePacked(CONTEXT_MANAGER_ID, contextId));
_setValueBySubId(
PROPERTY_MANAGER_ID,
subId,
manager,
value
);
// Set context manager premissions
bytes32[] memory methods = new bytes32[](6);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).approveCreateContext.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateContext.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).disableContext.selector));
methods[3] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).enableContext.selector));
methods[4] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createSource.selector));
methods[5] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createProperty.selector));
_setPermissions(
CONTEXT_MANAGER_ID,
contextId,
value,
methods
);
// Set context manager property manager permissions
methods = new bytes32[](3);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
_setPermissions(
PROPERTY_MANAGER_ID,
subId,
value,
methods
);
}
}
/**
* @title Compliance Oracle property permissions common methods
*/
contract ComplianceOraclePropertyPermissionsCommons is ComplianceOraclePermissionsSetupCommons {
/**
* @notice Setups property
* @param propertyId Property identifier
* @param manager Property manager address
*/
function _setupProperty(
bytes32 propertyId,
address manager
)
internal
{
bytes[] memory value = new bytes[](1);
value[0] = hex"01";
// Set property manager role
_setValueBySubId(
PROPERTY_MANAGER_ID,
propertyId,
manager,
value
);
// Set property manager property manager role
bytes32 subId = keccak256(abi.encodePacked(PROPERTY_MANAGER_ID, propertyId));
_setValueBySubId(
PROPERTY_MANAGER_ID,
subId,
manager,
value
);
// Set property manager premissions
bytes32[] memory methods = new bytes32[](6);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateProperty.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).disableProperty.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).enableProperty.selector));
methods[3] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[4] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[5] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
_setPermissions(
PROPERTY_MANAGER_ID,
propertyId,
value,
methods
);
// Set manager property property manager permissions
methods = new bytes32[](3);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
_setPermissions(
PROPERTY_MANAGER_ID,
subId,
value,
methods
);
}
}
/**
* @title Compliance Oracle default objects
*/
contract ComplianceOracleDefaultObjects is
IComplianceOracleEvents,
ComplianceOracleStorage,
ComplianceOracleDefaults
{
/**
* @notice Creates property
* @param propertyId Propery id
* @param name Propery name
* @param description Propery update description
* @param keys Property keys
*/
function _createDefaultPropertyRole(
bytes32 propertyId,
string memory name,
string memory description,
bytes32[] memory keys
)
internal
{
_propertiesMetadata[propertyId].name = name;
_propertiesMetadata[propertyId].contextId = DEFAULT_CONTEXT_ID;
_propertiesMetadata[propertyId].sourceId = DEFAULT_SOURCE_ID;
_propertiesMetadata[propertyId].dataType = DataType.Boolean;
_propertiesMetadata[propertyId].isDefault = true;
_propertiesEnumerable[propertyId].modifiedAt = block.timestamp;
_propertiesAccessible[propertyId].keys = keys;
_contextsEnumerable[DEFAULT_CONTEXT_ID].propertiesTotal += 1;
_sourcesEnumerable[DEFAULT_SOURCE_ID].propertiesTotal += 1;
emit PropertyUpdated(
propertyId,
DEFAULT_CONTEXT_ID,
bytes32(0x00),
OperationType.Create,
description
);
}
/**
* @notice Creates Default context
*/
function _createDefaultContext() internal {
_contextsMetadata[DEFAULT_CONTEXT_ID].name = DEFAULT_CONTEXT;
_contextsMetadata[DEFAULT_CONTEXT_ID].isDefault = true;
_contextsEnumerable[DEFAULT_CONTEXT_ID].modifiedAt = block.timestamp;
emit ContextUpdated(
DEFAULT_CONTEXT_ID,
bytes32(0x00),
OperationType.Create,
"Securreny context created"
);
}
/**
* @notice Creates Default source
*/
function _createDefaultSource() internal {
_sourcesMetadata[DEFAULT_SOURCE_ID].name = DEFAULT_SOURCE;
_sourcesMetadata[DEFAULT_SOURCE_ID].contextId = DEFAULT_CONTEXT_ID;
_sourcesMetadata[DEFAULT_SOURCE_ID].source = address(this);
_sourcesMetadata[DEFAULT_SOURCE_ID].sourceType = SourceType.Storage;
_sourcesMetadata[DEFAULT_SOURCE_ID].isDefault = true;
_sourcesEnumerable[DEFAULT_SOURCE_ID].modifiedAt = block.timestamp;
_contextsEnumerable[DEFAULT_CONTEXT_ID].sourcesTotal += 1;
emit SourceUpdated(
DEFAULT_SOURCE_ID,
DEFAULT_CONTEXT_ID,
OperationType.Create,
"Internal storage source created"
);
}
}
/**
* @title Init Compliance Oracle defaults
*/
contract ComplianceOracleInitDefaults is
ComplianceOraclePropertyKeys,
ComplianceOracleContextPermissionsCommons,
ComplianceOraclePropertyPermissionsCommons,
ComplianceOracleDefaultObjects
{
/**
* @notice Setups system defaults
*/
function _initializeDefaults() internal {
_createDefaultContext();
_createDefaultSource();
bytes32[] memory keys = new bytes32[](2);
keys[0] = SUB_ID;
keys[1] = WALLET;
_createContextManagerRole(keys);
_createSourceManagerRole(keys);
_createPropertyManagerRole(keys);
_setupContext(DEFAULT_CONTEXT_ID, msg.sender);
_setupProperty(CONTEXT_MANAGER_ID, msg.sender);
_setupProperty(SOURCE_MANAGER_ID, msg.sender);
_setupProperty(PROPERTY_MANAGER_ID, msg.sender);
_createDCMRole(keys);
}
/**
* @notice Creates context manager role
*/
function _createContextManagerRole(bytes32[] memory keys) internal {
_createDefaultPropertyRole(
CONTEXT_MANAGER_ID,
CONTEXT_MANAGER,
"Context manager created",
keys
);
}
/**
* @notice Creates source manager role
*/
function _createSourceManagerRole(bytes32[] memory keys) internal {
_createDefaultPropertyRole(
SOURCE_MANAGER_ID,
SOURCE_MANAGER,
"Source manager created",
keys
);
}
/**
* @notice Creates property manager role
*/
function _createPropertyManagerRole(bytes32[] memory keys) internal {
_createDefaultPropertyRole(
PROPERTY_MANAGER_ID,
PROPERTY_MANAGER,
"Property manager created",
keys
);
}
/**
* @notice Creates Default component manager role
*/
function _createDCMRole(bytes32[] memory keys) internal {
_createDefaultPropertyRole(
DEFAULT_COMPONENT_MANAGER_ROLE_ID,
DEFAULT_COMPONENT_MANAGER_ROLE,
"Default component manager role created",
keys
);
}
}
/**
* @title Base permission action interface
*/
interface IBasePermissionAction {
/**
* @notice Verify permissions
* @param wallet A wallet that has signed a transaction
* @param destination Destination wallet address
* @param smartContract Smart contract address that wants to check the permission
* @param targetSmartContract A Smart contract for which will be executed some action
* @param action Action to be verified
* @param subId Some additional identifier that can be used in the smart contract
* @param data Raw bates sequence that represents direct properties that are provided with tx
* @dev action = bytes4(keccak256(abi.encodePacked(bytes("permission*"),msg.sig)))
*/
function verify(
address wallet,
address destination,
address smartContract,
address targetSmartContract,
bytes32 action,
bytes32 subId,
bytes calldata data
)
external
returns (
VerificationStatus status,
bytes32 requestId,
bytes[] memory expectedValue
);
}
/**
* @title Updates repository components registration interface
*/
interface IUpdatesRepositoryComponentsRegistration {
/**
* @notice Register component in the registry
* @param componentAddress Address of the component to be registered
*/
function registerComponent(address componentAddress) external;
/**
* @notice Register components in the registry
* @param componentsAddresses Addresses of the components to be registered
*/
function batchRegisterComponents(address[] calldata componentsAddresses) external;
/**
* @param componentAddress Component address
* @return Component id
*/
function getComponentId(address componentAddress) external view returns (bytes32);
/**
* @param componentAddress Component address
* @return bool - true if component registered
*/
function isComponentRegistered(address componentAddress) external view returns (bool);
}
/**
* @title Updates repository components updates interface
*/
interface IUpdatesRepositoryComponentsUpdates {
/**
* @notice Publish new version for component
* @dev Must check package interfaces and version before adding
* @param updatePackage Address of the package
* @param version Package version
*/
function publishUpdate(address updatePackage, string calldata version) external;
/**
* @notice Someone who has rights can create a new package and publish it
* @dev Must check package interfaces and versions before adding
* @param updates Updates addresses and versions
*/
function batchPublishUpdates(BatchUpdateDetails[] calldata updates) external;
/**
* @notice Applying a next version to the package (msg.sender)
* @dev If the is more than one update package this method must be invoked
* @dev as many time as many update packages are in the registry
* @dev one transaction - one update package
* @dev must revert:
* @dev component is not registered
* @dev component already has a lastest version
*/
function updateComponent() external returns (address);
/**
* @notice Checks current component version in the registry
* @param componentAddress Component address
* @return Version of the component
*/
function getCurrentComponentVersion(address componentAddress) external view returns (string memory);
/**
* @notice Checks latest component version in the registry
* @param componentId Component id
* @return Version of the component
*/
function getLatestComponentVersion(bytes32 componentId) external view returns (string memory);
}
/**
* @title Updates repository events
*/
interface IUpdatesRepositoryEvents {
/**
* @notice Write info to the log when a new component is registered
* @param componentId Component identifier
* @param component Component address
* @param version Init component version
*/
event ComponentRegistered(
bytes32 indexed componentId,
address indexed component,
string indexed version
);
/**
* @notice Write info to the log when a new pacakge is published
* @param componentId Component identifier
* @param updatePackage Package address
* @param version Version of the update
*/
event UpdatePublished(
bytes32 indexed componentId,
address indexed updatePackage,
string indexed version
);
/**
* @notice Write info to the log when component updated to the new version
* @param componentAddress Component address
* @param newVersion New component version
* @param package Package address
*/
event ComponentUpdated(
address indexed componentAddress,
string indexed newVersion,
address indexed package
);
}
/**
* @title Updates repository interface
*/
interface IUpdatesRepository is
IUpdatesRepositoryComponentsRegistration,
IUpdatesRepositoryComponentsUpdates,
IUpdatesRepositoryEvents
{ }
/**
* @title Common functions that are used to call base permission action
*/
library CommonBasePermissionAction {
// Define libraries
using CustomRevert for *;
/**
* @notice Verify permissions
* @param wallet A wallet that has signed a transaction
* @param destination Destination wallet address
* @param smartContract Smart contract address that wants to check the permission
* @param targetSmartContract A Smart contract for which will be executed some action
* @param action Action to be verified
* @param subId Some additional identifier that can be used in the smart contract
* @param data Raw bates sequence that represents direct properties that are provided with tx
* @dev action = bytes4(keccak256(abi.encodePacked(bytes("permission*"),msg.sig)))
*/
function _tryVerify(
address actionHandler,
address wallet,
address destination,
address smartContract,
address targetSmartContract,
bytes32 action,
bytes32 subId,
bytes memory data
)
internal
returns (
VerificationStatus status,
bytes32 requestId,
bytes[] memory expectedValue
)
{
try IBasePermissionAction(actionHandler).verify(
wallet,
destination,
smartContract,
targetSmartContract,
action,
subId,
data
)
returns (
VerificationStatus stat,
bytes32 req,
bytes[] memory val
)
{
status = stat;
requestId = req;
expectedValue = val;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Compliance oracle system utils
*/
contract ComplianceOracleInitSystemUtilsInternal is
IComplianceOracleEvents,
ComplianceOracleInitDefaults
{
// Define libraries
using BytesHelper for *;
using SystemUtils for *;
using ComplianceOraclePropertyCommons for *;
/**
* @notice Registered owner for the component
* @param owner Owner address
* @param component Component address
*/
function _registerOwner(address owner, address component) internal {
require(
owner != address(0x00),
"_registerOwner: Empty owner"
);
require(
component != address(0x00),
"_registerOwner: Empty component"
);
_setOwnerValue(owner, component);
// Set DCM property manager permissions
bytes32 subId = keccak256(abi.encodePacked(DEFAULT_COMPONENT_MANAGER_ROLE_ID, component._addressToBytes32()));
bytes[] memory value = new bytes[](1);
value[0] = hex"01";
bytes32[] memory methods = new bytes32[](6);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateProperty.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).disableProperty.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).enableProperty.selector));
methods[3] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[4] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[5] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
_setPermissions(
PROPERTY_MANAGER_ID,
subId,
value,
methods
);
_setValueBySubId(
PROPERTY_MANAGER_ID,
subId,
owner,
value
);
}
/**
* @notice Sets owner value to storage
* @param owner Owner address
* @param component Component address
*/
function _setOwnerValue(address owner, address component) internal {
bytes32[] memory propertyKeysValues = new bytes32[](2);
propertyKeysValues[0] = component._addressToBytes32();
propertyKeysValues[1] = owner._addressToBytes32();
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
DEFAULT_COMPONENT_MANAGER_ROLE_ID
);
_propertiesValues[valueKey]._verifyPropertyHasNoValue();
_propertiesValues[valueKey].value.push(hex"01");
_propertiesValues[valueKey].timestamp = block.timestamp;
emit PropertyValuesUpdated(
DEFAULT_COMPONENT_MANAGER_ROLE_ID,
valueKey,
OperationType.Create
);
}
}
/**
* @title Compliance Oracle context common methods
*/
library ComplianceOracleContextCommons {
/**
* @notice Verifies that context isn't default
* @param context Value context metadata struct
*/
function _verifyContextNotDefault(ContextMetadata storage context) internal view {
require(
!context.isDefault,
"_verifyContextNotDefault: Default context"
);
}
/**
* @notice Verifies that context isn't created
* @param context Value context enumerable struct
*/
function _verifyContextExists(ContextEnumerable storage context) internal view {
require(
context.modifiedAt > 0,
"_verifyContextExists: Unexisting context"
);
}
/**
* @notice Verifies that context isn't created
* @param context Value context enumerable struct
*/
function _verifyContextNotExists(ContextEnumerable storage context) internal view {
require(
context.modifiedAt == 0,
"_verifyContextNotExists: Existing context"
);
}
/**
* @notice Verifies that context is disabled
* @param context Value context enumerable struct
*/
function _verifyContextDisabled(ContextEnumerable storage context) internal view {
require(
context.isDisabled,
"_verifyContextDisabled: Not disabled context"
);
}
/**
* @notice Verifies that context isn't disabled
* @param context Value context enumerable struct
*/
function _verifyContextNotDisabled(ContextEnumerable storage context) internal view {
require(
!context.isDisabled,
"_verifyContextNotDisabled: Disabled context"
);
}
/**
* @notice Calculates context identifier
* @param name Context name
* @return Context identifier
*/
function _getContextId(string memory name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(name));
}
}
/**
* @title Compliance Oracle source common methods
*/
library ComplianceOracleSourceCommons {
/**
* @notice Safely gets source policy address
* @param source Value source metadata struct
* @return policy
*/
function _safeGetSourcePolicyAddress(SourceMetadata storage source) internal view returns (address) {
address policyAddress = source.policyAddress;
require(
policyAddress != address(0x00),
"_safeGetSourcePolicyAddress: Unexisting policy"
);
return policyAddress;
}
/**
* @notice Safely returns source storage contract
* @param source Value source metadata struct
* @return Storage contract address
*/
function _safeGetSourceStorageContract(SourceMetadata storage source) internal view returns (address) {
address storageContract = source.source;
require(
storageContract != address(0x00),
"_safeGetSourceStorageContract: Empty source storage contract"
);
return storageContract;
}
/**
* @notice Verifies that source isn't default
* @param source Value source metadata struct
*/
function _verifySourceNotDefault(SourceMetadata storage source) internal view {
require(
!source.isDefault,
"_verifySourceNotDefault: Default source"
);
}
/**
* @notice Verifies that source is disabled
* @param source Value source enumerable struct
*/
function _verifySourceDisabled(SourceEnumerable storage source) internal view {
require(
source.isDisabled,
"_verifySourceDisabled: Source isn't disabled"
);
}
/**
* @notice Verifies that source isn't disabled
* @param source Value source enumerable struct
*/
function _verifySourceNotDisabled(SourceEnumerable storage source) internal view {
require(
!source.isDisabled,
"_verifySourceNotDisabled: Disabled source"
);
}
/**
* @notice Verifies that source is created
* @param source Value source enumerable struct
*/
function _verifySourceExists(SourceEnumerable storage source) internal view {
require(
source.modifiedAt > 0,
"_verifySourceExists: Unexisting data source"
);
}
/**
* @notice Verifies that source isn't created
* @param source Value source enumerable struct
*/
function _verifySourceNotExists(SourceEnumerable storage source) internal view {
require(
source.modifiedAt == 0,
"_verifySourceNotExists: Existing data source"
);
}
/**
* @notice Calculates source identifier
* @param contextId Context identifier
* @param name Source name
* @return Source identifier
*/
function _getSourceId(bytes32 contextId, string memory name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(contextId, name));
}
}
/**
* Original work Copyright 2016 Smart Contract Solutions, Inc.
* Modified work Copyright 2018 SECURRENCY INC.
*/
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* @dev Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* @dev as the code is not actually created until after the constructor finishes.
* @param _addr address to check
* @return whether the target address is a contract
*/
function isContract(address _addr) internal view returns (bool) {
uint size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(_addr) }
return size > 0;
}
}
/**
* @title Compliance Oracle storage internal verifications
*/
contract ComplianceOracleValuesGettersInternalVerifications is
ComplianceOracleStorage,
ComplianceOraclePropertyKeys
{
// Define libraries
using BytesHelper for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Safely returns property source id and type
* @param propertyId Property unique identifier
* @return Source id
* @return Source type
*/
function _safeGetSourceIdAndType(bytes32 propertyId) internal view returns (bytes32, SourceType) {
bytes32 sourceId = _propertiesMetadata[propertyId]._safeGetPropertySourceId();
_sourcesEnumerable[sourceId]._verifySourceExists();
return (sourceId, _sourcesMetadata[sourceId].sourceType);
}
/**
* @notice Verifies role keys
* @param propertyId Role unique identifier
* @param subId Sub identifier
* @return Flag - true if sub id key exists for role and it's zero
*/
function _verifyRoleKeys(bytes32 propertyId, bytes32 subId) internal view returns (bool) {
bytes32[] memory keys = _propertiesAccessible[propertyId].keys;
uint keysLength = keys.length;
require(
keysLength > 0,
"_verifyRoleKeys: Empty keys"
);
bool subIdExistsAndZero;
if (keys[0] == SUB_ID) {
require(
keysLength > 1,
"_verifyRoleKeys: Not enough keys"
);
require(
keys[1] == WALLET,
"_verifyRoleKeys: Second key not WALLET"
);
subIdExistsAndZero = true;
} else {
require(
keys[0] == WALLET,
"_verifyRoleKeys; First key not WALLET"
);
require(
subId == bytes32(0x00),
"_verifyRoleKeys: SUB_ID key"
);
for (uint i = 1; i < keysLength; i++) {
require(
keys[i] != SUB_ID,
"_verifyRoleKeys: SUB_ID key"
);
}
}
return subIdExistsAndZero;
}
}
interface IOnRefuse {
/**
* @dev Performs on refuse for role action
*/
function onRefuse(
bytes32 propertyId,
bytes32[] calldata propertyKeysValues,
uint managerKeyPosition
)
external;
}
/**
* @title Compliance Oracle storage internal verifications
*/
contract ComplianceOracleStorageSettersInternalVerifications is
ComplianceOracleStorage,
ComplianceOracleStorageSettersProtectedVerifications
{
// Define libraries
using BytesHelper for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Property veifications for values operations
* @param propertyId Property unique id
*/
modifier validateProperty(bytes32 propertyId) {
_propertiesEnumerable[propertyId]._verifyPropertyExists();
_propertiesEnumerable[propertyId]._verifyPropertyNotDisabled();
_propertiesMetadata[propertyId]._verifyNotChild();
_;
}
/**
* @notice Property veifications for refuse action
* @param propertyId Property unique id
*/
modifier validateBeforeRefuse(bytes32 propertyId) {
_verifyPropertyHasBooleanDataType(propertyId);
_propertiesEnumerable[propertyId]._verifyPropertyExists();
_propertiesEnumerable[propertyId]._verifyPropertyNotDisabled();
_propertiesMetadata[propertyId]._verifyNotChild();
_propertiesMetadata[propertyId]._verifyNotConstant();
_;
}
/**
* @notice Verifies that role has true value
* @param valueKey Value key
* @return true value
*/
function _verifyRoleHasTrueValue(bytes32 valueKey) internal view returns (bytes[] memory) {
bytes[] storage value = _propertiesValues[valueKey].value;
require(
value.length == 1,
"_verifyRoleHasTrueValue: Wrong value length"
);
require(
keccak256(value[0]) == keccak256(abi.encodePacked(true)),
"_verifyRoleHasTrueValue: Wrong value"
);
return value;
}
/**
* @param valueKey Value key
* @return true if role has true value
*/
function _doesRoleHaveTrueValue(bytes32 valueKey) internal view returns (bool) {
bytes[] storage value = _propertiesValues[valueKey].value;
return
value.length == 1
&& keccak256(value[0]) == keccak256(abi.encodePacked(true));
}
/**
* @notice Verifies that property has boolean data type
* @param propertyId Property unique identifier
*/
function _verifyPropertyHasBooleanDataType(bytes32 propertyId) internal view {
DataType dataType = _propertiesMetadata[propertyId].dataType;
require(
dataType == DataType.Boolean,
"_verifyPropertyHasBooleanDataType: Not bool data type"
);
}
/**
* @notice Verifies that source has storage type
* @param sourceId Source unique identifier
*/
function _verifySourceHasStorageType(bytes32 sourceId) internal view {
require(
_sourcesMetadata[sourceId].sourceType == SourceType.Storage,
"_verifySourceHasStorageType: Not storage source type"
);
}
/**
* @notice Verifies property source and returns it if has storage type
* @return source address
* @param propertyId Property unique id
*/
function _safeGetSourceIfItHasStorageType(bytes32 propertyId) internal view returns (address) {
bytes32 sourceId = _propertiesMetadata[propertyId]._safeGetPropertySourceId();
_sourcesEnumerable[sourceId]._verifySourceExists();
_verifySourceHasStorageType(sourceId);
return _sourcesMetadata[sourceId]._safeGetSourceStorageContract();
}
/**
* @notice Verifies role keys
* @param propertyId Role unique identifier
* @param subId Sub identifier
* @return Flag - true if sub id key exists for role and it's zero
*/
function _verifyRoleKeys(bytes32 propertyId, bytes32 subId) internal view returns (bool) {
bytes32[] memory keys = _propertiesAccessible[propertyId]._getPropertyKeys();
uint keysLength = keys.length;
require(
keysLength > 0,
"_verifyRoleKeys: No keys"
);
bool subIdExistsAndZero;
if (keys[0] == SUB_ID) {
require(
keysLength > 1,
"_verifyRoleKeys: Not enough keys"
);
require(
keys[1] == WALLET,
"_verifyRoleKeys: Second key not WALLET"
);
subIdExistsAndZero = true;
} else {
require(
keys[0] == WALLET,
"_verifyRoleKeys: First key not WALLET"
);
require(
subId == bytes32(0x00),
"_verifyRoleKeys: SUB_ID not null"
);
for (uint i = 1; i < keysLength; i++) {
require(
keys[i] != SUB_ID,
"_verifyRoleKeys: SUB_ID keye"
);
}
}
return subIdExistsAndZero;
}
/**
* @notice Verifies that property onRefuse value isn't empty
* @param propertyId property identifier
* @return onRefuse address
*/
function _safeGetOnRefuse(bytes32 propertyId) internal view returns (address) {
address onRefuse = _propertiesAccessible[propertyId].onRefuse;
require(
onRefuse != address(0x00),
"_verifyOnRefuseNotEmpty: Empty on refuse"
);
return onRefuse;
}
/**
* @notice Verifies value not to be empty
* @param value Value to be verified
*/
function _verifyValueNotEmpty(bytes[] memory value) internal pure {
require(
value.length > 0,
"_verifyValueNotEmpty: Empty value"
);
}
}
/**
* @title SystemUtils
* @dev Different system specific operations
*/
library SystemUtils {
/**
* @notice Generates key for the value that will be inserted
* @param keysValues Property keys values
* @param propertyKey Property key
* @return generated key
*/
function generateKey(bytes32[] memory keysValues, bytes32 propertyKey) internal pure returns (bytes32) {
uint length = keysValues.length + 1;
// properties key + value keys
bytes32[] memory data = new bytes32[](length);
data[0] = propertyKey;
// iterate all value keys
for (uint i = 1; i < length; i++) {
if (keysValues[i - 1] == bytes32(0x00)) {
continue;
}
data[i] = keysValues[i - 1];
}
return keccak256(abi.encodePacked(data));
}
/**
* @notice Builds values of keys
* @param propertyKeys Properties keys
* @param inputKeys Values keys that can be provided with transaction
* @param inputKeysValues Values by keys that can be provided with transaction
* @return values of the keys
*/
function _getKeysValues(
bytes32[] memory propertyKeys,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
pure
returns (bytes32[] memory)
{
_verifyKeysLengths(inputKeys, inputKeysValues);
// iterate all property keys
uint keysLength = propertyKeys.length;
bytes32[] memory data = new bytes32[](keysLength);
for (uint i = 0; i < keysLength; i++) {
if (propertyKeys[i] == bytes32(0x00)) {
continue;
}
data[i] = propertyKeys[i];
}
// get keys values
for (uint i = 0; i < keysLength; i++) {
if (data[i] == bytes32(0x00)) {
break;
}
bytes32 value = _getValueFromKeys(
data[i],
inputKeys,
inputKeysValues
);
data[i] = value;
}
return data;
}
/**
* @notice Selects values from the provided input
* @param key Search key
* @param inputKeys Values keys that can be provided with transaction
* @param inputKeysValues Values by keys that can be provided with transaction
* @return value of the key
*/
function _getValueFromKeys(
bytes32 key,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
pure
returns (bytes32)
{
uint inputKeysLength = inputKeys.length;
for (uint i = 0; i < inputKeysLength; i++) {
if (inputKeys[i] == key) {
return inputKeysValues[i];
}
}
revert(
string(abi.encodePacked(
"_getValueFromKeys: No key {",
BytesHelper._bytes32ToASCIIBytes(key),
"}"
))
);
}
/**
* @notice Verifies keys and values to have equal lengths
* @param keys [optional] Values keys that can be provided with transaction
* @param keysValues [optional] Values by keys that can be provided with transaction
*/
function _verifyKeysLengths(
bytes32[] memory keys,
bytes32[] memory keysValues
)
internal
pure
{
require(
keys.length == keysValues.length,
"_verifyKeysLengths: Wrong lengths"
);
}
}
/**
* @title Compliance Oracle property common methods
*/
library ComplianceOraclePropertyCommons {
/**
* @notice Safely returns properties source id
* @param property Property metadata struct
* @return Properties source id
*/
function _safeGetPropertySourceId(PropertyMetadata storage property) internal view returns (bytes32) {
bytes32 sourceId = property.sourceId;
require(
sourceId != bytes32(0x00),
"_safeGetPropertySourceId: Empty source id"
);
return sourceId;
}
/**
* @notice Verifies value keys values to have same length as property keys
* @param property Property accessible struct
*/
function _getPropertyKeys(PropertyAccessible storage property) internal view returns (bytes32[] memory keys) {
return property.keys;
}
/**
* @notice Verifies that property has no value
* @param value Value struct
*/
function _verifyPropertyHasNoValue(Value storage value) internal view {
require(
value.value.length == 0,
"_verifyPropertyHasNoValue: Not empty value"
);
}
/**
* @notice Verifies that property has value
* @param value Value struct
*/
function _verifyPropertyHasValue(Value storage value) internal view {
require(
value.value.length > 0,
"_verifyPropertyHasValue: Empty value"
);
}
/**
* @notice Verifies that property isn't constant
* @param property Property metadata struct
*/
function _verifyNotConstant(PropertyMetadata storage property) internal view {
require(
!property.isConstant,
"_verifyNotConstant: Is constant"
);
}
/**
* @notice Verifies that property isn't child
* @param property Property metadata struct
*/
function _verifyNotChild(PropertyMetadata storage property) internal view {
require(
property.parentId == bytes32(0x00),
"_verifyNotChild: Is child"
);
}
/**
* @notice Verifies that property isn't default
* @param property Property metadata struct
*/
function _verifyPropertyNotDefault(PropertyMetadata storage property) internal view {
require(
!property.isDefault,
"_verifyPropertyNotDefault: Default property"
);
}
/**
* @notice Verifies that property isn't created
* @param property Property enumerable struct
*/
function _verifyPropertyNotExists(PropertyEnumerable storage property) internal view {
require(
property.modifiedAt == 0,
"_verifyPropertyNotExists: Existing property"
);
}
/**
* @notice Verifies that property is created
* @param property Property enumerable struct
*/
function _verifyPropertyExists(PropertyEnumerable storage property) internal view {
require(
property.modifiedAt > 0,
"_verifyPropertyExists: Not existing property"
);
}
/**
* @notice Verifies that property isn't disabled
* @param property Property enumerable struct
*/
function _verifyPropertyNotDisabled(PropertyEnumerable storage property) internal view {
require(
!property.isDisabled,
"_verifyPropertyNotDisabled: Disabled property"
);
}
/**
* @notice Verifies that property is disabled
* @param property Property enumerable struct
*/
function _verifyPropertyDisabled(PropertyEnumerable storage property) internal view {
require(
property.isDisabled,
"_verifyPropertyDisabled: Property isn't disabled"
);
}
/**
* @notice Calculates property identifier
* @param name Property name
* @param contextId Context identifier
* @return Property identifier
*/
function _getPropertyId(bytes32 contextId, string memory name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(contextId, name));
}
}
/**
* @title CustomRevert Library
* @dev Custom revert / require error message
*/
library CustomRevert {
// Define libraries
using BytesHelper for *;
/**
* @dev Converts a uint to its string representation
* @param value, uint to be converted
* @return String representation of the uint
*/
function _toString(uint value) internal pure returns (string memory) {
return string(value._uintToASCIIBytes());
}
/**
* @dev Converts an address to its string representation
* @param value, address to be converted
* @return String representation of the address
*/
function _toString(address value) internal pure returns (string memory) {
return string(value._addressToASCIIBytes());
}
/**
* @dev Converts a boolean to its string representation
* @param value, boolean to be converted
* @return String representation of the boolean
*/
function _toString(bool value) internal pure returns (string memory) {
return value ? "true" : "false";
}
/**
* @dev Custom Revert, revert with a custom message
* @param value, uint value to be added at the end of the custom revert message
* @param tmpl, first part of the error message to be displayed while reverting
*/
function _revert(uint value, string memory tmpl) internal pure {
revert(_buildStr(tmpl, value));
}
/**
* @dev Custom Revert, revert with a custom message
* @param value, address value to be added at the end of the custom revert message
* @param tmpl, first part of the error message to be displayed while reverting
*/
function _revert(address value, string memory tmpl) internal pure {
revert(_buildStr(tmpl, value));
}
/**
* @dev Custom Revert, revert with a custom message
* @param value, boolean value to be added at the end of the custom revert message
* @param tmpl, first part of the error message to be displayed while reverting
*/
function _revert(bool value, string memory tmpl) internal pure {
revert(_buildStr(tmpl, value));
}
/**
* @dev Custom Revert, revert with a custom message
* @param values, string values to be added to the custom revert message
* @param tmpl, templated message following this pattern "message {1} message {2}..."
*/
function _revert(string[] memory values, string memory tmpl) internal pure {
revert(_buildStr(tmpl, values));
}
/**
* @dev respectively the index of opening and closing braces
* @param template, String to be checked
* @return uint256 start, opening braces index
* @return uint256 end, closing braces index
*/
function _getBracesPosition(string memory template) private pure returns (uint, uint) {
uint start;
uint end;
uint len = bytes(template).length;
bytes memory str = bytes(template);
for (uint i = 0; i < len; i++) {
if ((str[i]) == ("{")) {
start = i + 1;
}
if ((str[i]) == ("}")) {
require(start < i, "_getBracesPosition: Wrong template");
return (start, i);
}
}
return (start, end);
}
/**
* @dev Count the amount of opened/closed braces
* @param template String to be checked
* @return nbBraces, number of braces
*/
function _countBraces(string memory template) private pure returns (uint) {
uint len = bytes(template).length;
bytes memory str = bytes(template);
uint count = 0;
for (uint i = 0; i < len; i++) {
if ((str[i]) == ("{")) {
for (uint j = i; j < len; j++) {
if ((str[j]) == ("}")) {
count++;
i = j;
break;
}
if ((str[j]) == ("{")) {
i = j;
}
}
}
}
return count;
}
/**
* @dev replace the content of the array by the content of the specific index
* @param _str, string containing the index to be replaced with
* @param _toReplace, array of strings to replace braces content
* @return string, replaced string (index by content)
*/
function _extractBracesContent(
string memory _str,
string[] memory _toReplace
)
private
pure
returns (string memory)
{
return (_toReplace[bytes(_str)._ASCIIBytesToUint()]);
}
/**
* @dev Gets the content of braces (should contain the index to be replaced with)
* @param _str, string to be checked
* @return string, returns the content of braces
*/
function _extractBraces(string memory _str) private pure returns (string memory) {
bytes memory str = bytes(_str);
(uint a, uint b) = _getBracesPosition(_str);
uint j = 0;
for (uint i = a; i < b; i++) {
j++;
}
uint k = 0;
bytes memory _store = bytes(new string(j));
for (uint i = a; i < b; i++) {
_store[k] = str[i];
k++;
}
return string(_store);
}
/**
* @dev Takes a string and uint and build its string representation
* @param tmpl, the string template to be represented
* @param value, the uint value to be represented
* @return String, merged parameters into a string
*/
function _buildStr(string memory tmpl, uint value) private pure returns (string memory) {
return string(abi.encodePacked(tmpl, value._uintToASCIIBytes()));
}
/**
* @dev Takes a string and an address and build its string representation
* @param tmpl, the string template to be represented
* @param value, the address value to be represented
* @return String, merged parameters into a string
*/
function _buildStr(string memory tmpl, address value) private pure returns (string memory) {
return string(abi.encodePacked(tmpl, value._addressToASCIIBytes()));
}
/**
* @dev Takes a string and a boolean and build its string representation
* @param tmpl, the string template to be represented
* @param value, the boolean value to be represented
* @return String, merged parameters into a string
*/
function _buildStr(string memory tmpl, bool value) private pure returns (string memory) {
string memory res = value == true ? "true" : "false";
return string(abi.encodePacked(tmpl, res));
}
/**
* @dev Takes a string and an array of strings and build its string representation
* @dev following this template e,g.: "Here is first arg {1}, here third {3} and, here second {2}"
* @param tmpl, templated string to be represented
* @param values, string to be replaced into the templated string (tmpl)
* @return String, merged parameters into a string in a formatted manner.
*/
function _buildStr(string memory tmpl, string[] memory values) private pure returns (string memory) {
uint nbBrackets = _countBraces(tmpl);
require(
nbBrackets == values.length,
"_buildStr: Wrong brackets num"
);
for (uint i = 0; i < nbBrackets; i++) {
(uint start, uint end) = _getBracesPosition(tmpl);
bytes memory beforeBracket = bytes(tmpl)._getSlice(0, start - 1);
string memory getBracket = _extractBracesContent(
_extractBraces(tmpl),
values
);
bytes memory afterBracket = bytes(tmpl)._getSlice(
end + 1,
bytes(tmpl).length - (end + 1)
);
tmpl = string(
abi.encodePacked(
beforeBracket,
getBracket,
afterBracket
)
);
}
return tmpl;
}
}
/**
* @title Compliance oracle storage interface
*/
interface IComplianceOracleStorage is
IComplianceOracleValuesGetters,
IComplianceOracleStorageSetters,
IComplianceOracleStorageRolesSetters,
IComplianceOracleStorageBatchSetters
{ }
/**
* @title Compliance Oracle system utils interface
*/
interface IComplianceOracleSystemUtils {
/**
* @notice Registered owner for the sender
* @param owner Owner address
*/
function registerOwner(address owner) external;
/**
* @notice Requests attestation property
* @dev Is overriden by the same method from later package
* @dev Works only for storage source and default properties
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function requestValue(
bytes32 propertyId,
bytes32[] calldata inputKeys,
bytes32[] calldata inputKeysValues,
address,
bytes32
)
external
payable
returns (
DataType valueDataType,
Value memory value,
bytes32,
bytes32
);
/**
* @notice Get property existance flag by identifier
* @param propertyId Property identifier
* @return existance flag
*/
function isExistingProperty(bytes32 propertyId) external view returns (bool);
/**
* @notice Get property by identifier
* @param propertyId Property unique identifier
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function getPropertyById(bytes32 propertyId) external view returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
);
/**
* @notice Get context data by id
* @param contextId Context identifier
* @return contextMetadata Contex metadata
* @return contextEnumerable Contex enumerable data
*/
function getContextById(bytes32 contextId) external view returns (
ContextMetadata memory contextMetadata,
ContextEnumerable memory contextEnumerable
);
/**
* @notice Get Data source by identifier
* @param sourceId source identifier
* @return metadata Source metadata
* @return enumerable Source enumerable data
*/
function getSourceById(bytes32 sourceId) external view returns (
SourceMetadata memory metadata,
SourceEnumerable memory enumerable
);
}
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title Interface of the upgradable component
*/
interface IUpgradable {
/**
* @notice Updates component to the next version
* that is registered in the updates registry
*/
function update() external;
/**
* @notice Applying a specific version of the update package related to the repository
* @dev If the is more than one update package between the current and specified
* @dev versions this method will perform every single update
* @param version Specified version
*/
function updateToSpecificVersion(string calldata version) external;
/**
* @return current version of the component
*/
function getCurrentVersion() external view returns (string memory);
/**
* @return latest version of the component
*/
function getLatestVersion() external view returns (string memory);
}
/**
* @title Interface of the Rules Engine actions handlers
*/
interface IRulesEngineActionsHandlers {
/**
* @notice Set address of the action handler
* @param smartContract Smart contract address
* @param action Action
* @param subId Sub identifier
* @param handler Action handler address
*/
function setActionHandler(
address smartContract,
bytes32 action,
bytes32 subId,
address handler
)
external;
/**
* @notice Set actions handler for the sender
* @param actionsList List of the actions
* @param handler Actions handler
* @param subId Sub identifier
*/
function selfSetMultipleActionsHandler(
bytes32[] calldata actionsList,
bytes32 subId,
address handler
)
external;
/**
* @return action handler
* @param smartContract Smart contract address
* @param action Action to be handled
*/
function getActionHandler(
address smartContract,
bytes32 action,
bytes32 subId
)
external
view
returns (address);
/**
* @return action handler
* @dev returns base permission action handler if there is no action handler
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
*/
function getSafeActionHandler(
address smartContract,
bytes32 action,
bytes32 subId
)
external
view
returns (address);
}
/**
* @title Interface of the Rules Engine
*/
interface IRulesEngine is
IRulesEngineActionsHandlers,
IRulesEngineActionsPermissions,
IRulesEngineEvents
{ }
/**
* @title Common functions that are used to call rules engine functions
*/
library CommonRulesEngine {
// Define libraries
using CustomRevert for *;
/**
* @notice Set address of the action handler
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action
* @param subId Addition identifier (may be empty)
* @param handler Action handler address
*/
function _trySetActionHandler(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId,
address handler
)
internal
{
try IRulesEngine(rulesEngine).setActionHandler(
smartContract,
action,
subId,
handler
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Set actions handler for the sender
* @dev call selfSetMultipleActionsHandler() using exception handling (try/catch)
* @param rulesEngine, Rules Engine Address
* @param subId Addition identifier (may be empty)
* @param actionsHandler Actions handler
*/
function _trySelfSetMultipleActionsHandler(
address rulesEngine,
bytes32[] memory protectedMethodsList,
bytes32 subId,
address actionsHandler
)
internal
{
try IRulesEngine(rulesEngine).selfSetMultipleActionsHandler(
protectedMethodsList,
subId,
actionsHandler
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Set actions action permission for the sender
* @dev call setActionPermission() using exception handling (try/catch)
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action
* @param subId Addition identifier (may be empty)
* @param permission Permission to set
*/
function _trySetActionPermission(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId,
Permission memory permission
)
internal
{
try IRulesEngine(rulesEngine).setActionPermission(
smartContract,
action,
subId,
permission
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Set permission for multiple actions for the sender
* @param rulesEngine, Rules Engine Address
* @param actionsList List of the actions
* @param subId Sub identifier
* @param permission Permission
*/
function _trySetMultipleActionsPermission(
address rulesEngine,
bytes32[] memory actionsList,
bytes32 subId,
Permission memory permission
)
internal
{
try IRulesEngine(rulesEngine).setMultipleActionsPermission(
actionsList,
subId,
permission
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action to be handled
* @param subId Addition identifier (may be empty)
* @return result action handler
*/
function _tryGetActionHandler(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId
)
internal
view
returns (address result)
{
try IRulesEngine(rulesEngine).getActionHandler(
smartContract,
action,
subId
)
returns
(address actionHandler)
{
result = actionHandler;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @dev call getSafeActionHandler() using exception handling (try/catch)
* @dev throws if action handler not set
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action to be handled
* @param subId Addition identifier (may be empty)
* @return result action handler
*/
function _tryGetSafeActionHandler(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId
)
internal
view
returns (address result)
{
try IRulesEngine(rulesEngine).getSafeActionHandler(
smartContract,
action,
subId
)
returns (address actionHandler)
{
result = actionHandler;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @dev call getActionPermission() using exception handling (try/catch)
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action
* @param subId Addition identifier (may be empty)
* @return result action permission
*/
function _tryGetActionPermission(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId
)
internal
view
returns (Permission memory result)
{
try IRulesEngine(rulesEngine).getActionPermission(
smartContract,
action,
subId
)
returns (Permission memory permission)
{
result = permission;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @dev call safeGetActionPermission() using exception handling (try/catch)
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action
* @param subId Addition identifier (may be empty)
* @return resultPermission - action permission
* @return resultExists - flag - true if action permission exists
*/
function _trySafeGetActionPermission(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId
)
internal
view
returns (Permission memory resultPermission, bool resultExists)
{
try IRulesEngine(rulesEngine).safeGetActionPermission(
smartContract,
action,
subId
)
returns (Permission memory permission, bool exists)
{
resultPermission = permission;
resultExists = exists;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Calculates permission verification price
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
* @param gasPrice Gas price
* @return result - permission verification price
*/
function _tryGetPermissionVerificationPrice(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId,
uint gasPrice
)
internal
view
returns (uint result)
{
try IRulesEngine(rulesEngine).getPermissionVerificationPrice(
smartContract,
action,
subId,
gasPrice
)
returns (uint res)
{
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Returns external calls total number for permission
* @param rulesEngine, Rules Engine Address
* @param smartContract Smart contract address
* @param action Action to be verified
* @param subId Addition identifier (may be empty)
* @return result permission external calls total number
*/
function _tryGetPermissionExternalCallsTotal(
address rulesEngine,
address smartContract,
bytes32 action,
bytes32 subId
)
internal
view
returns (uint result)
{
try IRulesEngine(rulesEngine).getPermissionExternalCallsTotal(
smartContract,
action,
subId
)
returns (uint res)
{
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Common functions that are used to call upgradability functions
* @dev Common functionality that is used in any upgradable component
*/
library CommonUpgradability {
// Define libraries
using CustomRevert for *;
/**
* @notice Updates component to the next version
* that is registered in the updates registry
* @param contractAddress, contract address
*/
function _tryUpdate(address contractAddress) internal {
try IUpgradable(contractAddress).update()
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Applying a specific version of the update package related to the repository
* @dev call updateToSpecificVersion() using exception handling (try/catch)
* @dev If the is more than one update package between the current and specified
* @dev versions this method will perform every single update
* @param contractAddress, contract address
* @param version Specified version
*/
function _tryUpdateToSpecificVersion(address contractAddress, string memory version) internal {
try IUpgradable(contractAddress).updateToSpecificVersion(version)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @dev call getCurrentVersion() using exception handling (try/catch)
* @return result current version of the component
*/
function _tryGetCurrentVersion(address contractAddress) internal view returns (string memory result) {
try IUpgradable(contractAddress).getCurrentVersion() returns (string memory version) {
result = version;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @dev call getLatestVersion() using exception handling (try/catch)
* @return result latest version of the component
*/
function _tryGetLatestVersion(address contractAddress) internal view returns (string memory result) {
try IUpgradable(contractAddress).getLatestVersion() returns (string memory version) {
result = version;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Package that contain updates for the some component
*/
interface IPackage is IERC165 {
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external;
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external;
/**
* @return Component identifier
*/
function getComponentId() external view returns (bytes32);
}
/**
* @title Compliance oracle sources interface
*/
interface IComplianceOracleSources {
/**
* @notice Create a Data source
* @param sourceInput Data source input structure
* @return sourceId Unique source identifier
*/
function createSource(SourceInput calldata sourceInput) external returns (bytes32 sourceId);
/**
* @notice Update a Data source
* @param sourceInput Data source input structure
*/
function updateSource(SourceInput calldata sourceInput) external;
/**
* @notice Disable a Data source
* @param contextId Context identifier
* @param name Data source name
*/
function disableSource(bytes32 contextId, string calldata name) external;
/**
* @notice Enable a Data source
* @param contextId Context identifier
* @param name Data source name
*/
function enableSource(bytes32 contextId, string calldata name) external;
/**
* @notice Get Data source
* @param contextId Context identifier
* @param name Source name
* @return metadata Source metadata
* @return enumerable Source enumerable data
*/
function getSource(
bytes32 contextId,
string calldata name
)
external
view
returns (
SourceMetadata memory metadata,
SourceEnumerable memory enumerable
);
}
/**
* @title Compliance oracle properties interface
*/
interface IComplianceOracleProperties {
/**
* @notice Create an Attestation property
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*
* @dev Creating property which inherits from another requires the following
* fields to be empty (because they are inherited from a parent property):
* - sourceId
* - dataType
* - externalId
* - isConstant
* - expirationTime
* - onRefuse
* @return propertyId Unique property identifier
*/
function createProperty(
PropertyInput calldata propertyInput,
PropertyAccessible calldata propertyAccessible
)
external
returns (bytes32 propertyId);
/**
* @notice Update an Attestation property
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function updateProperty(
PropertyInput calldata propertyInput,
PropertyAccessible calldata propertyAccessible
)
external;
/**
* @notice Disable an Attestation property
* @param contextId Context identifier
* @param name Property name
*/
function disableProperty(bytes32 contextId, string calldata name) external;
/**
* @notice Enable an Attestation property
* @param contextId Context identifier
* @param name Property name
*/
function enableProperty(bytes32 contextId, string calldata name) external;
/**
* @notice Get property
* @param contextId Context identifier
* @param name Property name
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function getProperty(bytes32 contextId, string calldata name) external view returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
);
/**
* @notice Get property by external identifier in context
* @param contextId Context identifier
* @param externalId External identifier
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function getPropertyByExternalId(bytes32 contextId, bytes32 externalId) external view returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
);
/**
* @notice Get property external calls total
* @param propertyId Property identifier
*/
function getPropertyExternalCallsTotal(bytes32 propertyId) external view returns (uint);
}
/**
* @title Compliance oracle contexts interface
*/
interface IComplianceOracleContexts {
/**
* @notice Approves context creation
* @param contextInput Context input struct
* @param owner Future context owner address
* @return Request identifier
*/
function approveCreateContext(
ContextInput calldata contextInput,
address owner
)
external
returns (uint);
/**
* @notice Accepts context creation
* @param requestId Request identifier
* @param contextInput Context input struct
* @return Created context identifier
*/
function acceptCreateContext(
uint requestId,
ContextInput calldata contextInput
)
external
returns (bytes32);
/**
* @notice Update a context
* @param contextInput Context metadata
*/
function updateContext(ContextInput calldata contextInput) external;
/**
* @notice Disables context
* @param name Context name
*/
function disableContext(string calldata name) external;
/**
* @notice Enable a Data context
* @param name Data context name
*/
function enableContext(string calldata name) external;
/**
* @notice Get context data
* @param name Context name
* @return contextMetadata Contex metadata
* @return contextEnumerable Contex enumerable data
*/
function getContext(string calldata name) external view returns (
ContextMetadata memory contextMetadata,
ContextEnumerable memory contextEnumerable
);
}
/**
* @title Compliance oracle interface
*/
interface IComplianceOracle is
IComplianceOracleEvents,
IComplianceOracleSources,
IComplianceOracleContexts,
IComplianceOracleProperties,
IComplianceOracleStorage,
IComplianceOracleSystemUtils
{
/**
* @notice Requests attestation property
* @notice if property has external data source
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function requestValue(
bytes32 propertyId,
bytes32[] calldata inputKeys,
bytes32[] calldata inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
external
payable
override(IComplianceOracleValuesGetters, IComplianceOracleSystemUtils)
returns (
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
);
}
/**
* @title Compliance Oracle defaults setup internal
*/
contract ComplianceOracleRolesSystemSetupInternal is
ComplianceOraclePropertyKeys,
ComplianceOraclePermissionsSetupCommons,
ComplianceOracleDefaultObjects
{
// Define libraries
using BytesHelper for *;
using ComplianceOraclePropertyCommons for *;
/**
* @notice Setups system defaults
*/
function _initializeDefaults() internal {
_createRootL1Role();
_createRootL2Role();
_createSystemRole();
}
/**
* @notice Creates RootL1 role
*/
function _createRootL1Role() internal {
bytes32[] memory keys = new bytes32[](1);
keys[0] = WALLET;
_createDefaultPropertyRole(
ROOT_L1_ROLE_ID,
ROOT_L1_ROLE,
"Root level 1 role",
keys
);
_setupRole(
ROOT_L1_ROLE_ID,
PROPERTY_MANAGER_ID
);
}
/**
* @notice Creates RootL2 role
*/
function _createRootL2Role() internal {
bytes32[] memory keys = new bytes32[](1);
keys[0] = WALLET;
_createDefaultPropertyRole(
ROOT_L2_ROLE_ID,
ROOT_L2_ROLE,
"Root level 2 role",
keys
);
_setupRole(
ROOT_L2_ROLE_ID,
ROOT_L1_ROLE_ID
);
}
/**
* @notice Creates System role
*/
function _createSystemRole() internal {
bytes32[] memory keys = new bytes32[](1);
keys[0] = WALLET;
_createDefaultPropertyRole(
SYSTEM_ROLE_ID,
SYSTEM_ROLE,
"System role",
keys
);
_setupRole(
SYSTEM_ROLE_ID,
ROOT_L2_ROLE_ID
);
}
/**
* @notice Setups property
* @param propertyId Property identifier
* @param managerPropertyId Manager property identifier
*/
function _setupRole(
bytes32 propertyId,
bytes32 managerPropertyId
)
internal
{
bytes[] memory value = new bytes[](1);
value[0] = hex"01";
// Set value
_setValue(
managerPropertyId,
value
);
// Set premissions
bytes32[] memory methods = new bytes32[](3);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
_setPermissions(
managerPropertyId,
propertyId,
value,
methods
);
}
/**
* @notice Stores Property value
* @param propertyId Property identifier
* @param value Value to be stored
*/
function _setValue(
bytes32 propertyId,
bytes[] memory value
)
internal
{
bytes32[] memory propertyKeysValues = new bytes32[](1);
// DEV: tx.origin used for manager role because only compliance oracle
// manager can run compliance oracle update process and she will receive
// property manager role
propertyKeysValues[0] = tx.origin._addressToBytes32();
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
_propertiesValues[valueKey]._verifyPropertyHasNoValue();
_propertiesValues[valueKey].value = value;
_propertiesValues[valueKey].timestamp = block.timestamp;
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Update
);
}
}
/**
* @title Common functions that are used to call Package functions
*/
library CommonPackage {
// Define libraries
using CustomRevert for *;
/**
* @notice Applying an update to the component
* @dev call applyUpdate() using exception handling (try/catch)
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if needed and update storage
* @param package, package address
*/
function _tryApplyUpdate(address package) internal {
try IPackage(package).applyUpdate()
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Applying an update to the component up to specified version
* @dev call applyUpdateToSpecificVersion() using exception handling (try/catch)
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if needed and update storage
* @param package, package address
* @param version, Specified version
*/
function _tryApplyUpdateToSpecificVersion(address package, string memory version) internal {
try IPackage(package).applyUpdateToSpecificVersion(version)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @return result Component identifier
*/
function _tryGetComponentId(address packageAddress) internal view returns (bytes32 result) {
try IPackage(packageAddress).getComponentId() returns (bytes32 res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Verifies whether component supports interface or not (ERC-165)
* @param componentAddress Component to be verified
* @param sig Method signature to be verified
* @return result an interface support flag
*/
function _trySupportsInterface(address componentAddress, bytes4 sig) internal view returns (bool result) {
try IPackage(componentAddress).supportsInterface(sig) returns (bool res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Compliance Oracle initialization interface
*/
interface IComplianceOracleInit {
/**
* @notice Initialize smart contract
* @param rulesEngine Rules engine address
* @param updatesRepository Updates repository address
* @param oraclizeResolver Oraclize resolver address
*/
function initialize(
address rulesEngine,
address updatesRepository,
address oraclizeResolver
)
external;
}
/**
* @title Common functions that are used to call Policy parser functions
*/
library CommonCallbackHandler {
// Define libraries
using CustomRevert for *;
/**
* @notice Process callback with value to callback handler
* @param callbackHandler, Callback handler
* @param result, Callback result
*/
function _tryValueCallback(
address callbackHandler,
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
)
internal
returns (bool result)
{
try IValuesCallback(callbackHandler).__valueCallback(
valueDataType,
value,
errorCode,
requestId
)
returns (bool res)
{
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Values callback interface
*/
interface IValuesCallback {
/**
* @notice Process callback with value to callback handler
*/
function __valueCallback(
DataType valueDataType,
Value calldata value,
bytes32 errorCode,
bytes32 requestId
)
external
returns (bool);
}
/**
* @title Interface of the policy
*/
interface IPolicy {
/**
* @notice Verify policy and returns a result
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return verification result and error code
*/
function verifyPolicy(
bytes32[] calldata inputKeys,
bytes32[] calldata inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
external
returns (PolicyResult, bytes32);
/**
* @return number of external calls (includes subpolicy) in policy
*/
function getExternalCallsTotal() external view returns (uint);
}
/**
* @title Common functions that are used to call Policy parser functions
*/
library CommonPolicyParser {
// Define libraries
using CustomRevert for *;
/**
* @notice Verify policy and returns a result
* @param policyParser, Policy parser address
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return result verification result
* @return errorCode error code
*/
function _tryVerifyPolicy(
address policyParser,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
internal
returns (PolicyResult result, bytes32 errorCode)
{
try IPolicy(policyParser).verifyPolicy(
inputKeys,
inputKeysValues,
callbackAddress,
externalCallsSession
)
returns (PolicyResult res, bytes32 errCode)
{
result = res;
errorCode = errCode;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @param policyParser, Policy parser address
* @return result Component identifier
*/
function _tryGetExternalCallsTotal(address policyParser) internal view returns (uint result) {
try IPolicy(policyParser).getExternalCallsTotal() returns (uint res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Compliance Oracle storage setters internal methods
*/
contract ComplianceOracleStorageSettersInternal is
ComplianceOracleStorageSettersInternalVerifications,
IComplianceOracleEvents
{
// Define libraries
using BytesHelper for *;
using AddressUtils for *;
using CommonPackage for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Stores Property value if the source is this address
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
* @return Source storage contract
*/
function _createValueIfSourceIsThis(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
verifyValueActionPermissions(
IComplianceOracle(address(0x00)).createValue.selector,
propertyId,
propertyKeysValues
)
validateProperty(propertyId)
returns (address)
{
_verifyValueNotEmpty(value);
address sourceContract = _safeGetSourceIfItHasStorageType(propertyId);
if (sourceContract == address(this)) {
_createValueForThisStorage(
value,
propertyId,
propertyKeysValues
);
}
return sourceContract;
}
/**
* @notice Creates stored Property value in this storage
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function _createValueForThisStorage(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
_propertiesValues[valueKey]._verifyPropertyHasNoValue();
_setValue(
value,
valueKey
);
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Create
);
}
/**
* @notice Updates stored Property value
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function _updateValueIfSourceIsThis(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
verifyValueActionPermissions(
IComplianceOracle(address(0x00)).updateValue.selector,
propertyId,
propertyKeysValues
)
validateProperty(propertyId)
returns (address)
{
_verifyValueNotEmpty(value);
_propertiesMetadata[propertyId]._verifyNotConstant();
address sourceContract = _safeGetSourceIfItHasStorageType(propertyId);
if (sourceContract == address(this)) {
_updateValueForThisStorage(
value,
propertyId,
propertyKeysValues
);
}
return sourceContract;
}
/**
* @notice Updates stored Property value in this storage
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function _updateValueForThisStorage(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
_propertiesValues[valueKey]._verifyPropertyHasValue();
bytes[] memory oldValue = _propertiesValues[valueKey].value;
bool sameValue = keccak256(abi.encode(value)) == keccak256(abi.encode(oldValue));
uint expirationTime = _propertiesAccessible[propertyId].expirationTime;
if (!sameValue || expirationTime > 0) {
_setValue(
value,
valueKey
);
}
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Update
);
}
/**
* @notice Removes stored Property value
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function _removeValueIfSourceIsThis(
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
verifyValueActionPermissions(
IComplianceOracle(address(0x00)).removeValue.selector,
propertyId,
propertyKeysValues
)
validateProperty(propertyId)
returns (address)
{
_propertiesMetadata[propertyId]._verifyNotConstant();
address sourceContract = _safeGetSourceIfItHasStorageType(propertyId);
if (sourceContract == address(this)) {
_removeValueForThisStorage(
propertyId,
propertyKeysValues
);
}
return sourceContract;
}
/**
* @notice Removes stored Property value from this storage
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function _removeValueForThisStorage(
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
_propertiesValues[valueKey]._verifyPropertyHasValue();
_deleteValue(valueKey);
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Remove
);
}
/**
* @notice Sender removes role from herself, initiating onRefuse action
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
* @param managerKeyPosition Manager key position
*/
function _refuseFromRoleIfSourceIsThis(
bytes32 propertyId,
bytes32[] memory propertyKeysValues,
uint managerKeyPosition
)
internal
validateBeforeRefuse(propertyId)
returns (address)
{
address onRefuse = _safeGetOnRefuse(propertyId);
address sourceContract = _safeGetSourceIfItHasStorageType(propertyId);
if (sourceContract == address(this)) {
_onRefuseFlowForThisStorage(
propertyId,
propertyKeysValues,
onRefuse,
managerKeyPosition
);
}
return sourceContract;
}
/**
* @notice Flow for onRefuse process if storage is this
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
* @param onRefuse Address to perform on refuse action
* @param managerKeyPosition Manager key position
*/
function _onRefuseFlowForThisStorage(
bytes32 propertyId,
bytes32[] memory propertyKeysValues,
address onRefuse,
uint managerKeyPosition
)
internal
{
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
bytes[] memory trueValue = _verifyRoleHasTrueValue(valueKey);
_performOnRefuseAction(
propertyId,
propertyKeysValues,
onRefuse,
managerKeyPosition,
trueValue
);
_deleteValue(valueKey);
emit PropertyValuesUpdated(
propertyId,
valueKey,
OperationType.Remove
);
}
/**
* @notice Performs on refuse action
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
* @param onRefuse On refuse action address
* @param managerKeyPosition Manager key position
* @param valueToSet Value to set for manager
*/
function _performOnRefuseAction(
bytes32 propertyId,
bytes32[] memory propertyKeysValues,
address onRefuse,
uint managerKeyPosition,
bytes[] memory valueToSet
)
internal
{
if (
onRefuse.isContract()
&& onRefuse._trySupportsInterface(
type(IOnRefuse).interfaceId
)
) {
IOnRefuse(onRefuse).onRefuse(
propertyId,
propertyKeysValues,
managerKeyPosition
);
} else {
_onRefuseEOA(
propertyId,
propertyKeysValues,
onRefuse,
managerKeyPosition,
valueToSet
);
}
}
/**
* @notice Performs on refuse for EOA manager
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
* @param onRefuse On refuse action address
* @param managerKeyPosition Manager key position
* @param valueToSet Value to set for manager
*/
function _onRefuseEOA(
bytes32 propertyId,
bytes32[] memory propertyKeysValues,
address onRefuse,
uint managerKeyPosition,
bytes[] memory valueToSet
)
internal
{
propertyKeysValues[managerKeyPosition] = onRefuse._addressToBytes32();
bytes32 newValueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
if (!_doesRoleHaveTrueValue(newValueKey)) {
_setValue(
valueToSet,
newValueKey
);
emit PropertyValuesUpdated(
propertyId,
newValueKey,
OperationType.Create
);
}
}
/**
* @notice Stores Property value
* @param value Value to be stored
* @param valueKey Value unique identifier
*/
function _setValue(
bytes[] memory value,
bytes32 valueKey
)
internal
{
_propertiesValues[valueKey].value = value;
_propertiesValues[valueKey].timestamp = block.timestamp;
}
/**
* @notice Deletes Property value
* @param valueKey Value unique identifier
*/
function _deleteValue(bytes32 valueKey) internal {
delete _propertiesValues[valueKey];
}
/**
* @notice Verifies role keys
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be removed
* @param additionalKeysValues Additional property keys values
* @return keys values and manager key position
*/
function _buildRoleKeysValues(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
view
returns (bytes32[] memory, uint)
{
require(
manager != address(0x00),
"_buildRoleKeysValues: Empty manager"
);
bool subIdExistsAndZero = _verifyRoleKeys(propertyId, subId);
bool subIdIzNone = subId == bytes32(0x00)
? (subIdExistsAndZero ? false : true)
: false;
uint mainLength = subIdIzNone ? 1 : 2;
uint length = additionalKeysValues.length + mainLength;
bytes32[] memory propertyKeysValues = new bytes32[](length);
uint additionalStart;
if (subIdIzNone) {
additionalStart = 1;
propertyKeysValues[0] = manager._addressToBytes32();
} else {
additionalStart = 2;
propertyKeysValues[0] = subId;
propertyKeysValues[1] = manager._addressToBytes32();
}
for (uint i = additionalStart; i < length; i++) {
propertyKeysValues[i] = additionalKeysValues[i - additionalStart];
}
return (propertyKeysValues, additionalStart - 1);
}
}
/**
* @title Common functions that are used to call compliance oracle
*/
library CommonComplianceOracle {
// Define libraries
using CustomRevert for *;
/**
* @notice Approves context creation
* @param complianceOracle Compliance oracle address
* @param contextInput Context input metadata
* @param owner Future context owner address
* @return result Unique context identifier
*/
function _tryApproveCreateContext(
address complianceOracle,
ContextInput memory contextInput,
address owner
)
internal
returns (uint result)
{
try IComplianceOracle(complianceOracle).approveCreateContext(
contextInput,
owner
)
returns (uint res)
{
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Accepts context creation
* @param complianceOracle Compliance oracle address
* @param requestId Request identifier
* @param contextInput Context input struct
* @return result Created context identifier
*/
function _tryAcceptCreateContext(
address complianceOracle,
uint requestId,
ContextInput memory contextInput
)
internal
returns (bytes32 result)
{
try IComplianceOracle(complianceOracle).acceptCreateContext(
requestId,
contextInput
)
returns (bytes32 res)
{
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Update a context
* @param complianceOracle Compliance oracle address
* @param contextInput Context input metadata
*/
function _tryUpdateContext(address complianceOracle, ContextInput memory contextInput) internal {
try IComplianceOracle(complianceOracle).updateContext(contextInput)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Disable a context
* @param complianceOracle Compliance oracle address
* @param name Context name
*/
function _tryDisableContext(address complianceOracle, string memory name) internal {
try IComplianceOracle(complianceOracle).disableContext(name)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Enable a context
* @param complianceOracle Compliance oracle address
* @param name Context name
*/
function _tryEnableContext(address complianceOracle, string memory name) internal {
try IComplianceOracle(complianceOracle).enableContext(name)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Create an Attestation property
* @param complianceOracle Property oracle address
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*
* @dev Creating property which inherits from another requires the following
* fields to be empty (because they are inherited from a parent property):
* - sourceId
* - dataType
* - externalId
* - isConstant
* - expirationTime
* - onRefuse
* @return result Unique property identifier
*/
function _tryCreateProperty(
address complianceOracle,
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
returns (bytes32 result)
{
try IComplianceOracle(complianceOracle).createProperty(
propertyInput,
propertyAccessible
) returns (bytes32 res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Update an Attestation property
* @param complianceOracle Property oracle address
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _tryUpdateProperty(
address complianceOracle,
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
{
try IComplianceOracle(complianceOracle).updateProperty(
propertyInput,
propertyAccessible
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Disable an Attestation property
* @param complianceOracle Property oracle address
* @param contextId Context identifier
* @param name Property name
*/
function _tryDisableProperty(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
{
try IComplianceOracle(complianceOracle).disableProperty(
contextId,
name
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Enable an Attestation property
* @param complianceOracle Property oracle address
* @param contextId Context identifier
* @param name Property name
*/
function _tryEnableProperty(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
{
try IComplianceOracle(complianceOracle).enableProperty(
contextId,
name
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Create a Data source
* @param complianceOracle Property oracle address
* @param sourceInput Data source input structure
* @return result Unique property identifier
*/
function _tryCreateSource(
address complianceOracle,
SourceInput memory sourceInput
)
internal
returns (bytes32 result)
{
try IComplianceOracle(complianceOracle).createSource(sourceInput) returns (bytes32 res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Update a Data source
* @param complianceOracle Property oracle address
* @param sourceInput Data source input structure
*/
function _tryUpdateSource(
address complianceOracle,
SourceInput memory sourceInput
)
internal
{
try IComplianceOracle(complianceOracle).updateSource(sourceInput)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Disable a Data source
* @param complianceOracle Property oracle address
* @param contextId Context identifier
* @param name Data source name
*/
function _tryDisableSource(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
{
try IComplianceOracle(complianceOracle).disableSource(
contextId,
name
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Enable a Data source
* @param complianceOracle Property oracle address
* @param contextId Context identifier
* @param name Data source name
*/
function _tryEnableSource(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
{
try IComplianceOracle(complianceOracle).enableSource(
contextId,
name
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Stores Property value if it is the first time
* @param complianceOracle Property oracle address
* @param value Value to be stored
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function _tryCreateValue(
address complianceOracle,
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).createValue(
value,
propertyId,
propertyKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Updates stored Property value
* @param complianceOracle Property oracle address
* @param value Value to be stored
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function _tryUpdateValue(
address complianceOracle,
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).updateValue(
value,
propertyId,
propertyKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Removes stored Property value
* @param complianceOracle Property oracle address
* @param propertyId Property name
* @param propertyKeysValues Property keys values
*/
function _tryRemoveValue(
address complianceOracle,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).removeValue(
propertyId,
propertyKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Stores Role value if it is the first time
* @param complianceOracle Property oracle address
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
*/
function _trySetRole(
address complianceOracle,
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).setRole(
value,
propertyId,
subId,
manager,
additionalKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Updates stored Role value
* @param complianceOracle Property oracle address
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be updated
* @param additionalKeysValues Additional property keys values
*/
function _tryUpdateRole(
address complianceOracle,
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).updateRole(
value,
propertyId,
subId,
manager,
additionalKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Removes stored Role value
* @param complianceOracle Property oracle address
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be removed
* @param additionalKeysValues Additional property keys values
*/
function _tryRemoveRole(
address complianceOracle,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).removeRole(
propertyId,
subId,
manager,
additionalKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Sender removes role from herself, initiating onRefuse action
* @param complianceOracle Property oracle address
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param additionalKeysValues Additional property keys values
*/
function _tryRefuseFromRole(
address complianceOracle,
bytes32 propertyId,
bytes32 subId,
bytes32[] memory additionalKeysValues
)
internal
{
try IComplianceOracle(complianceOracle).refuseFromRole(
propertyId,
subId,
additionalKeysValues
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Stores Properties values if it is the first time in batch
* @param complianceOracle Property oracle address
* @param valuesDetails Values by properties with keys to be stored
*/
function _tryBatchCreateValues(
address complianceOracle,
BatchSetValueDetails[] memory valuesDetails
)
internal
{
try IComplianceOracle(complianceOracle).batchCreateValues(valuesDetails)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Updates stored Properties values in batch
* @param complianceOracle Property oracle address
* @param valuesDetails Values by properties with keys to be stored
*/
function _tryBatchUpdateValues(
address complianceOracle,
BatchSetValueDetails[] memory valuesDetails
)
internal
{
try IComplianceOracle(complianceOracle).batchUpdateValues(valuesDetails)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice propertiesIds stored Properties values in batch
* @param complianceOracle Property oracle address
* @param valuesDetails Values by properties with keys to be deleted
*/
function _tryBatchRemoveValues(
address complianceOracle,
BatchDeleteValueDetails[] memory valuesDetails
)
internal
{
try IComplianceOracle(complianceOracle).batchRemoveValues(valuesDetails)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Requests attestation property
* @notice if property has external data source
* @param complianceOracle Property oracle address
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function _tryRequestValue(
address complianceOracle,
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
internal
returns (
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
)
{
try IComplianceOracle(complianceOracle).requestValue(
propertyId,
inputKeys,
inputKeysValues,
callbackAddress,
externalCallsSession
)
returns (
DataType valueDataTypeRes,
Value memory valueRes,
bytes32 errorCodeRes,
bytes32 requestIdRes
)
{
valueDataType = valueDataTypeRes;
value = valueRes;
errorCode = errorCodeRes;
requestId = requestIdRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Accepts callback from the oracles
* @param complianceOracle Property oracle address
* @param id Request identifier
* @param result External call responce
*/
function _tryCallback(
address complianceOracle,
bytes32 id,
string memory result
)
internal
{
try IComplianceOracle(complianceOracle).__callback(
id,
result
)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Registers owner for the sender
* @dev call registerOwner() using exception handling (try/catch)
* @param complianceOracle Compliance oracle address
* @param newOwner Owner address
*/
function _tryRegisterOwner(
address complianceOracle,
address newOwner
)
internal
{
try IComplianceOracle(complianceOracle).registerOwner(newOwner)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get context data
* @param complianceOracle Compliance oracle address
* @param name Context name
* @return contextMetadata Contex metadata
* @return contextEnumerable Contex enumerable data
*/
function _tryGetContext(
address complianceOracle,
string memory name
)
internal
view
returns (
ContextMetadata memory contextMetadata,
ContextEnumerable memory contextEnumerable
)
{
try IComplianceOracle(complianceOracle).getContext(name) returns (
ContextMetadata memory contextMetadataRes,
ContextEnumerable memory contextEnumerableRes
) {
contextMetadata = contextMetadataRes;
contextEnumerable = contextEnumerableRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get context data
* @param complianceOracle Compliance oracle address
* @param contextId Context identifier
* @return contextMetadata Contex metadata
* @return contextEnumerable Contex enumerable data
*/
function _tryGetContextById(
address complianceOracle,
bytes32 contextId
)
internal
view
returns (
ContextMetadata memory contextMetadata,
ContextEnumerable memory contextEnumerable
)
{
try IComplianceOracle(complianceOracle).getContextById(contextId) returns (
ContextMetadata memory contextMetadataRes,
ContextEnumerable memory contextEnumerableRes
) {
contextMetadata = contextMetadataRes;
contextEnumerable = contextEnumerableRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get property
* @param complianceOracle Compliance oracle address
* @param contextId Context identifier
* @param name Property name
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function _tryGetProperty(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
view
returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
try IComplianceOracle(complianceOracle).getProperty(contextId, name) returns (
PropertyMetadata memory metadataRes,
PropertyEnumerable memory enumerableRes,
PropertyAccessible memory accessibleRes
) {
metadata = metadataRes;
enumerable = enumerableRes;
accessible = accessibleRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get property by id
* @param complianceOracle Compliance oracle address
* @param propertyId Property identifier
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function _tryGetPropertyById(
address complianceOracle,
bytes32 propertyId
)
internal
view
returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
try IComplianceOracle(complianceOracle).getPropertyById(propertyId) returns (
PropertyMetadata memory metadataRes,
PropertyEnumerable memory enumerableRes,
PropertyAccessible memory accessibleRes
) {
metadata = metadataRes;
enumerable = enumerableRes;
accessible = accessibleRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get property by external identifier in context
* @param complianceOracle Compliance oracle address
* @param contextId Context identifier
* @param externalId External identifier
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function _tryGetPropertyByExternalId(
address complianceOracle,
bytes32 contextId,
bytes32 externalId
)
internal
view
returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
try IComplianceOracle(complianceOracle).getPropertyByExternalId(contextId, externalId) returns (
PropertyMetadata memory metadataRes,
PropertyEnumerable memory enumerableRes,
PropertyAccessible memory accessibleRes
) {
metadata = metadataRes;
enumerable = enumerableRes;
accessible = accessibleRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get property external calls total
* @param complianceOracle Compliance oracle address
* @param propertyId Property identifier
*/
function _tryGetPropertyExternalCallsTotal(
address complianceOracle,
bytes32 propertyId
)
internal
view
returns (uint result)
{
try IComplianceOracle(complianceOracle).getPropertyExternalCallsTotal(propertyId) returns (uint res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @return result - bool flag - true if property is existing
* @dev call isExistingProperty() using exception handling (try/catch)
* @param complianceOracle Compliance oracle address
* @param propertyId Property identifier
*/
function _tryIsExistingProperty(
address complianceOracle,
bytes32 propertyId
)
internal
view
returns (bool result)
{
try IComplianceOracle(complianceOracle).isExistingProperty(propertyId) returns (bool res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get Data source
* @param complianceOracle Compliance oracle address
* @param contextId Context identifier
* @param name Source name
* @return metadata Source metadata
* @return enumerable Source enumerable data
*/
function _tryGetSource(
address complianceOracle,
bytes32 contextId,
string memory name
)
internal
view
returns (
SourceMetadata memory metadata,
SourceEnumerable memory enumerable
)
{
try IComplianceOracle(complianceOracle).getSource(contextId, name) returns (
SourceMetadata memory metadataRes,
SourceEnumerable memory enumerableRes
) {
metadata = metadataRes;
enumerable = enumerableRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Get Data source by identifier
* @param complianceOracle Compliance oracle address
* @param sourceId Source identifier
* @return metadata Source metadata
* @return enumerable Source enumerable data
*/
function _tryGetSourceById(
address complianceOracle,
bytes32 sourceId
)
internal
view
returns (
SourceMetadata memory metadata,
SourceEnumerable memory enumerable
)
{
try IComplianceOracle(complianceOracle).getSourceById(sourceId) returns (
SourceMetadata memory metadataRes,
SourceEnumerable memory enumerableRes
) {
metadata = metadataRes;
enumerable = enumerableRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Returns Property value
* @notice if property has a storage or direct data source
* @param complianceOracle Property oracle address
* @param propertyId [required] Property unique id
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function _tryGetValue(
address complianceOracle,
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
view
returns (
DataType valueDataType,
Value memory value
)
{
try IComplianceOracle(complianceOracle).getValue(
propertyId,
inputKeys,
inputKeysValues
)
returns (
DataType valueDataTypeRes,
Value memory valueRes
)
{
valueDataType = valueDataTypeRes;
value = valueRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Returns Storage Property value
* @notice if property has a storage or direct data source
* @param complianceOracle Property oracle address
* @param propertyId [required] Property unique id
* @param keysValues [optional] Already calculated keys values
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function _tryGetStorageValue(
address complianceOracle,
bytes32 propertyId,
bytes32[] memory keysValues
)
internal
view
returns (
DataType valueDataType,
Value memory value
)
{
try IComplianceOracle(complianceOracle).getStorageValue(
propertyId,
keysValues
)
returns (
DataType valueDataTypeRes,
Value memory valueRes
)
{
valueDataType = valueDataTypeRes;
value = valueRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Returns role value
* @param complianceOracle Property oracle address
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
* @return value Value of the property
*/
function _tryGetRole(
address complianceOracle,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
view
returns (Value memory value)
{
try IComplianceOracle(complianceOracle).getRole(
propertyId,
subId,
manager,
additionalKeysValues
)
returns (Value memory valueRes)
{
value = valueRes;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Compliance Oracle storage setters methods
*/
contract ComplianceOracleStorageSetters is
ComplianceOracleStorageSettersInternal,
IComplianceOracleStorageSetters
{
// Define libraries
using CommonComplianceOracle for *;
/**
* @notice Stores Property value if it is the first time
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function createValue(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
public
override
{
address sourceContract = _createValueIfSourceIsThis(
value,
propertyId,
propertyKeysValues
);
if (sourceContract != address(this)) {
sourceContract._tryCreateValue(
value,
propertyId,
propertyKeysValues
);
}
}
/**
* @notice Updates stored Property value
* @param value Value to be stored
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function updateValue(
bytes[] memory value,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
public
override
{
address sourceContract = _updateValueIfSourceIsThis(
value,
propertyId,
propertyKeysValues
);
if (sourceContract != address(this)) {
sourceContract._tryUpdateValue(
value,
propertyId,
propertyKeysValues
);
}
}
/**
* @notice Removes stored Property value
* @param propertyId Property unique id
* @param propertyKeysValues Property keys values
*/
function removeValue(
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
public
override
{
address sourceContract = _removeValueIfSourceIsThis(
propertyId,
propertyKeysValues
);
if (sourceContract != address(this)) {
sourceContract._tryRemoveValue(
propertyId,
propertyKeysValues
);
}
}
}
/**
* @title Compliance Oracle storage roles setters methods
*/
contract ComplianceOracleStorageRolesSetters is
ComplianceOracleStorageSetters,
IComplianceOracleStorageRolesSetters
{
// Define libraries
using CommonComplianceOracle for *;
using BytesHelper for *;
/**
* @notice Create Role value
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
*/
function setRole(
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
public
override
{
_verifyPropertyHasBooleanDataType(propertyId);
(bytes32[] memory propertyKeysValues,) = _buildRoleKeysValues(
propertyId,
subId,
manager,
additionalKeysValues
);
createValue(
value._boolToBytes(),
propertyId,
propertyKeysValues
);
}
/**
* @notice Updates Role value
* @param value True or false
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be updated
* @param additionalKeysValues Additional property keys values
*/
function updateRole(
bool value,
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
public
override
{
_verifyPropertyHasBooleanDataType(propertyId);
(bytes32[] memory propertyKeysValues,) = _buildRoleKeysValues(
propertyId,
subId,
manager,
additionalKeysValues
);
updateValue(
value._boolToBytes(),
propertyId,
propertyKeysValues
);
}
/**
* @notice Removes stored Role value
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be removed
* @param additionalKeysValues Additional property keys values
*/
function removeRole(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
public
override
{
_verifyPropertyHasBooleanDataType(propertyId);
(bytes32[] memory propertyKeysValues,) = _buildRoleKeysValues(
propertyId,
subId,
manager,
additionalKeysValues
);
removeValue(
propertyId,
propertyKeysValues
);
}
/**
* @notice Sender removes role from herself, initiating onRefuse action
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param additionalKeysValues Additional property keys values
*/
function refuseFromRole(
bytes32 propertyId,
bytes32 subId,
bytes32[] memory additionalKeysValues
)
public
override
{
(
bytes32[] memory propertyKeysValues,
uint managerKeyPosition
) = _buildRoleKeysValues(
propertyId,
subId,
msg.sender,
additionalKeysValues
);
address sourceContract = _refuseFromRoleIfSourceIsThis(
propertyId,
propertyKeysValues,
managerKeyPosition
);
if (sourceContract != address(this)) {
sourceContract._tryRefuseFromRole(
propertyId,
subId,
additionalKeysValues
);
}
}
}
/**
* @title Compliance Oracle storage storage getters internal methods
*/
contract ComplianceOracleValuesGettersInternal is ComplianceOracleValuesGettersInternalVerifications {
// Define libraries
using BytesHelper for *;
using SystemUtils for *;
using CommonComplianceOracle for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Verifies a property for requesting/getting value
* @param propertyId Property unique identifier
*/
modifier validateBeforeFetch(bytes32 propertyId) {
// Verify property exists
_propertiesEnumerable[propertyId]._verifyPropertyExists();
_propertiesEnumerable[propertyId]._verifyPropertyNotDisabled();
_;
}
/**
* @notice Internal method to return a Property value
* @notice if property has a storage or direct data source
* @param propertyId [required] Property unique id
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function _getValue(
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
view
returns (
DataType valueDataType,
Value memory value
)
{
// Get source
(bytes32 sourceId, SourceType sourceType) = _safeGetSourceIdAndType(propertyId);
// Get values for property keys
bytes32[] memory keys = _propertiesAccessible[propertyId]._getPropertyKeys();
bytes32[] memory keysValues = keys._getKeysValues(
inputKeys,
inputKeysValues
);
// Process get storage value
if (sourceType == SourceType.Storage) {
return _getStorageValue(
sourceId,
propertyId,
keysValues
);
}
// Process get direct value
if (sourceType == SourceType.Direct) {
return _getDirectValue(
propertyId,
keysValues,
inputKeys,
inputKeysValues
);
}
revert("_getValue: Wrong source");
}
/**
* @notice Returns Storage Property value
* @param sourceId [required] Storage source id
* @param propertyId [required] Property unique identifier
* @param propertyKeysValues [optional] Values of the Property keys
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function _getStorageValue(
bytes32 sourceId,
bytes32 propertyId,
bytes32[] memory propertyKeysValues
)
internal
view
returns (
DataType valueDataType,
Value memory value
)
{
address sourceContract = _sourcesMetadata[sourceId]._safeGetSourceStorageContract();
if (sourceContract != address(this)) {
return sourceContract._tryGetStorageValue(
propertyId,
propertyKeysValues
);
}
bytes32 valueKey = propertyKeysValues.generateKey(propertyId);
PropertyMetadata memory propertyMetadata = _propertiesMetadata[propertyId];
uint expirationTime = _propertiesAccessible[propertyId].expirationTime;
if (expirationTime > 0) {
uint timestamp = _propertiesValues[valueKey].timestamp;
uint currentTime = block.timestamp;
require(
currentTime - timestamp <= expirationTime,
string(abi.encodePacked(
"Expired value {",
propertyMetadata.name,
"}, context {",
_contextsMetadata[propertyMetadata.contextId].name,
"}, keys values: {",
propertyKeysValues._bytes32ArrayToASCIIBytes(),
"}"
))
);
}
return (
_propertiesMetadata[propertyId].dataType,
_propertiesValues[valueKey]
);
}
/**
* @notice Returns Direct Property value
* @param propertyId [required] Property unique identifier
* @param propertyKeysValues [opional] Values of the Property keys
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function _getDirectValue(
bytes32 propertyId,
bytes32[] memory propertyKeysValues,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
view
returns (
DataType valueDataType,
Value memory value
)
{
bytes32 valueKey = SystemUtils.generateKey(
propertyKeysValues,
propertyId
);
bytes[] memory valueBytes = new bytes[](1);
valueBytes[0] = abi.encodePacked(valueKey._getValueFromKeys(
inputKeys,
inputKeysValues
));
return (
_propertiesMetadata[propertyId].dataType,
Value({
value: valueBytes,
timestamp: block.timestamp
})
);
}
/**
* @notice Verifies role keys
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be removed
* @param additionalKeysValues Additional property keys values
* @return keys values
*/
function _buildRoleKeysValues(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
internal
view
returns (bytes32[] memory)
{
require(
manager != address(0x00),
"Empty manager address"
);
bool subIdExistsAndZero = _verifyRoleKeys(propertyId, subId);
bool subIdIzNone = subId == bytes32(0x00)
? (subIdExistsAndZero ? false : true)
: false;
uint mainLength = subIdIzNone ? 1 : 2;
uint length = additionalKeysValues.length + mainLength;
bytes32[] memory propertyKeysValues = new bytes32[](length);
uint additionalStart;
if (subIdIzNone) {
additionalStart = 1;
propertyKeysValues[0] = manager._addressToBytes32();
} else {
additionalStart = 2;
propertyKeysValues[0] = subId;
propertyKeysValues[1] = manager._addressToBytes32();
}
for (uint i = additionalStart; i < length; i++) {
propertyKeysValues[i] = additionalKeysValues[i - additionalStart];
}
return propertyKeysValues;
}
}
/*
ORACLIZE_API
Original Copyright (c) 2015-2016 Oraclize SRL
Original Copyright (c) 2016 Oraclize LTD
Modified Copyright (c) 2020 SECURRENCY INC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Dummy contract only used to emit to end-user they are using wrong solc
abstract contract solcChecker {
/* INCOMPATIBLE SOLC: import the following instead: "github.com/oraclize/ethereum-api/oraclizeAPI_0.4.sol" */ function f(bytes calldata x) external virtual;
}
/*
Begin solidity-cborutils
https://github.com/smartcontractkit/solidity-cborutils
MIT License
Copyright (c) 2018 SmartContract ChainLink, Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
library Buffer {
struct buffer {
bytes buf;
uint capacity;
}
function init(buffer memory _buf, uint _capacity) internal pure {
uint capacity = _capacity;
if (capacity % 32 != 0) {
capacity += 32 - (capacity % 32);
}
_buf.capacity = capacity; // Allocate space for the buffer data
assembly {
let ptr := mload(0x40)
mstore(_buf, ptr)
mstore(ptr, 0)
mstore(0x40, add(ptr, capacity))
}
}
function resize(buffer memory _buf, uint _capacity) private pure {
bytes memory oldbuf = _buf.buf;
init(_buf, _capacity);
append(_buf, oldbuf);
}
function max(uint _a, uint _b) private pure returns (uint _max) {
if (_a > _b) {
return _a;
}
return _b;
}
/**
* @dev Appends a bytes1 array to the end of the buffer. Resizes if doing so
* would exceed the capacity of the buffer.
* @param _buf The buffer to append to.
* @param _data The data to append.
* @return _buffer The original buffer.
*
*/
function append(buffer memory _buf, bytes memory _data) internal pure returns (buffer memory _buffer) {
if (_data.length + _buf.buf.length > _buf.capacity) {
resize(_buf, max(_buf.capacity, _data.length) * 2);
}
uint dest;
uint src;
uint len = _data.length;
assembly {
let bufptr := mload(_buf) // Memory address of the buffer data
let buflen := mload(bufptr) // Length of existing buffer data
dest := add(add(bufptr, buflen), 32) // Start address = buffer address + buffer length + sizeof(buffer length)
mstore(bufptr, add(buflen, mload(_data))) // Update buffer length
src := add(_data, 32)
}
for(; len >= 32; len -= 32) { // Copy word-length chunks while possible
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
uint mask = 256 ** (32 - len) - 1; // Copy remaining bytes
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
return _buf;
}
/**
*
* @dev Appends a bytes1 to the end of the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param _buf The buffer to append to.
* @param _data The data to append.
*
*/
function append(buffer memory _buf, uint8 _data) internal pure {
if (_buf.buf.length + 1 > _buf.capacity) {
resize(_buf, _buf.capacity * 2);
}
assembly {
let bufptr := mload(_buf) // Memory address of the buffer data
let buflen := mload(bufptr) // Length of existing buffer data
let dest := add(add(bufptr, buflen), 32) // Address = buffer address + buffer length + sizeof(buffer length)
mstore8(dest, _data)
mstore(bufptr, add(buflen, 1)) // Update buffer length
}
}
/**
*
* @dev Appends a bytes1 to the end of the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param _buf The buffer to append to.
* @param _data The data to append.
* @return _buffer The original buffer.
*
*/
function appendInt(buffer memory _buf, uint _data, uint _len) internal pure returns (buffer memory _buffer) {
if (_len + _buf.buf.length > _buf.capacity) {
resize(_buf, max(_buf.capacity, _len) * 2);
}
uint mask = 256 ** _len - 1;
assembly {
let bufptr := mload(_buf) // Memory address of the buffer data
let buflen := mload(bufptr) // Length of existing buffer data
let dest := add(add(bufptr, buflen), _len) // Address = buffer address + buffer length + sizeof(buffer length) + len
mstore(dest, or(and(mload(dest), not(mask)), _data))
mstore(bufptr, add(buflen, _len)) // Update buffer length
}
return _buf;
}
}
library CBOR {
using Buffer for Buffer.buffer;
uint8 private constant MAJOR_TYPE_INT = 0;
uint8 private constant MAJOR_TYPE_MAP = 5;
uint8 private constant MAJOR_TYPE_BYTES = 2;
uint8 private constant MAJOR_TYPE_ARRAY = 4;
uint8 private constant MAJOR_TYPE_STRING = 3;
uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1;
uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7;
function encodeType(Buffer.buffer memory _buf, uint8 _major, uint _value) private pure {
if (_value <= 23) {
_buf.append(uint8((_major << 5) | _value));
} else if (_value <= 0xFF) {
_buf.append(uint8((_major << 5) | 24));
_buf.appendInt(_value, 1);
} else if (_value <= 0xFFFF) {
_buf.append(uint8((_major << 5) | 25));
_buf.appendInt(_value, 2);
} else if (_value <= 0xFFFFFFFF) {
_buf.append(uint8((_major << 5) | 26));
_buf.appendInt(_value, 4);
} else if (_value <= 0xFFFFFFFFFFFFFFFF) {
_buf.append(uint8((_major << 5) | 27));
_buf.appendInt(_value, 8);
}
}
function encodeIndefiniteLengthType(Buffer.buffer memory _buf, uint8 _major) private pure {
_buf.append(uint8((_major << 5) | 31));
}
function encodeUInt(Buffer.buffer memory _buf, uint _value) internal pure {
encodeType(_buf, MAJOR_TYPE_INT, _value);
}
function encodeInt(Buffer.buffer memory _buf, int _value) internal pure {
if (_value >= 0) {
encodeType(_buf, MAJOR_TYPE_INT, uint(_value));
} else {
encodeType(_buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - _value));
}
}
function encodeBytes(Buffer.buffer memory _buf, bytes memory _value) internal pure {
encodeType(_buf, MAJOR_TYPE_BYTES, _value.length);
_buf.append(_value);
}
function encodeString(Buffer.buffer memory _buf, string memory _value) internal pure {
encodeType(_buf, MAJOR_TYPE_STRING, bytes(_value).length);
_buf.append(bytes(_value));
}
function startArray(Buffer.buffer memory _buf) internal pure {
encodeIndefiniteLengthType(_buf, MAJOR_TYPE_ARRAY);
}
function startMap(Buffer.buffer memory _buf) internal pure {
encodeIndefiniteLengthType(_buf, MAJOR_TYPE_MAP);
}
function endSequence(Buffer.buffer memory _buf) internal pure {
encodeIndefiniteLengthType(_buf, MAJOR_TYPE_CONTENT_FREE);
}
}
/*
End solidity-cborutils
*/
contract usingOraclize is ComplianceOracleStorage {
using CBOR for Buffer.buffer;
modifier oraclizeAPI {
if ((address(_oraclizeAddressResolver) == address(0)) || (getCodeSize(address(_oraclizeAddressResolver)) == 0)) {
oraclize_setNetwork(networkID_auto);
}
if (address(_oraclize) != _oraclizeAddressResolver.getAddress()) {
_oraclize = OraclizeI(_oraclizeAddressResolver.getAddress());
}
_;
}
modifier oraclize_randomDS_proofVerify(bytes32 _queryId, string memory _result, bytes memory _proof) {
// RandomDS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1)
require((_proof[0] == "L") && (_proof[1] == "P") && (uint8(_proof[2]) == uint8(1)));
bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName());
require(proofVerified);
_;
}
function oraclize_setNetwork(uint8 _networkID) internal returns (bool _networkSet) {
_networkID; // NOTE: Silence the warning and remain backwards compatible
return oraclize_setNetwork();
}
function oraclize_setNetworkName(string memory _network_name) internal {
oraclize_network_name = _network_name;
}
function oraclize_getNetworkName() internal view returns (string memory _networkName) {
return oraclize_network_name;
}
function oraclize_setNetwork() internal returns (bool _networkSet) {
if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed) > 0) { //mainnet
_oraclizeAddressResolver = OraclizeAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed);
oraclize_setNetworkName("eth_mainnet");
return true;
}
if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1) > 0) { //ropsten testnet
_oraclizeAddressResolver = OraclizeAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1);
oraclize_setNetworkName("eth_ropsten3");
return true;
}
if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e) > 0) { //kovan testnet
_oraclizeAddressResolver = OraclizeAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e);
oraclize_setNetworkName("eth_kovan");
return true;
}
if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48) > 0) { //rinkeby testnet
_oraclizeAddressResolver = OraclizeAddrResolverI(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48);
oraclize_setNetworkName("eth_rinkeby");
return true;
}
if (getCodeSize(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41) > 0) { //goerli testnet
_oraclizeAddressResolver = OraclizeAddrResolverI(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41);
oraclize_setNetworkName("eth_goerli");
return true;
}
if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475) > 0) { //ethereum-bridge
_oraclizeAddressResolver = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475);
return true;
}
if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF) > 0) { //ether.camp ide
_oraclizeAddressResolver = OraclizeAddrResolverI(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF);
return true;
}
if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA) > 0) { //browser-solidity
_oraclizeAddressResolver = OraclizeAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA);
return true;
}
return false;
}
/**
* @dev The following `__callback` functions are just placeholders ideally
* meant to be defined in child contract when proofs are used.
* The function bodies simply silence compiler warnings.
*/
function __callback(bytes32 _myid, string memory _result) public virtual {
__callback(_myid, _result, new bytes(0));
}
function __callback(bytes32 _myid, string memory _result, bytes memory _proof) public {
_myid; _result; _proof;
_oraclize_randomDS_args[bytes32(0)] = bytes32(0);
}
function oraclize_getPrice(string memory _datasource) oraclizeAPI internal returns (uint _queryPrice) {
return _oraclize.getPrice(_datasource);
}
function oraclize_getPrice(string memory _datasource, uint _gasLimit) oraclizeAPI internal returns (uint _queryPrice) {
return _oraclize.getPrice(_datasource, _gasLimit);
}
function oraclize_query(string memory _datasource, string memory _arg) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
return _oraclize.query{value: price}(0, _datasource, _arg);
}
function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
return _oraclize.query{value: price}(_timestamp, _datasource, _arg);
}
function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource,_gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
return _oraclize.query_withGasLimit{value: price}(_timestamp, _datasource, _arg, _gasLimit);
}
function oraclize_query(string memory _datasource, string memory _arg, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
return _oraclize.query_withGasLimit{value: price}(0, _datasource, _arg, _gasLimit);
}
function oraclize_query(string memory _datasource, string memory _arg1, string memory _arg2) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
return _oraclize.query2{value: price}(0, _datasource, _arg1, _arg2);
}
function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
return _oraclize.query2{value: price}(_timestamp, _datasource, _arg1, _arg2);
}
function oraclize_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
return _oraclize.query2_withGasLimit{value: price}(_timestamp, _datasource, _arg1, _arg2, _gasLimit);
}
function oraclize_query(string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
return _oraclize.query2_withGasLimit{value: price}(0, _datasource, _arg1, _arg2, _gasLimit);
}
function oraclize_query(string memory _datasource, string[] memory _argN) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
bytes memory args = stra2cbor(_argN);
return _oraclize.queryN{value: price}(0, _datasource, args);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[] memory _argN) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
bytes memory args = stra2cbor(_argN);
return _oraclize.queryN{value: price}(_timestamp, _datasource, args);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
bytes memory args = stra2cbor(_argN);
return _oraclize.queryN_withGasLimit{value: price}(_timestamp, _datasource, args, _gasLimit);
}
function oraclize_query(string memory _datasource, string[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
bytes memory args = stra2cbor(_argN);
return _oraclize.queryN_withGasLimit{value: price}(0, _datasource, args, _gasLimit);
}
function oraclize_query(string memory _datasource, string[1] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](1);
dynargs[0] = _args[0];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[1] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](1);
dynargs[0] = _args[0];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](1);
dynargs[0] = _args[0];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](1);
dynargs[0] = _args[0];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[2] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[2] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[3] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[3] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[4] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[4] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[5] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[5] memory _args) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, string[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, string[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
string[] memory dynargs = new string[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[] memory _argN) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
bytes memory args = ba2cbor(_argN);
return _oraclize.queryN{value: price}(0, _datasource, args);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[] memory _argN) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource);
if (price > 1 ether + tx.gasprice * 200000) {
return 0; // Unexpectedly high price
}
bytes memory args = ba2cbor(_argN);
return _oraclize.queryN{value: price}(_timestamp, _datasource, args);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
bytes memory args = ba2cbor(_argN);
return _oraclize.queryN_withGasLimit{value: price}(_timestamp, _datasource, args, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[] memory _argN, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
uint price = _oraclize.getPrice(_datasource, _gasLimit);
if (price > 1 ether + tx.gasprice * _gasLimit) {
return 0; // Unexpectedly high price
}
bytes memory args = ba2cbor(_argN);
return _oraclize.queryN_withGasLimit{value: price}(0, _datasource, args, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[1] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](1);
dynargs[0] = _args[0];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[1] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](1);
dynargs[0] = _args[0];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](1);
dynargs[0] = _args[0];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[1] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](1);
dynargs[0] = _args[0];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[2] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[2] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[2] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](2);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[3] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[3] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[3] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](3);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[4] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[4] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[4] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](4);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[5] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[5] memory _args) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_timestamp, _datasource, dynargs);
}
function oraclize_query(uint _timestamp, string memory _datasource, bytes[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_timestamp, _datasource, dynargs, _gasLimit);
}
function oraclize_query(string memory _datasource, bytes[5] memory _args, uint _gasLimit) oraclizeAPI internal returns (bytes32 _id) {
bytes[] memory dynargs = new bytes[](5);
dynargs[0] = _args[0];
dynargs[1] = _args[1];
dynargs[2] = _args[2];
dynargs[3] = _args[3];
dynargs[4] = _args[4];
return oraclize_query(_datasource, dynargs, _gasLimit);
}
function oraclize_setProof(bytes1 _proofP) oraclizeAPI internal {
return _oraclize.setProofType(_proofP);
}
function oraclize_cbAddress() oraclizeAPI internal returns (address _callbackAddress) {
return _oraclize.cbAddress();
}
function getCodeSize(address _addr) view internal returns (uint _size) {
assembly {
_size := extcodesize(_addr)
}
}
function oraclize_setCustomGasPrice(uint _gasPrice) oraclizeAPI internal {
return _oraclize.setCustomGasPrice(_gasPrice);
}
function oraclize_randomDS_getSessionPubKeyHash() oraclizeAPI internal returns (bytes32 _sessionKeyHash) {
return _oraclize.randomDS_getSessionPubKeyHash();
}
function parseAddr(string memory _a) internal pure returns (address _parsedAddress) {
bytes memory tmp = bytes(_a);
uint160 iaddr = 0;
uint160 b1;
uint160 b2;
for (uint i = 2; i < 2 + 2 * 20; i += 2) {
iaddr *= 256;
b1 = uint160(uint8(tmp[i]));
b2 = uint160(uint8(tmp[i + 1]));
if ((b1 >= 97) && (b1 <= 102)) {
b1 -= 87;
} else if ((b1 >= 65) && (b1 <= 70)) {
b1 -= 55;
} else if ((b1 >= 48) && (b1 <= 57)) {
b1 -= 48;
}
if ((b2 >= 97) && (b2 <= 102)) {
b2 -= 87;
} else if ((b2 >= 65) && (b2 <= 70)) {
b2 -= 55;
} else if ((b2 >= 48) && (b2 <= 57)) {
b2 -= 48;
}
iaddr += (b1 * 16 + b2);
}
return address(iaddr);
}
function strCompare(string memory _a, string memory _b) internal pure returns (int _returnCode) {
bytes memory a = bytes(_a);
bytes memory b = bytes(_b);
uint minLength = a.length;
if (b.length < minLength) {
minLength = b.length;
}
for (uint i = 0; i < minLength; i ++) {
if (a[i] < b[i]) {
return -1;
} else if (a[i] > b[i]) {
return 1;
}
}
if (a.length < b.length) {
return -1;
} else if (a.length > b.length) {
return 1;
} else {
return 0;
}
}
function indexOf(string memory _haystack, string memory _needle) internal pure returns (int _returnCode) {
bytes memory h = bytes(_haystack);
bytes memory n = bytes(_needle);
if (h.length < 1 || n.length < 1 || (n.length > h.length)) {
return -1;
} else if (h.length > (2 ** 128 - 1)) {
return -1;
} else {
uint subindex = 0;
for (uint i = 0; i < h.length; i++) {
if (h[i] == n[0]) {
subindex = 1;
while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) {
subindex++;
}
if (subindex == n.length) {
return int(i);
}
}
}
return -1;
}
}
function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) {
return strConcat(_a, _b, "", "", "");
}
function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) {
return strConcat(_a, _b, _c, "", "");
}
function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) {
return strConcat(_a, _b, _c, _d, "");
}
function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) {
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
bytes memory _bc = bytes(_c);
bytes memory _bd = bytes(_d);
bytes memory _be = bytes(_e);
string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
bytes memory babcde = bytes(abcde);
uint k = 0;
uint i = 0;
for (i = 0; i < _ba.length; i++) {
babcde[k++] = _ba[i];
}
for (i = 0; i < _bb.length; i++) {
babcde[k++] = _bb[i];
}
for (i = 0; i < _bc.length; i++) {
babcde[k++] = _bc[i];
}
for (i = 0; i < _bd.length; i++) {
babcde[k++] = _bd[i];
}
for (i = 0; i < _be.length; i++) {
babcde[k++] = _be[i];
}
return string(babcde);
}
function safeParseInt(string memory _a) internal pure returns (uint _parsedInt) {
return safeParseInt(_a, 0);
}
function safeParseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) {
bytes memory bresult = bytes(_a);
uint mint = 0;
bool decimals = false;
for (uint i = 0; i < bresult.length; i++) {
if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) {
if (decimals) {
if (_b == 0) break;
else _b--;
}
mint *= 10;
mint += uint(uint8(bresult[i])) - 48;
} else if (uint(uint8(bresult[i])) == 46) {
require(!decimals, 'More than one decimal encountered in string!');
decimals = true;
} else {
revert("Non-numeral character encountered in string!");
}
}
if (_b > 0) {
mint *= 10 ** _b;
}
return mint;
}
function parseInt(string memory _a) internal pure returns (uint _parsedInt) {
return parseInt(_a, 0);
}
function parseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) {
bytes memory bresult = bytes(_a);
uint mint = 0;
bool decimals = false;
for (uint i = 0; i < bresult.length; i++) {
if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) {
if (decimals) {
if (_b == 0) {
break;
} else {
_b--;
}
}
mint *= 10;
mint += uint(uint8(bresult[i])) - 48;
} else if (uint(uint8(bresult[i])) == 46) {
decimals = true;
}
}
if (_b > 0) {
mint *= 10 ** _b;
}
return mint;
}
function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return "0";
}
uint j = _i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;
while (_i != 0) {
bstr[k--] = bytes1(uint8(48 + _i % 10));
_i /= 10;
}
return string(bstr);
}
function stra2cbor(string[] memory _arr) internal pure returns (bytes memory _cborEncoding) {
safeMemoryCleaner();
Buffer.buffer memory buf;
Buffer.init(buf, 1024);
buf.startArray();
for (uint i = 0; i < _arr.length; i++) {
buf.encodeString(_arr[i]);
}
buf.endSequence();
return buf.buf;
}
function ba2cbor(bytes[] memory _arr) internal pure returns (bytes memory _cborEncoding) {
safeMemoryCleaner();
Buffer.buffer memory buf;
Buffer.init(buf, 1024);
buf.startArray();
for (uint i = 0; i < _arr.length; i++) {
buf.encodeBytes(_arr[i]);
}
buf.endSequence();
return buf.buf;
}
function oraclize_newRandomDSQuery(uint _delay, uint _nbytes, uint _customGasLimit) internal returns (bytes32 _queryId) {
require((_nbytes > 0) && (_nbytes <= 32));
_delay *= 10; // Convert from seconds to ledger timer ticks
bytes memory nbytes = new bytes(1);
nbytes[0] = bytes1(uint8(_nbytes));
bytes memory unonce = new bytes(32);
bytes memory sessionKeyHash = new bytes(32);
bytes32 sessionKeyHash_bytes32 = oraclize_randomDS_getSessionPubKeyHash();
assembly {
mstore(unonce, 0x20)
/*
The following variables can be relaxed.
Check the relaxed random contract at https://github.com/oraclize/ethereum-examples
for an idea on how to override and replace commit hash variables.
*/
mstore(add(unonce, 0x20), xor(blockhash(sub(number(), 1)), xor(coinbase(), timestamp())))
mstore(sessionKeyHash, 0x20)
mstore(add(sessionKeyHash, 0x20), sessionKeyHash_bytes32)
}
bytes memory delay = new bytes(32);
assembly {
mstore(add(delay, 0x20), _delay)
}
bytes memory delay_bytes8 = new bytes(8);
copyBytes(delay, 24, 8, delay_bytes8, 0);
bytes[4] memory args = [unonce, nbytes, sessionKeyHash, delay];
bytes32 queryId = oraclize_query("random", args, _customGasLimit);
bytes memory delay_bytes8_left = new bytes(8);
assembly {
let x := mload(add(delay_bytes8, 0x20))
mstore8(add(delay_bytes8_left, 0x27), div(x, 0x100000000000000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x26), div(x, 0x1000000000000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x25), div(x, 0x10000000000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x24), div(x, 0x100000000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x23), div(x, 0x1000000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x22), div(x, 0x10000000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x21), div(x, 0x100000000000000000000000000000000000000000000000000))
mstore8(add(delay_bytes8_left, 0x20), div(x, 0x1000000000000000000000000000000000000000000000000))
}
oraclize_randomDS_setCommitment(queryId, keccak256(abi.encodePacked(delay_bytes8_left, args[1], sha256(args[0]), args[2])));
return queryId;
}
function oraclize_randomDS_setCommitment(bytes32 _queryId, bytes32 _commitment) internal {
_oraclize_randomDS_args[_queryId] = _commitment;
}
function verifySig(bytes32 _tosignh, bytes memory _dersig, bytes memory _pubkey) internal returns (bool _sigVerified) {
bool sigok;
address signer;
bytes32 sigr;
bytes32 sigs;
bytes memory sigr_ = new bytes(32);
uint offset = 4 + (uint(uint8(_dersig[3])) - 0x20);
sigr_ = copyBytes(_dersig, offset, 32, sigr_, 0);
bytes memory sigs_ = new bytes(32);
offset += 32 + 2;
sigs_ = copyBytes(_dersig, offset + (uint(uint8(_dersig[offset - 1])) - 0x20), 32, sigs_, 0);
assembly {
sigr := mload(add(sigr_, 32))
sigs := mload(add(sigs_, 32))
}
(sigok, signer) = safer_ecrecover(_tosignh, 27, sigr, sigs);
if (address(uint160(uint256(keccak256(_pubkey)))) == signer) {
return true;
} else {
(sigok, signer) = safer_ecrecover(_tosignh, 28, sigr, sigs);
return (address(uint160(uint256(keccak256(_pubkey)))) == signer);
}
}
function oraclize_randomDS_proofVerify__sessionKeyValidity(bytes memory _proof, uint _sig2offset) internal returns (bool _proofVerified) {
bool sigok;
// Random DS Proof Step 6: Verify the attestation signature, APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH)
bytes memory sig2 = new bytes(uint(uint8(_proof[_sig2offset + 1])) + 2);
copyBytes(_proof, _sig2offset, sig2.length, sig2, 0);
bytes memory appkey1_pubkey = new bytes(64);
copyBytes(_proof, 3 + 1, 64, appkey1_pubkey, 0);
bytes memory tosign2 = new bytes(1 + 65 + 32);
tosign2[0] = bytes1(uint8(1)); //role
copyBytes(_proof, _sig2offset - 65, 65, tosign2, 1);
bytes memory CODEHASH = hex"fd94fa71bc0ba10d39d464d0d8f465efeef0a2764e3887fcc9df41ded20f505c";
copyBytes(CODEHASH, 0, 32, tosign2, 1 + 65);
sigok = verifySig(sha256(tosign2), sig2, appkey1_pubkey);
if (!sigok) {
return false;
}
// Random DS Proof Step 7: Verify the APPKEY1 provenance (must be signed by Ledger)
bytes memory LEDGERKEY = hex"7fb956469c5c9b89840d55b43537e66a98dd4811ea0a27224272c2e5622911e8537a2f8e86a46baec82864e98dd01e9ccc2f8bc5dfc9cbe5a91a290498dd96e4";
bytes memory tosign3 = new bytes(1 + 65);
tosign3[0] = 0xFE;
copyBytes(_proof, 3, 65, tosign3, 1);
bytes memory sig3 = new bytes(uint(uint8(_proof[3 + 65 + 1])) + 2);
copyBytes(_proof, 3 + 65, sig3.length, sig3, 0);
sigok = verifySig(sha256(tosign3), sig3, LEDGERKEY);
return sigok;
}
function oraclize_randomDS_proofVerify__returnCode(bytes32 _queryId, string memory _result, bytes memory _proof) internal returns (uint8 _returnCode) {
// Random DS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1)
if ((_proof[0] != "L") || (_proof[1] != "P") || (uint8(_proof[2]) != uint8(1))) {
return 1;
}
bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName());
if (!proofVerified) {
return 2;
}
return 0;
}
function matchBytes32Prefix(bytes32 _content, bytes memory _prefix, uint _nRandomBytes) internal pure returns (bool _matchesPrefix) {
bool match_ = true;
require(_prefix.length == _nRandomBytes);
for (uint256 i = 0; i< _nRandomBytes; i++) {
if (_content[i] != _prefix[i]) {
match_ = false;
}
}
return match_;
}
function oraclize_randomDS_proofVerify__main(bytes memory _proof, bytes32 _queryId, bytes memory _result, string memory _contextName) internal returns (bool _proofVerified) {
// Random DS Proof Step 2: The unique keyhash has to match with the sha256 of (context name + _queryId)
uint ledgerProofLength = 3 + 65 + (uint(uint8(_proof[3 + 65 + 1])) + 2) + 32;
bytes memory keyhash = new bytes(32);
copyBytes(_proof, ledgerProofLength, 32, keyhash, 0);
if (!(keccak256(keyhash) == keccak256(abi.encodePacked(sha256(abi.encodePacked(_contextName, _queryId)))))) {
return false;
}
bytes memory sig1 = new bytes(uint(uint8(_proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1])) + 2);
copyBytes(_proof, ledgerProofLength + (32 + 8 + 1 + 32), sig1.length, sig1, 0);
// Random DS Proof Step 3: We assume sig1 is valid (it will be verified during step 5) and we verify if '_result' is the _prefix of sha256(sig1)
if (!matchBytes32Prefix(sha256(sig1), _result, uint(uint8(_proof[ledgerProofLength + 32 + 8])))) {
return false;
}
// Random DS Proof Step 4: Commitment match verification, keccak256(delay, nbytes, unonce, sessionKeyHash) == commitment in storage.
// This is to verify that the computed args match with the ones specified in the query.
bytes memory commitmentSlice1 = new bytes(8 + 1 + 32);
copyBytes(_proof, ledgerProofLength + 32, 8 + 1 + 32, commitmentSlice1, 0);
bytes memory sessionPubkey = new bytes(64);
uint sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1.length + 65;
copyBytes(_proof, sig2offset - 64, 64, sessionPubkey, 0);
bytes32 sessionPubkeyHash = sha256(sessionPubkey);
if (_oraclize_randomDS_args[_queryId] == keccak256(abi.encodePacked(commitmentSlice1, sessionPubkeyHash))) { //unonce, nbytes and sessionKeyHash match
delete _oraclize_randomDS_args[_queryId];
} else return false;
// Random DS Proof Step 5: Validity verification for sig1 (keyhash and args signed with the sessionKey)
bytes memory tosign1 = new bytes(32 + 8 + 1 + 32);
copyBytes(_proof, ledgerProofLength, 32 + 8 + 1 + 32, tosign1, 0);
if (!verifySig(sha256(tosign1), sig1, sessionPubkey)) {
return false;
}
// Verify if sessionPubkeyHash was verified already, if not.. let's do it!
if (!_oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash]) {
_oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] = oraclize_randomDS_proofVerify__sessionKeyValidity(_proof, sig2offset);
}
return _oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash];
}
/*
The following function has been written by Alex Beregszaszi, use it under the terms of the MIT license
*/
function copyBytes(bytes memory _from, uint _fromOffset, uint _length, bytes memory _to, uint _toOffset) internal pure returns (bytes memory _copiedBytes) {
uint minLength = _length + _toOffset;
require(_to.length >= minLength); // Buffer too small. Should be a better way?
uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables
uint j = 32 + _toOffset;
while (i < (32 + _fromOffset + _length)) {
assembly {
let tmp := mload(add(_from, i))
mstore(add(_to, j), tmp)
}
i += 32;
j += 32;
}
return _to;
}
/*
The following function has been written by Alex Beregszaszi, use it under the terms of the MIT license
Duplicate Solidity's ecrecover, but catching the CALL return value
*/
function safer_ecrecover(bytes32 _hash, uint8 _v, bytes32 _r, bytes32 _s) internal returns (bool _success, address _recoveredAddress) {
/*
We do our own memory management here. Solidity uses memory offset
0x40 to store the current end of memory. We write past it (as
writes are memory extensions), but don't update the offset so
Solidity will reuse it. The memory used here is only needed for
this context.
FIXME: inline assembly can't access return values
*/
bool ret;
address addr;
assembly {
let size := mload(0x40)
mstore(size, _hash)
mstore(add(size, 32), _v)
mstore(add(size, 64), _r)
mstore(add(size, 96), _s)
ret := call(3000, 1, 0, size, 128, size, 32) // NOTE: we can reuse the request memory because we deal with the return code.
addr := mload(size)
}
return (ret, addr);
}
/*
The following function has been written by Alex Beregszaszi, use it under the terms of the MIT license
*/
function ecrecovery(bytes32 _hash, bytes memory _sig) internal returns (bool _success, address _recoveredAddress) {
bytes32 r;
bytes32 s;
uint8 v;
if (_sig.length != 65) {
return (false, address(0));
}
/*
The signature format is a compact form of:
{bytes32 r}{bytes32 s}{uint8 v}
Compact means, uint8 is not padded to 32 bytes.
*/
assembly {
r := mload(add(_sig, 32))
s := mload(add(_sig, 64))
/*
Here we are loading the last 32 bytes. We exploit the fact that
'mload' will pad with zeroes if we overread.
There is no 'mload8' to do this, but that would be nicer.
*/
v := byte(0, mload(add(_sig, 96)))
/*
Alternative solution:
'bytes1' is not working due to the Solidity parser, so lets
use the second best option, 'and'
v := and(mload(add(_sig, 65)), 255)
*/
}
/*
albeit non-transactional signatures are not specified by the YP, one would expect it
to match the YP range of [27, 28]
geth uses [0, 1] and some clients have followed. This might change, see:
https://github.com/ethereum/go-ethereum/issues/2053
*/
if (v < 27) {
v += 27;
}
if (v != 27 && v != 28) {
return (false, address(0));
}
return safer_ecrecover(_hash, v, r, s);
}
function safeMemoryCleaner() internal pure {
assembly {
let fmem := mload(0x40)
codecopy(fmem, codesize(), sub(msize(), fmem))
}
}
}
/*
END ORACLIZE_API
*/
/**
* @title Compliance Oracle storage external getters internal methods
*/
contract ComplianceOracleExternalGettersInternal is
ComplianceOracleValuesGettersInternalVerifications,
usingOraclize
{
// Define libraries
using CommonPolicyParser for *;
using BytesHelper for *;
using AddressUtils for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Processes callback from the oracles
* @param id Request identifier
* @param result External call responce
* @return finished Flag indicates if callback is finished
* @return callbackAddress Address where result will be send
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id
*/
function _processCallback(
bytes32 id,
string memory result
)
internal
returns
(
bool finished,
address callbackAddress,
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
)
{
ExternalCall storage externalCall = _externalCalls[id];
bytes32 sessionId = externalCall.sessionId;
if (sessionId != bytes32(0x00)) {
// Go to session if external call exists in it
(
finished,
callbackAddress,
valueDataType,
value,
errorCode
) = _tryProcessSessionFromCallback(
sessionId,
externalCall,
result
);
return (
finished,
callbackAddress,
valueDataType,
value,
errorCode,
sessionId
);
}
// Process request outside session
(
callbackAddress,
valueDataType,
value,
errorCode
) = _processExternalCallFromCallback(
id,
externalCall,
result
);
return (
true,
callbackAddress,
valueDataType,
value,
errorCode,
id
);
}
/**
* @notice Processes session from callback
* @param sessionId Session identifier
* @param externalCall External call in storage
* @param result External call responce
* @return finished Flag indicates if session is finished
* @return callbackAddress Address where result will be send
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
*/
function _tryProcessSessionFromCallback(
bytes32 sessionId,
ExternalCall storage externalCall,
string memory result
)
internal
returns (
bool finished,
address callbackAddress,
DataType valueDataType,
Value memory value,
bytes32 errorCode
)
{
// Set external call result
externalCall.result = result;
externalCall.pending = false;
ExternalCallsSession storage session = _externalCallsSessions[sessionId];
require(
session.externalCallsCounter > 0,
"_tryProcessSessionFromCallback: No external calls"
);
// Decrease external calls counter
session.externalCallsCounter--;
// Process policy if it is the last pending external call
if (session.externalCallsCounter == 0) {
(
callbackAddress,
valueDataType,
value,
errorCode
) = _processSessionPolicy(
session,
sessionId
);
return (
true,
callbackAddress,
valueDataType,
value,
errorCode
);
}
// Return empty results
return (
false,
session.callbackAddress,
valueDataType,
value,
errorCode
);
}
/**
* @notice Processes main session policy
* @param session Session in storage
* @param sessionId Session identifier
* @return callbackAddress Address where result will be send
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
*/
function _processSessionPolicy(
ExternalCallsSession storage session,
bytes32 sessionId
)
internal
returns (
address callbackAddress,
DataType valueDataType,
Value memory value,
bytes32 errorCode
)
{
// Set flag that session is reprocessing
session.isReprocessing = true;
bytes32 propertyId = session.policyPropertyId;
callbackAddress = session.callbackAddress;
bytes32[] memory emptyBytes32;
// Reprocess policy
bytes32 sourceId = _propertiesMetadata[propertyId]._safeGetPropertySourceId();
(
valueDataType,
value,
errorCode,
) = _requestPolicyValue(
sourceId,
propertyId,
emptyBytes32,
emptyBytes32,
callbackAddress,
sessionId
);
// Delete session
delete _externalCallsSessions[sessionId];
return (
callbackAddress,
valueDataType,
value,
errorCode
);
}
/**
* @notice Processes external call from callback
* @param id External call identifier
* @param externalCall External call in storage
* @param result External call responce
* @return callbackAddress Address where result will be send
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
*/
function _processExternalCallFromCallback(
bytes32 id,
ExternalCall storage externalCall,
string memory result
)
internal
returns (
address callbackAddress,
DataType valueDataType,
Value memory value,
bytes32 errorCode
)
{
bytes32 propertyId = externalCall.propertyId;
valueDataType = _propertiesMetadata[propertyId].dataType;
value.value = new bytes[](1);
value.value[0] = bytes(result);
callbackAddress = externalCall.callbackAddress;
delete _externalCalls[id];
return (
callbackAddress,
valueDataType,
value,
errorCode
);
}
/**
* @notice Requests Policy Property attestation if property has policy data source
* @param sourceId [required] Storage source id
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function _requestPolicyValue(
bytes32 sourceId,
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
internal
returns (
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
)
{
address policyAddress = _sourcesMetadata[sourceId]._safeGetSourcePolicyAddress();
if (externalCallsSession == bytes32(0x00)) {
uint externalCallsTotal = policyAddress._tryGetExternalCallsTotal();
if (externalCallsTotal > 0) {
_requireCallbackToBeContract(callbackAddress);
// Set new session
externalCallsSession = _setSession(
propertyId,
callbackAddress,
externalCallsTotal
);
}
}
(PolicyResult result, bytes32 error) = policyAddress._tryVerifyPolicy(
inputKeys,
inputKeysValues,
address(this),
externalCallsSession
);
if (result != PolicyResult.Pending) {
value.value = new bytes[](1);
value.value[0] = result == PolicyResult.True
? abi.encodePacked(true)
: abi.encodePacked(false);
value.timestamp = block.timestamp;
}
return (
DataType.Boolean,
value,
error,
externalCallsSession
);
}
/**
* @notice Requests External Property attestation if property has external data source
* @param sourceId [required] Storage source id
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return emptyError No error is returned from external call
* @return externalId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function _requestExternalValue(
bytes32 sourceId,
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
internal
returns (
DataType valueDataType,
Value memory value,
bytes32 emptyError,
bytes32 externalId
)
{
_requireCallbackToBeContract(callbackAddress);
// If external calls session exists and its status is reprocessing - return call result
if (externalCallsSession != bytes32(0x00)) {
ExternalCallsSession storage session = _externalCallsSessions[externalCallsSession];
require(
session.externalCallsTotal > session.externalCallsCounter,
"_requestExternalValue: Max external calls"
);
if (session.isReprocessing) {
return _getExternalCallResult(
propertyId,
externalCallsSession
);
}
}
// Create oraclize request
externalId = _createOraclizeRequest(
sourceId,
inputKeys,
inputKeysValues
);
// set external call and session if needed
_setExternalCallWithSession(
externalId,
externalCallsSession,
propertyId,
callbackAddress
);
return (
_propertiesMetadata[propertyId].dataType,
value,
emptyError,
externalId
);
}
/**
* @notice Get external call result for session if it is not pending
* @param propertyId [required] Property unique identifier
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return emptyError No error is returned from external call
* @return externalId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function _getExternalCallResult(
bytes32 propertyId,
bytes32 externalCallsSession
)
internal
returns (
DataType valueDataType,
Value memory value,
bytes32 emptyError,
bytes32 externalId
)
{
// Get session
ExternalCallsSession storage session = _externalCallsSessions[externalCallsSession];
// Get external id by counter
uint counter = session.externalCallsCounter;
externalId = session.externalCallsIds[counter];
// External id must exist
require(
externalId != bytes32(0x00),
"_getExternalCallResult: Unknown external call"
);
// Get external call by id
ExternalCall storage externalCall = _externalCalls[externalId];
// External call must be finished
require(
!externalCall.pending,
"_getExternalCallResult: Pending external call"
);
// Increase session external calls counter
session.externalCallsCounter++;
// Return value
value.value = new bytes[](1);
value.value[0] = bytes(externalCall.result);
// Remove external call from storage after getting result
delete _externalCalls[externalId];
return (
_propertiesMetadata[propertyId].dataType,
value,
emptyError,
externalId
);
}
/**
* @param sourceId [required] Storage source id
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return external identifier
*/
function _createOraclizeRequest(
bytes32 sourceId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
returns (bytes32)
{
string memory url = _sourcesMetadata[sourceId].url;
string memory jsonFormat = _sourcesMetadata[sourceId].jsonFormat;
require(
bytes(url).length > 0,
"_createOraclizeRequest: Empty url"
);
require(
bytes(jsonFormat).length > 0,
"_createOraclizeRequest: Empty json format"
);
string memory URL = _prepareURL(
url,
jsonFormat,
inputKeys,
inputKeysValues
);
oraclize_setCustomGasPrice(tx.gasprice);
return oraclize_query(
ORACLIZE_DATA_SOURCE,
URL,
ORACLIZE_GAS_LIMIT
);
}
/**
* @notice Sets external call with session
* @param externalId [required] External call id
* @param externalCallsSession [optional] Current external calls session
* @param propertyId [optional] Extenal property id
* @param callbackAddress [optional] Callback address
*/
function _setExternalCallWithSession(
bytes32 externalId,
bytes32 externalCallsSession,
bytes32 propertyId,
address callbackAddress
)
internal
{
if (externalCallsSession != bytes32(0x00)) {
_setExternalCall(
externalId,
address(0x00),
bytes32(0x00)
);
_addExternalCallToSession(
externalCallsSession,
externalId
);
} else {
_setExternalCall(
externalId,
callbackAddress,
propertyId
);
}
}
/**
* @notice Instantiates new session
* @param policyPropertyId [required] Policy property id
* @param callbackAddress [required] Address where result will be send
* @param externalCallsTotal [required] External calls total number in policy
* @return new session id
*/
function _setSession(
bytes32 policyPropertyId,
address callbackAddress,
uint externalCallsTotal
)
internal
returns (bytes32)
{
_lastExternalCallsSession += 1;
bytes32 externalCallsSession = keccak256(abi.encodePacked(
"session*",
_lastExternalCallsSession
));
ExternalCallsSession storage session = _externalCallsSessions[externalCallsSession];
session.policyPropertyId = policyPropertyId;
session.callbackAddress = callbackAddress;
session.externalCallsTotal = externalCallsTotal;
return externalCallsSession;
}
/**
* @notice Adds external call to session
* @param externalCallsSession [required] Current external calls session (if empty and value has policy source - a new one will be generated)
* @param externalId [required] External call id
*/
function _addExternalCallToSession(
bytes32 externalCallsSession,
bytes32 externalId
)
internal
{
ExternalCallsSession storage session = _externalCallsSessions[externalCallsSession];
session.externalCallsCounter++;
session.externalCallsIds.push(externalId);
_externalCalls[externalId].sessionId = externalCallsSession;
}
/**
* @notice Sets exteranl call
* @param externalId [required] External call id
* @param callbackAddress [required] Callback address
* @param propertyId [required] Extenal property id
*/
function _setExternalCall(
bytes32 externalId,
address callbackAddress,
bytes32 propertyId
)
internal
{
ExternalCall storage externalCall = _externalCalls[externalId];
externalCall.pending = true;
externalCall.callbackAddress = callbackAddress;
externalCall.propertyId = propertyId;
}
/**
* @notice Prepare url for the request to the oraclize
* @param urlTemplate Template of the url (https://api.pro.coinbase.com/products/{pair}/ticker)
* @param jsonFormat Json format ($.price)
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @dev For example with pair "ETH-USD" output will be "json(https://api.pro.coinbase.com/products/ETH-USD/ticker).price"
*/
function _prepareURL(
string memory urlTemplate,
string memory jsonFormat,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
internal
pure
returns (string memory)
{
bytes memory tmpl = bytes(urlTemplate);
bytes memory jF = bytes(jsonFormat);
bool opened;
uint length;
bytes[10] memory properties;
bytes[10] memory values;
(length, properties) = _getLengthAndPropertiesKeys(tmpl, jF);
values = _getValuesForExternalCall(
inputKeys,
inputKeysValues,
properties
);
uint valuesLength = values.length;
for (uint i = 0; i < valuesLength; i++) {
if (values[i].length == 0) {
break;
}
length += values[i].length;
}
// Declare varaible which will store result
bytes memory converted = new bytes(length);
// Declare varaible which will store last index in the result
uint cIn = 0;
// Add prefix to the result
uint prefixLength = _jsonPrefix.length;
for (uint i = 0; i < prefixLength; i++) {
converted[cIn++] = _jsonPrefix[i];
}
uint addedValueIndex = 0;
uint tmplLength = tmpl.length;
for (uint i = 0; i < tmplLength; i++) {
if (tmpl[i] == bytes1(0x7b)) {
require(
!opened,
"_prepareURL: Double opened bracket"
);
uint valueLength = values[addedValueIndex].length;
require(
valueLength != 0,
"_prepareURL: No property value"
);
opened = true;
for (uint j = 0; j < valueLength; j++) {
converted[cIn++] = values[addedValueIndex][j];
}
addedValueIndex++;
}
if (tmpl[i] == bytes1(0x7d)) {
require(
opened,
"_prepareURL: No open bracket"
);
opened = false;
continue;
}
if (opened) {
continue;
}
converted[cIn++] = tmpl[i];
}
converted[cIn++] = 0x29;
uint jFLength = jF.length;
for (uint i = 1; i < jFLength; i++) {
converted[cIn++] = jF[i];
}
return string(converted);
}
/**
* @notice Calculates url length without properties and collect list of the properties.
* @notice Properties are limited by quantity to 10.
* @param urlTemplate Template of the url (https://api.pro.coinbase.com/products/{pair}/ticker)
* @param jsonFormat Json format ($.price)
*/
function _getLengthAndPropertiesKeys(
bytes memory urlTemplate,
bytes memory jsonFormat
)
internal
pure
returns (
uint length,
bytes[10] memory properties
)
{
length = _jsonPrefix.length;
bool opened;
uint propInd = 0;
uint prop = 0;
uint urlTemplateLength = urlTemplate.length;
for (uint i = 0; i < urlTemplateLength; i++) {
if (urlTemplate[i] == bytes1(0x7b)) {
require(
!opened,
"_getLengthAndPropertiesKeys: Double opened bracket"
);
opened = true;
properties[prop] = new bytes(_getPropertyLength(urlTemplate, i+1));
continue;
}
if (urlTemplate[i] == bytes1(0x7d)) {
require(
opened,
"_getLengthAndPropertiesKeys: No open bracket"
);
opened = false;
propInd = 0;
prop++;
continue;
}
if (opened) {
properties[prop][propInd++] = urlTemplate[i];
continue;
}
length++;
}
return (length + jsonFormat.length, properties);
}
/**
* @notice Calculates properties length
* @param urlTemplate Template of the url
* @param startFrom Offset for the calculation
*/
function _getPropertyLength(bytes memory urlTemplate, uint startFrom) internal pure returns (uint) {
uint i;
uint urlTemplateLength = urlTemplate.length;
for (i = startFrom; i < urlTemplateLength; i++) {
if (urlTemplate[i] == 0x7d) {
break;
}
}
return i - startFrom;
}
/**
* @notice Selects values for the external call
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param properties Properties that are required for URL preparation ({pair})
*/
function _getValuesForExternalCall(
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
bytes[10] memory properties
)
internal
pure
returns (bytes[10] memory values)
{
bytes32 propertyHash;
bytes32 property;
bytes memory tmpProperty;
bytes memory value;
uint propertiesLength = properties.length;
for (uint i = 0; i < propertiesLength; i++) {
if (properties[i].length == 0) {
break;
}
property = bytes32(0x00);
propertyHash = keccak256(properties[i]);
if (properties[i].length <= 32) {
tmpProperty = properties[i];
assembly {
property := mload(add(tmpProperty, 32))
}
}
uint inputKeysLength = inputKeys.length;
for (uint j = 0; j < inputKeysLength; j++) {
if (inputKeys[j] == bytes32(0x00)) {
break;
}
if (inputKeys[j] == propertyHash || (property != bytes32(0x00) && inputKeys[j] == property)) {
value = inputKeysValues[j]._trimBytes32();
values[i] = new bytes(value.length);
values[i] = value;
break;
}
}
require(
values[i].length != 0,
"_getValuesForExternalCall: No property value"
);
}
}
/**
* @notice Verifies that callback address is contract
*/
function _requireCallbackToBeContract(address callbackAddress) internal view {
require(
callbackAddress.isContract(),
"_getValuesForExternalCall: Callback address isn't contract"
);
}
}
/**
* @title Compliance oracle storage getters methods
*/
contract ComplianceOracleValuesGetters is
ComplianceOracleValuesGettersInternal,
ComplianceOracleExternalGettersInternal,
IComplianceOracleValuesGetters,
IComplianceOracleEvents
{
// Define libraries
using SystemUtils for *;
using CommonCallbackHandler for *;
/**
* @notice Accepts callback from the oracles
* @param id Request identifier
* @param result External call responce
*/
function __callback(bytes32 id, string memory result) public override (usingOraclize, IComplianceOracleValuesGetters) {
require(
_externalCalls[id].pending,
"__callback: Not pending"
);
require(
msg.sender == oraclize_cbAddress(),
"__callback: Only oraclize"
);
(
bool finished,
address callbackAddress,
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
) = _processCallback(
id,
result
);
if (finished) {
if (callbackAddress != address(this)) {
_requireCallbackToBeContract(callbackAddress);
callbackAddress._tryValueCallback(
valueDataType,
value,
errorCode,
requestId
);
} else {
emit ValueCallback(
valueDataType,
value,
errorCode,
requestId
);
}
}
emit CallbackResponce(
callbackAddress,
id,
result
);
}
/**
* @notice Requests attestation property
* @notice if property has external data source
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @param callbackAddress [required] Address where result will be send
* @param externalCallsSession [optional] Current external calls session (if empty and value has policy source - a new one will be generated)
* @return valueDataType Data type of the property value
* @return value Value of the property
* @return errorCode Error code for policy type
* @return requestId External calls session id or external call id. Session id generated if needed (existing external calls and empty input session id)
*/
function requestValue(
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address callbackAddress,
bytes32 externalCallsSession
)
public
payable
override
validateBeforeFetch(propertyId)
returns (
DataType valueDataType,
Value memory value,
bytes32 errorCode,
bytes32 requestId
)
{
// If property has parent - request parents value
bytes32 parentId = _propertiesMetadata[propertyId].parentId;
if (parentId != bytes32(0x00)) {
return requestValue(
parentId,
inputKeys,
inputKeysValues,
callbackAddress,
externalCallsSession
);
}
// Get source
(bytes32 sourceId, SourceType sourceType) = _safeGetSourceIdAndType(propertyId);
// Process request policy value
if (sourceType == SourceType.Policy) {
return _requestPolicyValue(
sourceId,
propertyId,
inputKeys,
inputKeysValues,
callbackAddress,
externalCallsSession
);
}
// Process request external call value
if (sourceType == SourceType.External) {
return _requestExternalValue(
sourceId,
propertyId,
inputKeys,
inputKeysValues,
callbackAddress,
externalCallsSession
);
}
(
valueDataType,
value
) = _getValue(
propertyId,
inputKeys,
inputKeysValues
);
return (
valueDataType,
value,
errorCode,
requestId
);
}
/**
* @notice Returns Property value
* @notice if property has a storage or direct data source
* @param propertyId [required] Property unique id
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function getValue(
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues
)
public
view
override
validateBeforeFetch(propertyId)
returns (
DataType valueDataType,
Value memory value
)
{
// If property has parent - get parents value
bytes32 parentId = _propertiesMetadata[propertyId].parentId;
if (parentId != bytes32(0x00)) {
return getValue(
parentId,
inputKeys,
inputKeysValues
);
}
return _getValue(
propertyId,
inputKeys,
inputKeysValues
);
}
/**
* @notice Returns Storage Property value
* @notice if property has a storage source
* @param propertyId [required] Property unique id
* @param keysValues [optional] Already calculated keys values
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function getStorageValue(
bytes32 propertyId,
bytes32[] memory keysValues
)
public
view
override
validateBeforeFetch(propertyId)
returns (
DataType valueDataType,
Value memory value
)
{
// If property has parent - get parents value
bytes32 parentId = _propertiesMetadata[propertyId].parentId;
if (parentId != bytes32(0x00)) {
return getStorageValue(parentId, keysValues);
}
// Get source
(bytes32 sourceId, SourceType sourceType) = _safeGetSourceIdAndType(propertyId);
require(
sourceType == SourceType.Storage,
"getStorageValue: Wrong source"
);
// Return storage value
return _getStorageValue(
sourceId,
propertyId,
keysValues
);
}
/**
* @notice Returns Role value
* @notice if property has a storage source
* @param propertyId Role unique id
* @param subId Additional identifier for property value
* @param manager Property manager to be set
* @param additionalKeysValues Additional property keys values
* @return value Value of the property
*/
function getRole(
bytes32 propertyId,
bytes32 subId,
address manager,
bytes32[] memory additionalKeysValues
)
public
view
override
returns (Value memory)
{
bytes32[] memory keysValues = _buildRoleKeysValues(
propertyId,
subId,
manager,
additionalKeysValues
);
(DataType valueDataType, Value memory value) = getStorageValue(
propertyId,
keysValues
);
require(
valueDataType == DataType.Boolean,
"getRole: Not bool data type"
);
return value;
}
}
/**
* @title Storage Compliance Oracle getters package upgradable
*/
contract ComplianceOracleValuesGettersPackageUpgradable is
IPackage,
ComplianceOracleStorage
{
// Define libraries
using CommonUpgradability for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeMethods(_methodsImplementations[msg.sig]);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IComplianceOracle(address(0x00)).getValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getStorageValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getRole.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).requestValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).__callback.selector] = package;
}
}
/**
* @title Storage Compliance Oracle getters package
*/
contract ComplianceOracleValuesGettersPackage is
IPackage,
ComplianceOracleValuesGettersPackageUpgradable,
ComplianceOracleValuesGetters
{ }
/**
* @title Compliance Oracle storage batch setters methods
*/
contract ComplianceOracleStorageBatchSetters is
ComplianceOracleStorageSettersInternal,
IComplianceOracleStorageBatchSetters
{
// Define libraries
using CommonComplianceOracle for *;
/**
* @notice Stores Properties values if it is the first time in batch
* @param valuesDetails Values by properties with keys to be stored
*/
function batchCreateValues(BatchSetValueDetails[] memory valuesDetails) public override {
address lastSourceContract;
uint length = valuesDetails.length;
// Iterate for properties
for (uint i = 0; i < length; i++) {
// Verify create value inputs, create in current storage if source is this, get source address
address sourceContract = _createValueIfSourceIsThis(
valuesDetails[i].value,
valuesDetails[i].propertyId,
valuesDetails[i].keysValues
);
// Source must be the same for all properties
if (sourceContract != lastSourceContract) {
require(
lastSourceContract == address(0x00),
"batchCreateValues: Not single source"
);
lastSourceContract = sourceContract;
}
}
// Store values in another source if needed
if (lastSourceContract != address(this)) {
lastSourceContract._tryBatchCreateValues(valuesDetails);
}
}
/**
* @notice Updates stored Properties values in batch
* @param valuesDetails Values by properties with keys to be stored
*/
function batchUpdateValues(BatchSetValueDetails[] memory valuesDetails) public override {
address lastSourceContract;
uint length = valuesDetails.length;
// Iterate for properties
for (uint i = 0; i < length; i++) {
// Verify update value inputs, update in current storage if source is this, get source address
address sourceContract = _updateValueIfSourceIsThis(
valuesDetails[i].value,
valuesDetails[i].propertyId,
valuesDetails[i].keysValues
);
// Source must be the same for all properties
if (sourceContract != lastSourceContract) {
require(
lastSourceContract == address(0x00),
"batchUpdateValues: Not single source"
);
lastSourceContract = sourceContract;
}
}
// Update values in another source if needed
if (lastSourceContract != address(this)) {
lastSourceContract._tryBatchUpdateValues(valuesDetails);
}
}
/**
* @notice Removes stored Properties values in batch
* @param valuesDetails Values by properties with keys to be deleted
*/
function batchRemoveValues(BatchDeleteValueDetails[] memory valuesDetails) public override {
address lastSourceContract;
uint length = valuesDetails.length;
// Iterate for properties
for (uint i = 0; i < length; i++) {
// Verify remove value inputs, remove from current storage if source is this, get source address
address sourceContract = _removeValueIfSourceIsThis(
valuesDetails[i].propertyId,
valuesDetails[i].keysValues
);
// Source must be the same for all properties
if (sourceContract != lastSourceContract) {
require(
lastSourceContract == address(0x00),
"batchRemoveValues: Not single source"
);
lastSourceContract = sourceContract;
}
}
// Update values in another source if needed
if (lastSourceContract != address(this)) {
lastSourceContract._tryBatchRemoveValues(valuesDetails);
}
}
}
/**
* @title Storage Compliance Oracle setters package upgradable
*/
contract ComplianceOracleStorageSettersPackageUpgradable is
IPackage,
ComplianceOracleStorage
{
// Define libraries
using CommonUpgradability for *;
using CommonRulesEngine for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeMethods(_methodsImplementations[msg.sig]);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IComplianceOracle(address(0x00)).createValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).updateValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).removeValue.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).setRole.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).updateRole.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).removeRole.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).refuseFromRole.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).batchCreateValues.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).batchUpdateValues.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).batchRemoveValues.selector] = package;
}
}
/**
* @title Compliance Oracle storage setters package
*/
contract ComplianceOracleStorageSettersPackage is
ComplianceOracleStorageRolesSetters,
ComplianceOracleStorageBatchSetters,
ComplianceOracleStorageSettersPackageUpgradable
{ }
/**
* @title Compliance Oracle sources internal verifications methods
*/
contract ComplianceOracleSourcesInternalVerifications is ComplianceOracleProtectedVerifications {
// Define libraries
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Verifies a source inputs before a source creation
* @param sourceInput Source input structure
*/
modifier validateBeforeCreate(SourceInput memory sourceInput) {
// Verify inputs
_verifySourceNameNotEmpty(sourceInput.name);
bytes32 sourceId = _verifySourceInput(sourceInput);
_sourcesEnumerable[sourceId]._verifySourceNotExists();
_contextsEnumerable[sourceInput.contextId]._verifyContextNotDisabled();
_;
}
/**
* @notice Verifies a source inputs before a source update
* @param sourceInput Source input structure
*/
modifier validateBeforeUpdate(SourceInput memory sourceInput) {
// Verify inputs
_verifySourceNameNotEmpty(sourceInput.name);
bytes32 sourceId = _verifySourceInput(sourceInput);
_sourcesEnumerable[sourceId]._verifySourceExists();
_sourcesMetadata[sourceId]._verifySourceNotDefault();
_sourcesEnumerable[sourceId]._verifySourceNotDisabled();
_contextsEnumerable[sourceInput.contextId]._verifyContextNotDisabled();
// Verify source type
_verifySourceTypeUnchanged(sourceId, sourceInput.sourceType);
// Go to new scope to prevent possible stack-too-deep error
{
bytes32 policyBytecodeHash;
if (sourceInput.sourceType == SourceType.Policy) {
policyBytecodeHash = keccak256(sourceInput.policyBytecode);
}
// Verify that new data hash differs
bytes32 newHash = keccak256(abi.encodePacked(
sourceInput.source,
policyBytecodeHash,
sourceInput.url,
sourceInput.jsonFormat
));
SourceMetadata memory oldMetadata = _sourcesMetadata[sourceId];
bytes32 oldHash = keccak256(abi.encodePacked(
oldMetadata.source,
oldMetadata.policyBytecodeHash,
oldMetadata.url,
oldMetadata.jsonFormat
));
require(
oldHash != newHash,
"validateBeforeUpdate: no updates"
);
}
_;
}
/**
* @notice Verifies a source inputs before a source disable
* @param contextId Context identifier
* @param name Source name
*/
modifier validateBeforeDisable(bytes32 contextId, string memory name) {
// Verify source id
bytes32 sourceId = _safeGetSourceId(contextId, name);
_sourcesEnumerable[sourceId]._verifySourceNotDisabled();
_sourcesMetadata[sourceId]._verifySourceNotDefault();
// Verify possibility to disable
_verifySourceEnumerableForDisable(sourceId);
_;
}
/**
* @notice Verifies a source inputs before a source enable
* @param contextId Context identifier
* @param name Source name
*/
modifier validateBeforeEnable(bytes32 contextId, string memory name) {
// Verify source id
bytes32 sourceId = _safeGetSourceId(contextId, name);
_sourcesEnumerable[sourceId]._verifySourceDisabled();
_contextsEnumerable[contextId]._verifyContextNotDisabled();
_;
}
/**
* @notice Verifies a source input
* @param sourceInput Source input structure
* @return Unique source identifier sourceInput Source input structure
*/
function _verifySourceInput(SourceInput memory sourceInput) internal view returns (bytes32) {
_verifySourceNameNotEmpty(sourceInput.name);
_contextsEnumerable[sourceInput.contextId]._verifyContextExists();
// Verify source id
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(sourceInput.contextId, sourceInput.name);
// Verification depends on the source type. If found - verify and return, if not - revert
SourceType sourceType = sourceInput.sourceType;
if (sourceType == SourceType.Storage) {
_verifyStorageSourceTypes(sourceInput);
return sourceId;
}
if (sourceType == SourceType.Policy) {
_verifyPolicySourceTypes(sourceInput);
return sourceId;
}
if (sourceType == SourceType.External) {
_verifyExternalSourceTypes(sourceInput);
return sourceId;
}
if (sourceType == SourceType.Direct) {
_verifyDirectSourceTypes(sourceInput);
return sourceId;
}
revert("_verifySourceInput: Unknown source type");
}
/**
* @notice Verifies that source type hasn't been changed
* @param sourceId Source unique identifier
* @param newSourceType New source type
*/
function _verifySourceTypeUnchanged(bytes32 sourceId, SourceType newSourceType) internal view {
require(
_sourcesMetadata[sourceId].sourceType == newSourceType,
"_verifySourceTypeUnchanged: Source changes"
);
}
/**
* @notice Verifies a source enumerable data before disable
* @param sourceId Source unique identifier
*/
function _verifySourceEnumerableForDisable(bytes32 sourceId) internal view {
require(
_sourcesEnumerable[sourceId].propertiesTotal == 0,
"_verifySourceEnumerableForDisable: Existing properties"
);
}
/**
* @notice Get a source internal method with verifications
* @param contextId Context unique identifier
* @param name Source name
* @return Unique source identifier
*/
function _safeGetSourceId(bytes32 contextId, string memory name) internal view returns (bytes32) {
_verifySourceNameNotEmpty(name);
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(contextId, name);
_sourcesEnumerable[sourceId]._verifySourceExists();
return sourceId;
}
/**
* @notice Verifies that name is not empty
* @param name Name string
*/
function _verifySourceNameNotEmpty(string memory name) internal pure {
require(
bytes(name).length > 0,
"_verifySourceNameNotEmpty: Empty name"
);
}
/**
* @notice Verifies a source metadata for storage source type
* @param sourceInput Source metadata structure
*/
function _verifyStorageSourceTypes(SourceInput memory sourceInput) internal pure {
require(
sourceInput.source != address(0x00),
"_verifyStorageSourceTypes: Empty source"
);
require(
sourceInput.policyBytecode.length == 0,
"_verifyStorageSourceTypes: Not empty policy"
);
require(
bytes(sourceInput.url).length == 0,
"_verifyStorageSourceTypes: Not empty url"
);
require(
bytes(sourceInput.jsonFormat).length == 0,
"_verifyStorageSourceTypes: Not empty json"
);
}
/**
* @notice Verifies a source metadata for policy source type
* @param sourceInput Source metadata structure
*/
function _verifyPolicySourceTypes(SourceInput memory sourceInput) internal pure {
require(
sourceInput.source == address(0x00),
"_verifyPolicySourceTypes: Not empty source"
);
require(
bytes(sourceInput.url).length == 0,
"_verifyPolicySourceTypes: Not empty url"
);
require(
bytes(sourceInput.jsonFormat).length == 0,
"_verifyPolicySourceTypes: Not empty json"
);
require(
sourceInput.policyBytecode.length > 0,
"_verifyPolicySourceTypes: Empty policy"
);
}
/**
* @notice Verifies a source metadata for external source type
* @param sourceInput Source metadata structure
*/
function _verifyExternalSourceTypes(SourceInput memory sourceInput) internal pure {
require(
sourceInput.source == address(0x00),
"_verifyExternalSourceTypes: Not empty source"
);
require(
sourceInput.policyBytecode.length == 0,
"_verifyExternalSourceTypes: Not empty policy"
);
require(
bytes(sourceInput.url).length > 0,
"_verifyExternalSourceTypes: Empty url"
);
require(
bytes(sourceInput.jsonFormat).length > 0,
"_verifyExternalSourceTypes: Empty json"
);
}
/**
* @notice Verifies a source metadata for direct source type
* @param sourceInput Source metadata structure
*/
function _verifyDirectSourceTypes(SourceInput memory sourceInput) internal pure {
require(
sourceInput.source == address(0x00),
"_verifyDirectSourceTypes: Not empty source"
);
require(
sourceInput.policyBytecode.length == 0,
"_verifyDirectSourceTypes: Not empty policy"
);
require(
bytes(sourceInput.url).length == 0,
"_verifyDirectSourceTypes: Not empty url"
);
require(
bytes(sourceInput.jsonFormat).length == 0,
"_verifyDirectSourceTypes: Not empty json"
);
}
}
/**
* @title Compliance Oracle sources internal methods
*/
contract ComplianceOracleSourcesInternal is ComplianceOracleSourcesInternalVerifications {
// Define libraries
using ComplianceOracleSourceCommons for *;
using CommonPackage for *;
using CommonPolicyParser for *;
using AddressUtils for *;
/**
* @notice Saves the source in the storage
* @param sourceInput Source input structure
* @return sourceId Source unique identifier
*/
function _setSource(SourceInput memory sourceInput) internal returns (bytes32) {
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(sourceInput.contextId, sourceInput.name);
(address policyAddress, bytes32 policyBytecodeHash) = _calculatePolicyDetails(
sourceId,
sourceInput
);
SourceMetadata memory sourceMetadata = _getSourceMetadata(
sourceInput,
policyBytecodeHash,
policyAddress
);
// Create source
_setSourceMetadataUnsafe(sourceId, sourceMetadata);
return sourceId;
}
/**
* @notice Updates a storage with source metadata
* @param sourceId Source identifier
* @param sourceMetadata Source metadata structure
*/
function _setSourceMetadataUnsafe(bytes32 sourceId, SourceMetadata memory sourceMetadata) internal {
_sourcesMetadata[sourceId] = sourceMetadata;
_sourcesEnumerable[sourceId].modifiedAt = block.timestamp;
}
/**
* @notice Policy data type handler
* @notice Should create a policy if it doesn't exists
*
* @param sourceId Source identifier
* @param sourceInput Source input structure
*
* @return address A policy address (should be empty if data source is not "policy")
* @return hash A policy bytecode hash (should be empty if data source is not "policy")
*/
function _calculatePolicyDetails(bytes32 sourceId, SourceInput memory sourceInput) internal returns (address, bytes32) {
if (sourceInput.sourceType != SourceType.Policy) {
return (address(0x00), bytes32(0x00));
}
bytes32 policyBytecodeHash = keccak256(sourceInput.policyBytecode);
// If source type is Policy and bytecode changed - deploy policy
// else - take an old address
if (
sourceInput.sourceType == SourceType.Policy
&& _sourcesMetadata[sourceId].policyBytecodeHash != policyBytecodeHash
) {
bytes32 salt = keccak256(abi.encodePacked(
block.timestamp,
block.difficulty,
msg.sender
));
return (
_deployPolicy(
salt,
sourceInput.policyBytecode
),
policyBytecodeHash
);
}
return (_sourcesMetadata[sourceId].policyAddress, policyBytecodeHash);
}
/**
* @notice Deploys policy contract
* @param salt Arbitrary value
* @param policyBytecode Contract bytecode
* @return policyAddress Created contract address
*/
function _deployPolicy(
bytes32 salt,
bytes memory policyBytecode
)
internal
returns (address policyAddress)
{
policyBytecode = abi.encodePacked(policyBytecode, abi.encode(address(this))); // Policy constructor MUST accept address
assembly {
policyAddress := create2(0x00, add(policyBytecode, 32), mload(policyBytecode), salt)
}
require(
policyAddress.isContract(),
"_deployPolicy: policy hasn't been deployed"
);
require(
policyAddress._trySupportsInterface(
type(IPolicy).interfaceId
),
"_deployPolicy: deployed contract doesn't support Policy Parser interface"
);
return policyAddress;
}
/**
* @notice Get a source internal method with verifications
* @param contextId Context identifier
* @param name Source name
* @return Unique source identifier
*/
function _safeGetSource(bytes32 contextId, string memory name) internal view returns (bytes32) {
_verifySourceNameNotEmpty(name);
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(contextId, name);
_sourcesEnumerable[sourceId]._verifySourceExists();
return sourceId;
}
/**
* @notice Returns source metadata from input
* @param sourceInput A source input structure
* @param policyBytecodeHash A policy bytes code hash (not empty only for the policy data source)
* @param policyAddress A policy address (not empty only for the policy data source)
* @return Source metadata
*/
function _getSourceMetadata(
SourceInput memory sourceInput,
bytes32 policyBytecodeHash,
address policyAddress
)
internal
pure
returns (SourceMetadata memory)
{
return SourceMetadata({
name: sourceInput.name,
contextId: sourceInput.contextId,
sourceType: sourceInput.sourceType,
policyBytecodeHash: policyBytecodeHash,
source: sourceInput.source,
policyAddress: policyAddress,
isDefault: false,
url: sourceInput.url,
jsonFormat: sourceInput.jsonFormat
});
}
}
/**
* @title Compliance Oracle sources package
*/
contract ComplianceOracleSourcesPackageUpgradable is
IPackage,
ComplianceOracleStorage
{
// Define libraries
using CommonUpgradability for *;
using CommonRulesEngine for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeMethods(_methodsImplementations[msg.sig]);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IComplianceOracle(address(0x00)).createSource.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).updateSource.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).disableSource.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).enableSource.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getSource.selector] = package;
}
}
/**
* @title Compliance Oracle source permissions common methods
*/
contract ComplianceOracleSourcePermissionsCommons is ComplianceOraclePermissionsSetupCommons {
/**
* @notice Setups source
* @param sourceId Source identifier
* @param manager Source manager address
*/
function _setupSource(
bytes32 sourceId,
address manager
)
internal
{
bytes[] memory value = new bytes[](1);
value[0] = hex"01";
// Set source manager role
_setValueBySubId(
SOURCE_MANAGER_ID,
sourceId,
manager,
value
);
// Set source property manager role
bytes32 subId = keccak256(abi.encodePacked(SOURCE_MANAGER_ID, sourceId));
_setValueBySubId(
PROPERTY_MANAGER_ID,
subId,
manager,
value
);
// Set source manager premissions
bytes32[] memory methods = new bytes32[](3);
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateSource.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).disableSource.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).enableSource.selector));
_setPermissions(
SOURCE_MANAGER_ID,
sourceId,
value,
methods
);
methods = new bytes32[](3);
// Only update values, no update properties (no need because it's the default property)
methods[0] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).createValue.selector));
methods[1] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).updateValue.selector));
methods[2] = keccak256(abi.encodePacked(PERMISSION, IComplianceOracle(address(0x00)).removeValue.selector));
// Set source property manager permissions
_setPermissions(
PROPERTY_MANAGER_ID,
subId,
value,
methods
);
}
}
/**
* @title Compliance Oracle sources methods
*/
contract ComplianceOracleSourcesPackage is
IComplianceOracleSources,
ComplianceOracleSourcesInternal,
ComplianceOracleSourcesPackageUpgradable,
ComplianceOracleSourcePermissionsCommons
{
// Define libraries
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Create a Data source
* @param sourceInput Data source input structure
* @return Unique property identifier
*/
function createSource(SourceInput calldata sourceInput)
external
override
verifyPermission(sourceInput.contextId)
validateBeforeCreate(sourceInput)
returns (bytes32)
{
bytes32 sourceId = _setSource(sourceInput);
// Update a context sources counter
_contextsEnumerable[sourceInput.contextId].sourcesTotal += 1;
_setupSource(
sourceId,
msg.sender
);
emit SourceUpdated(
sourceId,
sourceInput.contextId,
OperationType.Create,
sourceInput.description
);
return sourceId;
}
/**
* @notice Update a Data source
* @param sourceInput Data source input structure
*/
function updateSource(SourceInput calldata sourceInput)
external
override
verifyPermission(ComplianceOracleSourceCommons._getSourceId(
sourceInput.contextId,
sourceInput.name
))
validateBeforeUpdate(sourceInput)
{
bytes32 sourceId = _setSource(sourceInput);
emit SourceUpdated(
sourceId,
sourceInput.contextId,
OperationType.Update,
sourceInput.description
);
}
/**
* @notice Disable a Data source
* @param contextId Context identifier
* @param name Data source name
*/
function disableSource(bytes32 contextId, string calldata name)
external
override
verifyPermission(ComplianceOracleSourceCommons._getSourceId(
contextId,
name
))
validateBeforeDisable(contextId, name)
{
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(contextId, name);
// Disable source from context
if (contextId != bytes32(0x00)) {
_contextsEnumerable[contextId].sourcesTotal -= 1;
}
// Disable source
_sourcesEnumerable[sourceId].isDisabled = true;
emit SourceUpdated(
sourceId,
contextId,
OperationType.Disable,
""
);
}
/**
* @notice Enable a Data source
* @param contextId Context identifier
* @param name Data source name
*/
function enableSource(bytes32 contextId, string calldata name)
external
override
verifyPermission(ComplianceOracleSourceCommons._getSourceId(
contextId,
name
))
validateBeforeEnable(contextId, name)
{
bytes32 sourceId = ComplianceOracleSourceCommons._getSourceId(contextId, name);
// Enable source from context
if (contextId != bytes32(0x00)) {
_contextsEnumerable[contextId].sourcesTotal += 1;
}
// Enable source
_sourcesEnumerable[sourceId].isDisabled = false;
emit SourceUpdated(
sourceId,
contextId,
OperationType.Enable,
""
);
}
/**
* @notice Get Data source
* @param contextId Context identifier
* @param name Source name
* @return Source metadata
* @return Source enumerable data
*/
function getSource(
bytes32 contextId,
string calldata name
)
external
view
override
returns (
SourceMetadata memory,
SourceEnumerable memory
)
{
bytes32 sourceId = _safeGetSource(contextId, name);
return (
_sourcesMetadata[sourceId],
_sourcesEnumerable[sourceId]
);
}
}
/**
* @title Compliance Oracle properties internal verifications methods
*/
contract ComplianceOraclePropertiesInternalVerifications is ComplianceOracleProtectedVerifications {
// Define libraries
using AddressUtils for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Verifies a property inputs before creation
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
modifier validateBeforeCreate(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
) {
// Verify inputs
_verifyPropertyNameNotEmpty(propertyInput.name);
bytes32 propertyId = ComplianceOraclePropertyCommons._getPropertyId(propertyInput.contextId, propertyInput.name);
_propertiesEnumerable[propertyId]._verifyPropertyNotExists();
_sourcesEnumerable[propertyInput.sourceId]._verifySourceNotDisabled();
_contextsEnumerable[propertyInput.contextId]._verifyContextNotDisabled();
_verifyExternalIdFree(propertyInput.contextId, propertyInput.externalId);
_verifyPropertyInheritanceRelatedInput(
propertyId,
propertyInput,
propertyAccessible
);
_;
}
/**
* @notice Verifies a property inputs before update
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
modifier validateBeforeUpdate(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
) {
// Verify property id
bytes32 propertyId = _safeGetProperty(propertyInput.contextId, propertyInput.name);
_propertiesEnumerable[propertyId]._verifyPropertyNotDisabled();
_propertiesMetadata[propertyId]._verifyPropertyNotDefault();
_sourcesEnumerable[propertyInput.sourceId]._verifySourceNotDisabled();
_contextsEnumerable[propertyInput.contextId]._verifyContextNotDisabled();
_verifySourceHasSameType(propertyInput.sourceId, _propertiesMetadata[propertyId].sourceId);
_verifyPropertyInheritanceRelatedInput(
propertyId,
propertyInput,
propertyAccessible
);
_verifyExternalIdToBeFree(
propertyId,
propertyInput
);
_verifyHasDifference(
propertyId,
propertyInput,
propertyAccessible
);
_;
}
/**
* @notice Verifies a property inputs before disable
* @param contextId Context identifier
* @param name Property name
*/
modifier validateBeforeDisable(bytes32 contextId, string memory name) {
// Verify property id
bytes32 propertyId = _safeGetProperty(contextId, name);
_propertiesEnumerable[propertyId]._verifyPropertyNotDisabled();
_propertiesMetadata[propertyId]._verifyPropertyNotDefault();
// Verify possibility to disable
_verifyPropertyEnumerableForDisable(propertyId);
_;
}
/**
* @notice Verifies a property inputs before enable
* @param contextId Context identifier
* @param name Property name
*/
modifier validateBeforeEnable(bytes32 contextId, string memory name) {
// Verify property id
bytes32 propertyId = _safeGetProperty(contextId, name);
_propertiesEnumerable[propertyId]._verifyPropertyDisabled();
_contextsEnumerable[contextId]._verifyContextNotDisabled();
_sourcesEnumerable[_propertiesMetadata[propertyId].sourceId]._verifySourceNotDisabled();
if (_propertiesMetadata[propertyId].parentId != bytes32(0x00)) {
// Verify parent
_propertiesEnumerable[_propertiesMetadata[propertyId].parentId]._verifyPropertyNotDisabled();
}
_;
}
/**
* @notice Verifications depend on inheritance
* @param propertyId Property unique identifier
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyPropertyInheritanceRelatedInput(
bytes32 propertyId,
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
view
{
// Verification depends on inheritance
bytes32 parentId = propertyInput.parentId;
bytes32 oldParentId = _propertiesMetadata[propertyId].parentId;
if (parentId != oldParentId && parentId != bytes32(0x00)) {
// Verify that parent exists for inherited propery
_propertiesEnumerable[parentId]._verifyPropertyExists();
_propertiesEnumerable[parentId]._verifyPropertyNotDisabled();
// Verify inheritance specific data
_verifyInheritance(
parentId,
propertyInput,
propertyAccessible
);
} else {
// Verify property context
_verifyPropertyContextExists(propertyInput.contextId);
// Verify property source
_verifyPropertySource(propertyInput, propertyAccessible);
// Property data type isn't none
_verifyDataTypeNotNone(propertyInput.dataType);
}
}
/**
* @notice Verifies that external id for property is free
* @param propertyId Property unique identifier
* @param propertyInput Attestation property input metadata structure
*/
function _verifyExternalIdToBeFree(
bytes32 propertyId,
PropertyInput memory propertyInput
)
internal
view
{
// Get old and new external ids
bytes32 oldExternalId = _propertiesMetadata[propertyId].externalId;
bytes32 newExternalId = propertyInput.externalId;
if (oldExternalId != newExternalId) {
// If old and new external ids aren't equal - verify that new is free
_verifyExternalIdFree(propertyInput.contextId, newExternalId);
}
}
/**
* @notice Verifies that external id for property is free
* @param propertyId Property unique identifier
* @param propertyInput Attestation property input metadata structure
*/
function _verifyHasDifference(
bytes32 propertyId,
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
view
{
bytes32 newHash = keccak256(abi.encodePacked(
propertyInput.sourceId,
propertyInput.dataType,
propertyInput.parentId,
propertyInput.externalId,
propertyInput.isConstant,
propertyAccessible.expirationTime,
propertyAccessible.onRefuse
));
PropertyMetadata memory oldMetadata = _propertiesMetadata[propertyId];
PropertyAccessible memory oldAccessible = _propertiesAccessible[propertyId];
bytes32 oldHash = keccak256(abi.encodePacked(
oldMetadata.sourceId,
oldMetadata.dataType,
oldMetadata.parentId,
oldMetadata.externalId,
oldMetadata.isConstant,
oldAccessible.expirationTime,
oldAccessible.onRefuse
));
require(
oldHash != newHash,
"_verifyHasDifference: No updates"
);
}
/**
* @notice Verifies that context is created
* @param contextId Context unique identifier
*/
function _verifyPropertyContextExists(bytes32 contextId) internal view {
require(
_contextsEnumerable[contextId].modifiedAt > 0,
"_verifyPropertyContextExists: Unexisting context"
);
}
/**
* @notice Verifies that source is created
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyPropertySource(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
view
{
require(
_sourcesEnumerable[propertyInput.sourceId].modifiedAt > 0,
"_verifyPropertySource: Unexisting data source"
);
require(
_sourcesMetadata[propertyInput.sourceId].contextId == propertyInput.contextId,
"_verifyPropertySource: Not same context"
);
SourceType sourceType = _sourcesMetadata[propertyInput.sourceId].sourceType;
if (sourceType == SourceType.Direct) {
_verifyDirectSource(propertyInput, propertyAccessible);
} else if (sourceType == SourceType.External) {
_verifyExternalSource(propertyInput, propertyAccessible);
} else if (sourceType == SourceType.Policy) {
_verifyPolicySource(propertyInput, propertyAccessible);
}
}
/**
* @notice Verifies property inputs for direct source
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyDirectSource(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
pure
{
_verifyNotStorageSource(propertyInput, propertyAccessible);
}
/**
* @notice Verifies property inputs for extermal source
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyExternalSource(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
pure
{
_verifyNotStorageSource(propertyInput, propertyAccessible);
require(
propertyAccessible.keys.length == 0,
"_verifyExternalSource: Not empty keys"
);
}
/**
* @notice Verifies property inputs for policy source
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyPolicySource(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
pure
{
_verifyNotStorageSource(propertyInput, propertyAccessible);
require(
propertyInput.dataType == DataType.Boolean,
"_verifyPropertySource: Not boolean data type"
);
require(
propertyAccessible.keys.length == 0,
"_verifyPolicySource: Not empty keys"
);
}
/**
* @notice Verifies property inputs for not storage source
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyNotStorageSource(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
pure
{
require(
propertyAccessible.expirationTime == 0,
"_verifyNotStorageSource: Not zero expiration time"
);
require(
propertyAccessible.onRefuse == address(0x00),
"_verifyNotStorageSource: Not empty on refuse address"
);
require(
propertyInput.isConstant == false,
"_verifyNotStorageSource: Is constant"
);
}
/**
* @notice Verifies an Attestation property metadata for disabling
* @param propertyId Property unique identifier
*/
function _verifyPropertyEnumerableForDisable(bytes32 propertyId) internal view {
require(
_propertiesEnumerable[propertyId].childrenTotal == 0,
"_verifyPropertyEnumerableForDisable: Existing children"
);
require(
_propertiesEnumerable[propertyId].valuesTotal == 0,
"_verifyPropertyEnumerableForDisable: Existing values"
);
}
/**
* @notice Verifies an external id to be free
* @param contextId Context unique identifier
* @param externalId Property unique identifier in context
*/
function _verifyExternalIdFree(bytes32 contextId, bytes32 externalId) internal view {
require(
_propertiesIdsByExternal[contextId][externalId] == bytes32(0x00),
"_verifyExternalIdFree: Not free external id"
);
}
/**
* @notice Verifies an external id to be free and returns its property identifier
* @param contextId Context unique identifier
* @param externalId External identifier for property in context
* @return property unique identifier in contexts
*/
function _safeGetPropertyIdByExternalId(bytes32 contextId, bytes32 externalId) internal view returns (bytes32) {
bytes32 propertyId = _propertiesIdsByExternal[contextId][externalId];
require(
propertyId != bytes32(0x00),
"_verifyExternalIdFree: Not existing external id"
);
return propertyId;
}
/**
* @notice Verifies inheritance rules
* @param parentId Parent Property unique identifier
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function _verifyInheritance(
bytes32 parentId,
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
view
{
require(
_propertiesMetadata[parentId].parentId == bytes32(0x00),
"_verifyInheritance: too deep inheritance"
);
require(
_propertiesMetadata[parentId].contextId == propertyInput.contextId,
"_verifyInheritance: parent property must have the same context"
);
require(
propertyInput.sourceId == bytes32(0x00),
"_verifyInheritance: Source id must be empty"
);
require(
propertyInput.dataType == DataType.None, // 0
"_verifyInheritance: data type must be empty"
);
require(
propertyInput.isConstant == false,
"_verifyInheritance: Mustn't be constant"
);
require(
propertyAccessible.keys.length == 0,
"_verifyInheritance: keys must be empty"
);
require(
propertyAccessible.expirationTime == 0,
"_verifyInheritance: Expiration time must be empty"
);
require(
propertyAccessible.onRefuse == address(0x00),
"_verifyInheritance: on refuse must be empty"
);
}
/**
* @notice Verifies that premission is empty or exists
* @param permissionPropertyId Perission property identifier
*/
function _verifyRecipientPermission(bytes32 permissionPropertyId) internal view {
if (permissionPropertyId == bytes32(0x00)) { return; }
_propertiesEnumerable[permissionPropertyId]._verifyPropertyExists();
}
/**
* @notice Verifies that source has same type
* @param newSourceId New value source identifier
* @param oldSourceId Old value source identifier
*/
function _verifySourceHasSameType(bytes32 newSourceId, bytes32 oldSourceId) internal view {
require(
_sourcesMetadata[newSourceId].sourceType == _sourcesMetadata[oldSourceId].sourceType,
"_verifySourceHasSameType: New source is different type"
);
}
/**
* @notice Verifies that name is not empty
* @param name Name string
*/
function _verifyPropertyNameNotEmpty(string memory name) internal pure {
require(
bytes(name).length > 0,
"_verifyPropertyNameNotEmpty: Empty name"
);
}
/**
* @notice Verifies that name is not empty
* @param externalId External identifier
*/
function _verifyExternalIdNotEmpty(bytes32 externalId) internal pure {
require(
externalId != bytes32(0x00),
"_verifyExternalIdNotEmpty: Empty external id"
);
}
/**
* @notice Verifies that property data type is specified
* @param newDataType New property data type
*/
function _verifyDataTypeNotNone(DataType newDataType) internal pure {
require(
newDataType != DataType.None,
"_verifyDataTypeNotNone: Data type is none"
);
}
/**
* @notice Verifies that address is contract
*/
function _requireContract(address addr) internal view {
require(
addr.isContract(),
"_requireContract: Address isn't contract"
);
}
/**
* @notice Get an Attestation property internal method with verifications
* @param contextId Context identifier
* @param name Property name
* @return Unique property identifier
*/
function _safeGetProperty(bytes32 contextId, string memory name) internal view returns (bytes32) {
_verifyPropertyNameNotEmpty(name);
bytes32 propertyId = ComplianceOraclePropertyCommons._getPropertyId(contextId, name);
_propertiesEnumerable[propertyId]._verifyPropertyExists();
return propertyId;
}
}
/**
* @title Compliance Oracle properties internal methods
*/
contract ComplianceOraclePropertiesInternal is ComplianceOraclePropertiesInternalVerifications {
/**
* @notice Saves an Attestation property in storage
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
* @return propertyId property unique identifier
*/
function _setProperty(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
internal
returns (bytes32)
{
bytes32 propertyId = ComplianceOraclePropertyCommons._getPropertyId(propertyInput.contextId, propertyInput.name);
_updatePropertyRelations(
propertyId,
propertyInput
);
PropertyMetadata memory propertyMetadata = _getPropertyMetadata(propertyInput);
_propertiesEnumerable[propertyId].modifiedAt = block.timestamp;
_propertiesMetadata[propertyId] = propertyMetadata;
_propertiesAccessible[propertyId] = propertyAccessible;
_propertiesIdsByExternal[propertyInput.contextId][propertyInput.externalId] = propertyId;
return propertyId;
}
/**
* @notice Updates property relations
* @param propertyId Property identifier
* @param propertyInput Property input struct
*/
function _updatePropertyRelations(
bytes32 propertyId,
PropertyInput memory propertyInput
)
internal
{
// Add child to parent property
bytes32 oldParentId = _propertiesMetadata[propertyId].parentId;
bytes32 newParentId = propertyInput.parentId;
if (newParentId != oldParentId) {
if (oldParentId != bytes32(0x00)) {
_propertiesEnumerable[oldParentId].childrenTotal -= 1;
}
if (newParentId != bytes32(0x00)) {
_propertiesEnumerable[newParentId].childrenTotal += 1;
}
}
// Add property to source
bytes32 oldSourceId = _propertiesMetadata[propertyId].sourceId;
bytes32 newSourceId = propertyInput.sourceId;
if (newSourceId != oldSourceId) {
if (oldSourceId != bytes32(0x00)) {
_sourcesEnumerable[oldSourceId].propertiesTotal -= 1;
}
if (newSourceId != bytes32(0x00)) {
_sourcesEnumerable[newSourceId].propertiesTotal += 1;
}
}
}
/**
* @notice Updates properties relations basing on enable/disable flag
* @param contextId Context identifier
* @param propertyId Property identifier
* @param toBeEnabled flag indicating if it's an enable action or disable
*/
function _updatePropertyRelationsOnAction(
bytes32 contextId,
bytes32 propertyId,
bool toBeEnabled
)
internal
{
// Enable child in parent propery
bytes32 parentId = _propertiesMetadata[propertyId].parentId;
if (parentId != bytes32(0x00)) {
_propertiesEnumerable[parentId].childrenTotal = toBeEnabled
? _propertiesEnumerable[parentId].childrenTotal + 1
: _propertiesEnumerable[parentId].childrenTotal - 1;
}
// Enable property in source
bytes32 sourceId = _propertiesMetadata[propertyId].sourceId;
if (sourceId != bytes32(0x00)) {
_sourcesEnumerable[sourceId].propertiesTotal = toBeEnabled
? _sourcesEnumerable[sourceId].propertiesTotal + 1
: _sourcesEnumerable[sourceId].propertiesTotal - 1;
}
// Enable property in context
if (contextId != bytes32(0x00)) {
_contextsEnumerable[contextId].propertiesTotal = toBeEnabled
? _contextsEnumerable[contextId].propertiesTotal + 1
: _contextsEnumerable[contextId].propertiesTotal - 1;
}
// Enable property
_propertiesEnumerable[propertyId].isDisabled = !toBeEnabled;
}
/**
* @notice Get an Attestation property by external identifier internal method with verifications
* @param contextId Context identifier
* @param externalId External property identifier
* @return Unique property identifier
*/
function _getPropertyByExternalId(bytes32 contextId, bytes32 externalId) internal view returns (bytes32) {
_verifyExternalIdNotEmpty(externalId);
return _safeGetPropertyIdByExternalId(contextId, externalId);
}
/**
* @notice Returns property metadata from input
* @param propertyInput A property input structure
* @return Property metadata
*/
function _getPropertyMetadata(PropertyInput memory propertyInput)
internal
pure
returns (PropertyMetadata memory)
{
return PropertyMetadata({
name: propertyInput.name,
contextId: propertyInput.contextId,
sourceId: propertyInput.sourceId,
dataType: propertyInput.dataType,
parentId: propertyInput.parentId,
externalId: propertyInput.externalId,
isConstant: propertyInput.isConstant,
isDefault: false
});
}
}
/**
* @title Compliance Oracle properties package
*/
contract ComplianceOraclePropertiesPackageUpgradable is
IPackage,
ComplianceOracleStorage
{
// Define libraries
using CommonUpgradability for *;
using CommonRulesEngine for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeMethods(_methodsImplementations[msg.sig]);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IComplianceOracle(address(0x00)).createProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).updateProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).disableProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).enableProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getPropertyByExternalId.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getPropertyExternalCallsTotal.selector] = package;
}
}
/**
* @title Compliance Oracle properties methods
*/
contract ComplianceOraclePropertiesPackage is
IComplianceOracleProperties,
ComplianceOraclePropertiesInternal,
ComplianceOraclePropertiesPackageUpgradable,
ComplianceOraclePropertyPermissionsCommons
{
// Define libraries
using CommonPolicyParser for *;
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Disable an Attestation property
* @param contextId Context identifier
* @param name Property name
*/
function disableProperty(bytes32 contextId, string calldata name)
external
override
verifyPermission(ComplianceOraclePropertyCommons._getPropertyId(
contextId,
name
))
validateBeforeDisable(contextId, name)
{
bytes32 propertyId = ComplianceOraclePropertyCommons._getPropertyId(contextId, name);
_updatePropertyRelationsOnAction(contextId, propertyId, false);
emit PropertyUpdated(
propertyId,
contextId,
_propertiesMetadata[propertyId].parentId,
OperationType.Disable,
""
);
}
/**
* @notice Enable an Attestation property
* @param contextId Context identifier
* @param name Property name
*/
function enableProperty(bytes32 contextId, string calldata name)
external
override
verifyPermission(ComplianceOraclePropertyCommons._getPropertyId(
contextId,
name
))
validateBeforeEnable(contextId, name)
{
bytes32 propertyId = ComplianceOraclePropertyCommons._getPropertyId(contextId, name);
_updatePropertyRelationsOnAction(contextId, propertyId, true);
emit PropertyUpdated(
propertyId,
contextId,
_propertiesMetadata[propertyId].parentId,
OperationType.Enable,
""
);
}
/**
* @notice Get property
* @param contextId Context identifier
* @param name Property name
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function getProperty(bytes32 contextId, string calldata name) external view override returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
bytes32 propertyId = _safeGetProperty(contextId, name);
return (
_propertiesMetadata[propertyId],
_propertiesEnumerable[propertyId],
_propertiesAccessible[propertyId]
);
}
/**
* @notice Get property external calls total
* @param propertyId Property identifier
*/
function getPropertyExternalCallsTotal(bytes32 propertyId) external view override returns (uint) {
_propertiesEnumerable[propertyId]._verifyPropertyExists();
bytes32 sourceId = _propertiesMetadata[propertyId].sourceId;
_sourcesEnumerable[sourceId]._verifySourceExists();
if (_sourcesMetadata[sourceId].sourceType == SourceType.Policy) {
address policyAddress = _sourcesMetadata[sourceId].policyAddress;
_requireContract(policyAddress);
return policyAddress._tryGetExternalCallsTotal();
}
if (_sourcesMetadata[sourceId].sourceType == SourceType.External) {
return 1;
}
return 0;
}
/**
* @notice Get property by external identifier in context
* @param contextId Context identifier
* @param externalId External identifier
*/
function getPropertyByExternalId(bytes32 contextId, bytes32 externalId) external view override returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
bytes32 propertyId = _getPropertyByExternalId(contextId, externalId);
return (
_propertiesMetadata[propertyId],
_propertiesEnumerable[propertyId],
_propertiesAccessible[propertyId]
);
}
/**
* @notice Create an Attestation property
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*
* @dev Creating property which inherits from another requires the following
* fields to be empty (because they are inherited from a parent property):
* - sourceId
* - dataType
* - externalId
* - isConstant
* - expirationTime
* - onRefuse
* @return propertyId Unique property identifier
*/
function createProperty(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
public
override
verifyPermission(propertyInput.contextId)
validateBeforeCreate(
propertyInput,
propertyAccessible
)
returns (bytes32)
{
bytes32 propertyId = _setProperty(propertyInput, propertyAccessible);
// Add property to context
_contextsEnumerable[propertyInput.contextId].propertiesTotal += 1;
_setupProperty(
propertyId,
msg.sender
);
emit PropertyUpdated(
propertyId,
propertyInput.contextId,
propertyInput.parentId,
OperationType.Create,
propertyInput.description
);
return propertyId;
}
/**
* @notice Update an Attestation property
* @dev Parent must be the same as before
* @param propertyInput Attestation property input metadata structure
* @param propertyAccessible Attestation property accessible structure
*/
function updateProperty(
PropertyInput memory propertyInput,
PropertyAccessible memory propertyAccessible
)
public
override
verifyPermission(ComplianceOraclePropertyCommons._getPropertyId(
propertyInput.contextId,
propertyInput.name
))
validateBeforeUpdate(
propertyInput,
propertyAccessible
)
{
bytes32 propertyId = _setProperty(propertyInput, propertyAccessible);
emit PropertyUpdated(
propertyId,
propertyInput.contextId,
propertyInput.parentId,
OperationType.Update,
propertyInput.description
);
}
}
/**
* @title Compliance Oracle contexts internal verifications methods
*/
contract ComplianceOracleContextsInternalVerifications is ComplianceOracleProtectedVerifications {
// Define libraries
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Verifies a context inputs before a context creation
* @param contextInput Context input structure
*/
modifier validateBeforeCreate(ContextInput memory contextInput) {
// Verify inputs
_verifyContextNameNotEmpty(contextInput.name);
// Verify context id
bytes32 contextId = ComplianceOracleContextCommons._getContextId(contextInput.name);
_contextsEnumerable[contextId]._verifyContextNotExists();
_verifyContextInheritanceRelatedInput(contextId, contextInput);
_;
}
/**
* @notice Verifies a context inputs before a context update
* @param contextInput Context input structure
*/
modifier validateBeforeUpdate(ContextInput memory contextInput) {
// Verify context id
bytes32 contextId = _safeGetContextId(contextInput.name);
_contextsEnumerable[contextId]._verifyContextNotDisabled();
_contextsMetadata[contextId]._verifyContextNotDefault();
_verifyContextInheritanceRelatedInput(contextId, contextInput);
_verifyHasDifference(contextId, contextInput);
_;
}
/**
* @notice Verifies a context inputs before a context disable
* @param name Data context name
*/
modifier validateBeforeDisable(string memory name) {
// Verify context id
bytes32 contextId = _safeGetContextId(name);
_contextsEnumerable[contextId]._verifyContextNotDisabled();
_contextsMetadata[contextId]._verifyContextNotDefault();
// Verify possibility to disable
_verifyContextEnumerableForDisable(contextId);
_;
}
/**
* @notice Verifies a context inputs before a context enable
* @param name Data context name
*/
modifier validateBeforeEnable(string memory name) {
// Verify context id
bytes32 contextId = _safeGetContextId(name);
_contextsEnumerable[contextId]._verifyContextDisabled();
if (_contextsMetadata[contextId].parentId != bytes32(0x00)) {
// Verify context parent
_contextsEnumerable[_contextsMetadata[contextId].parentId]._verifyContextNotDisabled();
}
_;
}
/**
* @notice Update a context internal method with verifications
* @param contextId Context unique identifier
* @param contextInput Context input structure
*/
function _verifyHasDifference(bytes32 contextId, ContextInput memory contextInput) internal view {
// Verify that new data hash differs
bytes32 newHash = keccak256(abi.encodePacked(contextInput.parentId));
ContextMetadata memory oldMetadata = _contextsMetadata[contextId];
bytes32 oldHash = keccak256(abi.encodePacked(oldMetadata.parentId));
require(
oldHash != newHash,
"_verifyHasDifference: No updates"
);
}
/**
* @notice Verifications depend on inheritance
* @param contextId Context unique identifier
* @param contextInput Context input metadata structure
*/
function _verifyContextInheritanceRelatedInput(
bytes32 contextId,
ContextInput memory contextInput
)
internal
view
{
if (
contextInput.parentId != _contextsMetadata[contextId].parentId
&& contextInput.parentId != bytes32(0x00)
)
{
_contextsEnumerable[contextInput.parentId]._verifyContextNotDisabled();
_verifyInheritance(contextInput);
}
}
/**
* @notice Verifies a context enumerable data before disable
* @param contextId Context unique identifier
*/
function _verifyContextEnumerableForDisable(bytes32 contextId) internal view {
require(
_contextsEnumerable[contextId].childrenTotal == 0,
"_verifyContextEnumerableForDisable: Existing children"
);
require(
_contextsEnumerable[contextId].propertiesTotal == 0,
"_verifyContextEnumerableForDisable: Existing properties"
);
require(
_contextsEnumerable[contextId].sourcesTotal == 0,
"_verifyContextEnumerableForDisable: Existing data sources"
);
}
/**
* @notice Verifies inheritance rules
* @param contextInput Context input structure
*/
function _verifyInheritance(ContextInput memory contextInput) internal view {
uint parentDepth = _contextsEnumerable[contextInput.parentId].depth;
require(
parentDepth < MAX_DEPTH,
"_verifyInheritance: Too deep"
);
}
/**
* @notice Get a context internal method with verifications
* @param name Context name
* @return Unique context identifier
*/
function _safeGetContextId(string memory name) internal view returns (bytes32) {
_verifyContextNameNotEmpty(name);
bytes32 contextId = ComplianceOracleContextCommons._getContextId(name);
_contextsEnumerable[contextId]._verifyContextExists();
return contextId;
}
/**
* @notice Verifies that name is not empty
* @param name Name string
*/
function _verifyContextNameNotEmpty(string memory name) internal pure {
require(
bytes(name).length > 0,
"_verifyContextNameNotEmpty: Empty name"
);
}
}
/**
* @title Compliance Oracle contexts internal methods
*/
contract ComplianceOracleContextsInternal is ComplianceOracleContextsInternalVerifications {
/**
* @notice Approves context creation
* @param contextInput Context input struct
* @param owner Future context owner address
* @return Request identifier
*/
function _approveCreateContext(
ContextInput memory contextInput,
address owner
)
internal
returns (uint)
{
bytes32 requestHash = keccak256(abi.encode(
owner,
contextInput.name,
contextInput.parentId
));
_createContextsRequests[_currentCreateContextRequestId] = requestHash;
return _currentCreateContextRequestId;
}
/**
* @notice Removes request hash from storage
* @param requestId Context request identifier
* @param contextInput Context input struct
*/
function _acceptCreateContext(
uint requestId,
ContextInput memory contextInput
)
internal
{
bytes32 requestHash = keccak256(abi.encode(
msg.sender,
contextInput.name,
contextInput.parentId
));
require(
_createContextsRequests[requestId] == requestHash,
"_acceptCreateContext: Unmatching hashes"
);
delete _createContextsRequests[requestId];
}
/**
* @notice Create a context
* @param contextInput Context structure
* @return Unique context identifier
*/
function _createContext(ContextInput memory contextInput) internal returns (bytes32) {
bytes32 contextId = ComplianceOracleContextCommons._getContextId(contextInput.name);
_updateContextRelations(contextId, contextInput);
// Update context
_contextsMetadata[contextId] = _getContextMetadata(contextInput);
_contextsEnumerable[contextId].modifiedAt = block.timestamp;
return contextId;
}
/**
* @notice Update a context
* @param contextInput Context structure
* @return Unique context identifier
*/
function _updateContext(ContextInput memory contextInput) internal returns (bytes32) {
bytes32 contextId = ComplianceOracleContextCommons._getContextId(contextInput.name);
_updateContextRelations(contextId, contextInput);
// Update context
_contextsMetadata[contextId] = _getContextMetadata(contextInput);
_contextsEnumerable[contextId].modifiedAt = block.timestamp;
return contextId;
}
/**
* @notice Disables a context
* @param name Context name
* @return contextId Unique context identifier
* @return parentId Unique context parent identifier
*/
function _disableContext(string memory name) internal returns (bytes32 contextId, bytes32 parentId) {
contextId = ComplianceOracleContextCommons._getContextId(name);
// Disable child from parent propery
parentId = _contextsMetadata[contextId].parentId;
if (parentId != bytes32(0x00)) {
_contextsEnumerable[parentId].childrenTotal -= 1;
}
// Disable context
_contextsEnumerable[contextId].isDisabled = true;
return (contextId, parentId);
}
/**
* @notice Disables a context
* @param name Context name
* @return contextId Unique context identifier
* @return parentId Unique context parent identifier
*/
function _enableContext(string memory name) internal returns (bytes32 contextId, bytes32 parentId) {
contextId = ComplianceOracleContextCommons._getContextId(name);
// Disable child from parent propery
parentId = _contextsMetadata[contextId].parentId;
if (parentId != bytes32(0x00)) {
_contextsEnumerable[parentId].childrenTotal += 1;
}
// Disable context
_contextsEnumerable[contextId].isDisabled = false;
return (contextId, parentId);
}
/**
* @notice Updates context relations
* @param contextId Context identifier
* @param contextInput Context input struct
*/
function _updateContextRelations(
bytes32 contextId,
ContextInput memory contextInput
)
internal
{
bytes32 oldInheritsFrom = _contextsMetadata[contextId].parentId;
bytes32 newInheritsFrom = contextInput.parentId;
if (newInheritsFrom != oldInheritsFrom) {
if (oldInheritsFrom != bytes32(0x00)) {
_contextsEnumerable[oldInheritsFrom].childrenTotal -= 1;
}
if (newInheritsFrom != bytes32(0x00)) {
_contextsEnumerable[newInheritsFrom].childrenTotal += 1;
_contextsEnumerable[contextId].depth = _contextsEnumerable[newInheritsFrom].depth + 1;
}
}
}
/**
* @notice Returns context metadata from input
* @param contextInput A context input structure
* @return Context metadata
*/
function _getContextMetadata(ContextInput memory contextInput)
internal
pure
returns (ContextMetadata memory)
{
return ContextMetadata({
name: contextInput.name,
parentId: contextInput.parentId,
isDefault: false
});
}
}
/**
* @title Compliance Oracle contexts package
*/
contract ComplianceOracleContextsPackageUpgradable is
IPackage,
ComplianceOracleStorage
{
// Define libraries
using CommonUpgradability for *;
using CommonRulesEngine for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeMethods(_methodsImplementations[msg.sig]);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IComplianceOracle(address(0x00)).approveCreateContext.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).acceptCreateContext.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).updateContext.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).disableContext.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).enableContext.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getContext.selector] = package;
}
}
/**
* @title Compliance Oracle contexts package
*/
contract ComplianceOracleContextsPackage is
IComplianceOracleContexts,
ComplianceOracleContextsPackageUpgradable,
ComplianceOracleContextsInternal,
ComplianceOracleContextPermissionsCommons
{
// Define libraries
using ComplianceOracleSourceCommons for *;
using ComplianceOraclePropertyCommons for *;
using ComplianceOracleContextCommons for *;
/**
* @notice Approves context creation
* @param contextInput Context input struct
* @param owner Future context owner address
* @return Request identifier
*/
function approveCreateContext(
ContextInput calldata contextInput,
address owner
)
external
override
verifyPermission(
contextInput.parentId == bytes32(0x00)
? DEFAULT_CONTEXT_ID
: contextInput.parentId
)
validateBeforeCreate(contextInput)
returns (uint)
{
uint requestId = _approveCreateContext(
contextInput,
owner
);
_currentCreateContextRequestId++;
emit CreateContextApproved(requestId);
return requestId;
}
/**
* @notice Accepts context creation
* @param requestId Request identifier
* @param contextInput Context input struct
* @return Created context identifier
*/
function acceptCreateContext(
uint requestId,
ContextInput calldata contextInput
)
external
override
validateBeforeCreate(contextInput)
returns (bytes32)
{
_acceptCreateContext(
requestId,
contextInput
);
bytes32 contextId = _createContext(contextInput);
_setupContext(
contextId,
msg.sender
);
emit CreateContextAccepted(requestId);
emit ContextUpdated(
contextId,
contextInput.parentId,
OperationType.Create,
contextInput.description
);
return contextId;
}
/**
* @notice Update a Data context
* @param contextInput Data context structure
*/
function updateContext(ContextInput calldata contextInput)
external
override
verifyPermission(ComplianceOracleContextCommons._getContextId(contextInput.name))
validateBeforeUpdate(contextInput)
{
bytes32 contextId = _updateContext(contextInput);
emit ContextUpdated(
contextId,
contextInput.parentId,
OperationType.Update,
contextInput.description
);
}
/**
* @notice Disable a Data context
* @param name Data context name
*/
function disableContext(string calldata name)
external
override
verifyPermission(ComplianceOracleContextCommons._getContextId(name))
validateBeforeDisable(name)
{
(bytes32 contextId, bytes32 parentId) = _disableContext(name);
emit ContextUpdated(
contextId,
parentId,
OperationType.Disable,
""
);
}
/**
* @notice Enable a Data context
* @param name Data context name
*/
function enableContext(string calldata name)
external
override
verifyPermission(ComplianceOracleContextCommons._getContextId(name))
validateBeforeEnable(name)
{
(bytes32 contextId, bytes32 parentId) = _enableContext(name);
emit ContextUpdated(
contextId,
parentId,
OperationType.Enable,
""
);
}
/**
* @notice Get Data context
* @param name Context name
* @return Context metadata
* @return Context enumerable data
*/
function getContext(string calldata name) external view override returns (
ContextMetadata memory,
ContextEnumerable memory
)
{
bytes32 contextId = _safeGetContextId(name);
return (
_contextsMetadata[contextId],
_contextsEnumerable[contextId]
);
}
}
/**
* @title Compliance oracle system utils
*/
contract ComplianceOracleInitSystemUtils is
IComplianceOracleSystemUtils,
ComplianceOracleInitSystemUtilsInternal
{
// Define libraries
using BytesHelper for *;
using SystemUtils for *;
using ComplianceOraclePropertyCommons for *;
/**
* @notice Get property existance flag by identifier
* @param propertyId Property identifier
* @return existance flag
*/
function isExistingProperty(bytes32 propertyId) external view override returns (bool) {
return _propertiesEnumerable[propertyId].modifiedAt > 0;
}
/**
* @notice Get property by identifier
* @param propertyId Property unique identifier
* @return metadata Property metadata
* @return enumerable Property enumerable data
* @return accessible Property accessible data
*/
function getPropertyById(bytes32 propertyId) external view override returns (
PropertyMetadata memory metadata,
PropertyEnumerable memory enumerable,
PropertyAccessible memory accessible
)
{
return (
_propertiesMetadata[propertyId],
_propertiesEnumerable[propertyId],
_propertiesAccessible[propertyId]
);
}
/**
* @notice Get context data by id
* @param contextId Context identifier
* @return contextMetadata Contex metadata
* @return contextEnumerable Contex enumerable data
*/
function getContextById(bytes32 contextId) external view override returns (
ContextMetadata memory contextMetadata,
ContextEnumerable memory contextEnumerable
)
{
return (
_contextsMetadata[contextId],
_contextsEnumerable[contextId]
);
}
/**
* @notice Get Data source by identifier
* @param sourceId source identifier
* @return metadata Source metadata
* @return enumerable Source enumerable data
*/
function getSourceById(bytes32 sourceId) external view override returns (
SourceMetadata memory,
SourceEnumerable memory
)
{
return (
_sourcesMetadata[sourceId],
_sourcesEnumerable[sourceId]
);
}
/**
* @notice Registered owner for the sender
* @param owner Owner address
*/
function registerOwner(address owner) public override {
_registerOwner(owner, msg.sender);
}
/**
* @notice Requests attestation property
* @dev In init works only with internal data source. There is no verification
* @dev for parent, source,
* @dev to save bytecode. Will be overriten by properties getter update
* @param propertyId [required] Property unique identifier
* @param inputKeys [optional] Values keys that can be provided with transaction
* @param inputKeysValues [optional] Values by keys that can be provided with transaction
* @return valueDataType Data type of the property value
* @return value Value of the property
*/
function requestValue(
bytes32 propertyId,
bytes32[] memory inputKeys,
bytes32[] memory inputKeysValues,
address,
bytes32
)
public
payable
override
returns (
DataType valueDataType,
Value memory value,
bytes32,
bytes32
)
{
bytes32[] memory keys = _propertiesAccessible[propertyId]._getPropertyKeys();
bytes32[] memory keysValues = keys._getKeysValues(
inputKeys,
inputKeysValues
);
bytes32 valueKey = keysValues.generateKey(propertyId);
return (
_propertiesMetadata[propertyId].dataType,
_propertiesValues[valueKey],
bytes32(0x00),
bytes32(0x00)
);
}
}
/**
* @title Common functions that are used to call updates repository functions
*/
library CommonUpdatesRepository {
// Define libraries
using CustomRevert for *;
/**
* @notice Register component in the registry
* @param updatesRepository, updates repository address
* @param componentAddress Address of the component to be registered
*/
function _tryRegisterComponent(address updatesRepository, address componentAddress) internal {
try IUpdatesRepository(updatesRepository).registerComponent(componentAddress)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Register components in the registry
* @param updatesRepository, updates repository address
* @param componentsAddresses Addresses of the components to be registered
*/
function _tryBatchRegisterComponents(address updatesRepository, address[] memory componentsAddresses) internal {
try IUpdatesRepository(updatesRepository).batchRegisterComponents(componentsAddresses)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Publish new version for component
* @dev Must check package interfaces and version before adding
* @param updatesRepository, updates repository address
* @param updatePackage Address of the package
* @param version Package version
*/
function _tryPublishUpdate(
address updatesRepository,
address updatePackage,
string memory version
)
internal
{
try IUpdatesRepository(updatesRepository).publishUpdate(updatePackage, version)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Someone who has rights can create a new package and publish it
* @dev Must check package interfaces and versions before adding
* @param updatesRepository, updates repository address
* @param updates Updates addresses and versions
*/
function _tryBatchPublishUpdates(
address updatesRepository,
BatchUpdateDetails[] calldata updates
)
internal
{
try IUpdatesRepository(updatesRepository).batchPublishUpdates(updates)
{ } catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Applying a next version to the package (msg.sender)
* @dev call updateComponent() using exception handling (try/catch)
* @param updatesRepository, updates repository address
* @dev If the is more than one update package this method must be invoked
* @dev as many time as many update packages are in the registry
* @dev one transaction - one update package
* @dev must revert:
* @dev component is not registered
* @dev component already has latest version
* @return result address of the updated component
*/
function _tryUpdateComponent(address updatesRepository) internal returns (address result) {
try IUpdatesRepository(updatesRepository).updateComponent() returns (address package) {
result = package;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @param updatesRepository, updates repository address
* @param componentAddress Component address
* @return result Component id
*/
function _tryGetComponentId(
address updatesRepository,
address componentAddress
)
internal
view
returns (bytes32 result)
{
try IUpdatesRepository(updatesRepository).getComponentId(componentAddress) returns (bytes32 res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @param updatesRepository, updates repository address
* @param componentAddress Component address
* @return result bool - true if component registered
*/
function _tryIsComponentRegistered(
address updatesRepository,
address componentAddress
)
internal
view
returns (bool result)
{
try IUpdatesRepository(updatesRepository).isComponentRegistered(componentAddress) returns (bool res) {
result = res;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Checks current component version in the registry
* @dev call getCurrentComponentVersion() using exception handling (try/catch)
* @param updatesRepository, updates repository address
* @param componentAddress Component address
* @return result Version of the component
*/
function _tryGetCurrentComponentVersion(
address updatesRepository,
address componentAddress
)
internal
view
returns (string memory result)
{
try IUpdatesRepository(updatesRepository).getCurrentComponentVersion(componentAddress) returns (string memory version) {
result = version;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
/**
* @notice Checks latest component version in the registry
* @dev call getLatestComponentVersion() using exception handling (try/catch)
* @param updatesRepository, updates repository address
* @param componentId Component id
* @return result Version of the component
*/
function _tryGetLatestComponentVersion(
address updatesRepository,
bytes32 componentId
)
internal
view
returns (string memory result)
{
try IUpdatesRepository(updatesRepository).getLatestComponentVersion(componentId) returns (string memory version) {
result = version;
} catch Error(string memory reason) {
revert(reason);
} catch Panic(uint reason) {
reason._revert("Panic Revert: ");
} catch (bytes memory reason) {
revert(string(reason));
}
}
}
/**
* @title Init Compliance Oracle init package upgradable
*/
contract ComplianceOracleInitPackageUpgradable is
IPackage,
IUpgradable,
IComplianceOracleInit,
ComplianceOracleInitSystemUtils,
ComplianceOracleProtectedVerifications
{
// Define libraries
using BytesHelper for *;
using CommonUpdatesRepository for *;
using CommonPackage for *;
using CommonRulesEngine for *;
/**
* @notice Initialize smart contract
* @param rulesEngine Rules engine address
* @param updatesRepository Updates repository address
* @param oraclizeResolver Oraclize resolver address
*/
function initialize(
address rulesEngine,
address updatesRepository,
address oraclizeResolver
)
external
override
{
require(rulesEngine != address(0x00), "Rules Engine address is empty");
require(updatesRepository != address(0x00), "Updates repository address is empty");
address package = _methodsImplementations[msg.sig];
_methodsImplementations[msg.sig] = address(0x00);
_rulesEngineAddress = rulesEngine;
_updatesRepositoryAddress = updatesRepository;
if (oraclizeResolver != address(0x00)) {
_oraclizeAddressResolver = OraclizeAddrResolverI(oraclizeResolver);
}
_initializeMethods(package);
_initializeDefaults();
_registerOwner(msg.sender, address(this));
}
/**
* @notice Updates component to the next version
* that is registered in the updates registry
*/
function update() external override verifyPermission(bytes32(0x00)) {
address package =_updatesRepositoryAddress._tryUpdateComponent();
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = package;
address(this)._tryApplyUpdate();
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(0x00);
}
/**
* @notice Applying a specific version of the update package related to the repository
* @dev If the is more than one update package bitween the current and specified
* @dev versions this method will perform every single update
*/
function updateToSpecificVersion(string calldata version) external override {
if (msg.sender != address(this)) {
_verifyPermission(bytes32(0x00));
}
address package = _updatesRepositoryAddress._tryUpdateComponent();
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = package;
address(this)._tryApplyUpdateToSpecificVersion(version);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(0x00);
}
/**
* @notice Applying an update to the component
* @dev Update can containe next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can containe next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @return current version of the component
*/
function getCurrentVersion() external view override returns (string memory) {
return _updatesRepositoryAddress._tryGetCurrentComponentVersion(address(this));
}
/**
* @return latest version of the component
*/
function getLatestVersion() external view override returns (string memory) {
return _updatesRepositoryAddress._tryGetLatestComponentVersion(COMPONENT_ID);
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Provides initialization of the component methods
* @param package Current package address
*/
function _initializeMethods(address package) internal {
_methodsImplementations[IUpgradable(address(0x00)).getCurrentVersion.selector] = package;
_methodsImplementations[IUpgradable(address(0x00)).getLatestVersion.selector] = package;
_methodsImplementations[IUpgradable(address(0x00)).update.selector] = package;
_methodsImplementations[IUpgradable(address(0x00)).updateToSpecificVersion.selector] = package;
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = package;
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).registerOwner.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).isExistingProperty.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getPropertyById.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getContextById.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).getSourceById.selector] = package;
_methodsImplementations[IComplianceOracle(address(0x00)).requestValue.selector] = package;
}
}
/**
* @title Compliance Oracle init package
*/
contract ComplianceOracleInitPackage is
ComplianceOracleInitPackageUpgradable
{ }
/**
* @title Compliance Oracle contexts package
*/
contract ComplianceOracleRolesSystemPackageUpgradable is
IPackage,
ComplianceOracleRolesSystemSetupInternal
{
// Define libraries
using CommonUpgradability for *;
using CommonRulesEngine for *;
/**
* @notice Constructor just for updates checks. This implementations won't be saved for proxy
*/
constructor() {
_methodsImplementations[IPackage(address(0x00)).applyUpdate.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).applyUpdateToSpecificVersion.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).getComponentId.selector] = address(this);
_methodsImplementations[IPackage(address(0x00)).supportsInterface.selector] = address(this);
}
/**
* @notice Applying an update to the component
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
*/
function applyUpdate() external override {
_applyUpdate();
}
/**
* @notice Applying an update to the component up to specified version
* @dev Update can contain next functionality:
* @dev - adding new methods
* @dev - removing some methods
* @dev - update logic in the existing methods
* @dev - do some calculations if it needed and update storage
* @param version Specified version
*/
function applyUpdateToSpecificVersion(string calldata version) external override {
_applyUpdate();
string memory thisVersion = address(this)._tryGetCurrentVersion();
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(thisVersion))) {
address(this)._tryUpdateToSpecificVersion(version);
}
}
/**
* @return component id
*/
function getComponentId() external pure override returns (bytes32) {
return COMPONENT_ID;
}
/**
* @notice Query if a contract implements an interface
* @param interfaceID The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
* @return `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
*/
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == 0xffffffff ? false : _methodsImplementations[interfaceID] != address(0x00);
}
/**
* @notice Applying an update to the component
*/
function _applyUpdate() internal {
_initializeDefaults();
}
}
/**
* @title Compliance Oracle roles system package
*/
contract ComplianceOracleRolesSystemPackage is ComplianceOracleRolesSystemPackageUpgradable { }
/**
* @title Compliance Oracle proxy
*/
contract ComplianceOracle is ComplianceOracleStorage {
/**
* @notice Initialize default parameters
* @param setup Setup contract address
*/
constructor(address setup) {
_methodsImplementations[IComplianceOracleInit(address(0x00)).initialize.selector] = setup;
_methodsImplementations[IERC165(address(0x00)).supportsInterface.selector] = setup;
}
/**
* @notice Receive function just to receive ether.
*/
receive() external payable {}
/**
* @notice Fallback function allowing to perform a delegatecall.
* @notice This function will return whatever the implementation call returns
*/
fallback() external payable {
address _impl = _methodsImplementations[msg.sig];
require(_impl != address(0x00), "Compliance Oracle method not found. 404");
assembly {
let p := mload(0x40)
calldatacopy(p, 0x00, calldatasize())
let result := delegatecall(gas(), _impl, p, calldatasize(), 0x00, 0x00)
let size := returndatasize()
returndatacopy(p, 0x00, size)
switch result
case 0x00 { revert(p, size) }
default { return(p, size) }
}
}
}

Claims (9)

  1. 분산 원장 플랫폼을 포함하는 분산형 컴퓨팅 네트워크에서, 상태 변경으로 나타내어지는 데이터 트랜잭션의 보안 승인을 위한 방법으로서,
    상기 분산형 컴퓨팅 네트워크에서 실행되는 스마트 계약 코드(smart contract code)로 표현되는 루트 권한(root right) - 상기 루트 권한은 특정 기능(functions)의 실행을 허용함으로써 불변 권한(immutable right)을 생성함 - 을 생성하는 단계;
    상기 특정 기능 중 적어도 하나를 허용하기 위한 권한을 상기 루트 권한으로부터 상기 분산형 네트워크 상의 월렛(wallet)으로 위임하여, 상기 월렛이 상기 기능 중 상기 적어도 하나에 대응하는 트랜잭션에 서명할 권한을 갖도록 위임된 권한을 생성하는 단계 - 상기 위임된 권한은 상기 루트 권한에 의해 철회되거나 재지정될 수 있음-;
    권한의 위임을 나타내는 데이터 구조를 위임 레지스트리에 기록하는 단계; 및
    상기 월렛으로 상기 트랜잭션에 서명하여 상기 월렛 권한을 행사함으로써 상기 분산형 컴퓨팅 네트워크에서 트랜잭션을 승인하는 단계; 를 포함하는,
    방법.
  2. 제1항에 있어서,
    상기 스마트 계약 코드는, 상기 루트 권한에 지정된 위임자 월렛(assigning wallet)에 대한 서명 권한을 가진 당사자에게, 위임된 권한을 상기 월렛에 위임할 허가를 제공하며, 위임에 대응하여 기록이 이루어지고, 이에 따라 상기 위임된 권한이 적용되는 상기 스마트 계약, 상기 명명된 권한, 상기 위임자 월렛, 및 상기 권한이 위임된 상기 월렛을 인용하는 기록이 상기 위임 레지스트리에 생성되는,
    방법.
  3. 제2항에 있어서,
    상기 스마트 계약과 상기 위임 레지스트리 간의 커뮤니케이션을 위하여 스마트 계약 인터페이스 규격(smart contract interface specification)이 사용되는,
    방법.
  4. 제3항에 있어서,
    상기 루트 월렛은, 위임된 권한을 가진 상기 월렛이 훼손될(compromised)경우, 상기 위임 레지스트리로부터 해당 기록을 제거함으로써, 또는 상기 위임 레지스트리에서 해당 기록을 변경하여 상기 권한을 다른 월렛에 위임함에 따라 위임을 교체함으로써, 위임된 권한을 철회할 수 있는,
    방법.
  5. 제3항에 있어서,
    상기 월렛이 상기 위임된 권한을 다른 월렛에 위임함으로써, 상기 루트 월렛을 트랜잭션의 서명과 관련된 사이버 위험으로부터 추가적으로 분리시키는 단계를 더 포함하는,
    방법.
  6. 제5항에 있어서,
    권한을 생성하고, 관리하고, 시행하는 권한을 포함하는, 권한으로부터 권한이 형성될 수 있는 권한 관리 데이터 구조 모델이 적용되며,
    상기 권한 관리 데이터 구조 모델은, 권한 관리 구조를 가능케 하는 스마트 계약을 배포(deploy)함으로써 상태 변경을 승인하는 권한을 표현하며,
    상기 권한 관리 데이터 구조 모델은, 상기 루트 권한을 소유하는 당사자에 의하여 정의되는,
    방법.
  7. 제6항에 있어서,
    상기 권한 관리 데이터 구조 모델은, 상기 권한에 의하여 관리되는 속성 관리 레지스트리(attribute management registry)를 포함하며,
    각각의 속성은, 명칭, 저장 위치 및 데이터 유형, 및 어느 당사자가 데이터를 생성, 열람, 업데이트, 또는 삭제할 수 있는지에 관한 권한을 포함하는 데이터 구조이고,
    증명 레지스트리(attestation registry)는, 승인된 당사자에 의해 지정된 객체(objects)와 연관된 증명 속성 값을 저장하며, 상기 증명 속성 값은 월렛에 지정된 권한을 캡쳐(capture)하기 위해 사용될 수 있는 역할을 나타내며, 이에 따라 다른 권한을 통제(govern)하는 데 사용될 수 있는 권한의 생성 및 관리를 용이하게 하는,
    방법.
  8. 제7항에 있어서,
    정책 관리 데이터 구조는, 서명자의 트랜잭션에 영향을 미칠 권한을 판단하기 위해, 제안된 상태 변경에서 영향을 받는 객체의 속성을 사용하는 로직(logic)으로 구성된 하나 이상의 규칙을 평가하는, 인코딩된 인스트럭션(instruction)의 집합을 포함하는,
    방법.
  9. 제2항에 있어서,
    모든 권한은 상기 루트 권한으로부터 직접 또는 간접적으로 파생되며, 이에 따라 상기 루트 권한으로 거슬러올라갈 수 있는 권한의 체인(chain)을 제공하는,
    방법.
KR1020237000934A 2020-06-10 2021-06-10 연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체 KR20230046291A (ko)

Applications Claiming Priority (3)

Application Number Priority Date Filing Date Title
US202063037034P 2020-06-10 2020-06-10
US63/037,034 2020-06-10
PCT/US2021/036826 WO2021252773A1 (en) 2020-06-10 2021-06-10 Method, apparatus, and computer-readable medium for confederated rights and hierarchical key management

Publications (1)

Publication Number Publication Date
KR20230046291A true KR20230046291A (ko) 2023-04-05

Family

ID=78825509

Family Applications (1)

Application Number Title Priority Date Filing Date
KR1020237000934A KR20230046291A (ko) 2020-06-10 2021-06-10 연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체

Country Status (7)

Country Link
US (1) US11954212B2 (ko)
EP (1) EP4165573A1 (ko)
JP (1) JP2023529671A (ko)
KR (1) KR20230046291A (ko)
CN (1) CN116324844A (ko)
CA (1) CA3181478A1 (ko)
WO (1) WO2021252773A1 (ko)

Families Citing this family (4)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US11526954B2 (en) 2019-05-14 2022-12-13 Microsoft Technology Licensing, Llc User interface and smart contract interaction model for generating user interface representations
US11514457B2 (en) * 2019-05-23 2022-11-29 Microsoft Technology Licensing, Llc Smart contract generation and execution system with built-in mediator selection and enforcement tools
US20210042737A1 (en) * 2019-08-07 2021-02-11 Seatig Inc. Distributed computing architecture with settlement mechanism to enable traceability of credit tokenization, disbursement and repayment
CN111539813B (zh) * 2020-07-10 2020-12-11 支付宝(杭州)信息技术有限公司 业务行为的回溯处理方法、装置、设备及系统

Family Cites Families (23)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US11250423B2 (en) 2012-05-04 2022-02-15 Institutional Cash Distributors Technology, Llc Encapsulated security tokens for electronic transactions
US11521290B2 (en) * 2013-05-22 2022-12-06 Patrick Damien O'Brien Systems and methods for storing contract information on multiple blockchain ledgers
KR101816653B1 (ko) * 2017-02-14 2018-02-21 주식회사 코인플러그 스마트 컨트랙트 및 블록체인 데이터베이스를 사용하여 서비스 제공 서버에 의하여 제공되는 서비스를 이용하기 위한 사용자의 로그인 요청에 대하여 pki 기반의 인증을 통해 로그인을 대행하는 방법 및 이를 이용한 서버
US10489597B2 (en) 2017-03-28 2019-11-26 General Electric Company Blockchain verification of network security service
US10735202B2 (en) 2017-07-24 2020-08-04 International Business Machines Corporation Anonymous consent and data sharing on a blockchain
US11200569B1 (en) * 2018-02-12 2021-12-14 Winklevoss Ip, Llc System, method and program product for making payments using fiat-backed digital assets
US11308487B1 (en) * 2018-02-12 2022-04-19 Gemini Ip, Llc System, method and program product for obtaining digital assets
US10540654B1 (en) * 2018-02-12 2020-01-21 Winklevoss Ip, Llc System, method and program product for generating and utilizing stable value digital assets
FR3079322B1 (fr) 2018-03-26 2021-07-02 Commissariat Energie Atomique Methode et systeme de gestion d'acces a des donnees personnelles au moyen d'un contrat intelligent
US10673626B2 (en) 2018-03-30 2020-06-02 Spyrus, Inc. Threshold secret share authentication proof and secure blockchain voting with hardware security modules
US11250466B2 (en) 2018-07-30 2022-02-15 Hewlett Packard Enterprise Development Lp Systems and methods for using secured representations of user, asset, and location distributed ledger addresses to prove user custody of assets at a location and time
US11159307B2 (en) 2018-08-08 2021-10-26 International Business Machines Corporation Ad-hoc trusted groups on a blockchain
US10997251B2 (en) * 2018-10-15 2021-05-04 Bao Tran Smart device
US20200334773A1 (en) * 2019-04-18 2020-10-22 Erich Lawson Spangenberg System and Method of IP Ownership and IP Transactions with Guaranteed Buy Valuation and Broker Rewards
WO2020092351A1 (en) 2018-10-29 2020-05-07 Login Id Inc. Decentralized computing systems for strong user authentication and related methods
US11960473B2 (en) * 2019-01-15 2024-04-16 Fisher-Rosemount Systems, Inc. Distributed ledgers in process control systems
KR102478132B1 (ko) * 2019-01-18 2022-12-15 웁살라 프라이비트 리미티드 컴퍼니 사이버보안 장치 및 방법
US11783024B2 (en) * 2019-01-31 2023-10-10 Salesforce, Inc. Systems, methods, and apparatuses for protecting consumer data privacy using solid, blockchain and IPFS integration
WO2020215083A1 (en) * 2019-04-19 2020-10-22 Coinbase, Inc. Systems and methods for blockchain administration
US11038771B2 (en) * 2019-04-26 2021-06-15 Salesforce.Com, Inc. Systems, methods, and apparatuses for implementing a metadata driven rules engine on blockchain using distributed ledger technology (DLT)
US20200342449A1 (en) * 2019-04-29 2020-10-29 Salesforce.Com, Inc. Systems, methods, and apparatuses for implementing an api gateway to authorize and charge a fee for a transaction between cloud computing customers using distributed ledger technologies (dlt)
US11755998B2 (en) * 2020-05-18 2023-09-12 International Business Machines Corporation Smart data annotation in blockchain networks
US20220051261A1 (en) 2020-05-29 2022-02-17 Samuel Vetas Processes and systems of blockchain with verification through a consortium of stakeholders

Also Published As

Publication number Publication date
US11954212B2 (en) 2024-04-09
CN116324844A (zh) 2023-06-23
EP4165573A1 (en) 2023-04-19
CA3181478A1 (en) 2021-12-16
WO2021252773A1 (en) 2021-12-16
JP2023529671A (ja) 2023-07-11
US20210390191A1 (en) 2021-12-16

Similar Documents

Publication Publication Date Title
Sharma et al. Blockchain technology for cloud storage: A systematic literature review
US11315110B2 (en) Private resource discovery and subgroup formation on a blockchain
US11860822B2 (en) Immutable ledger with efficient and secure data destruction, system and method
CN109691015B (zh) 一种区块链上的动态访问控制方法及系统
Bhaskaran et al. Double-blind consent-driven data sharing on blockchain
KR100996784B1 (ko) 공개 키 암호화에 기초한 데이터의 저장 및 검색을 위한, 컴퓨팅 장치에서 구현되는 방법, 시스템 및 복수의 명령어를 저장하는 하나 이상의 컴퓨터 판독가능 매체
JP2021519531A (ja) ブロックチェーン・ネットワークに対するドキュメント・アクセス
US11362842B2 (en) Membership compiler for applications
KR20230046291A (ko) 연맹 권한 및 계층적 키 관리를 위한 방법, 장치 및 컴퓨터 판독 가능 매체
US10936552B2 (en) Performing bilateral negotiations on a blockchain
US20180218364A1 (en) Managing distributed content using layered permissions
JP2023527811A (ja) ネットワーク化されたデータ・トランザクションの認証及び認可のための方法、装置、及びコンピュータ可読媒体
KR20090052321A (ko) 다기능 제어 구조를 이용하는 콘텐트 제어 시스템과 방법
US20200082391A1 (en) Performing bilateral negotiations on a blockchain
Nyman et al. Citizen electronic identities using TPM 2.0
Ghorbel et al. Accountable privacy preserving attribute-based access control for cloud services enforced using blockchain
Rashid et al. RC-AAM: blockchain-enabled decentralized role-centric authentication and access management for distributed organizations
US20240005307A1 (en) Method, apparatus, and computer-readable medium for confederated rights and hierarchical key management
CN115699003A (zh) 文件验证系统和方法
KR101751316B1 (ko) 연산 리소스 실행의 안전 보호
US11263333B2 (en) Multi-subject device access authorization
Nowrozy et al. A blockchain-based secure data sharing framework for healthcare
WO2022116761A1 (en) Self auditing blockchain
Mandarino et al. Issues related to ehr blockchain applications
Thakkar et al. A Privacy-Preserving Framework Using Hyperledger Fabric for EHR Sharing Applications