Skip to content

Commit

Permalink
rebase on main branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Leonard authored and Robert Leonard committed Oct 3, 2023
2 parents dc55093 + 5f4c770 commit d3be0ea
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 48 deletions.
78 changes: 45 additions & 33 deletions src/DidRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,22 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg

// Each flag is represented by a specific bit. This enum specifies what flag corresponds to which bit.
enum VerificationMethodFlagBitMask {
/// The VM can be used for encryption
KEY_AGREEMENT, // bit 0
/// The VM is able to authenticate the subject
AUTHENTICATION, // bit 1
/// The VM is able to proof assertions on the subject
ASSERTION, // bit 2
/// The VM can be used for issuing capabilities. Required for DID Update
CAPABILITY_INVOCATION, // bit 3
/// The VM can be used for delegating capabilities.
CAPABILITY_DELEGATION, // bit 4
/// The subject did proof to be in possession of the private key
OWNERSHIP_PROOF, // bit 5
PROTECTED // bit 6
/// The Verification Method is marked as protected. This means it cannot be removed
PROTECTED, // bit 6
/// The VM is hidden from the DID Document (off-chain only)
DID_DOC_HIDDEN //bit 7
}

struct VerificationMethod {
Expand Down Expand Up @@ -101,6 +110,7 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg
function addVerificationMethod(address didIdentifier, VerificationMethod calldata verificationMethod) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public {

require(!_doesFragmentExist(didIdentifier, verificationMethod.fragment), "Fragment already exist");
require(_isValidFlag(verificationMethod.flags), "Attempted to add unsupported flag");

// Apply a bitmask on the verificationMethodFlags
bool hasOwnershipFlag = _hasFlag(verificationMethod.flags, VerificationMethodFlagBitMask.OWNERSHIP_PROOF);
Expand Down Expand Up @@ -146,6 +156,7 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg

function updateVerificationMethodFlags(address didIdentifier, string calldata fragment, uint16 flags) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public returns(bool) {
require(_doesFragmentExist(didIdentifier, fragment), "Fragment does not match any verification methods with this did");
require(_isValidFlag(flags), "Attempted to add unsupported flag");

DidState storage didState = didStates[didIdentifier];

Expand Down Expand Up @@ -203,55 +214,50 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg
}

function addNativeController(address didIdentifier, address controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public {
require(!_doesNativeControllerExist(didIdentifier,controller), "Native controller already exist");
require(_doesNativeControllerExist(didIdentifier,controller) == -1, "Native controller already exist");
didStates[didIdentifier].nativeControllers.push(controller);

emit ControllerAdded(didIdentifier, abi.encodePacked(controller), true);
}

function removeNativeController(address didIdentifier, address controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public returns(bool) {
function removeNativeController(address didIdentifier, address controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public {
require(didIdentifier != controller, "Cannot remove default authority key");
require(_doesNativeControllerExist(didIdentifier,controller), "Native controller does not exist");

// If an index is returned the controller exits
int index = _doesNativeControllerExist(didIdentifier,controller);
require(index >= 0, "Native controller does not exist");

DidState storage didState = didStates[didIdentifier];

for(uint i=0; i < didState.nativeControllers.length; i++) {
if(didState.nativeControllers[i] == controller) {
// Remove native controller from array (not built into solidity so manipulating array to remove)
didState.nativeControllers[i] = didState.nativeControllers[didState.nativeControllers.length - 1];
didState.nativeControllers.pop();
// Remove native controller from array (not built into solidity so manipulating array to remove)
didState.nativeControllers[uint(index)] = didState.nativeControllers[didState.nativeControllers.length - 1];
didState.nativeControllers.pop();

emit ControllerRemoved(didIdentifier, abi.encodePacked(controller), true);
return true;
}
}
return false;
emit ControllerRemoved(didIdentifier, abi.encodePacked(controller), true);
}

function addExternalController(address didIdentifier, string calldata controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public {
require(!_doesExternalControllerExist(didIdentifier,controller), "External controller already exist");
_doesExternalControllerHaveCorrectPrefix(controller);
require(_doesExternalControllerExist(didIdentifier,controller) == -1, "External controller already exist");
didStates[didIdentifier].externalControllers.push(controller);

emit ControllerAdded(didIdentifier, abi.encodePacked(controller), false);
}

function removeExternalController(address didIdentifier, string calldata controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public returns(bool) {
require(_doesExternalControllerExist(didIdentifier,controller), "External controller does not exist");
function removeExternalController(address didIdentifier, string calldata controller) onlyNonGenerativeDid(didIdentifier) onlyAuthorizedKeys(didIdentifier) public {

// If an index is returned the controller exits
int index = _doesExternalControllerExist(didIdentifier,controller);
require(index >= 0, "External controller does not exist");

DidState storage didState = didStates[didIdentifier];

for(uint i=0; i < didState.externalControllers.length; i++) {
if(_stringCompare(didState.externalControllers[i], controller)) {
// Remove native controller from array (not built into solidity so manipulating array to remove)
didState.externalControllers[i] = didState.externalControllers[didState.externalControllers.length - 1];
didState.externalControllers.pop();

emit ControllerRemoved(didIdentifier, abi.encodePacked(controller), false);
return true;
}
}
return false;
// Remove native controller from array (not built into solidity so manipulating array to remove)
didState.externalControllers[uint(index)] = didState.externalControllers[didState.externalControllers.length - 1];
didState.externalControllers.pop();

emit ControllerRemoved(didIdentifier, abi.encodePacked(controller), false);
}

function _isKeyAuthority(address didIdentifier, address authority) internal view returns(bool) {
Expand Down Expand Up @@ -309,24 +315,25 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg
return false;
}

function _doesNativeControllerExist(address didIdentifier, address controller) internal view returns(bool) {
function _doesNativeControllerExist(address didIdentifier, address controller) internal view returns(int index) {
DidState storage didState = didStates[didIdentifier];
for(uint i=0; i < didState.nativeControllers.length; i++) {
if(didState.nativeControllers[i] == controller) {
return true;
// Return index if controller found
return int(i);
}
}
return false;
return -1;
}

function _doesExternalControllerExist(address didIdentifier, string calldata controller) internal view returns(bool) {
function _doesExternalControllerExist(address didIdentifier, string calldata controller) internal view returns(int index) {
DidState storage didState = didStates[didIdentifier];
for(uint i=0; i < didState.externalControllers.length; i++) {
if(_stringCompare(didState.externalControllers[i], controller)) {
return true;
return int(i);
}
}
return false;
return -1;
}

function _hasAuthorityVerificationMethod(address didIdentifier) internal view returns(bool) {
Expand Down Expand Up @@ -361,4 +368,9 @@ contract DIDRegistry is IDidRegistry, Initializable, UUPSUpgradeable, OwnableUpg
function _hasFlag(uint16 flags, VerificationMethodFlagBitMask flag) internal pure returns(bool) {
return flags & uint16(uint16(1) << uint16(flag)) != 0;
}

function _isValidFlag(uint16 flags) internal pure returns(bool) {
// Shifts the input flag by the amount of flags avalible.
return uint16(flags) >> (uint16(type(VerificationMethodFlagBitMask).max) + 1) == 0;
}
}
20 changes: 19 additions & 1 deletion test/DidRegistryVerificationMethodTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,27 @@ contract DidRegistryVerificationMethodTest is DidRegistryTest {
didRegistry.initializeDidState(user);

vm.expectRevert("Cannot remove last authority verification method");
didRegistry.updateVerificationMethodFlags(user, 'default', uint16(0));
didRegistry.updateVerificationMethodFlags(user, 'default', uint16(uint16(1) << uint16(DIDRegistry.VerificationMethodFlagBitMask.DID_DOC_HIDDEN)));
}

function test_revert_should_not_allow_adding_unknown_flag_to_verification_method() public {
address user = vm.addr(1);

vm.startPrank(user); // Send transaction as the user

didRegistry.initializeDidState(user);
DIDRegistry.DidState memory didState = didRegistry.resolveDidState(user);

DIDRegistry.VerificationMethod memory defaultVerificationMethod = didState.verificationMethods[0];

// Add none supported flag
uint16 newFlags = defaultVerificationMethod.flags | uint16(uint16(1) << uint16(type(DIDRegistry.VerificationMethodFlagBitMask).max) + 2);

vm.expectRevert("Attempted to add unsupported flag");
didRegistry.updateVerificationMethodFlags(user, defaultVerificationMethod.fragment, newFlags);
}


function test_revert_only_authorized_key_can_remove_ownership_proof_flag_on_verification_method() public {
address userOne = vm.addr(1);
address userTwo = vm.addr(2);
Expand Down
6 changes: 1 addition & 5 deletions ts-client/src/service/DidRegistryInternal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { Overrides } from 'ethers';

import {
MappedWriteOperation,
Options,
ReadOnlyOperation,
} from '../utils';
import { MappedWriteOperation, Options, ReadOnlyOperation } from '../utils';
import { omit } from 'ramda';
import { DIDRegistry } from '../contracts/typechain-types';
import { DidIdentifier } from './DidIdentifier';
Expand Down
10 changes: 5 additions & 5 deletions ts-client/src/utils/doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum BitwiseVerificationMethodFlag {
CapabilityDelegation = 1 << 4,
OwnershipProof = 1 << 5,
Protected = 1 << 6,
DidDocHidden = 1 << 7,
}

export enum VerificationMethodType {
Expand Down Expand Up @@ -56,7 +57,7 @@ export type RawVerificationMethod = {
keyData: string;
};

export type ChainEnviroment = 'mainnet' | 'testnet' | 'localnet'
export type ChainEnviroment = 'mainnet' | 'testnet' | 'localnet';

export type RawService = {
fragment: string;
Expand All @@ -78,10 +79,9 @@ export const mapVerificationMethodsToDidComponents = (
};

for (const method of methods) {
// skip hidden methods (TODO: do we want to suppor this again?)
// if (method.flags.has(BitwiseVerificationMethodFlag.DidDocHidden)) {
// continue;
// }
if (method.flags.has(BitwiseVerificationMethodFlag.DidDocHidden)) {
continue;
}
if (method.flags.has(BitwiseVerificationMethodFlag.Authentication)) {
didComponents.authentication.push(
`${identifier.toString()}#${method.fragment}`
Expand Down
39 changes: 35 additions & 4 deletions ts-client/test/integration/DidRegistry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,23 @@ describe('Native TS Client Integration Test', () => {
const { address } = await deployDidRegistryContractInstance(deployerWallet);

// Connect didWallet and otherDidWallet to didRegistryContract
didRegistry = new DidRegistry(didWallet, address, {chainEnvironment: 'localnet'});
otherDidRegistry = new DidRegistry(otherDidWallet, address, {chainEnvironment: 'localnet'});
didRegistry = new DidRegistry(didWallet, address, {
chainEnvironment: 'localnet',
});
otherDidRegistry = new DidRegistry(otherDidWallet, address, {
chainEnvironment: 'localnet',
});
otherDidRegistry.setDidIdentifier(didWallet.address); // otherDidWallet is message sender, but DID is still from didWallet
unauthorizedDidRegistry = new DidRegistry(unauthorizedWallet, address, {chainEnvironment: 'localnet'});
unauthorizedDidRegistry = new DidRegistry(unauthorizedWallet, address, {
chainEnvironment: 'localnet',
});
unauthorizedDidRegistry.setDidIdentifier(didWallet.address); // unauthorizedDidWallet is message sender, but DID is still from didWallet
});

it('should generate DIDs with the correct prefix', () => {
expect(didRegistry.getDid().toString()).to.be.a('string').and.satisfy((did: string) => did.startsWith('did:bnb:localnet:'));
expect(didRegistry.getDid().toString())
.to.be.a('string')
.and.satisfy((did: string) => did.startsWith('did:bnb:localnet:'));
});

it('should resolve an initial DID State of the didWallet', async () => {
Expand Down Expand Up @@ -233,6 +241,29 @@ describe('Native TS Client Integration Test', () => {
);
});

it('should hide verification methods in the did document if the related flag is set', async () => {
const currentVerificationMethods = await otherDidRegistry
.resolve()
.then((doc) => doc.verificationMethod);

const tx = await otherDidRegistry
.setVerificationMethodFlags(SECONDARY_KEY_ID, [
BitwiseVerificationMethodFlag.DidDocHidden,
BitwiseVerificationMethodFlag.CapabilityInvocation,
])
.then((tx) => tx.wait());
expect(tx.status).to.equal(1);

const doc = await otherDidRegistry.resolve();
expect(doc.verificationMethod).to.have.lengthOf(
(currentVerificationMethods?.length || Number.NaN) - 1
);
// make sure the hidden verification method is not in the did document
expect(
doc.verificationMethod?.find((vm) => vm.id.endsWith(SECONDARY_KEY_ID))
).to.be.undefined;
});

it('should not able set Ownership proof for a different key', async () => {
return expect(
didRegistry.setVerificationMethodFlags(SECONDARY_KEY_ID, [
Expand Down

0 comments on commit d3be0ea

Please sign in to comment.