Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the usage of SELFDESTRUCT in BytecodeStorage #668

Merged
merged 15 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ erDiagram
ADMIN_ACL ||--o{ CORE_V3_V3_FLEX : manages
ADMIN_ACL ||--o{ MINTERFILTER : manages
CORE_V3_V3_FLEX ||--|| MINTERFILTER : uses
CORE_V3_V3_FLEX }o--|| BYTECODESTORAGEREADER : uses
MINTERFILTER ||--o{ MINTER_1 : uses
MINTERFILTER ||--o{ MINTER_2 : uses
MINTERFILTER ||--o{ MINTER_N : uses
Expand Down Expand Up @@ -141,6 +142,15 @@ The following represents the current set of flagship core contracts deployed on

For deployed core contracts, see the deployment details in the `/deployments/engine/[V2|V3]/<engine-partner>/` directories. For V2 core contracts, archived source code is available in the `/posterity/engine/` directory.

## BytecodeStorageReader

BytecodeStorageReader (currently on V1 version) is public library for reading from storage contracts. This library is intended to be deployed as a standalone contract, and provides all _read_ functionality by being used as an externally linked library within the Art Blocks ecosystem contracts that use contract storage for writes.

Given that it is an externally linked library with a shared public deployment, the deployment addresses for these shared deployments are referenced in our shared deployments `constants.ts` util (in the `BYTECODE_STORAGE_READER_LIBRARY_ADDRESSES` constant) so that they may be linked at time of deployment and are also linked here below for shared reference:

- V1 `BytecodeStorageReader` (goerli): https://goerli.etherscan.io/address/0xB8B806A10d16cc80dB788552B54B3ECb4A2A3C3D#code
- V1 `BytecodeStorageReader` (mainnet): https://etherscan.io/address/0xf0585dF582A0ad119F1616FB82f3b449a98EeCd5#code

## Minter Suite

For details on the Flagship and Engine Minter Suite, see the [minter suite documenation](./MINTER_SUITE.md).
Expand Down
41 changes: 14 additions & 27 deletions contracts/DependencyRegistryV0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "@openzeppelin-4.8/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin-4.8/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin-4.5/contracts/utils/math/SafeCast.sol";

import "./libs/0.8.x/BytecodeStorage.sol";
import "./libs/0.8.x/BytecodeStorageV1.sol";
import "./libs/0.8.x/Bytes32Strings.sol";

/**
Expand All @@ -33,8 +33,7 @@ contract DependencyRegistryV0 is
OwnableUpgradeable,
IDependencyRegistryV0
{
using BytecodeStorage for string;
using BytecodeStorage for address;
using BytecodeStorageWriter for string;
using Bytes32Strings for bytes32;
using Strings for uint256;
using EnumerableSet for EnumerableSet.Bytes32Set;
Expand Down Expand Up @@ -211,16 +210,6 @@ contract DependencyRegistryV0 is
_scriptId < dependencyType.scriptCount,
"scriptId out of range"
);
// purge old contract bytecode contract from the blockchain state
// note: Although this does reduce usage of Ethereum state, it does not
// reduce the gas costs of removal transactions. We believe this is the
// best behavior at the time of writing, and do not expect this to
// result in any breaking changes in the future. All current proposals
// to change the self-destruct opcode are backwards compatible, but may
// result in not removing the bytecode from the blockchain state. This
// implementation is compatible with that architecture, as it does not
// rely on the bytecode being removed from the blockchain state.
dependencyType.scriptBytecodeAddresses[_scriptId].purgeBytecode();
// store script in contract bytecode, replacing reference address from
// the contract that no longer exists with the newly created one
dependencyType.scriptBytecodeAddresses[_scriptId] = _script
Expand All @@ -238,19 +227,7 @@ contract DependencyRegistryV0 is
_onlyExistingDependencyType(_dependencyType);
Dependency storage dependency = dependencyDetails[_dependencyType];
require(dependency.scriptCount > 0, "there are no scripts to remove");
// purge old contract bytecode contract from the blockchain state
// note: Although this does reduce usage of Ethereum state, it does not
// reduce the gas costs of removal transactions. We believe this is the
// best behavior at the time of writing, and do not expect this to
// result in any breaking changes in the future. All current proposals
// to change the self-destruct opcode are backwards compatible, but may
// result in not removing the bytecode from the blockchain state. This
// implementation is compatible with that architecture, as it does not
// rely on the bytecode being removed from the blockchain state.
dependency
.scriptBytecodeAddresses[dependency.scriptCount - 1]
.purgeBytecode();
// delete reference to contract address that no longer exists
// delete reference to old storage contract address
delete dependency.scriptBytecodeAddresses[dependency.scriptCount - 1];
unchecked {
dependency.scriptCount = dependency.scriptCount - 1;
Expand Down Expand Up @@ -757,7 +734,7 @@ contract DependencyRegistryV0 is
return "";
}

return dependency.scriptBytecodeAddresses[_index].readFromBytecode();
return _readFromBytecode(dependency.scriptBytecodeAddresses[_index]);
}

/**
Expand Down Expand Up @@ -851,4 +828,14 @@ contract DependencyRegistryV0 is
OwnableUpgradeable._transferOwnership(newOwner);
adminACLContract = IAdminACLV0(newOwner);
}

/**
* Helper for calling `BytecodeStorageReader` external library reader method,
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return BytecodeStorageReader.readFromBytecode(_address);
}
}
45 changes: 16 additions & 29 deletions contracts/GenArt721CoreV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "./interfaces/0.8.x/IManifold.sol";
import "@openzeppelin-4.7/contracts/utils/Strings.sol";
import "@openzeppelin-4.7/contracts/access/Ownable.sol";
import "./libs/0.8.x/ERC721_PackedHashSeed.sol";
import "./libs/0.8.x/BytecodeStorage.sol";
import "./libs/0.8.x/BytecodeStorageV1.sol";
import "./libs/0.8.x/Bytes32Strings.sol";

/**
Expand Down Expand Up @@ -93,8 +93,7 @@ contract GenArt721CoreV3 is
IGenArt721CoreContractV3,
IGenArt721CoreContractExposesHashSeed
{
using BytecodeStorage for string;
using BytecodeStorage for address;
using BytecodeStorageWriter for string;
using Bytes32Strings for bytes32;
using Strings for uint256;
uint256 constant ONE_HUNDRED = 100;
Expand Down Expand Up @@ -242,7 +241,7 @@ contract GenArt721CoreV3 is
bool public newProjectsForbidden;

/// version & type of this core contract
string public constant coreVersion = "v3.1.0";
string public constant coreVersion = "v3.2.0";
string public constant coreType = "GenArt721CoreV3";

/// default base URI to initialize all new project projectBaseURI values to
Expand Down Expand Up @@ -1072,18 +1071,8 @@ contract GenArt721CoreV3 is
_onlyNonEmptyString(_script);
Project storage project = projects[_projectId];
require(_scriptId < project.scriptCount, "scriptId out of range");
// purge old contract bytecode contract from the blockchain state
// note: Although this does reduce usage of Ethereum state, it does not
// reduce the gas costs of removal transactions. We believe this is the
// best behavior at the time of writing, and do not expect this to
// result in any breaking changes in the future. All current proposals
// to change the self-destruct opcode are backwards compatible, but may
// result in not removing the bytecode from the blockchain state. This
// implementation is compatible with that architecture, as it does not
// rely on the bytecode being removed from the blockchain state.
project.scriptBytecodeAddresses[_scriptId].purgeBytecode();
// store script in contract bytecode, replacing reference address from
// the contract that no longer exists with the newly created one
// the old storage contract with the newly created one
project.scriptBytecodeAddresses[_scriptId] = _script.writeToBytecode();
emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
}
Expand All @@ -1100,19 +1089,7 @@ contract GenArt721CoreV3 is
);
Project storage project = projects[_projectId];
require(project.scriptCount > 0, "there are no scripts to remove");
// purge old contract bytecode contract from the blockchain state
// note: Although this does reduce usage of Ethereum state, it does not
// reduce the gas costs of removal transactions. We believe this is the
// best behavior at the time of writing, and do not expect this to
// result in any breaking changes in the future. All current proposals
// to change the self-destruct opcode are backwards compatible, but may
// result in not removing the bytecode from the blockchain state. This
// implementation is compatible with that architecture, as it does not
// rely on the bytecode being removed from the blockchain state.
project
.scriptBytecodeAddresses[project.scriptCount - 1]
.purgeBytecode();
// delete reference to contract address that no longer exists
// delete reference to old storage contract address
delete project.scriptBytecodeAddresses[project.scriptCount - 1];
unchecked {
project.scriptCount = project.scriptCount - 1;
Expand Down Expand Up @@ -1515,7 +1492,7 @@ contract GenArt721CoreV3 is
if (_index >= project.scriptCount) {
return "";
}
return project.scriptBytecodeAddresses[_index].readFromBytecode();
return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
}

/**
Expand Down Expand Up @@ -1967,4 +1944,14 @@ contract GenArt721CoreV3 is
(block.timestamp - projectCompletedTimestamp <
FOUR_WEEKS_IN_SECONDS);
}

/**
* Helper for calling `BytecodeStorageReader` external library reader method,
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return BytecodeStorageReader.readFromBytecode(_address);
}
}
23 changes: 15 additions & 8 deletions contracts/engine/V3/GenArt721CoreV3_Engine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import "../../interfaces/0.8.x/IManifold.sol";
import "@openzeppelin-4.7/contracts/utils/Strings.sol";
import "@openzeppelin-4.7/contracts/access/Ownable.sol";
import "../../libs/0.8.x/ERC721_PackedHashSeed.sol";
import "../../libs/0.8.x/BytecodeStorage.sol";
import "../../libs/0.8.x/BytecodeStorageV1.sol";
import "../../libs/0.8.x/Bytes32Strings.sol";

/**
Expand Down Expand Up @@ -100,8 +100,7 @@ contract GenArt721CoreV3_Engine is
IGenArt721CoreContractV3_Engine,
IGenArt721CoreContractExposesHashSeed
{
using BytecodeStorage for string;
using BytecodeStorage for address;
using BytecodeStorageWriter for string;
using Bytes32Strings for bytes32;
using Strings for uint256;
using Strings for address;
Expand Down Expand Up @@ -1134,9 +1133,8 @@ contract GenArt721CoreV3_Engine is
_onlyNonEmptyString(_script);
Project storage project = projects[_projectId];
require(_scriptId < project.scriptCount, "scriptId out of range");

// store script in contract bytecode, replacing reference address from
// the contract that no longer exists with the newly created one
// the old storage contract with the newly created one
project.scriptBytecodeAddresses[_scriptId] = _script.writeToBytecode();
emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
}
Expand All @@ -1153,8 +1151,7 @@ contract GenArt721CoreV3_Engine is
);
Project storage project = projects[_projectId];
require(project.scriptCount > 0, "No scripts to remove");

// delete reference to contract address that no longer exists
// delete reference to old storage contract address
delete project.scriptBytecodeAddresses[project.scriptCount - 1];
unchecked {
project.scriptCount = project.scriptCount - 1;
Expand Down Expand Up @@ -1579,7 +1576,7 @@ contract GenArt721CoreV3_Engine is
if (_index >= project.scriptCount) {
return "";
}
return project.scriptBytecodeAddresses[_index].readFromBytecode();
return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
}

/**
Expand Down Expand Up @@ -1998,4 +1995,14 @@ contract GenArt721CoreV3_Engine is
(block.timestamp - projectCompletedTimestamp <
FOUR_WEEKS_IN_SECONDS);
}

/**
* Helper for calling `BytecodeStorageReader` external library reader method,
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return BytecodeStorageReader.readFromBytecode(_address);
}
}
25 changes: 16 additions & 9 deletions contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "../../interfaces/0.8.x/IManifold.sol";

import "@openzeppelin-4.7/contracts/access/Ownable.sol";
import "../../libs/0.8.x/ERC721_PackedHashSeed.sol";
import "../../libs/0.8.x/BytecodeStorage.sol";
import "../../libs/0.8.x/BytecodeStorageV1.sol";
import "../../libs/0.8.x/Bytes32Strings.sol";

/**
Expand Down Expand Up @@ -108,8 +108,7 @@ contract GenArt721CoreV3_Engine_Flex is
IGenArt721CoreContractV3_Engine_Flex,
IGenArt721CoreContractExposesHashSeed
{
using BytecodeStorage for string;
using BytecodeStorage for address;
using BytecodeStorageWriter for string;
using Bytes32Strings for bytes32;
uint256 constant ONE_HUNDRED = 100;
uint256 constant ONE_MILLION = 1_000_000;
Expand Down Expand Up @@ -1320,9 +1319,8 @@ contract GenArt721CoreV3_Engine_Flex is
_onlyNonEmptyString(_script);
Project storage project = projects[_projectId];
require(_scriptId < project.scriptCount, "scriptId out of range");

// store script in contract bytecode, replacing reference address from
// the contract that no longer exists with the newly created one
// the old storage contract with the newly created one
project.scriptBytecodeAddresses[_scriptId] = _script.writeToBytecode();
emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
}
Expand All @@ -1339,8 +1337,7 @@ contract GenArt721CoreV3_Engine_Flex is
);
Project storage project = projects[_projectId];
require(project.scriptCount > 0, "No scripts to remove");

// delete reference to contract address that no longer exists
// delete reference to old storage contract address
delete project.scriptBytecodeAddresses[project.scriptCount - 1];
unchecked {
project.scriptCount = project.scriptCount - 1;
Expand Down Expand Up @@ -1765,7 +1762,7 @@ contract GenArt721CoreV3_Engine_Flex is
if (_index >= project.scriptCount) {
return "";
}
return project.scriptBytecodeAddresses[_index].readFromBytecode();
return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
}

/**
Expand Down Expand Up @@ -1996,7 +1993,7 @@ contract GenArt721CoreV3_Engine_Flex is
bytecodeAddress: _bytecodeAddress,
data: (_dependency.dependencyType ==
ExternalAssetDependencyType.ONCHAIN)
? _bytecodeAddress.readFromBytecode()
? _readFromBytecode(_bytecodeAddress)
: ""
});
}
Expand Down Expand Up @@ -2219,6 +2216,16 @@ contract GenArt721CoreV3_Engine_Flex is
FOUR_WEEKS_IN_SECONDS);
}

/**
* Helper for calling `BytecodeStorageReader` external library reader method,
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return BytecodeStorageReader.readFromBytecode(_address);
}

// strings library from OpenZeppelin, modified for no constants

bytes16 private _HEX_SYMBOLS = "0123456789abcdef";
Expand Down
19 changes: 14 additions & 5 deletions contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "../../../interfaces/0.8.x/IManifold.sol";

import "@openzeppelin-4.7/contracts/access/Ownable.sol";
import "../../../libs/0.8.x/ERC721_PackedHashSeed.sol";
import "../../../libs/0.8.x/BytecodeStorage.sol";
import "../../../libs/0.8.x/BytecodeStorageV1.sol";
import "../../../libs/0.8.x/Bytes32Strings.sol";

/**
Expand Down Expand Up @@ -107,8 +107,7 @@ contract GenArt721CoreV3_Engine_Flex_PROOF is
IManifold,
IGenArt721CoreContractV3_Engine_Flex
{
using BytecodeStorage for string;
using BytecodeStorage for address;
using BytecodeStorageWriter for string;
using Bytes32Strings for bytes32;

uint256 constant ONE_HUNDRED = 100;
Expand Down Expand Up @@ -1773,7 +1772,7 @@ contract GenArt721CoreV3_Engine_Flex_PROOF is
if (_index >= project.scriptCount) {
return "";
}
return project.scriptBytecodeAddresses[_index].readFromBytecode();
return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
}

/**
Expand Down Expand Up @@ -2004,7 +2003,7 @@ contract GenArt721CoreV3_Engine_Flex_PROOF is
bytecodeAddress: _bytecodeAddress,
data: (_dependency.dependencyType ==
ExternalAssetDependencyType.ONCHAIN)
? _bytecodeAddress.readFromBytecode()
? _readFromBytecode(_bytecodeAddress)
: ""
});
}
Expand Down Expand Up @@ -2227,6 +2226,16 @@ contract GenArt721CoreV3_Engine_Flex_PROOF is
FOUR_WEEKS_IN_SECONDS);
}

/**
* Helper for calling `BytecodeStorageReader` external library reader method,
* added for bytecode size reduction purposes.
*/
function _readFromBytecode(
address _address
) internal view returns (string memory) {
return BytecodeStorageReader.readFromBytecode(_address);
}

// strings library from OpenZeppelin, modified for no constants

bytes16 private _HEX_SYMBOLS = "0123456789abcdef";
Expand Down
Loading