From e7cf047584aca294b4cbd8959e83ed91443f1298 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Sun, 27 Sep 2020 13:25:29 -0400 Subject: [PATCH] Automatically merged updates to draft EIP(s) 2535 (#3004) Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-2535.md | 150 +++++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 58 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index f8722e189513d1..43bb0f05bbce6c 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -35,19 +35,20 @@ Facets are separate, independent contracts that can share internal functions, li A diamond stores within it a mapping of function selector to facet address, for example `selectorToFacet`. -When an external function is called on a diamond its fallback function is executed. The fallback function finds in the `selectorToFacet` mapping which facet has the function that has been called and then executes that function from the facet using `delegatecall`. +When an external function is called on a diamond its fallback function is executed. The fallback function finds in the `selectorToFacet` mapping which facet has the function that has been called and then executes that function from the facet using `delegatecall`. A diamond's fallback function and `delegatecall` enable a diamond to execute a facet's external function as its own external function. The `msg.sender` and `msg.value` values do not change and only the diamond's contract storage is read and written to. Here is a simple example of a diamond's fallback function: + ```Solidity // Find facet for function that is called and execute the // function if a facet is found and return any value. -fallback() external payable { - address facet = selectorTofacet[msg.sig]; +fallback() external payable { + address facet = selectorTofacet[msg.sig]; require(facet != address(0)); // Execute external function from facet using delegatecall and return any value. - assembly { + assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) @@ -55,7 +56,7 @@ fallback() external payable { case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } -} +} ``` A diamond can use a `diamondCut` function to add/replace/remove any number of functions from any number of facets in a single transaction. `diamondCut` updates the mapping of function selector to facet address. Other such functions can be used. @@ -86,7 +87,7 @@ Here is a simple example that shows diamond storage and its use in a facet: // A contract that implements diamond storage. // This contract is inherited and used by facets. contract DiamondStorageContract { - + // This struct contains state variables we care about. struct DiamondStorage { address owner; @@ -95,10 +96,10 @@ contract DiamondStorageContract { // Returns the struct from a specified position in contract storage // ds is short for DiamondStorage - function diamondStorage() internal pure returns(DiamondStorage storage ds) { - // Specifies a random position from a hash of a string + function diamondStorage() internal pure returns(DiamondStorage storage ds) { + // Specifies a random position from a hash of a string bytes32 storagePosition = keccak256("diamond.storage") - // Set the position of our struct in contract storage + // Set the position of our struct in contract storage assembly {ds.slot := storagePosition} } } @@ -111,7 +112,7 @@ contract FacetA is DiamondStorageContract { require(ds.owner == msg.sender, "Must be owner."); ds.dataA = _dataA } - + function getDataA() external view returns (bytes32) { return diamondStorage().dataA } @@ -140,11 +141,11 @@ A deployed facet can be used by any number of diamonds. Different combinations of facets can be used with different diamonds. -It is possible to create and deploy a set of facets that are reused by different diamonds over time. +It is possible to create and deploy a set of facets that are reused by different diamonds over time. The ability to use the same deployed facets for many diamonds reduces deployment costs. -A limitation is that two external functions with the same function signature can't be added to a diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. +A limitation is that two external functions with the same function signature can't be added to a diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. ## Diagrams @@ -161,11 +162,12 @@ The diagram below shows facets with their own data and data shared between them. Notice that all data is stored in the diamond. But different facets have different access to data. In this diagram -* Only FacetA can access DataA -* Only FacetB can access DataB -* Only the diamond's own code can access DataD. -* FacetA and FacetB share access to DataAB. -* The diamond's own code, FacetA and FacetB share access to DataABD. + +- Only FacetA can access DataA +- Only FacetB can access DataB +- Only the diamond's own code can access DataD. +- FacetA and FacetB share access to DataAB. +- The diamond's own code, FacetA and FacetB share access to DataABD. @@ -175,10 +177,10 @@ A deployed facet can be used by any number of diamonds. The diagram below shows two diamonds using the same two facets. -* FacetA is used by Diamond1 -* FacetA is used by Diamond2 -* FacetB is used by Diamond1 -* FacetB is used by Diamond2 +- FacetA is used by Diamond1 +- FacetA is used by Diamond2 +- FacetB is used by Diamond1 +- FacetB is used by Diamond2 @@ -190,7 +192,7 @@ The diagram below shows two diamonds using the same two facets. 1. Fine-grained upgrades, so you can change just the parts of a diamond that need to be changed. 1. Have greater control over when and what functions exist. 1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. -1. An event that shows what functions are added, replaced and removed. +1. An event that shows what functions are added, replaced and removed. 1. The ability to show all changes made to a diamond. 1. Increase trust over time by showing all changes made to a diamond. 1. A way to look at a diamond to see its current facets and functions. @@ -201,8 +203,8 @@ The diagram below shows two diamonds using the same two facets. 1. Enables zero, partial or full diamond immutability as desired, and when desired. 1. The ability to develop and improve an application over time with an upgradeable diamond and then make it immutable and trustless if desired. 1. Develop incrementally and let your diamond grow with your application. -1. Upgrade diamonds to fix bugs, add functionality and implement new standards. -1. Organize your code with a diamond and facets. +1. Upgrade diamonds to fix bugs, add functionality and implement new standards. +1. Organize your code with a diamond and facets. 1. Diamonds can be large (have many functions) but still be modular because they are compartmented with facets. 1. Contract architectures that call multiple contracts in a single transaction can save gas by condensing those contracts into a single diamond and accessing state variables directly. 1. Save gas by creating external functions for specific use cases, such as bulk transfers. @@ -217,6 +219,7 @@ Diamond events can be filtered from the Ethereum blockchain to show all changes Existing and new programming libraries and software can be used to deploy, show, upgrade and use diamonds. ### Upgradeable Diamond vs. Centralized Private Database + Why have an upgradeable diamond instead of a centralized, private, mutable database? 1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. @@ -227,6 +230,7 @@ Why have an upgradeable diamond instead of a centralized, private, mutable datab 1. It is possible for an upgradeable diamond to become immutable and trustless. ### Different Kinds of Diamonds + Many designs of diamonds are possible. Here are a few kinds of diamonds and their uses. **Upgradeable Diamond** @@ -236,22 +240,24 @@ An upgradeable diamond has the `diamondCut` function and/or possibly other funct A finished diamond was an upgradeable diamond and had a number of upgrades. Then its `diamondCut` function and/or other upgrade functions were removed and upgrades are no longer possible. It is no longer possible to add/replace/remove functions. It has become an immutable diamond. **Single Cut Diamond** -A single cut diamond adds all functions to itself in its constructor function, but it does not add the `diamondCut` function or any other function that can add/replace/remove functions. This means that a single cut diamond is fully created in its constructor and once created can never be upgraded. It has the same immutability and trustless guarantees as a regular vanilla contract. Why would someone do this? There may be a number of reasons. The two use cases below are good reasons. +A single cut diamond adds all functions to itself in its constructor function, but it does not add the `diamondCut` function or any other function that can add/replace/remove functions. This means that a single cut diamond is fully created in its constructor and once created can never be upgraded. It has the same immutability and trustless guarantees as a regular vanilla contract. Why would someone do this? There may be a number of reasons. The two use cases below are good reasons. -1. Your contract hits the max contract size limit. Make it into a single cut diamond. You still break your big contract into smaller facets, modularizing your code. +1. Your contract hits the max contract size limit. Make it into a single cut diamond. You still break your big contract into smaller facets, modularizing your code. 2. You start with an upgradeable diamond in your development and testing and upgrade it to your heart's delight. Reap the advantages of easy upgrading and a stable address as you work out new features, bugs and kinks. Release the upgradeable diamond on a test network with your application for beta testing and upgrade it when needed. This is iterative development. When it is solid then deploy it as a single cut diamond on the main network. ## Specification -> **Note:** -The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract, not the contract where the functions are defined. + +> **Note:** +> The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract, not the contract where the functions are defined. > **Note:** This specification specifies what needs to be implemented for a contract to be a diamond. ### Terms + 1. A **diamond** is a contract that uses functions from its facets to execute function calls. A diamond can have one or more facets. 2. The word **facet** comes from the diamond industry. It is a side, or flat surface of a diamond. A diamond can have many facets. In this standard a facet is a contract with one or more functions that executes functionality of a diamond. 3. A **loupe** is a magnifying glass that is used to look at diamonds. In this standard a loupe is a facet that provides functions to look at a diamond and its facets. -3. An **immutable function** is a function that is defined directly in a diamond and so cannot be replaced or removed. Or it is a function that is defined in a facet that cannot be replaced or removed because all upgrade functions have been removed from a diamond. +4. An **immutable function** is a function that is defined directly in a diamond and so cannot be replaced or removed. Or it is a function that is defined in a facet that cannot be replaced or removed because all upgrade functions have been removed from a diamond. ### General Summary @@ -261,13 +267,13 @@ In the diamond industry diamonds are created and shaped by being cut, creating f #### The diamondCut Function -The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. It has been designed for maximum flexibility while using the least amount of gas. +The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. The standard `diamondCut` function below is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. Diamonds that might work with diamond specific tooling to add/replace/remove functions should implement the standard `diamondCut` function. The `diamondCut` function is optional. It does not have to be implemented in a diamond. For example an immutable diamond wouldn't have this function. -You can implement your own custom functions that add or replace or remove functions. You can also implement your own non-standard versions of `diamondCut` that have different parameters. +You can implement your own custom functions that add or replace or remove functions. You can also implement your own non-standard versions of `diamondCut` that have different parameters. If you want to create your own custom function(s) for adding/replacing/removing functions you might also want to implement the standard `diamondCut` function for interoperability with tools. @@ -276,10 +282,14 @@ If you want to create your own custom function(s) for adding/replacing/removing The `DiamondCut` event records all changes to a diamond. ### Diamond Interface + ```Solidity interface IDiamondCut { - struct Facet { + enum FacetCutAction {Add, Replace, Remove} + + struct FacetCut { address facetAddress; + FacetCutAction action; bytes4[] functionSelectors; } @@ -290,12 +300,12 @@ interface IDiamondCut { /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( - Facet[] calldata _diamondCut, + FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; - event DiamondCut(Facet[] _diamondCut, address _init, bytes _calldata); + event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); } ``` @@ -303,23 +313,33 @@ interface IDiamondCut { A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. -The `_diamondCut` argument is an array of Facet structs. +The `_diamondCut` argument is an array of FacetCut structs. + +Each FacetCut struct contains a facet address and array of function selectors that are updated in a diamond. + +To add new functions create a FacetCut struct with `facetAddress` set to the facet that has the new functions and `functionSelectors` set with the function selectors to add. Set the `action` enum to `Add`. + +To replace functions create a FacetCut struct with `facetAddress` set to the facet that has the replacement functions and `functionSelectors` set with the function selectors to replace. Set the `action` enum to `Replace`. + +To remove functions create a FacetCut struct with `facetAddress` set to `address(0)` and `functionSelectors` set with the function selectors to remove. Set the `action` enum to `Remove`. + +It is the design of the `diamondCut` function that cuts are explicit and intentional. -Each Facet struct contains a facet address and an array of one or more function selectors. The mapping in a diamond is updated by each Facets struct. The selectors in a Facet struct are associated with the facet address in the same Facet struct. This is how functions are added or replaced. +The `diamondCut` function reverts when attempting to add a function that already exist. -However if a facet address in a Facet struct is `address(0)` then the function selectors in the same Facet struct are removed. This is how functions are removed from a diamond. +The `diamondCut` function reverts when attempting to replace a function with a facet it is already using. -The `diamondCut` function does nothing if a supplied function selector is already associated with the supplied facet address. The `diamondCut` function does nothing if a supplied function selector can’t be removed because it already does not exist. +The `diamondCut` function reverts when attempting to remove a function that already doesn't exist. -#### Executing _calldata +#### Executing \_calldata After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. -If the `_init` value is `address(0)` then `_calldata` must contain 0 bytes or the transaction reverts. +If the `_init` value is `address(0)` then `_calldata` must contain 0 bytes or the transaction reverts. -If the `_init` value is not `address(0)` then `_calldata` must contain more than 0 bytes or the transaction reverts. +If the `_init` value is not `address(0)` then `_calldata` must contain more than 0 bytes or the transaction reverts. -If `_init` is `address(0)` and `_calldata` contains 0 bytes then `_calldata` execution is skipped. `_calldata` is not executed and the `diamondCut` call can complete successfully. +If `_init` is `address(0)` and `_calldata` contains 0 bytes then `_calldata` execution is skipped. `_calldata` is not executed and the `diamondCut` call can complete successfully. #### DiamondCut Event @@ -328,12 +348,14 @@ The `_diamondCut`, `_init`, and `_calldata` arguments are passed directly to the Any time one or more functions are added, replaced or removed the `DiamondCut` event MUST be emitted to record changes. ### Diamond Loupe + > A loupe is a small magnifying glass used to look at diamonds. -These functions look at diamonds. +> These functions look at diamonds. The function selectors used by a diamond are queried to get what functions the diamond has and what facets are used. A diamond loupe is a facet that implements this interface: + ```Solidity // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds @@ -366,19 +388,22 @@ interface IDiamondLoupe { function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); } ``` + See the [reference implementation](https://github.com/mudgen/Diamond) to see an example of how this can be implemented. -The loupe functions are designed for user-interface software. A user interface calls these functions to provide information about and visualize diamonds. +The loupe functions can be used in user-interface software. A user interface calls these functions to provide information about and visualize diamonds. -In some implementations the loupe functions may not be gas efficient and so should not be called in on-chain transactions. In some implementations the loupe functions may be gas efficient and can be called in on-chain transactions. +The loupe functions can be used in deployment functionality, upgrade functionality, testing and other software. + +Some loupe implementations are not gas efficient and should not be called in on-chain transactions. Some loupe implementations may be gas efficient and can be called in on-chain transactions. Read the documentation of the loupe implementation you use. ### Implementation Points A diamond implements the following implementation points: -1. A diamond contains a fallback function and zero or more immutable functions that are defined directly within it. +1. A diamond contains a fallback function and zero or more immutable functions that are defined within it. 1. A diamond associates function selectors with facets. -1. When a function is called on a diamond it executes immediately if it is an "immutable function" defined directly in the diamond. Otherwise the diamond's fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function and no other mechanism to handle it then execution reverts. +1. When a function is called on a diamond it executes immediately if it is an "immutable function" defined in the diamond. Otherwise the diamond's fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function and no other mechanism to handle it then execution reverts. 1. Each time functions are added, replaced or removed a `DiamondCut` event is emitted to record it. 1. A diamond implements the DiamondLoupe interface. 1. A diamond implements ERC165. If a diamond has the `diamondCut` function then the interface ID used for it is `IDiamondCut.diamondCut.selector`. The interface ID used for the diamond loupe interface is `IDiamondLoupe.facets.selector ^ IDiamondLoupe.facetFunctionSelectors.selector ^ IDiamondLoupe.facetAddresses.selector ^ IDiamondLoupe.facetAddress.selector`. @@ -395,13 +420,14 @@ A reference implementation is given in the [Diamond repository](https://github.c User interface software can be used to retrieve function selectors and face addresses from a diamond in order show what functions a diamond has. -This standard is designed to make diamonds work well with user-interface software. Function selectors with the ABI of a contract provide enough information about functions to be useful for user-interface software. +This standard is designed to make diamonds work well with user-interface software. Function selectors with the ABI of a contract provide enough information about functions to be useful for user-interface software. ### Gas Considerations -Delegating function calls does have some gas overhead. This is mitigated in several ways: +Delegating function calls does have some gas overhead. This is mitigated in several ways: + 1. Because diamonds do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a diamond to implement the ERC721 standard and implement batch transfer functions from the [ERC1412 standard](https://github.com/ethereum/EIPs/issues/1412) to reduce gas (and make batch transfers more convenient). -1. Some contract architectures require calling many contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. +1. Some contract architectures require calling many contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. 1. Facets can be small, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. 1. The Solidity optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less gas when executed. @@ -417,12 +443,15 @@ Diamond storage is not the same thing as unstructured storage. Unstructured stor Software or a user can verify what version of a function is called by getting the facet address of the function. This can be done by calling the `facetAddress` function from the DiamondLoupe interface. This function takes a function selector as an argument and returns the facet address where it is implemented. ### Sharing Functions Between Facets + In some cases it might be necessary to call a function defined in a different facet. Here are some solutions to this: + 1. Copy internal function code in one facet to the other facet. 1. Put common internal functions in a contract that is inherited by multiple facets. 1. Put common internal functions in a Solidity library and use the library in facets. 1. Use `delegatecall` to call external functions defined in other facets. Here is an example of doing that: + ```Solidity DiamondStorage storage ds = diamondStorage(); bytes4 functionSelector = bytes4(keccak256("myFunction(uint256)")); @@ -432,26 +461,28 @@ bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); (bool success, uint result) = address(facet).delegatecall(myFunctionCall); require(success, "myFunction failed"); ``` -5. Instead of using `delegatecall` to call an external function defined in another facet you can instead create an internal function version of the external function and use that. +5. Instead of using `delegatecall` to call an external function defined in another facet you can instead create an internal function version of the external function and use that. ## Security Considerations ### Ownership and Authentication ->**Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. +> **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. -It is possible to create many different authentication or ownership schemes with the diamond standard. Authentication schemes can be very simple or complex, fine grained or coarse. The diamond standard does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. +It is possible to create many different authentication or ownership schemes with the diamond standard. Authentication schemes can be very simple or complex, fine grained or coarse. The diamond standard does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. -Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. +Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. The development of standards and implementations of ownership, control and authentication of diamonds is encouraged. ### Security of Diamond Storage + If a person can add/replace functions then that person can alter storage willy-nilly. This is very powerful and very dangerous. However the capability can be used while eliminating or reducing the danger. The danger is eliminated or reduced by limiting **who** can add/replace/remove functions, limiting **when** functions can be added/replaced/removed and by **transparency**. **Who** Here are some ways **who** can be limited: + 1. Only allow a trusted individual or organization to make diamond upgrades. 1. Only allow a distributed autonomous organization to make diamond upgrades. 1. Only allow multi-signature upgrades. @@ -460,22 +491,25 @@ Here are some ways **who** can be limited: **When** Here are some ways **when** can be limited: + 1. Only allow upgrades during development and testing. Make a single cut diamond for main network release. 1. Use an upgradeable diamond until it is certain that no new features are needed and then make it a finished diamond by removing the ability to add/replace/remove functions. 1. Program into the `diamondCut` function certain periods of time that the diamond can be upgraded. For example the `diamondCut` function could be programmed so that a diamond could only be upgraded during a specific five hour period each year. Attention and transparency could be applied to that five hour period to ensure upgrades are done right. **Transparency** Transparency provides certainty and proof that upgrades are done correctly and honestly. + 1. Publish and make available verified source code used by diamonds and facets. 1. Provide documentation for diamonds, facets, upgrade plans, and results of upgrades. 1. Provide tools and/or user interfaces that make your diamonds more visible and understandable. ### Function Selector Clash -A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. Function selector clashes are very rare but should be prevented by using tools that check for function selector clashes before calling the `diamondCut` function or similar function. + +A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with the `diamondCut` function because it prevents adding function selectors that already exist. ### Transparency -Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. +Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. Security and domain experts can review the history of change of a diamond to detect any history of foul play. @@ -519,10 +553,10 @@ The Diamond Standard is an improved design over [EIP-1538](https://eips.ethereum The Diamond Standard replaces EIP-1538. -This standard was inspired by [EIP-1538](https://eips.ethereum.org/EIPS/eip-1538) and ZeppelinOS's implementation of [Upgradeability with vtables](https://github.com/zeppelinos/labs/tree/master/upgradeability_with_vtable). +This standard was inspired by [EIP-1538](https://eips.ethereum.org/EIPS/eip-1538) and ZeppelinOS's implementation of [Upgradeability with vtables](https://github.com/zeppelinos/labs/tree/master/upgradeability_with_vtable). This standard was also inspired by the design and implementation of the [Mokens contract](https://etherscan.io/address/0xc1eab49cf9d2e23e43bcf23b36b2be14fc2f8838#code). - ## Copyright + Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).