diff --git a/projects/xid/.gitignore b/projects/xid/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/projects/xid/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/projects/xid/README.md b/projects/xid/README.md new file mode 100644 index 0000000..ec6810a --- /dev/null +++ b/projects/xid/README.md @@ -0,0 +1,3 @@ +# xID + +This project aims to provide a cross chain identity solution using attestations across multiple ecosystems. \ No newline at end of file diff --git a/projects/xid/chains/evm/.gitignore b/projects/xid/chains/evm/.gitignore new file mode 100644 index 0000000..44ec934 --- /dev/null +++ b/projects/xid/chains/evm/.gitignore @@ -0,0 +1,3 @@ +out/ +cache/ +lib/* diff --git a/projects/xid/chains/evm/foundry.toml b/projects/xid/chains/evm/foundry.toml new file mode 100644 index 0000000..e6810b2 --- /dev/null +++ b/projects/xid/chains/evm/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/projects/xid/chains/evm/src/ENS.sol b/projects/xid/chains/evm/src/ENS.sol new file mode 100644 index 0000000..52b444c --- /dev/null +++ b/projects/xid/chains/evm/src/ENS.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +interface ENS { + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + // Logged when an operator is added or removed. + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual; + function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual; + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32); + function setResolver(bytes32 node, address resolver) external virtual; + function setOwner(bytes32 node, address owner) external virtual; + function setTTL(bytes32 node, uint64 ttl) external virtual; + function setApprovalForAll(address operator, bool approved) external virtual; + function owner(bytes32 node) external virtual view returns (address); + function resolver(bytes32 node) external virtual view returns (address); + function ttl(bytes32 node) external virtual view returns (uint64); + function recordExists(bytes32 node) external virtual view returns (bool); + function isApprovedForAll(address owner, address operator) external virtual view returns (bool); +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/ForeignRegistrar.sol b/projects/xid/chains/evm/src/ForeignRegistrar.sol new file mode 100644 index 0000000..6afffb1 --- /dev/null +++ b/projects/xid/chains/evm/src/ForeignRegistrar.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +contract ForeignRegistrar { + +} diff --git a/projects/xid/chains/evm/src/HomeRegistrar.sol b/projects/xid/chains/evm/src/HomeRegistrar.sol new file mode 100644 index 0000000..c0bb3db --- /dev/null +++ b/projects/xid/chains/evm/src/HomeRegistrar.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "./XID.sol"; + +contract HomeRegistrar { + XID xid; + bytes32 rootNode; + + modifier only_owner(bytes32 label) { + address currentOwner = xid.owner(keccak256(abi.encodePacked(rootNode, label))); + require(currentOwner == address(0x0) || currentOwner == msg.sender); + _; + } + + constructor(XID xidAddr, bytes32 node){ + xid = xidAddr; + rootNode = node; + } + + /** + Registers label to the first account that claims it. + */ + function register(bytes32 label, address owner) public only_owner(label) { + xid.setSubnodeOwner(rootNode, label, owner); + } + +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/Registry.sol b/projects/xid/chains/evm/src/Registry.sol new file mode 100644 index 0000000..1cf5339 --- /dev/null +++ b/projects/xid/chains/evm/src/Registry.sol @@ -0,0 +1,176 @@ +pragma solidity ^0.8.0; + +/** + * The ENS registry contract. + */ +contract ENSRegistry { + + struct Record { + address owner; + address resolver; + uint64 ttl; + } + + mapping (bytes32 => Record) records; + mapping (address => mapping(address => bool)) operators; + + // Permits modifications only by the owner of the specified node. + modifier authorised(bytes32 node) { + address owner = records[node].owner; + require(owner == msg.sender || operators[owner][msg.sender]); + _; + } + + /** + * @dev Constructs a new ENS registrar. + */ + constructor() public { + records[0x0].owner = msg.sender; + } + + /** + * @dev Sets the record for a node. + * @param node The node to update. + * @param owner The address of the new owner. + * @param resolver The address of the resolver. + * @param ttl The TTL in seconds. + */ + function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual override { + setOwner(node, owner); + _setResolverAndTTL(node, resolver, ttl); + } + + /** + * @dev Sets the record for a subnode. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + * @param resolver The address of the resolver. + * @param ttl The TTL in seconds. + */ + function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual override { + bytes32 subnode = setSubnodeOwner(node, label, owner); + _setResolverAndTTL(subnode, resolver, ttl); + } + + /** + * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) public virtual override authorised(node) { + _setOwner(node, owner); + emit Transfer(node, owner); + } + + /** + * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) { + bytes32 subnode = keccak256(abi.encodePacked(node, label)); + _setOwner(subnode, owner); + emit NewOwner(node, label, owner); + return subnode; + } + + /** + * @dev Sets the resolver address for the specified node. + * @param node The node to update. + * @param resolver The address of the resolver. + */ + function setResolver(bytes32 node, address resolver) public virtual override authorised(node) { + emit NewResolver(node, resolver); + records[node].resolver = resolver; + } + + /** + * @dev Sets the TTL for the specified node. + * @param node The node to update. + * @param ttl The TTL in seconds. + */ + function setTTL(bytes32 node, uint64 ttl) public virtual override authorised(node) { + emit NewTTL(node, ttl); + records[node].ttl = ttl; + } + + /** + * @dev Enable or disable approval for a third party ("operator") to manage + * all of `msg.sender`'s ENS records. Emits the ApprovalForAll event. + * @param operator Address to add to the set of authorized operators. + * @param approved True if the operator is approved, false to revoke approval. + */ + function setApprovalForAll(address operator, bool approved) external virtual override { + operators[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + /** + * @dev Returns the address that owns the specified node. + * @param node The specified node. + * @return address of the owner. + */ + function owner(bytes32 node) public virtual override view returns (address) { + address addr = records[node].owner; + if (addr == address(this)) { + return address(0x0); + } + + return addr; + } + + /** + * @dev Returns the address of the resolver for the specified node. + * @param node The specified node. + * @return address of the resolver. + */ + function resolver(bytes32 node) public virtual override view returns (address) { + return records[node].resolver; + } + + /** + * @dev Returns the TTL of a node, and any records associated with it. + * @param node The specified node. + * @return ttl of the node. + */ + function ttl(bytes32 node) public virtual override view returns (uint64) { + return records[node].ttl; + } + + /** + * @dev Returns whether a record has been imported to the registry. + * @param node The specified node. + * @return Bool if record exists + */ + function recordExists(bytes32 node) public virtual override view returns (bool) { + return records[node].owner != address(0x0); + } + + /** + * @dev Query if an address is an authorized operator for another address. + * @param owner The address that owns the records. + * @param operator The address that acts on behalf of the owner. + * @return True if `operator` is an approved operator for `owner`, false otherwise. + */ + function isApprovedForAll(address owner, address operator) external virtual override view returns (bool) { + return operators[owner][operator]; + } + + function _setOwner(bytes32 node, address owner) internal virtual { + records[node].owner = owner; + } + + function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal { + if(resolver != records[node].resolver) { + records[node].resolver = resolver; + emit NewResolver(node, resolver); + } + + if(ttl != records[node].ttl) { + records[node].ttl = ttl; + emit NewTTL(node, ttl); + } + } +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/Wormhole/IWormhole.sol b/projects/xid/chains/evm/src/Wormhole/IWormhole.sol new file mode 100644 index 0000000..0c437eb --- /dev/null +++ b/projects/xid/chains/evm/src/Wormhole/IWormhole.sol @@ -0,0 +1,42 @@ +// contracts/Messages.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "./Structs.sol"; + +interface IWormhole is Structs { + event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel); + + function publishMessage( + uint32 nonce, + bytes memory payload, + uint8 consistencyLevel + ) external payable returns (uint64 sequence); + + function parseAndVerifyVM(bytes calldata encodedVM) external view returns (Structs.VM memory vm, bool valid, string memory reason); + + function verifyVM(Structs.VM memory vm) external view returns (bool valid, string memory reason); + + function verifySignatures(bytes32 hash, Structs.Signature[] memory signatures, Structs.GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason) ; + + function parseVM(bytes memory encodedVM) external pure returns (Structs.VM memory vm); + + function getGuardianSet(uint32 index) external view returns (Structs.GuardianSet memory) ; + + function getCurrentGuardianSetIndex() external view returns (uint32) ; + + function getGuardianSetExpiry() external view returns (uint32) ; + + function governanceActionIsConsumed(bytes32 hash) external view returns (bool) ; + + function isInitialized(address impl) external view returns (bool) ; + + function chainId() external view returns (uint16) ; + + function governanceChainId() external view returns (uint16); + + function governanceContract() external view returns (bytes32); + + function messageFee() external view returns (uint256) ; +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/Wormhole/Structs.sol b/projects/xid/chains/evm/src/Wormhole/Structs.sol new file mode 100644 index 0000000..b4d3329 --- /dev/null +++ b/projects/xid/chains/evm/src/Wormhole/Structs.sol @@ -0,0 +1,40 @@ +// contracts/Structs.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +interface Structs { + struct Provider { + uint16 chainId; + uint16 governanceChainId; + bytes32 governanceContract; + } + + struct GuardianSet { + address[] keys; + uint32 expirationTime; + } + + struct Signature { + bytes32 r; + bytes32 s; + uint8 v; + uint8 guardianIndex; + } + + struct VM { + uint8 version; + uint32 timestamp; + uint32 nonce; + uint16 emitterChainId; + bytes32 emitterAddress; + uint64 sequence; + uint8 consistencyLevel; + bytes payload; + + uint32 guardianSetIndex; + Signature[] signatures; + + bytes32 hash; + } +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/XID.sol b/projects/xid/chains/evm/src/XID.sol new file mode 100644 index 0000000..b785f8d --- /dev/null +++ b/projects/xid/chains/evm/src/XID.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +interface XID { + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + function owner(bytes32 node) external view returns (address); + function setOwner(bytes32 node, address owner) external; + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32); +} \ No newline at end of file diff --git a/projects/xid/chains/evm/src/XIDRegistry.sol b/projects/xid/chains/evm/src/XIDRegistry.sol new file mode 100644 index 0000000..8e6e449 --- /dev/null +++ b/projects/xid/chains/evm/src/XIDRegistry.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "./XID.sol"; +contract XIDRegistry is XID { + + struct Record { + address owner; + // WormholeChainId => Address Attestation + mapping(uint16 => bytes32[]) wallets; + // Web2 Attestations + mapping(string => Link) links; + uint64 ttl; + } + + struct Link { + string service; + string proof_link; + } + + mapping(bytes32 => Record) records; + mapping (address => mapping(address => bool)) operators; + + // Permits modifications only by the owner of the specified node. + modifier authorised(bytes32 node) { + address owner = records[node].owner; + require(owner == msg.sender || operators[owner][msg.sender]); + _; + } + + /** + * @dev Constructs a new XID Registry. + */ + constructor() public { + records[0x0].owner = msg.sender; + } + + /** + * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) public virtual override authorised(node) { + _setOwner(node, owner); + emit Transfer(node, owner); + } + + function _setOwner(bytes32 node, address owner) internal virtual { + records[node].owner = owner; + } + + /** + * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) { + bytes32 subnode = keccak256(abi.encodePacked(node, label)); + _setOwner(subnode, owner); + emit NewOwner(node, label, owner); + return subnode; + } +} \ No newline at end of file diff --git a/projects/xid/orchestrator.ts b/projects/xid/orchestrator.ts new file mode 100644 index 0000000..e69de29 diff --git a/projects/xid/package.json b/projects/xid/package.json new file mode 100644 index 0000000..8d27444 --- /dev/null +++ b/projects/xid/package.json @@ -0,0 +1,11 @@ +{ + "name": "xid", + "version": "1.0.0", + "description": "A cross chain identity program", + "main": "orchestrator.ts", + "author": "spacemandev", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.0" + } +} diff --git a/projects/xid/xdapp.config.json b/projects/xid/xdapp.config.json new file mode 100644 index 0000000..3b91d45 --- /dev/null +++ b/projects/xid/xdapp.config.json @@ -0,0 +1,18 @@ +{ + "networks": { + "eth": { + "type": "evm", + "wormholeChainId": 2, + "rpc": "http://localhost:8545", + "bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" + }, + "solana": { + "type": "solana", + "wormholeChainId": 1, + "bridgeAddress": "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o" + } + }, + "wormhole": { + "restAddress": "http://localhost:7071" + } +} \ No newline at end of file diff --git a/projects/xid/yarn.lock b/projects/xid/yarn.lock new file mode 100644 index 0000000..986646e --- /dev/null +++ b/projects/xid/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^18.11.0": + version "18.11.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.0.tgz#f38c7139247a1d619f6cc6f27b072606af7c289d" + integrity sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==