-
Notifications
You must be signed in to change notification settings - Fork 409
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
For the same input, useCompiler generates different bytecode #1099
Comments
I tried with v0.1.5 with different settings and it reproduces the odd behavior:
Note that the 3rd runs are always the same with the 2nd:
✅ With an empty contract with {
"language": "Solidity",
"sources": {
"contracts/Empty.sol": {
"content": "pragma solidity >=0.7.0;\n\ncontract Empty{\n constructor (uint256 a) {\n }\n}"
}
},
"settings": {
"optimizer": { "enabled": false, "runs": 200 },
"outputSelection": {
"*": {
"": ["ast"],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
}
}
} ✅ Different simple contract with same {
"language": "Solidity",
"sources": {
"contracts/Storage.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0\n\n// pragma solidity >=0.7.0 <0.9.0;\n\n/**\n * @title Storage\n * @dev Store & retrieve value in a variable\n */\ncontract Storage {\n\n uint256 number;\n\n /**\n * @dev Store value in variable\n * @param num value to store\n */\n function store(uint256 num) public {\n number = num;\n }\n\n /**\n * @dev Return value \n * @return value of 'number'\n */\n function retrieve() public returns (uint256){\n return number;\n }\n}\n"
}
},
"settings": {
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": ["*"]
}
}
}
} Which led me to thinking this is related to this specific contract I copied the same contract into Remix and put the standard-json compiler input from Remix into this script and WTF, now the 3rd consequent compilation (with {
"language": "Solidity",
"sources": {
"contracts/GroveLib.sol": {
"content": "// Grove v0.2\n\n\n/// @title GroveLib - Library for queriable indexed ordered data.\n/// @author PiperMerriam - <[email protected]>\nlibrary GroveLib {\n /*\n * Indexes for ordered data\n *\n * Address: 0xd07ce4329b27eb8896c51458468d98a0e4c0394c\n */\n struct Index {\n bytes32 id;\n bytes32 name;\n bytes32 root;\n mapping (bytes32 => Node) nodes;\n }\n\n struct Node {\n bytes32 nodeId;\n bytes32 indexId;\n bytes32 id;\n int value;\n bytes32 parent;\n bytes32 left;\n bytes32 right;\n uint height;\n }\n\n /// @dev This is merely a shortcut for `sha3(owner, indexName)`\n /// @param owner The address of the owner of this index.\n /// @param indexName The human readable name for this index.\n function computeIndexId(address owner, bytes32 indexName) constant returns (bytes32) {\n return sha3(owner, indexName);\n }\n\n /// @dev This is merely a shortcut for `sha3(indexId, id)`\n /// @param indexId The id for the index the node belongs to.\n /// @param id The unique identifier for the data this node represents.\n function computeNodeId(bytes32 indexId, bytes32 id) constant returns (bytes32) {\n return sha3(indexId, id);\n }\n\n function max(uint a, uint b) internal returns (uint) {\n if (a >= b) {\n return a;\n }\n return b;\n }\n\n /*\n * Node getters\n */\n /// @dev Retrieve the unique identifier for the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeId(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n return index.nodes[nodeId].id;\n }\n\n /// @dev Retrieve the index id for the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeIndexId(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n return index.nodes[nodeId].indexId;\n }\n\n /// @dev Retrieve the value for the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeValue(Index storage index, bytes32 nodeId) constant returns (int) {\n return index.nodes[nodeId].value;\n }\n\n /// @dev Retrieve the height of the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeHeight(Index storage index, bytes32 nodeId) constant returns (uint) {\n return index.nodes[nodeId].height;\n }\n\n /// @dev Retrieve the parent id of the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeParent(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n return index.nodes[nodeId].parent;\n }\n\n /// @dev Retrieve the left child id of the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeLeftChild(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n return index.nodes[nodeId].left;\n }\n\n /// @dev Retrieve the right child id of the node.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNodeRightChild(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n return index.nodes[nodeId].right;\n }\n\n /// @dev Retrieve the node id of the next node in the tree.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getPreviousNode(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n Node storage currentNode = index.nodes[nodeId];\n\n if (currentNode.nodeId == 0x0) {\n // Unknown node, just return 0x0;\n return 0x0;\n }\n\n Node memory child;\n\n if (currentNode.left != 0x0) {\n // Trace left to latest child in left tree.\n child = index.nodes[currentNode.left];\n\n while (child.right != 0) {\n child = index.nodes[child.right];\n }\n return child.nodeId;\n }\n\n if (currentNode.parent != 0x0) {\n // Now we trace back up through parent relationships, looking\n // for a link where the child is the right child of it's\n // parent.\n Node storage parent = index.nodes[currentNode.parent];\n child = currentNode;\n\n while (true) {\n if (parent.right == child.nodeId) {\n return parent.nodeId;\n }\n\n if (parent.parent == 0x0) {\n break;\n }\n child = parent;\n parent = index.nodes[parent.parent];\n }\n }\n\n // This is the first node, and has no previous node.\n return 0x0;\n }\n\n /// @dev Retrieve the node id of the previous node in the tree.\n /// @param index The index that the node is part of.\n /// @param nodeId The id for the node to be looked up.\n function getNextNode(Index storage index, bytes32 nodeId) constant returns (bytes32) {\n Node storage currentNode = index.nodes[nodeId];\n\n if (currentNode.nodeId == 0x0) {\n // Unknown node, just return 0x0;\n return 0x0;\n }\n\n Node memory child;\n\n if (currentNode.right != 0x0) {\n // Trace right to earliest child in right tree.\n child = index.nodes[currentNode.right];\n\n while (child.left != 0) {\n child = index.nodes[child.left];\n }\n return child.nodeId;\n }\n\n if (currentNode.parent != 0x0) {\n // if the node is the left child of it's parent, then the\n // parent is the next one.\n Node storage parent = index.nodes[currentNode.parent];\n child = currentNode;\n\n while (true) {\n if (parent.left == child.nodeId) {\n return parent.nodeId;\n }\n\n if (parent.parent == 0x0) {\n break;\n }\n child = parent;\n parent = index.nodes[parent.parent];\n }\n\n // Now we need to trace all the way up checking to see if any parent is the \n }\n\n // This is the final node.\n return 0x0;\n }\n\n\n /// @dev Updates or Inserts the id into the index at its appropriate location based on the value provided.\n /// @param index The index that the node is part of.\n /// @param id The unique identifier of the data element the index node will represent.\n /// @param value The value of the data element that represents it's total ordering with respect to other elementes.\n function insert(Index storage index, bytes32 id, int value) public {\n bytes32 nodeId = computeNodeId(index.id, id);\n\n if (index.nodes[nodeId].nodeId == nodeId) {\n // A node with this id already exists. If the value is\n // the same, then just return early, otherwise, remove it\n // and reinsert it.\n if (index.nodes[nodeId].value == value) {\n return;\n }\n remove(index, id);\n }\n\n uint leftHeight;\n uint rightHeight;\n\n bytes32 previousNodeId = 0x0;\n\n bytes32 rootNodeId = index.root;\n\n if (rootNodeId == 0x0) {\n rootNodeId = nodeId;\n index.root = nodeId;\n }\n Node storage currentNode = index.nodes[rootNodeId];\n\n // Do insertion\n while (true) {\n if (currentNode.indexId == 0x0) {\n // This is a new unpopulated node.\n currentNode.nodeId = nodeId;\n currentNode.parent = previousNodeId;\n currentNode.indexId = index.id;\n currentNode.id = id;\n currentNode.value = value;\n break;\n }\n\n // Set the previous node id.\n previousNodeId = currentNode.nodeId;\n\n // The new node belongs in the right subtree\n if (value >= currentNode.value) {\n if (currentNode.right == 0x0) {\n currentNode.right = nodeId;\n }\n currentNode = index.nodes[currentNode.right];\n continue;\n }\n\n // The new node belongs in the left subtree.\n if (currentNode.left == 0x0) {\n currentNode.left = nodeId;\n }\n currentNode = index.nodes[currentNode.left];\n }\n\n // Rebalance the tree\n _rebalanceTree(index, currentNode.nodeId);\n }\n\n /// @dev Checks whether a node for the given unique identifier exists within the given index.\n /// @param index The index that should be searched\n /// @param id The unique identifier of the data element to check for.\n function exists(Index storage index, bytes32 id) constant returns (bool) {\n bytes32 nodeId = computeNodeId(index.id, id);\n return (index.nodes[nodeId].nodeId == nodeId);\n }\n\n /// @dev Remove the node for the given unique identifier from the index.\n /// @param index The index that should be removed\n /// @param id The unique identifier of the data element to remove.\n function remove(Index storage index, bytes32 id) public {\n bytes32 nodeId = computeNodeId(index.id, id);\n \n Node storage replacementNode;\n Node storage parent;\n Node storage child;\n bytes32 rebalanceOrigin;\n\n Node storage nodeToDelete = index.nodes[nodeId];\n\n if (nodeToDelete.id != id) {\n // The id does not exist in the tree.\n return;\n }\n\n if (nodeToDelete.left != 0x0 || nodeToDelete.right != 0x0) {\n // This node is not a leaf node and thus must replace itself in\n // it's tree by either the previous or next node.\n if (nodeToDelete.left != 0x0) {\n // This node is guaranteed to not have a right child.\n replacementNode = index.nodes[getPreviousNode(index, nodeToDelete.nodeId)];\n }\n else {\n // This node is guaranteed to not have a left child.\n replacementNode = index.nodes[getNextNode(index, nodeToDelete.nodeId)];\n }\n // The replacementNode is guaranteed to have a parent.\n parent = index.nodes[replacementNode.parent];\n\n // Keep note of the location that our tree rebalancing should\n // start at.\n rebalanceOrigin = replacementNode.nodeId;\n\n // Join the parent of the replacement node with any subtree of\n // the replacement node. We can guarantee that the replacement\n // node has at most one subtree because of how getNextNode and\n // getPreviousNode are used.\n if (parent.left == replacementNode.nodeId) {\n parent.left = replacementNode.right;\n if (replacementNode.right != 0x0) {\n child = index.nodes[replacementNode.right];\n child.parent = parent.nodeId;\n }\n }\n if (parent.right == replacementNode.nodeId) {\n parent.right = replacementNode.left;\n if (replacementNode.left != 0x0) {\n child = index.nodes[replacementNode.left];\n child.parent = parent.nodeId;\n }\n }\n\n // Now we replace the nodeToDelete with the replacementNode.\n // This includes parent/child relationships for all of the\n // parent, the left child, and the right child.\n replacementNode.parent = nodeToDelete.parent;\n if (nodeToDelete.parent != 0x0) {\n parent = index.nodes[nodeToDelete.parent];\n if (parent.left == nodeToDelete.nodeId) {\n parent.left = replacementNode.nodeId;\n }\n if (parent.right == nodeToDelete.nodeId) {\n parent.right = replacementNode.nodeId;\n }\n }\n else {\n // If the node we are deleting is the root node so update\n // the indexId to root node mapping.\n index.root = replacementNode.nodeId;\n }\n\n replacementNode.left = nodeToDelete.left;\n if (nodeToDelete.left != 0x0) {\n child = index.nodes[nodeToDelete.left];\n child.parent = replacementNode.nodeId;\n }\n\n replacementNode.right = nodeToDelete.right;\n if (nodeToDelete.right != 0x0) {\n child = index.nodes[nodeToDelete.right];\n child.parent = replacementNode.nodeId;\n }\n }\n else if (nodeToDelete.parent != 0x0) {\n // The node being deleted is a leaf node so we only erase it's\n // parent linkage.\n parent = index.nodes[nodeToDelete.parent];\n\n if (parent.left == nodeToDelete.nodeId) {\n parent.left = 0x0;\n }\n if (parent.right == nodeToDelete.nodeId) {\n parent.right = 0x0;\n }\n\n // keep note of where the rebalancing should begin.\n rebalanceOrigin = parent.nodeId;\n }\n else {\n // This is both a leaf node and the root node, so we need to\n // unset the root node pointer.\n index.root = 0x0;\n }\n\n // Now we zero out all of the fields on the nodeToDelete.\n nodeToDelete.id = 0x0;\n nodeToDelete.nodeId = 0x0;\n nodeToDelete.indexId = 0x0;\n nodeToDelete.value = 0;\n nodeToDelete.parent = 0x0;\n nodeToDelete.left = 0x0;\n nodeToDelete.right = 0x0;\n\n // Walk back up the tree rebalancing\n if (rebalanceOrigin != 0x0) {\n _rebalanceTree(index, rebalanceOrigin);\n }\n }\n\n bytes2 constant GT = \">\";\n bytes2 constant LT = \"<\";\n bytes2 constant GTE = \">=\";\n bytes2 constant LTE = \"<=\";\n bytes2 constant EQ = \"==\";\n\n function _compare(int left, bytes2 operator, int right) internal returns (bool) {\n if (operator == GT) {\n return (left > right);\n }\n if (operator == LT) {\n return (left < right);\n }\n if (operator == GTE) {\n return (left >= right);\n }\n if (operator == LTE) {\n return (left <= right);\n }\n if (operator == EQ) {\n return (left == right);\n }\n\n // Invalid operator.\n throw;\n }\n\n function _getMaximum(Index storage index, bytes32 nodeId) internal returns (int) {\n Node storage currentNode = index.nodes[nodeId];\n\n while (true) {\n if (currentNode.right == 0x0) {\n return currentNode.value;\n }\n currentNode = index.nodes[currentNode.right];\n }\n }\n\n function _getMinimum(Index storage index, bytes32 nodeId) internal returns (int) {\n Node storage currentNode = index.nodes[nodeId];\n\n while (true) {\n if (currentNode.left == 0x0) {\n return currentNode.value;\n }\n currentNode = index.nodes[currentNode.left];\n }\n }\n\n\n /** @dev Query the index for the edge-most node that satisfies the\n * given query. For >, >=, and ==, this will be the left-most node\n * that satisfies the comparison. For < and <= this will be the\n * right-most node that satisfies the comparison.\n */\n /// @param index The index that should be queried\n /** @param operator One of '>', '>=', '<', '<=', '==' to specify what\n * type of comparison operator should be used.\n */\n function query(Index storage index, bytes2 operator, int value) public returns (bytes32) {\n bytes32 rootNodeId = index.root;\n \n if (rootNodeId == 0x0) {\n // Empty tree.\n return 0x0;\n }\n\n Node storage currentNode = index.nodes[rootNodeId];\n\n while (true) {\n if (_compare(currentNode.value, operator, value)) {\n // We have found a match but it might not be the\n // *correct* match.\n if ((operator == LT) || (operator == LTE)) {\n // Need to keep traversing right until this is no\n // longer true.\n if (currentNode.right == 0x0) {\n return currentNode.nodeId;\n }\n if (_compare(_getMinimum(index, currentNode.right), operator, value)) {\n // There are still nodes to the right that\n // match.\n currentNode = index.nodes[currentNode.right];\n continue;\n }\n return currentNode.nodeId;\n }\n\n if ((operator == GT) || (operator == GTE) || (operator == EQ)) {\n // Need to keep traversing left until this is no\n // longer true.\n if (currentNode.left == 0x0) {\n return currentNode.nodeId;\n }\n if (_compare(_getMaximum(index, currentNode.left), operator, value)) {\n currentNode = index.nodes[currentNode.left];\n continue;\n }\n return currentNode.nodeId;\n }\n }\n\n if ((operator == LT) || (operator == LTE)) {\n if (currentNode.left == 0x0) {\n // There are no nodes that are less than the value\n // so return null.\n return 0x0;\n }\n currentNode = index.nodes[currentNode.left];\n continue;\n }\n\n if ((operator == GT) || (operator == GTE)) {\n if (currentNode.right == 0x0) {\n // There are no nodes that are greater than the value\n // so return null.\n return 0x0;\n }\n currentNode = index.nodes[currentNode.right];\n continue;\n }\n\n if (operator == EQ) {\n if (currentNode.value < value) {\n if (currentNode.right == 0x0) {\n return 0x0;\n }\n currentNode = index.nodes[currentNode.right];\n continue;\n }\n\n if (currentNode.value > value) {\n if (currentNode.left == 0x0) {\n return 0x0;\n }\n currentNode = index.nodes[currentNode.left];\n continue;\n }\n }\n }\n }\n\n function _rebalanceTree(Index storage index, bytes32 nodeId) internal {\n // Trace back up rebalancing the tree and updating heights as\n // needed..\n Node storage currentNode = index.nodes[nodeId];\n\n while (true) {\n int balanceFactor = _getBalanceFactor(index, currentNode.nodeId);\n\n if (balanceFactor == 2) {\n // Right rotation (tree is heavy on the left)\n if (_getBalanceFactor(index, currentNode.left) == -1) {\n // The subtree is leaning right so it need to be\n // rotated left before the current node is rotated\n // right.\n _rotateLeft(index, currentNode.left);\n }\n _rotateRight(index, currentNode.nodeId);\n }\n\n if (balanceFactor == -2) {\n // Left rotation (tree is heavy on the right)\n if (_getBalanceFactor(index, currentNode.right) == 1) {\n // The subtree is leaning left so it need to be\n // rotated right before the current node is rotated\n // left.\n _rotateRight(index, currentNode.right);\n }\n _rotateLeft(index, currentNode.nodeId);\n }\n\n if ((-1 <= balanceFactor) && (balanceFactor <= 1)) {\n _updateNodeHeight(index, currentNode.nodeId);\n }\n\n if (currentNode.parent == 0x0) {\n // Reached the root which may be new due to tree\n // rotation, so set it as the root and then break.\n break;\n }\n\n currentNode = index.nodes[currentNode.parent];\n }\n }\n\n function _getBalanceFactor(Index storage index, bytes32 nodeId) internal returns (int) {\n Node storage node = index.nodes[nodeId];\n\n return int(index.nodes[node.left].height) - int(index.nodes[node.right].height);\n }\n\n function _updateNodeHeight(Index storage index, bytes32 nodeId) internal {\n Node storage node = index.nodes[nodeId];\n\n node.height = max(index.nodes[node.left].height, index.nodes[node.right].height) + 1;\n }\n\n function _rotateLeft(Index storage index, bytes32 nodeId) internal {\n Node storage originalRoot = index.nodes[nodeId];\n\n if (originalRoot.right == 0x0) {\n // Cannot rotate left if there is no right originalRoot to rotate into\n // place.\n throw;\n }\n\n // The right child is the new root, so it gets the original\n // `originalRoot.parent` as it's parent.\n Node storage newRoot = index.nodes[originalRoot.right];\n newRoot.parent = originalRoot.parent;\n\n // The original root needs to have it's right child nulled out.\n originalRoot.right = 0x0;\n\n if (originalRoot.parent != 0x0) {\n // If there is a parent node, it needs to now point downward at\n // the newRoot which is rotating into the place where `node` was.\n Node storage parent = index.nodes[originalRoot.parent];\n\n // figure out if we're a left or right child and have the\n // parent point to the new node.\n if (parent.left == originalRoot.nodeId) {\n parent.left = newRoot.nodeId;\n }\n if (parent.right == originalRoot.nodeId) {\n parent.right = newRoot.nodeId;\n }\n }\n\n\n if (newRoot.left != 0) {\n // If the new root had a left child, that moves to be the\n // new right child of the original root node\n Node storage leftChild = index.nodes[newRoot.left];\n originalRoot.right = leftChild.nodeId;\n leftChild.parent = originalRoot.nodeId;\n }\n\n // Update the newRoot's left node to point at the original node.\n originalRoot.parent = newRoot.nodeId;\n newRoot.left = originalRoot.nodeId;\n\n if (newRoot.parent == 0x0) {\n index.root = newRoot.nodeId;\n }\n\n // TODO: are both of these updates necessary?\n _updateNodeHeight(index, originalRoot.nodeId);\n _updateNodeHeight(index, newRoot.nodeId);\n }\n\n function _rotateRight(Index storage index, bytes32 nodeId) internal {\n Node storage originalRoot = index.nodes[nodeId];\n\n if (originalRoot.left == 0x0) {\n // Cannot rotate right if there is no left node to rotate into\n // place.\n throw;\n }\n\n // The left child is taking the place of node, so we update it's\n // parent to be the original parent of the node.\n Node storage newRoot = index.nodes[originalRoot.left];\n newRoot.parent = originalRoot.parent;\n\n // Null out the originalRoot.left\n originalRoot.left = 0x0;\n\n if (originalRoot.parent != 0x0) {\n // If the node has a parent, update the correct child to point\n // at the newRoot now.\n Node storage parent = index.nodes[originalRoot.parent];\n\n if (parent.left == originalRoot.nodeId) {\n parent.left = newRoot.nodeId;\n }\n if (parent.right == originalRoot.nodeId) {\n parent.right = newRoot.nodeId;\n }\n }\n\n if (newRoot.right != 0x0) {\n Node storage rightChild = index.nodes[newRoot.right];\n originalRoot.left = newRoot.right;\n rightChild.parent = originalRoot.nodeId;\n }\n\n // Update the new root's right node to point to the original node.\n originalRoot.parent = newRoot.nodeId;\n newRoot.right = originalRoot.nodeId;\n\n if (newRoot.parent == 0x0) {\n index.root = newRoot.nodeId;\n }\n\n // Recompute heights.\n _updateNodeHeight(index, originalRoot.nodeId);\n _updateNodeHeight(index, newRoot.nodeId);\n }\n}\n\n\n/// @title Grove - queryable indexes for ordered data.\n/// @author Piper Merriam <[email protected]>\ncontract Grove {\n /*\n * Indexes for ordered data\n *\n * Address: 0x8017f24a47c889b1ee80501ff84beb3c017edf0b\n */\n // Map index_id to index\n mapping (bytes32 => GroveLib.Index) index_lookup;\n\n // Map node_id to index_id.\n mapping (bytes32 => bytes32) node_to_index;\n\n /// @notice Computes the id for a Grove index which is sha3(owner, indexName)\n /// @param owner The address of the index owner.\n /// @param indexName The name of the index.\n function computeIndexId(address owner, bytes32 indexName) constant returns (bytes32) {\n return GroveLib.computeIndexId(owner, indexName);\n }\n\n /// @notice Computes the id for a node in a given Grove index which is sha3(indexId, id)\n /// @param indexId The id for the index the node belongs to.\n /// @param id The unique identifier for the data this node represents.\n function computeNodeId(bytes32 indexId, bytes32 id) constant returns (bytes32) {\n return GroveLib.computeNodeId(indexId, id);\n }\n\n /*\n * Node getters\n */\n /// @notice Retrieves the name of an index.\n /// @param indexId The id of the index.\n function getIndexName(bytes32 indexId) constant returns (bytes32) {\n return index_lookup[indexId].name;\n }\n\n /// @notice Retrieves the id of the root node for this index.\n /// @param indexId The id of the index.\n function getIndexRoot(bytes32 indexId) constant returns (bytes32) {\n return index_lookup[indexId].root;\n }\n\n\n /// @dev Retrieve the unique identifier this node represents.\n /// @param nodeId The id for the node\n function getNodeId(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNodeId(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the index id for the node.\n /// @param nodeId The id for the node\n function getNodeIndexId(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNodeIndexId(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the value of the node.\n /// @param nodeId The id for the node\n function getNodeValue(bytes32 nodeId) constant returns (int) {\n return GroveLib.getNodeValue(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the height of the node.\n /// @param nodeId The id for the node\n function getNodeHeight(bytes32 nodeId) constant returns (uint) {\n return GroveLib.getNodeHeight(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the parent id of the node.\n /// @param nodeId The id for the node\n function getNodeParent(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNodeParent(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the left child id of the node.\n /// @param nodeId The id for the node\n function getNodeLeftChild(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNodeLeftChild(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /// @dev Retrieve the right child id of the node.\n /// @param nodeId The id for the node\n function getNodeRightChild(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNodeRightChild(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /** @dev Retrieve the id of the node that comes immediately before this\n * one. Returns 0x0 if there is no previous node.\n */\n /// @param nodeId The id for the node\n function getPreviousNode(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getPreviousNode(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /** @dev Retrieve the id of the node that comes immediately after this\n * one. Returns 0x0 if there is no previous node.\n */\n /// @param nodeId The id for the node\n function getNextNode(bytes32 nodeId) constant returns (bytes32) {\n return GroveLib.getNextNode(index_lookup[node_to_index[nodeId]], nodeId);\n }\n\n /** @dev Update or Insert a data element represented by the unique\n * identifier `id` into the index.\n */\n /// @param indexName The human readable name for the index that the node should be upserted into.\n /// @param id The unique identifier that the index node represents.\n /// @param value The number which represents this data elements total ordering.\n function insert(bytes32 indexName, bytes32 id, int value) public {\n bytes32 indexId = computeIndexId(msg.sender, indexName);\n var index = index_lookup[indexId];\n\n if (index.name != indexName) {\n // If this is a new index, store it's name and id\n index.name = indexName;\n index.id = indexId;\n }\n\n // Store the mapping from nodeId to the indexId\n node_to_index[computeNodeId(indexId, id)] = indexId;\n\n GroveLib.insert(index, id, value);\n }\n\n /// @dev Query whether a node exists within the specified index for the unique identifier.\n /// @param indexId The id for the index.\n /// @param id The unique identifier of the data element.\n function exists(bytes32 indexId, bytes32 id) constant returns (bool) {\n return GroveLib.exists(index_lookup[indexId], id);\n }\n\n /// @dev Remove the index node for the given unique identifier.\n /// @param indexName The name of the index.\n /// @param id The unique identifier of the data element.\n function remove(bytes32 indexName, bytes32 id) public {\n GroveLib.remove(index_lookup[computeIndexId(msg.sender, indexName)], id);\n }\n\n /** @dev Query the index for the edge-most node that satisfies the\n * given query. For >, >=, and ==, this will be the left-most node\n * that satisfies the comparison. For < and <= this will be the\n * right-most node that satisfies the comparison.\n */\n /// @param indexId The id of the index that should be queried\n /** @param operator One of '>', '>=', '<', '<=', '==' to specify what\n * type of comparison operator should be used.\n */\n function query(bytes32 indexId, bytes2 operator, int value) public returns (bytes32) {\n return GroveLib.query(index_lookup[indexId], operator, value);\n }\n}"
}
},
"settings": {
"optimizer": { "enabled": false, "runs": 200 },
"outputSelection": {
"*": {
"": ["ast"],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
}
}
}
✅ Ok finally for the same initial contract with
❌ This leads me thinking if this is related to the "non determinism fix" mentioned in the v0.3.3 Changelog Just pushed everything here for the record: https://github.com/kuzdogan/usecompiler-consistency |
Maybe we can work around this by stopping compiling multiple times in general because AFAIK anything below I see you are verifying really old contracts in general. How are you able to verify them because back then there was no metadata and I recall you opening another issue about this. Are you just using the |
Thank you for spending time digging into this a bit. The behavior sure is strange. I am currently verifying them by creating a "shim" metadata object when (To reproduce, a change via #1105 is required and maybe one or two small things.) I was wondering if it would be possible to "reset" the compiler environment somehow for these old versions so that the first compiler run can be reproduced. My concern is that if it changes the output for the same contract, this bug might also change the verification result of other contracts compiled with the same version. That would be a greater problem, since I would like to be able to verify more than one old contract while the program is running. |
This is getting even weirder.. If I run it 10 times, the code changes on the 4th run and jumps around codes
Running 30 times
Now I have another
So even if we "reset" the input, as you said the "compiler environment" gets broken somehow. One last weird thing. I disable the optimizer and run:
Now if I switch the file in the middle the generated codes also change completely ?!
So I can't notice a pattern at all. One thing I can think of is playing around with how we "load" the sourcify/packages/lib-sourcify/src/lib/solidityCompiler.ts Lines 260 to 262 in 39705aa
There could be a "proper" way to do this. Either a completely different way or a way that will somehow "reset" the compiler environment. |
In the absence of module unloading (see nodejs/node#49442), one slightly hacky technique is to use WebWorkers, which have their own threads and import contexts. Nonetheless, it solves the problem, and I am getting the same correct compilation result every time:
So I renamed export async function useCompiler(
version: string,
solcJsonInput: JsonInput
): Promise<any> {
const coercedVersion =
semver.coerce(new semver.SemVer(version))?.version ?? '';
if (semver.gte(coercedVersion, '0.3.3')) {
return useCompilerBuiltin(version, solcJsonInput);
}
return new Promise((resolve, reject) => {
let worker = new Worker('./compilerWorker.js', {
workerData: { version, solcJsonInput },
});
worker.once('message', (result) => {
resolve(result);
});
worker.once('error', (error) => {
reject(error);
});
});
} And import { workerData, parentPort } from "worker_threads";
import { useCompilerBuiltin } from "@ethereum-sourcify/lib-sourcify";
async function runUseCompiler(version, solcJsonInput) {
const result = await useCompilerBuiltin(version, solcJsonInput);
if (parentPort === null) {
throw new Error("Parent port is null; cannot send compilation result");
}
parentPort.postMessage(result);
}
runUseCompiler(workerData.version, workerData.solcJsonInput); Unfortunately, |
Can you share |
In In the project directory (not If I have time later today, I could probably just push changes to my own branches of |
PR: #1130 Fixes for Also, can we always run workers instead of checking for the version? What would be the adv./disadvantage? |
I assumed there was some kind of caching mechanism through imports that caused the context to be reused. If the performance is the same, I think it's safe to run workers always. |
If you invoke
useCompiler
two times in a row with the same input, the resulting deployed bytecode is different. This makes verifications fail because the standard pattern is to first compile the sources to get some metadata and then recompile and check the output based on that metadata.The on-the-blockchain version of the bytecode is the result from the first invocation.
I've observed this on a contract made with solc v0.1.5 with the optimizer enabled. I am not sure if this also happens on other contracts or for other versions of Solidity. This could even be a solc-js problem...
Full example you can run: https://github.com/sealer3/usecompiler-inconsistency
View in Huly HI-450
The text was updated successfully, but these errors were encountered: