-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
Fix Inconsistencies in Stack Allocation Optimizer Settings for CLI and Standard JSON Interface #15655
base: develop
Are you sure you want to change the base?
Fix Inconsistencies in Stack Allocation Optimizer Settings for CLI and Standard JSON Interface #15655
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1736,13 +1736,12 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR | |
} | ||
else if (OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps)) | ||
{ | ||
solAssert(m_optimiserSettings.optimizeStackAllocation == false); | ||
details["yulDetails"] = Json::object(); | ||
details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I guess this is yet another bug, this time in #14657. It means that my mention of wrong metadata being produced was not entirely incorrect, it just happens not with Yul optimizer disabled but when using an empty sequence. So And another consequence is probably that |
||
details["yulDetails"]["optimizerSteps"] = ":"; | ||
} | ||
else | ||
{ | ||
solAssert(m_optimiserSettings.optimizeStackAllocation == false); | ||
solAssert(m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should add the Since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only case where the flag must be included is when it has a non-default value. I.e. if you'd have to set it explicitly in the input to reproduce the same bytecode. In other situations it's optional, and either choice has pros and cons. The advantage of always including it is that if you get into a weird situation, where you get two inconsistent defaults depending on how you invoke the compiler, the options reconstructed from metadata will still let you reproduce the bytecode. The advantage of omitting it is that if you make wrong assumptions about the default, you won't get wrong metadata, because you did not store the value there - reconstructed options will rely on the actual default. Hard to say which is better, because here we got bitten by both of those problems at the same time so neither choice would really have saved us :) So the choice is a bit arbitrary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In any case, in this situation the flag will always have the default value, because we don't allow changing it. You can choose whether to store it or not, both will work. Though the funny thing is that even though we know it must have the default value, we don't know what the default was. That's because it depends on the state of the I mean, we can see the current value so we "know" it but we must trust that it's actually the default; we can't assert anything about it to check if we're wrong. Which is not great because the assert is what allowed us to detect this bug (even if it wasn't the right assert). |
||
solAssert(m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps); | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -548,12 +548,14 @@ std::variant<OptimiserSettings, Json> parseOptimizerSettings(Json const& _jsonIn | |||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
OptimiserSettings settings = OptimiserSettings::minimal(); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
bool optimizerEnabled = false; | ||||||||||||||||||||||||||||||||||||||||||
if (_jsonInput.contains("enabled")) | ||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||
if (!_jsonInput["enabled"].is_boolean()) | ||||||||||||||||||||||||||||||||||||||||||
return formatFatalError(Error::Type::JSONError, "The \"enabled\" setting must be a Boolean."); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (_jsonInput["enabled"].get<bool>()) | ||||||||||||||||||||||||||||||||||||||||||
optimizerEnabled = _jsonInput["enabled"].get<bool>(); | ||||||||||||||||||||||||||||||||||||||||||
if (optimizerEnabled) | ||||||||||||||||||||||||||||||||||||||||||
settings = OptimiserSettings::standard(); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
|
@@ -588,7 +590,10 @@ std::variant<OptimiserSettings, Json> parseOptimizerSettings(Json const& _jsonIn | |||||||||||||||||||||||||||||||||||||||||
return *error; | ||||||||||||||||||||||||||||||||||||||||||
if (auto error = checkOptimizerDetail(details, "simpleCounterForLoopUncheckedIncrement", settings.simpleCounterForLoopUncheckedIncrement)) | ||||||||||||||||||||||||||||||||||||||||||
return *error; | ||||||||||||||||||||||||||||||||||||||||||
settings.optimizeStackAllocation = settings.runYulOptimiser; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (optimizerEnabled || settings.runYulOptimiser) | ||||||||||||||||||||||||||||||||||||||||||
settings.optimizeStackAllocation = true; | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+594
to
+595
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong, because it means that
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I think of it, this means that both on the command line and in Standard JSON we do disable the flag when the optimizer as a whole is disabled. That's not a part of the bug, but I wonder if I should have changed that behavior in #14149. After all, the way it is now means that the state of the flag depends on whether we run evmasm optimizer, which is weird. We should probably change that in a separate PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
However, on the CLI, the solidity/solc/CommandLineParser.cpp Lines 272 to 276 in b6486db
This value is determined either by using solidity/solc/CommandLineParser.cpp Lines 1242 to 1246 in b6486db
In contrast, in the Standard JSON, solidity/libsolidity/interface/StandardCompiler.cpp Lines 587 to 591 in b6486db
And it does not depend on
Therefore, on the CLI, the flag does not remain Since the Also from our docs:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No. I am assuming it should be tied to the internal state of the Yul optimizer. I.e. if the sequence runs, optimized stack allocation should happen unless the user explicitly disables it with Currently it's also disabled when you disable the optimizer as a whole (#15655 (comment)), which is inconsistent with that assumption and IMO should also be changed. At least this is the assumption I've been making in my description in the issue and in what I reviewed here so far. IIRC Daniel said that it's just an internal detail and we should always have it enabled in optimized compilation eventually and deprecate the setting. But I'm actually not entirely sure how indispensable this flag is for unoptimized compilation (or if it matters at all). On one hand we still want to avoid doing anything unnecessary that would be considered an optimization in that mode. On the other hand the reason for always running UnusedPruner in the first place was to help with stack issues and this flag is aimed at that too. I think kept flipping between those two views in the original issue and it's part of the reason why it ended up being this inconsistent. We should discuss it with Daniel and get this straight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It does depend on BTW, this is exactly what I meant when I complained about defaults depending on defaults depending on defaults. Why is this thing this tricky to reason about? It's just a bunch of simple boolean flags :) |
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (details.contains("yulDetails")) | ||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||
if (!settings.runYulOptimiser) | ||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -271,8 +271,6 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const | |
|
||
settings.runYulOptimiser = optimizer.optimizeYul; | ||
if (optimizer.optimizeYul) | ||
// NOTE: Standard JSON disables optimizeStackAllocation by default when yul optimizer is disabled. | ||
// --optimize --no-optimize-yul on the CLI does not have that effect. | ||
Comment on lines
-274
to
-275
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was removed because it is no longer true. |
||
settings.optimizeStackAllocation = true; | ||
|
||
if (optimizer.expectedExecutionsPerDeployment.has_value()) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--metadata --optimize --no-optimize-yul |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
contract C { } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
======= optimizer_enabled_yul_optimizer_disabled/input.sol:C ======= | ||
Metadata: | ||
{"compiler":{"version": "<VERSION REMOVED>"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"optimizer_enabled_yul_optimizer_disabled/input.sol":"C"},"evmVersion":"cancun","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":true,"cse":true,"deduplicate":true,"inliner":true,"jumpdestRemover":true,"orderLiterals":true,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false},"runs":200},"remappings":[]},"sources":{"optimizer_enabled_yul_optimizer_disabled/input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--metadata --optimize --no-optimize-yul --yul-optimizations : |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
contract C { } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
======= optimizer_enabled_yul_optimizer_disabled_empty_sequence/input.sol:C ======= | ||
Metadata: | ||
{"compiler":{"version": "<VERSION REMOVED>"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"optimizer_enabled_yul_optimizer_disabled_empty_sequence/input.sol":"C"},"evmVersion":"cancun","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":true,"cse":true,"deduplicate":true,"inliner":true,"jumpdestRemover":true,"orderLiterals":true,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":","stackAllocation":true}},"runs":200},"remappings":[]},"sources":{"optimizer_enabled_yul_optimizer_disabled_empty_sequence/input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
|
||
contract C { | ||
function f() public pure {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"language": "Solidity", | ||
"sources": { | ||
"A": {"urls": ["standard_metadata_no_optimizer_no_yul_empty_sequence/in.sol"]} | ||
}, | ||
"settings": { | ||
"optimizer": { | ||
"enabled": false, | ||
"details": { | ||
"yul": false, | ||
"yulDetails": { | ||
"optimizerSteps": ":" | ||
} | ||
} | ||
}, | ||
"outputSelection": { | ||
"*": {"*": ["metadata"]} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"contracts": { | ||
"A": { | ||
"C": { | ||
"metadata": "{\"compiler\":{\"version\":\"<VERSION REMOVED>\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":false,\"cse\":false,\"deduplicate\":false,\"inliner\":false,\"jumpdestRemover\":true,\"orderLiterals\":false,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\",\"stackAllocation\":false}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xadb715fb333a8148b6c34d75ffa489e541cf67b9539db523c0948564d2a8cbf1\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://226f3b9be90a1b9329f7d01771fdf38e941b8e64a977945ad40300e921d99051\",\"dweb:/ipfs/QmdB5oEAJrLUJFVPEv5nGQzKRU1MnrqEgEGKupdpDVDzgN\"]}},\"version\":1}" | ||
} | ||
} | ||
}, | ||
"sources": { | ||
"A": { | ||
"id": 0 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
|
||
contract C { | ||
function f() public pure {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"language": "Solidity", | ||
"sources": { | ||
"A": {"urls": ["standard_metadata_optimizer_no_yul/in.sol"]} | ||
}, | ||
"settings": { | ||
"optimizer": { | ||
"enabled": true, | ||
"details": { | ||
"yul": false | ||
} | ||
}, | ||
"outputSelection": { | ||
"*": {"*": ["metadata"]} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"contracts": { | ||
"A": { | ||
"C": { | ||
"metadata": "{\"compiler\":{\"version\":\"<VERSION REMOVED>\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":true,\"cse\":true,\"deduplicate\":true,\"inliner\":true,\"jumpdestRemover\":true,\"orderLiterals\":true,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xadb715fb333a8148b6c34d75ffa489e541cf67b9539db523c0948564d2a8cbf1\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://226f3b9be90a1b9329f7d01771fdf38e941b8e64a977945ad40300e921d99051\",\"dweb:/ipfs/QmdB5oEAJrLUJFVPEv5nGQzKRU1MnrqEgEGKupdpDVDzgN\"]}},\"version\":1}" | ||
} | ||
} | ||
}, | ||
"sources": { | ||
"A": { | ||
"id": 0 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.0; | ||
|
||
contract C { | ||
function f() public pure {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"language": "Solidity", | ||
"sources": { | ||
"A": {"urls": ["standard_metadata_optimizer_no_yul_empty_sequence/in.sol"]} | ||
}, | ||
"settings": { | ||
"optimizer": { | ||
"enabled": true, | ||
"details": { | ||
"yul": false, | ||
"yulDetails": { | ||
"optimizerSteps": ":" | ||
} | ||
} | ||
}, | ||
"outputSelection": { | ||
"*": {"*": ["metadata"]} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"contracts": { | ||
"A": { | ||
"C": { | ||
"metadata": "{\"compiler\":{\"version\":\"<VERSION REMOVED>\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":true,\"cse\":true,\"deduplicate\":true,\"inliner\":true,\"jumpdestRemover\":true,\"orderLiterals\":true,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\",\"stackAllocation\":true}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xadb715fb333a8148b6c34d75ffa489e541cf67b9539db523c0948564d2a8cbf1\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://226f3b9be90a1b9329f7d01771fdf38e941b8e64a977945ad40300e921d99051\",\"dweb:/ipfs/QmdB5oEAJrLUJFVPEv5nGQzKRU1MnrqEgEGKupdpDVDzgN\"]}},\"version\":1}" | ||
} | ||
} | ||
}, | ||
"sources": { | ||
"A": { | ||
"id": 0 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
|
||
======= yul_optimizer_disabled_sequence_empty/input.sol:C ======= | ||
Metadata: | ||
{"compiler":{"version": "<VERSION REMOVED>"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"yul_optimizer_disabled_sequence_empty/input.sol":"C"},"evmVersion":"cancun","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"yul_optimizer_disabled_sequence_empty/input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} | ||
{"compiler":{"version": "<VERSION REMOVED>"},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"yul_optimizer_disabled_sequence_empty/input.sol":"C"},"evmVersion":"cancun","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":","stackAllocation":false}},"runs":200},"remappings":[]},"sources":{"yul_optimizer_disabled_sequence_empty/input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remember that we need changelog entries for it all. I think this one will even require multiple entries because we're fixing multiple bugs.