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

Lower contract creation using the new operator to $zk_create intrinsics #7

Draft
wants to merge 1 commit into
base: 0.8.19
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,14 @@ LinkerObject const& Assembly::assemble() const
dataRef.insert(make_pair(h256(i.data()), ret.bytecode.size()));
ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef);
break;
case ZKEVMPushSub:
case PushSub:
assertThrow(i.data() <= numeric_limits<size_t>::max(), AssemblyException, "");
ret.bytecode.push_back(dataRefPush);
subRef.insert(make_pair(static_cast<size_t>(i.data()), ret.bytecode.size()));
ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef);
break;
case ZKEVMPushSubSize:
case PushSubSize:
{
assertThrow(i.data() <= numeric_limits<size_t>::max(), AssemblyException, "");
Expand Down
9 changes: 9 additions & 0 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ class Assembly
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; }
/// zkevm version of appendSubroutine():
AssemblyItem zkevmAppendSubroutine(AssemblyPointer const& _assembly)
{
m_subs.push_back(_assembly);
auto sub = AssemblyItem(ZKEVMPushSub, m_subs.size() - 1);
append(AssemblyItem(ZKEVMPushSubSize, size_t(sub.data())));
return sub;
}

void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); }
/// Pushes the offset of the subroutine.
void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
Expand Down
18 changes: 18 additions & 0 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ pair<string, string> AssemblyItem::nameAndData(langutil::EVMVersion _evmVersion)
return {"PUSH [$]", toString(util::h256(data()))};
case PushSubSize:
return {"PUSH #[$]", toString(util::h256(data()))};
case ZKEVMPushSub:
return {"$ZK_PUSH [$]", toString(util::h256(data()))};
case ZKEVMPushSubSize:
return {"$ZK_PUSH #[$]", toString(util::h256(data()))};
case PushProgramSize:
return {"PUSHSIZE", ""};
case PushLibraryAddress:
Expand Down Expand Up @@ -126,11 +130,13 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
case Push:
return 1 + max<size_t>(1, numberEncodingSize(data()));
case PushSubSize:
case ZKEVMPushSubSize:
case PushProgramSize:
return 1 + 4; // worst case: a 16MB program
case PushTag:
case PushData:
case PushSub:
case ZKEVMPushSub:
return 1 + _addressLength;
case PushLibraryAddress:
case PushDeployTimeAddress:
Expand Down Expand Up @@ -192,6 +198,8 @@ size_t AssemblyItem::returnValues() const
case PushData:
case PushSub:
case PushSubSize:
case ZKEVMPushSub:
case ZKEVMPushSubSize:
case PushProgramSize:
case PushLibraryAddress:
case PushImmutable:
Expand Down Expand Up @@ -220,6 +228,8 @@ bool AssemblyItem::canBeFunctional() const
case PushData:
case PushSub:
case PushSubSize:
case ZKEVMPushSub:
case ZKEVMPushSubSize:
case PushProgramSize:
case PushLibraryAddress:
case PushDeployTimeAddress:
Expand Down Expand Up @@ -281,6 +291,8 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
break;
case PushSub:
case PushSubSize:
case ZKEVMPushSub:
case ZKEVMPushSubSize:
{
vector<string> subPathComponents;
for (size_t subPathComponentId: _assembly.decodeSubPath(static_cast<size_t>(data())))
Expand Down Expand Up @@ -358,9 +370,15 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
case PushSub:
_out << " PushSub " << hex << static_cast<size_t>(_item.data()) << dec;
break;
case ZKEVMPushSub:
_out << " ZKEVMPushSub " << hex << static_cast<size_t>(_item.data()) << dec;
break;
case PushSubSize:
_out << " PushSubSize " << hex << static_cast<size_t>(_item.data()) << dec;
break;
case ZKEVMPushSubSize:
_out << " ZKEVMPushSubSize " << hex << static_cast<size_t>(_item.data()) << dec;
break;
case PushProgramSize:
_out << " PushProgramSize";
break;
Expand Down
2 changes: 2 additions & 0 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ enum AssemblyItemType
PushTag,
PushSub,
PushSubSize,
ZKEVMPushSub,
ZKEVMPushSubSize,
PushProgramSize,
Tag,
PushData,
Expand Down
2 changes: 2 additions & 0 deletions libevmasm/GasMeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case PushData:
case PushSub:
case PushSubSize:
case ZKEVMPushSub:
case ZKEVMPushSubSize:
case PushProgramSize:
case PushLibraryAddress:
case PushDeployTimeAddress:
Expand Down
6 changes: 6 additions & 0 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "LOG2", Instruction::LOG2 },
{ "LOG3", Instruction::LOG3 },
{ "LOG4", Instruction::LOG4 },
{ "$ZK_CREATE", Instruction::ZK_CREATE },
{ "$ZK_CREATE2", Instruction::ZK_CREATE2 },
{ "$ZK_DATACOPY", Instruction::ZK_DATACOPY },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
Expand Down Expand Up @@ -311,6 +314,9 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } },
{ Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } },
{ Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::Special } },
{ Instruction::ZK_CREATE, {"$ZK_CREATE", 0, 3, 1, true, Tier::Special } },
{ Instruction::ZK_CREATE2, {"$ZK_CREATE2", 0, 4, 1, true, Tier::Special } },
{ Instruction::ZK_DATACOPY, {"$ZK_DATACOPY", 0, 3, 0, true, Tier::Special } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } },
{ Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } },
Expand Down
4 changes: 4 additions & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ enum class Instruction: uint8_t
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.

ZK_CREATE = 0xb0, ///< ZKEVM version of CREATE
ZK_CREATE2, ///< ZKEVM version of CREATE2
ZK_DATACOPY, ///< ZKEVM version of yul's datacopy instruction

CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
Expand Down
5 changes: 5 additions & 0 deletions libsolidity/codegen/CompilerContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ class CompilerContext
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
/// zkevm version of addSubroutine():
evmasm::AssemblyItem zkevmAddSubroutine(evmasm::AssemblyPointer const& _assembly)
{
return m_asm->zkevmAppendSubroutine(_assembly);
}
/// Pushes the size of the subroutine.
void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
/// Pushes the offset of the subroutine.
Expand Down
12 changes: 7 additions & 5 deletions libsolidity/codegen/CompilerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1507,24 +1507,26 @@ void CompilerUtils::computeHashStatic()
m_context << u256(32) << u256(0) << Instruction::KECCAK256;
}

void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation)
void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation, bool _zkevm)
{
string which = _creation ? "Creation" : "Runtime";
m_context.callLowLevelFunction(
"$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(),
1,
1,
[&contract, _creation](CompilerContext& _context)
[&contract, _creation, _zkevm](CompilerContext& _context)
{
// copy the contract's code into memory
shared_ptr<evmasm::Assembly> assembly =
_creation ?
_context.compiledContract(contract) :
_context.compiledContractRuntime(contract);
// pushes size
auto subroutine = _context.addSubroutine(assembly);
_context << Instruction::DUP1 << subroutine;
_context << Instruction::DUP4 << Instruction::CODECOPY;
if (_zkevm)
_context << Instruction::DUP1 << _context.zkevmAddSubroutine(assembly);
else
_context << Instruction::DUP1 << _context.addSubroutine(assembly);
_context << Instruction::DUP4 << (_zkevm ? Instruction::ZK_DATACOPY : Instruction::CODECOPY);
_context << Instruction::ADD;
}
);
Expand Down
3 changes: 2 additions & 1 deletion libsolidity/codegen/CompilerUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,9 @@ class CompilerUtils
/// Stack pre: Memory position
/// Stack post: Updated memory position
/// @param creation if true, copies creation code, if false copies runtime code.
/// @param _zkevem enables zkevm specific lowering
/// @note the contract has to be compiled already, so beware of cyclic dependencies!
void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode);
void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode, bool _zkevm = false);

/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
Expand Down
6 changes: 3 additions & 3 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
ContractDefinition const* contract =
&dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
utils().fetchFreeMemoryPointer();
utils().copyContractCodeToMemory(*contract, true);
utils().copyContractCodeToMemory(*contract, true, /*zkevm=*/true);
utils().abiEncode(argumentTypes, function.parameterTypes());
// now on stack: [salt], [value], memory_end_ptr
// need: [salt], size, offset, value
Expand All @@ -775,9 +775,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)

// now: [salt], [value], [salt], size, offset, value
if (function.saltSet())
m_context << Instruction::CREATE2;
m_context << Instruction::ZK_CREATE2;
else
m_context << Instruction::CREATE;
m_context << Instruction::ZK_CREATE;

// now: [salt], [value], address

Expand Down
8 changes: 4 additions & 4 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1532,14 +1532,14 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
m_context.subObjectsCreated().insert(contract);

Whiskers t(R"(let <memPos> := <allocateUnbounded>()
let <memEnd> := add(<memPos>, datasize("<object>"))
let <memEnd> := add(<memPos>, $zk_datasize("<object>"))
if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { <panic>() }
datacopy(<memPos>, dataoffset("<object>"), datasize("<object>"))
$zk_datacopy(<memPos>, $zk_dataoffset("<object>"), $zk_datasize("<object>"))
<memEnd> := <abiEncode>(<memEnd><constructorParams>)
<?saltSet>
let <address> := create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>)
let <address> := $zk_create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>)
<!saltSet>
let <address> := create(<value>, <memPos>, sub(<memEnd>, <memPos>))
let <address> := $zk_create(<value>, <memPos>, sub(<memEnd>, <memPos>))
</saltSet>
<?isTryCall>
let <success> := iszero(iszero(<address>))
Expand Down
15 changes: 15 additions & 0 deletions libyul/backends/evm/AbstractAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#pragma once

#include "liblangutil/Exceptions.h"
#include <libyul/ASTForward.h>

#include <libsolutil/Common.h>
Expand Down Expand Up @@ -117,6 +118,20 @@ class AbstractAssembly

/// Mark this assembly as invalid. Any attempt to request bytecode from it should throw.
virtual void markAsInvalid() = 0;

/// zkevm version of appendDataOffset()
virtual void appendZKEVMDataOffset(std::vector<SubID> const& _subPath)
{
(void) _subPath;
solUnimplemented("ZKEVM dataoffset lowering not implemented");
}

/// zkevm version of appendDataSize()
virtual void appendZKEVMDataSize(std::vector<SubID> const& _subPath)
{
(void) _subPath;
solUnimplemented("ZKEVM datasize lowering not implemented");
}
};

enum class IdentifierContext { LValue, RValue, VariableDeclaration, NonExternal };
Expand Down
43 changes: 43 additions & 0 deletions libyul/backends/evm/EVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,49 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
})
);

builtins.emplace(createFunction("$zk_datasize", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext& _context
) {
yulAssert(_context.currentObject, "No object available.");
yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front();
YulString dataName = std::get<Literal>(arg).value;
if (_context.currentObject->name == dataName)
_assembly.appendAssemblySize();
else
{
vector<size_t> subIdPath =
_context.subIDs.count(dataName) == 0 ?
_context.currentObject->pathToSubObject(dataName) :
vector<size_t>{_context.subIDs.at(dataName)};
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
_assembly.appendZKEVMDataSize(subIdPath);
}
}));
builtins.emplace(createFunction("$zk_dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext& _context
) {
yulAssert(_context.currentObject, "No object available.");
yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front();
YulString dataName = std::get<Literal>(arg).value;
if (_context.currentObject->name == dataName)
_assembly.appendConstant(0);
else
{
vector<size_t> subIdPath =
_context.subIDs.count(dataName) == 0 ?
_context.currentObject->pathToSubObject(dataName) :
vector<size_t>{_context.subIDs.at(dataName)};
yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
_assembly.appendZKEVMDataOffset(subIdPath);
}
}));

builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call,
AbstractAssembly& _assembly,
Expand Down
24 changes: 24 additions & 0 deletions libyul/backends/evm/EthAssemblyAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ void EthAssemblyAdapter::appendDataOffset(vector<AbstractAssembly::SubID> const&
m_assembly.pushSubroutineOffset(m_assembly.encodeSubPath(_subPath));
}

void EthAssemblyAdapter::appendZKEVMDataOffset(vector<AbstractAssembly::SubID> const& _subPath)
{
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
{
yulAssert(_subPath.size() == 1, "");
m_assembly << evmasm::AssemblyItem(evmasm::PushData, it->second);
return;
}

m_assembly.append(evmasm::AssemblyItem(evmasm::ZKEVMPushSub, m_assembly.encodeSubPath(_subPath)));
}

void EthAssemblyAdapter::appendDataSize(vector<AbstractAssembly::SubID> const& _subPath)
{
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
Expand All @@ -153,6 +165,18 @@ void EthAssemblyAdapter::appendDataSize(vector<AbstractAssembly::SubID> const& _
m_assembly.pushSubroutineSize(m_assembly.encodeSubPath(_subPath));
}

void EthAssemblyAdapter::appendZKEVMDataSize(vector<AbstractAssembly::SubID> const& _subPath)
{
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
{
yulAssert(_subPath.size() == 1, "");
m_assembly << u256(m_assembly.data(h256(it->second)).size());
return;
}

m_assembly.append(evmasm::AssemblyItem(evmasm::ZKEVMPushSubSize, m_assembly.encodeSubPath(_subPath)));
}

AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data)
{
evmasm::AssemblyItem pushData = m_assembly.newData(_data);
Expand Down
2 changes: 2 additions & 0 deletions libyul/backends/evm/EthAssemblyAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class EthAssemblyAdapter: public AbstractAssembly
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = {}) override;
void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override;
void appendZKEVMDataOffset(std::vector<SubID> const& _subPath) override;
void appendZKEVMDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

void appendToAuxiliaryData(bytes const& _data) override;
Expand Down
Loading