Skip to content

Commit

Permalink
Merge pull request #16 from DSRCorporation/feature/issued-did-check
Browse files Browse the repository at this point in the history
Add check that issuer DID is controlled by sender on schema and cred def creation
  • Loading branch information
Toktar authored Nov 28, 2023
2 parents 1dd7ee2 + 8ce8e6e commit dca3cdc
Show file tree
Hide file tree
Showing 23 changed files with 236 additions and 189 deletions.
5 changes: 2 additions & 3 deletions indy-besu/docs/design/cl-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Contract name: **SchemaRegistry**
* Schema must have name.
* Schema must contain at least one attribute.
* Schema must have version.
* Corresponding issuer DID must exist and be active.
* Corresponding issuer DID must exist, be active, and owned by sender.
* Format:
```
SchemaRegistry.createSchema(Schema schema)
Expand Down Expand Up @@ -127,7 +127,6 @@ Contract name: **SchemaRegistry**
)
* Raised Event: None
## Credential Definition
### ID Syntax
Expand Down Expand Up @@ -217,7 +216,7 @@ Contract name: **CredentialDefinitionRegistry**
* Description: Transaction to create a new AnonCreds Credential Definition
* Restrictions:
* Credential Definition must be unique.
* Corresponding issuer DID must exist and be active.
* Corresponding issuer DID must exist, be active, and owned by sender.
* Corresponding schema must exist.
* Format:
```
Expand Down
60 changes: 30 additions & 30 deletions indy-besu/network/config/besu/genesis.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions indy-besu/smart_contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ cache
artifacts
typechain
typechain-types
compiled-contracts
ContractsGenesis.json

.openzeppelin
4 changes: 2 additions & 2 deletions indy-besu/smart_contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ You can find sample scripts demonstrating the usage of deployed contracts in the
```
> yarn demo/account
```
* [Demo flow](./demos/flow.ts) - create/reolve DID/Schema/Credential Dedinition.
* [Demo flow](./demos/flow.ts) - create/reolve DID/Schema/Credential Definition.
```
> yarn demo/flow
```
Expand Down Expand Up @@ -84,7 +84,7 @@ This section describes how to inject smart contracts into the genesis state of t
3. Compile runtime contracts byte code:
```
solc -o compiled-contracts --optimize --bin-runtime --evm-version=constantinople @dk1a=$(pwd)/node_modules/@dk1a @openzeppelin=$(pwd)/node_modules/@openzeppelin contracts/**/*.sol node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol
yarn solc-compile
```
* `compiled-contracts` folder with binary files will be generated as the result of the execution.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ interface RoleControlInterface {
/**
* @dev Function to assign role to an account.
*
* Restrcitions:
* - Only senders with certain roles as specified in the access rules are permitted assign specefic roles;
* Restrictions:
* - Only senders with certain roles as specified in the access rules are permitted assign specific roles;
* otherwise, the transaction will revert with an `Unauthorized` error.
*
* Events:
Expand All @@ -44,12 +44,12 @@ interface RoleControlInterface {
/**
* @dev Function to revoke role from an account.
*
* Restrcitions:
* - Only senders with certain roles as specified in the access rules are permitted revoke specefic roles;
* Restrictions:
* - Only senders with certain roles as specified in the access rules are permitted revoke specific roles;
* otherwise, the transaction will revert with an `Unauthorized` error.
*
* Events:
* - On successful role revokation, will emit a `RoleRevoked` event.
* - On successful role revocation, will emit a `RoleRevoked` event.
*
* @param role The role to be revoked from the account.
* @param account The address of the account from which the role will be revoked.
Expand Down
50 changes: 50 additions & 0 deletions indy-besu/smart_contracts/contracts/cl/CLRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import { DidNotFound } from "../did/DidErrors.sol";
import { DidRegistryInterface } from "../did/DidRegistry.sol";
import { DidDocumentStorage } from "../did/DidTypes.sol";
import { ControlledUpgradeable } from "../upgrade/ControlledUpgradeable.sol";
import { Errors } from "../utils/Errors.sol";

import { IssuerHasBeenDeactivated, IssuerNotFound, SchemaAlreadyExist, SchemaNotFound, SenderIsNotIssuerDidOwner } from "./ClErrors.sol";
import { CLRegistry } from "./CLRegistry.sol";
import { SchemaRegistryInterface } from "./SchemaRegistryInterface.sol";
import { Schema, SchemaWithMetadata } from "./SchemaTypes.sol";
import { SchemaValidator } from "./SchemaValidator.sol";
import { toSlice } from "@dk1a/solidity-stringutils/src/StrSlice.sol";

using SchemaValidator for Schema;
using { toSlice } for string;

// TODO: Think of adding `DidUniversalResolver` contract so that adding a support for new DID method (like `did:ethr`)
// will require updating version of only `DIDUniversalResolver` contract only.
// CLRegistry registry use `DIDUniversalResolver` to get DID Record and validate as needed
//interface DIDUniversalRegistryResolver {
// function resolveDid()
//}

contract CLRegistry {
/**
* @dev Reference to the contract that manages DIDs
*/
DidRegistryInterface internal _didRegistry; // did:indy2

/**
* Checks that the Issuer DID exist, controlled by sender, and active
*/
modifier _validIssuer(string memory id) {
try _didRegistry.resolveDid(id) returns (DidDocumentStorage memory didDocumentStorage) {
if (msg.sender != didDocumentStorage.metadata.creator)
revert SenderIsNotIssuerDidOwner(msg.sender, didDocumentStorage.metadata.creator);
if (didDocumentStorage.metadata.deactivated) revert IssuerHasBeenDeactivated(id);
_;
} catch (bytes memory reason) {
if (Errors.equals(reason, DidNotFound.selector)) {
revert IssuerNotFound(id);
}

Errors.rethrow(reason);
}
}
}
9 changes: 8 additions & 1 deletion indy-besu/smart_contracts/contracts/cl/ClErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ error InvalidCredentialDefinitionId(string id);
* @notice Error that occurs when an unsupported credential definition type is used.
* @param credDefType Credential definition ID.
*/
error UnsupportedCredentialDefintionType(string credDefType);
error UnsupportedCredentialDefinitionType(string credDefType);

/**
* @notice Error that occurs when trying to create an existing credential definition.
Expand All @@ -64,3 +64,10 @@ error CredentialDefinitionAlreadyExist(string id);
* @param id Credential definition ID.
*/
error CredentialDefinitionNotFound(string id);

/**
* @notice Error that occurs when issuer DID of Schema or CredentialDefinition is not owned by sender.
* @param sender Sender account address.
* @param owner DID owner account address.
*/
error SenderIsNotIssuerDidOwner(address sender, address owner);
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,15 @@ import { Errors } from "../utils/Errors.sol";
import { CredentialDefinition, CredentialDefinitionWithMetadata } from "./CredentialDefinitionTypes.sol";
import { CredentialDefinitionRegistryInterface } from "./CredentialDefinitionRegistryInterface.sol";
import { CredentialDefinitionValidator } from "./CredentialDefinitionValidator.sol";
import { CredentialDefinitionAlreadyExist, CredentialDefinitionNotFound, IssuerHasBeenDeactivated, IssuerNotFound } from "./ClErrors.sol";
import { CredentialDefinitionAlreadyExist, CredentialDefinitionNotFound, IssuerHasBeenDeactivated, IssuerNotFound, SenderIsNotIssuerDidOwner } from "./ClErrors.sol";
import { CLRegistry } from "./CLRegistry.sol";
import { SchemaRegistryInterface } from "./SchemaRegistryInterface.sol";
import { toSlice } from "@dk1a/solidity-stringutils/src/StrSlice.sol";

using CredentialDefinitionValidator for CredentialDefinition;
using { toSlice } for string;

contract CredentialDefinitionRegistry is CredentialDefinitionRegistryInterface, ControlledUpgradeable {
/**
* @dev Reference to the contract that manages DIDs
*/
DidRegistryInterface private _didRegistry;

contract CredentialDefinitionRegistry is CredentialDefinitionRegistryInterface, ControlledUpgradeable, CLRegistry {
/**
* @dev Reference to the contract that manages anoncreds schemas
*/
Expand All @@ -34,15 +30,15 @@ contract CredentialDefinitionRegistry is CredentialDefinitionRegistryInterface,
mapping(string id => CredentialDefinitionWithMetadata credDefWithMetadata) private _credDefs;

/**
* Checks the uniqness of the credential definition ID
* Checks the uniqueness of the credential definition ID
*/
modifier _uniqueCredDefId(string memory id) {
if (_credDefs[id].metadata.created != 0) revert CredentialDefinitionAlreadyExist(id);
_;
}

/**
* Сhecks that the credential definition exist
* Checks that the credential definition exist
*/
modifier _credDefExist(string memory id) {
if (_credDefs[id].metadata.created == 0) revert CredentialDefinitionNotFound(id);
Expand All @@ -57,22 +53,6 @@ contract CredentialDefinitionRegistry is CredentialDefinitionRegistryInterface,
_;
}

/**
* Сhecks that the Issuer exist and active
*/
modifier _issuerActive(string memory id) {
try _didRegistry.resolveDid(id) returns (DidDocumentStorage memory didDocumentStorage) {
if (didDocumentStorage.metadata.deactivated) revert IssuerHasBeenDeactivated(id);
_;
} catch (bytes memory reason) {
if (Errors.equals(reason, DidNotFound.selector)) {
revert IssuerNotFound(id);
}

Errors.rethrow(reason);
}
}

function initialize(
address didRegistryAddress,
address schemaRegistryAddress,
Expand All @@ -86,7 +66,7 @@ contract CredentialDefinitionRegistry is CredentialDefinitionRegistryInterface,
/// @inheritdoc CredentialDefinitionRegistryInterface
function createCredentialDefinition(
CredentialDefinition calldata credDef
) public virtual _uniqueCredDefId(credDef.id) _schemaExist(credDef.schemaId) _issuerActive(credDef.issuerId) {
) public virtual _uniqueCredDefId(credDef.id) _schemaExist(credDef.schemaId) _validIssuer(credDef.issuerId) {
// credDef.requireValidId(); For migration from Indy we need to disable this check as schema id there represented as seq_no
credDef.requireValidType();
credDef.requireTag();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface CredentialDefinitionRegistryInterface {
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `InvalidCredentialDefinitionId`: Raised if the Credential Definition ID syntax is invalid.
* - `FieldRequired`: Raised when a mandatory Credential Definition field such as `type`, `tag` or `value` is not provided
* - `SenderIsNotIssuerDidOwner`: Raised when an issuer DID specified in CredentialDefinition is not owned by sender
*
* @param credDef The new AnonCreds Credential Definition.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.20;

import { toSlice } from "@dk1a/solidity-stringutils/src/StrSlice.sol";
import { FieldRequired, InvalidCredentialDefinitionId, UnsupportedCredentialDefintionType } from "./ClErrors.sol";
import { FieldRequired, InvalidCredentialDefinitionId, UnsupportedCredentialDefinitionType } from "./ClErrors.sol";
import { CredentialDefinition } from "./CredentialDefinitionTypes.sol";

using { toSlice } for string;
Expand Down Expand Up @@ -32,7 +32,7 @@ library CredentialDefinitionValidator {
*/
function requireValidType(CredentialDefinition memory self) internal pure {
if (!self.credDefType.toSlice().eq(_ANONCREDS_TYPE.toSlice())) {
revert UnsupportedCredentialDefintionType(self.credDefType);
revert UnsupportedCredentialDefinitionType(self.credDefType);
}
}

Expand Down
34 changes: 7 additions & 27 deletions indy-besu/smart_contracts/contracts/cl/SchemaRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,38 @@ import { DidDocumentStorage } from "../did/DidTypes.sol";
import { ControlledUpgradeable } from "../upgrade/ControlledUpgradeable.sol";
import { Errors } from "../utils/Errors.sol";

import { IssuerHasBeenDeactivated, IssuerNotFound, SchemaAlreadyExist, SchemaNotFound } from "./ClErrors.sol";
import { IssuerHasBeenDeactivated, IssuerNotFound, SchemaAlreadyExist, SchemaNotFound, SenderIsNotIssuerDidOwner } from "./ClErrors.sol";
import { SchemaRegistryInterface } from "./SchemaRegistryInterface.sol";
import { Schema, SchemaWithMetadata } from "./SchemaTypes.sol";
import { SchemaValidator } from "./SchemaValidator.sol";
import { CLRegistry } from "./CLRegistry.sol";
import { toSlice } from "@dk1a/solidity-stringutils/src/StrSlice.sol";

using SchemaValidator for Schema;
using { toSlice } for string;

contract SchemaRegistry is SchemaRegistryInterface, ControlledUpgradeable {
contract SchemaRegistry is SchemaRegistryInterface, ControlledUpgradeable, CLRegistry {
/**
* @dev Reference to the contract that manages DIDs
*/
DidRegistryInterface private _didRegistry;

/**
* Mapping Scheman ID to its Schema Details and Metadata.
* Mapping Schema ID to its Schema Details and Metadata.
*/
mapping(string id => SchemaWithMetadata schemaWithMetadata) private _schemas;

/**
* Checks the uniqness of the Schema ID
* Checks the uniqueness of the Schema ID
*/
modifier _uniqueSchemaId(string memory id) {
if (_schemas[id].metadata.created != 0) revert SchemaAlreadyExist(id);
_;
}

/**
* Сhecks that the Schema exist
* Checks that the Schema exist
*/
modifier _schemaExist(string memory id) {
if (_schemas[id].metadata.created == 0) revert SchemaNotFound(id);
_;
}

/**
* Сhecks that the Issuer exist and active
*/
modifier _issuerActive(string memory id) {
try _didRegistry.resolveDid(id) returns (DidDocumentStorage memory didDocumentStorage) {
if (didDocumentStorage.metadata.deactivated) revert IssuerHasBeenDeactivated(id);
_;
} catch (bytes memory reason) {
if (Errors.equals(reason, DidNotFound.selector)) {
revert IssuerNotFound(id);
}

Errors.rethrow(reason);
}
}

function initialize(address didRegistryAddress, address upgradeControlAddress) public reinitializer(1) {
_didRegistry = DidRegistryInterface(didRegistryAddress);
_initializeUpgradeControl(upgradeControlAddress);
Expand All @@ -67,7 +47,7 @@ contract SchemaRegistry is SchemaRegistryInterface, ControlledUpgradeable {
/// @inheritdoc SchemaRegistryInterface
function createSchema(
Schema calldata schema
) public virtual _uniqueSchemaId(schema.id) _issuerActive(schema.issuerId) {
) public virtual _uniqueSchemaId(schema.id) _validIssuer(schema.issuerId) {
schema.requireValidId();
schema.requireName();
schema.requireVersion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface SchemaRegistryInterface {
* - `IssuerHasBeenDeactivated`: Raised if the associated issuer is not active.
* - `InvalidSchemaId`: Raised if the Schema ID syntax is invalid.
* - `FieldRequired`: Raised when a mandatory Schema field such as `name`, `version` or `attributes` is not provided
* - `SenderIsNotIssuerDidOwner`: Raised when an issuer DID specified in Schema is not owned by sender
*
* @param schema The new AnonCreds schema.
*/
Expand Down
4 changes: 2 additions & 2 deletions indy-besu/smart_contracts/contracts/did/DidRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ contract DidRegistry is DidRegistryInterface, ControlledUpgradeable {
}

/**
* Сhecks that the DID has not been deactivated
* Checks that the DID has not been deactivated
*/
modifier _didIsActive(string memory did) {
if (_dids[did].metadata.deactivated) revert DidHasBeenDeactivated(did);
_;
}

/**
* Сhecks that method was called by did creator
* Checks that method was called by did creator
*/
modifier _senderIsCreator(string memory did) {
if (msg.sender != _dids[did].metadata.creator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface DidRegistryInterface {
* - Sender address must be equal to DID creator address; otherwise, will revert with a `SenderIsNotCreator` error.
*
* Events:
* - On succesful DID update, will emit a `DIDDeactivated` event.
* - On successful DID update, will emit a `DIDDeactivated` event.
*
* @param document The updated DID Document
*/
Expand All @@ -56,7 +56,7 @@ interface DidRegistryInterface {
* - Sender address must be equal to DID creator address; otherwise, will revert with a `SenderIsNotCreator` error.
*
* Events:
* - On succesful DID deactivation, will emit a `DIDDeactivated` event.
* - On successful DID deactivation, will emit a `DIDDeactivated` event.
*
* @param id The DID to be deactivated.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ contract ValidatorControl is ValidatorSmartContractInterface, ControlledUpgradea
/**
* @dev Remove an existing validator from the list.
*
* Restrcitions:
* Restrictions:
* - Only accounts with the steward role are permitted to call this method; otherwise, will revert with an `Unauthorized` error.
* - The validator address must be non-zero; otherwise, will revert with an `InvalidValidatorAddress` error.
* - The validator must not be last one; otherwise, will revert with an `CannotDeactivateLastValidator` error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ contract UpgradeControl is UpgradeControlInterface, UUPSUpgradeable, Initializab
RoleControlInterface private _roleControl;

/**
* @dev Double mapping proxy and implmentation addresses to upgrade proposal
* @dev Double mapping proxy and implementation addresses to upgrade proposal
* The key relationship can be visualized as:
* `proxy address -> implementation address -> upgrade proposal`
*/
mapping(address proxy => mapping(address implementation => UpgradeProposal proposal)) private _upgradeProposals;

/**
* @dev Modifier that checks that the implmentation is UUPSUpgradable
* @dev Modifier that checks that the implementation is UUPSUpgradable
*/
modifier _isUupsProxy(address implementation) {
try IERC1822Proxiable(implementation).proxiableUUID() returns (bytes32 slot) {
Expand Down
Loading

0 comments on commit dca3cdc

Please sign in to comment.