Skip to content

Commit

Permalink
Lower contract creation using the new operator using zkevm intrinsics
Browse files Browse the repository at this point in the history
This commit enables the generation of $zk_create[2],
$zk_data{size|offset|copy} zkevm intrinsics when lowering the new
operator
  • Loading branch information
abinavpp committed Mar 24, 2023
1 parent ff69adb commit ff80408
Show file tree
Hide file tree
Showing 21 changed files with 175 additions and 37 deletions.
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() const
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 @@ -188,6 +194,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 @@ -216,6 +224,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 @@ -277,6 +287,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 @@ -353,9 +365,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 @@ -162,6 +162,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 @@ -309,6 +312,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 @@ -175,6 +175,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 @@ -708,7 +708,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 @@ -730,9 +730,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 @@ -1483,14 +1483,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 @@ -116,6 +117,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 @@ -249,6 +249,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

0 comments on commit ff80408

Please sign in to comment.