Create and output clone contracts.

This commit is contained in:
chriseth 2015-07-31 19:23:31 +02:00
parent f2f1e03007
commit 943fd623e1
4 changed files with 99 additions and 20 deletions

View File

@ -25,6 +25,7 @@
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmcore/Params.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h> #include <libsolidity/CompilerUtils.h>
@ -53,31 +54,45 @@ void Compiler::compileContract(ContractDefinition const& _contract,
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
{ {
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract); CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
initializeContext(_contract, _contracts); initializeContext(_contract, _contracts);
appendFunctionSelector(_contract); appendFunctionSelector(_contract);
set<Declaration const*> functions = m_context.getFunctionsWithoutCode(); appendFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
{
m_context.setStackOffset(0);
function->accept(*this);
}
functions = m_context.getFunctionsWithoutCode();
}
} }
// Swap the runtime context with the creation-time context // Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext); swap(m_context, m_runtimeContext);
CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract); CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
initializeContext(_contract, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize) if (m_optimize)
m_context.optimise(m_optimizeRuns); m_context.optimise(m_optimizeRuns);
} }
void Compiler::compileClone(
ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts
)
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _contracts);
appendInitAndConstructorCode(_contract);
//@todo determine largest return size of all runtime functions
eth::AssemblyItem runtimeSub = m_context.addSubroutine(getCloneRuntime());
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
m_runtimeSub = size_t(runtimeSub.data());
// stack contains sub size
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
appendFunctionsWithoutCode();
if (m_optimize)
m_context.optimise(m_optimizeRuns);
}
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
{ {
return m_runtimeContext.getFunctionEntryLabelIfExists(_function); return m_runtimeContext.getFunctionEntryLabelIfExists(_function);
@ -86,13 +101,14 @@ eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _fun
void Compiler::initializeContext(ContractDefinition const& _contract, void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
CompilerUtils(m_context).initialiseFreeMemoryPointer();
m_context.setCompiledContracts(_contracts); m_context.setCompiledContracts(_contracts);
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts()); m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
registerStateVariables(_contract); registerStateVariables(_contract);
m_context.resetVisitedNodes(&_contract); m_context.resetVisitedNodes(&_contract);
} }
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
{ {
// Determine the arguments that are used for the base constructors. // Determine the arguments that are used for the base constructors.
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
@ -126,22 +142,22 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
appendConstructor(*constructor); appendConstructor(*constructor);
else if (auto c = m_context.getNextConstructor(_contract)) else if (auto c = m_context.getNextConstructor(_contract))
appendBaseConstructor(*c); appendBaseConstructor(*c);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
appendInitAndConstructorCode(_contract);
eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly()); eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly());
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), ""); solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
m_runtimeSub = size_t(runtimeSub.data()); m_runtimeSub = size_t(runtimeSub.data());
// stack contains sub size // stack contains sub size
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY; m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN; m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to include the functions again because of absolute jump labels // note that we have to include the functions again because of absolute jump labels
set<Declaration const*> functions = m_context.getFunctionsWithoutCode(); appendFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
} }
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
@ -618,6 +634,20 @@ bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
return true; return true;
} }
void Compiler::appendFunctionsWithoutCode()
{
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
{
m_context.setStackOffset(0);
function->accept(*this);
}
functions = m_context.getFunctionsWithoutCode();
}
}
void Compiler::appendModifierOrFunctionCode() void Compiler::appendModifierOrFunctionCode()
{ {
solAssert(m_currentFunction, ""); solAssert(m_currentFunction, "");
@ -674,3 +704,21 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons
if (_targetType) if (_targetType)
CompilerUtils(m_context).convertType(*_expression.getType(), *_targetType); CompilerUtils(m_context).convertType(*_expression.getType(), *_targetType);
} }
eth::Assembly Compiler::getCloneRuntime()
{
eth::Assembly a;
a << eth::Instruction::CALLDATASIZE;
a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY;
//@todo adjust for larger return values, make this dynamic.
a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE;
a << u256(0) << eth::Instruction::DUP1;
// this is the address which has to be substituted by the linker.
//@todo implement as special "marker" AssemblyItem.
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
a << u256(eth::c_callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB;
a << eth::Instruction::CALLCODE;
//@todo adjust for larger return values, make this dynamic.
a << u256(0x20) << u256(0) << eth::Instruction::RETURN;
return a;
}

View File

@ -44,6 +44,12 @@ public:
void compileContract(ContractDefinition const& _contract, void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given
/// contract at runtime, but contains the full creation-time code.
void compileClone(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts
);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); } bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
@ -68,6 +74,8 @@ private:
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context /// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. Adds the constructor code. /// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
/// Appends state variable initialisation and constructor code.
void appendInitAndConstructorCode(ContractDefinition const& _contract);
void appendBaseConstructor(FunctionDefinition const& _constructor); void appendBaseConstructor(FunctionDefinition const& _constructor);
void appendConstructor(FunctionDefinition const& _constructor); void appendConstructor(FunctionDefinition const& _constructor);
void appendFunctionSelector(ContractDefinition const& _contract); void appendFunctionSelector(ContractDefinition const& _contract);
@ -103,6 +111,9 @@ private:
virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override;
virtual bool visit(PlaceholderStatement const&) override; virtual bool visit(PlaceholderStatement const&) override;
/// Repeatedly visits all function which are referenced but which are not compiled yet.
void appendFunctionsWithoutCode();
/// Appends one layer of function modifier code of the current function, or the function /// Appends one layer of function modifier code of the current function, or the function
/// body itself if the last modifier was reached. /// body itself if the last modifier was reached.
void appendModifierOrFunctionCode(); void appendModifierOrFunctionCode();
@ -110,6 +121,9 @@ private:
void appendStackVariableInitialisation(VariableDeclaration const& _variable); void appendStackVariableInitialisation(VariableDeclaration const& _variable);
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
/// @returns the runtime assembly for clone contracts.
static eth::Assembly getCloneRuntime();
bool const m_optimize; bool const m_optimize;
unsigned const m_optimizeRuns; unsigned const m_optimizeRuns;
CompilerContext m_context; CompilerContext m_context;

View File

@ -166,7 +166,13 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);
compiler = make_shared<Compiler>(_optimize, _runs);
compiler->compileContract(*contract, contractBytecode);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode; contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
Compiler cloneCompiler(_optimize, _runs);
cloneCompiler.compileClone(*contract, contractBytecode);
compiledContract.cloneBytecode = cloneCompiler.getAssembledBytecode();
} }
} }
@ -199,6 +205,11 @@ bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) cons
return getContract(_contractName).runtimeBytecode; return getContract(_contractName).runtimeBytecode;
} }
bytes const& CompilerStack::getCloneBytecode(string const& _contractName) const
{
return getContract(_contractName).cloneBytecode;
}
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
{ {
return dev::sha3(getRuntimeBytecode(_contractName)); return dev::sha3(getRuntimeBytecode(_contractName));

View File

@ -99,6 +99,11 @@ public:
bytes const& getBytecode(std::string const& _contractName = "") const; bytes const& getBytecode(std::string const& _contractName = "") const;
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
/// substituted by the actual address. Note that this sequence starts end ends in three X
/// characters but can contain anything in between.
bytes const& getCloneBytecode(std::string const& _contractName = "") const;
/// @returns normal contract assembly items /// @returns normal contract assembly items
eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const; eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items /// @returns runtime contract assembly items
@ -167,6 +172,7 @@ private:
std::shared_ptr<Compiler> compiler; std::shared_ptr<Compiler> compiler;
bytes bytecode; bytes bytecode;
bytes runtimeBytecode; bytes runtimeBytecode;
bytes cloneBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler; std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface; mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface; mutable std::unique_ptr<std::string const> solidityInterface;