diff --git a/packages/did-eth-registry/.devcontainer/devcontainer.json b/packages/did-eth-registry/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8d96444 --- /dev/null +++ b/packages/did-eth-registry/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile +{ + "name": "Existing Dockerfile", + "build": { + // Sets the run context to one level up instead of the .devcontainer folder. + "context": "..", + // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. + "dockerfile": "../Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} diff --git a/packages/did-eth-registry/.dockerignore b/packages/did-eth-registry/.dockerignore new file mode 100644 index 0000000..fce62a7 --- /dev/null +++ b/packages/did-eth-registry/.dockerignore @@ -0,0 +1,8 @@ +.git/ +cache/ +artifacts/ +lib/ +build/ +typechain/ +typechain-types/ +dist/ diff --git a/packages/did-eth-registry/.prettierrc b/packages/did-eth-registry/.prettierrc index 0c93995..8caa1aa 100644 --- a/packages/did-eth-registry/.prettierrc +++ b/packages/did-eth-registry/.prettierrc @@ -3,5 +3,20 @@ "printWidth": 120, "singleQuote": true, "trailingComma": "es5", - "semi": false -} + "semi": false, + "plugins": [ + "prettier-plugin-solidity" + ], + "overrides": [ + { + "files": "*.sol", + "options": { + "tabWidth": 4, + "semi": true, + "singleQuote": false, + "useTabs": false, + "bracketSpacing": true + } + } + ] +} \ No newline at end of file diff --git a/packages/did-eth-registry/Dockerfile b/packages/did-eth-registry/Dockerfile new file mode 100644 index 0000000..50609b9 --- /dev/null +++ b/packages/did-eth-registry/Dockerfile @@ -0,0 +1,67 @@ +FROM debian:stable-slim as python-builder + +# python3.10 is required for node-gyp + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt update && \ + apt install -y -q --no-install-recommends \ + npm build-essential curl \ + ca-certificates apt-transport-https \ + libncursesw5-dev libssl-dev \ + libsqlite3-dev tk-dev libgdbm-dev \ + libc6-dev libbz2-dev libffi-dev zlib1g-dev \ + python3-pip python3-dev && \ + apt clean && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /build +ADD https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz Python-3.10.13.tgz +RUN tar -xvf Python-3.10.13.tgz +WORKDIR /build/Python-3.10.13 +RUN ./configure --enable-optimizations +RUN make -j4 + +FROM ghcr.io/xmtp/foundry:latest + +RUN useradd --create-home -s /bin/bash did +RUN usermod -a -G sudo did +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# Python 3.10 + +COPY --from=python-builder /build /build +WORKDIR /build/Python-3.10.13 +RUN make install +RUN which python3 && python3 --version +RUN rm -rf /build + +RUN mkdir -p /usr/local/nvm +ENV NVM_DIR=/usr/local/nvm + +ENV NODE_VERSION=v14.21.3 + +ADD https://raw.githubusercontent.com/creationix/nvm/master/install.sh /usr/local/etc/nvm/install.sh +RUN bash /usr/local/etc/nvm/install.sh && \ + bash -c ". $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm alias default $NODE_VERSION && nvm use default" + +ENV NVM_NODE_PATH ${NVM_DIR}/versions/node/${NODE_VERSION} +ENV NODE_PATH ${NVM_NODE_PATH}/lib/node_modules +ENV PATH ${NVM_NODE_PATH}/bin:$PATH + +RUN npm install npm@7.24.2 -g +RUN npm install yarn -g + +ARG PROJECT=did-eth +WORKDIR /workspaces/${PROJECT} + +RUN chown -R did:did /workspaces +COPY --chown=did:did . . + +# build and test +RUN yarn install --frozen-lockfile +RUN yarn prettier:check +RUN yarn lint +RUN yarn build +RUN yarn test + +USER did diff --git a/packages/did-eth-registry/contracts/EthereumDIDRegistry.sol b/packages/did-eth-registry/contracts/EthereumDIDRegistry.sol index 4921506..aa86ea1 100644 --- a/packages/did-eth-registry/contracts/EthereumDIDRegistry.sol +++ b/packages/did-eth-registry/contracts/EthereumDIDRegistry.sol @@ -3,130 +3,270 @@ pragma solidity ^0.8.6; contract EthereumDIDRegistry { + mapping(address => address) public owners; + mapping(address => mapping(bytes32 => mapping(address => uint256))) public delegates; + mapping(address => uint256) public changed; + mapping(address => uint256) public nonce; - mapping(address => address) public owners; - mapping(address => mapping(bytes32 => mapping(address => uint))) public delegates; - mapping(address => uint) public changed; - mapping(address => uint) public nonce; - - modifier onlyOwner(address identity, address actor) { - require (actor == identityOwner(identity), "bad_actor"); - _; - } - - event DIDOwnerChanged( - address indexed identity, - address owner, - uint previousChange - ); - - event DIDDelegateChanged( - address indexed identity, - bytes32 delegateType, - address delegate, - uint validTo, - uint previousChange - ); - - event DIDAttributeChanged( - address indexed identity, - bytes32 name, - bytes value, - uint validTo, - uint previousChange - ); - - function identityOwner(address identity) public view returns(address) { - address owner = owners[identity]; - if (owner != address(0x00)) { - return owner; - } - return identity; - } - - function checkSignature(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 hash) internal returns(address) { - address signer = ecrecover(hash, sigV, sigR, sigS); - require(signer == identityOwner(identity), "bad_signature"); - nonce[signer]++; - return signer; - } - - function validDelegate(address identity, bytes32 delegateType, address delegate) public view returns(bool) { - uint validity = delegates[identity][keccak256(abi.encode(delegateType))][delegate]; - return (validity > block.timestamp); - } - - function changeOwner(address identity, address actor, address newOwner) internal onlyOwner(identity, actor) { - owners[identity] = newOwner; - emit DIDOwnerChanged(identity, newOwner, changed[identity]); - changed[identity] = block.number; - } - - function changeOwner(address identity, address newOwner) public { - changeOwner(identity, msg.sender, newOwner); - } - - function changeOwnerSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, address newOwner) public { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, nonce[identityOwner(identity)], identity, "changeOwner", newOwner)); - changeOwner(identity, checkSignature(identity, sigV, sigR, sigS, hash), newOwner); - } - - function addDelegate(address identity, address actor, bytes32 delegateType, address delegate, uint validity) internal onlyOwner(identity, actor) { - delegates[identity][keccak256(abi.encode(delegateType))][delegate] = block.timestamp + validity; - emit DIDDelegateChanged(identity, delegateType, delegate, block.timestamp + validity, changed[identity]); - changed[identity] = block.number; - } - - function addDelegate(address identity, bytes32 delegateType, address delegate, uint validity) public { - addDelegate(identity, msg.sender, delegateType, delegate, validity); - } - - function addDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate, uint validity) public { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, nonce[identityOwner(identity)], identity, "addDelegate", delegateType, delegate, validity)); - addDelegate(identity, checkSignature(identity, sigV, sigR, sigS, hash), delegateType, delegate, validity); - } - - function revokeDelegate(address identity, address actor, bytes32 delegateType, address delegate) internal onlyOwner(identity, actor) { - delegates[identity][keccak256(abi.encode(delegateType))][delegate] = block.timestamp; - emit DIDDelegateChanged(identity, delegateType, delegate, block.timestamp, changed[identity]); - changed[identity] = block.number; - } - - function revokeDelegate(address identity, bytes32 delegateType, address delegate) public { - revokeDelegate(identity, msg.sender, delegateType, delegate); - } - - function revokeDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate) public { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, nonce[identityOwner(identity)], identity, "revokeDelegate", delegateType, delegate)); - revokeDelegate(identity, checkSignature(identity, sigV, sigR, sigS, hash), delegateType, delegate); - } - - function setAttribute(address identity, address actor, bytes32 name, bytes memory value, uint validity ) internal onlyOwner(identity, actor) { - emit DIDAttributeChanged(identity, name, value, block.timestamp + validity, changed[identity]); - changed[identity] = block.number; - } - - function setAttribute(address identity, bytes32 name, bytes memory value, uint validity) public { - setAttribute(identity, msg.sender, name, value, validity); - } - - function setAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes memory value, uint validity) public { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, nonce[identityOwner(identity)], identity, "setAttribute", name, value, validity)); - setAttribute(identity, checkSignature(identity, sigV, sigR, sigS, hash), name, value, validity); - } - - function revokeAttribute(address identity, address actor, bytes32 name, bytes memory value ) internal onlyOwner(identity, actor) { - emit DIDAttributeChanged(identity, name, value, 0, changed[identity]); - changed[identity] = block.number; - } - - function revokeAttribute(address identity, bytes32 name, bytes memory value) public { - revokeAttribute(identity, msg.sender, name, value); - } - - function revokeAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes memory value) public { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0x19), bytes1(0), this, nonce[identityOwner(identity)], identity, "revokeAttribute", name, value)); - revokeAttribute(identity, checkSignature(identity, sigV, sigR, sigS, hash), name, value); - } + modifier onlyOwner(address identity, address actor) { + require(actor == identityOwner(identity), "bad_actor"); + _; + } + event DIDOwnerChanged(address indexed identity, address owner, uint256 previousChange); + + event DIDDelegateChanged( + address indexed identity, + bytes32 delegateType, + address delegate, + uint256 validTo, + uint256 previousChange + ); + + event DIDAttributeChanged( + address indexed identity, + bytes32 name, + bytes value, + uint256 validTo, + uint256 previousChange + ); + + function identityOwner(address identity) public view returns (address) { + address owner = owners[identity]; + if (owner != address(0x00)) { + return owner; + } + return identity; + } + + function checkSignature( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + bytes32 hash + ) internal returns (address) { + address signer = ecrecover(hash, sigV, sigR, sigS); + require(signer == identityOwner(identity), "bad_signature"); + nonce[signer]++; + return signer; + } + + function validDelegate( + address identity, + bytes32 delegateType, + address delegate + ) public view returns (bool) { + uint256 validity = delegates[identity][keccak256(abi.encode(delegateType))][delegate]; + return (validity > block.timestamp); + } + + function changeOwner( + address identity, + address actor, + address newOwner + ) internal onlyOwner(identity, actor) { + owners[identity] = newOwner; + emit DIDOwnerChanged(identity, newOwner, changed[identity]); + changed[identity] = block.number; + } + + function changeOwner(address identity, address newOwner) public { + changeOwner(identity, msg.sender, newOwner); + } + + function changeOwnerSigned( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + address newOwner + ) public { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0x19), + bytes1(0), + this, + nonce[identityOwner(identity)], + identity, + "changeOwner", + newOwner + ) + ); + changeOwner(identity, checkSignature(identity, sigV, sigR, sigS, hash), newOwner); + } + + function addDelegate( + address identity, + address actor, + bytes32 delegateType, + address delegate, + uint256 validity + ) internal onlyOwner(identity, actor) { + delegates[identity][keccak256(abi.encode(delegateType))][delegate] = block.timestamp + validity; + emit DIDDelegateChanged(identity, delegateType, delegate, block.timestamp + validity, changed[identity]); + changed[identity] = block.number; + } + + function addDelegate( + address identity, + bytes32 delegateType, + address delegate, + uint256 validity + ) public { + addDelegate(identity, msg.sender, delegateType, delegate, validity); + } + + function addDelegateSigned( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + bytes32 delegateType, + address delegate, + uint256 validity + ) public { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0x19), + bytes1(0), + this, + nonce[identityOwner(identity)], + identity, + "addDelegate", + delegateType, + delegate, + validity + ) + ); + addDelegate(identity, checkSignature(identity, sigV, sigR, sigS, hash), delegateType, delegate, validity); + } + + function revokeDelegate( + address identity, + address actor, + bytes32 delegateType, + address delegate + ) internal onlyOwner(identity, actor) { + delegates[identity][keccak256(abi.encode(delegateType))][delegate] = block.timestamp; + emit DIDDelegateChanged(identity, delegateType, delegate, block.timestamp, changed[identity]); + changed[identity] = block.number; + } + + function revokeDelegate( + address identity, + bytes32 delegateType, + address delegate + ) public { + revokeDelegate(identity, msg.sender, delegateType, delegate); + } + + function revokeDelegateSigned( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + bytes32 delegateType, + address delegate + ) public { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0x19), + bytes1(0), + this, + nonce[identityOwner(identity)], + identity, + "revokeDelegate", + delegateType, + delegate + ) + ); + revokeDelegate(identity, checkSignature(identity, sigV, sigR, sigS, hash), delegateType, delegate); + } + + function setAttribute( + address identity, + address actor, + bytes32 name, + bytes memory value, + uint256 validity + ) internal onlyOwner(identity, actor) { + emit DIDAttributeChanged(identity, name, value, block.timestamp + validity, changed[identity]); + changed[identity] = block.number; + } + + function setAttribute( + address identity, + bytes32 name, + bytes memory value, + uint256 validity + ) public { + setAttribute(identity, msg.sender, name, value, validity); + } + + function setAttributeSigned( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + bytes32 name, + bytes memory value, + uint256 validity + ) public { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0x19), + bytes1(0), + this, + nonce[identityOwner(identity)], + identity, + "setAttribute", + name, + value, + validity + ) + ); + setAttribute(identity, checkSignature(identity, sigV, sigR, sigS, hash), name, value, validity); + } + + function revokeAttribute( + address identity, + address actor, + bytes32 name, + bytes memory value + ) internal onlyOwner(identity, actor) { + emit DIDAttributeChanged(identity, name, value, 0, changed[identity]); + changed[identity] = block.number; + } + + function revokeAttribute( + address identity, + bytes32 name, + bytes memory value + ) public { + revokeAttribute(identity, msg.sender, name, value); + } + + function revokeAttributeSigned( + address identity, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + bytes32 name, + bytes memory value + ) public { + bytes32 hash = keccak256( + abi.encodePacked( + bytes1(0x19), + bytes1(0), + this, + nonce[identityOwner(identity)], + identity, + "revokeAttribute", + name, + value + ) + ); + revokeAttribute(identity, checkSignature(identity, sigV, sigR, sigS, hash), name, value); + } } diff --git a/packages/did-eth-registry/foundry.toml b/packages/did-eth-registry/foundry.toml new file mode 100644 index 0000000..556ed04 --- /dev/null +++ b/packages/did-eth-registry/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "contracts" +out = "out" +libs = ["node_modules"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/packages/did-eth-registry/package.json b/packages/did-eth-registry/package.json index 05d60ea..a01f222 100644 --- a/packages/did-eth-registry/package.json +++ b/packages/did-eth-registry/package.json @@ -66,6 +66,8 @@ "build:sol": "hardhat compile", "build": "yarn build:sol && yarn build:js", "test": "hardhat test", + "prettier:check": "prettier --check '(contracts|test|script)/**.sol'", + "prettier:fix": "prettier --write '(contracts|test|script)/**.sol'", "lint:js": "npx eslint '**/*.{js,ts}' --fix", "lint:sol": "npx solhint 'contracts/**/*.sol' --fix", "lint": "yarn lint:sol && yarn lint:js", @@ -89,4 +91,4 @@ "dist", "typechain-types" ] -} +} \ No newline at end of file