问题描述
使用remix 和 solcjs编译出来的bytecode不同(最后32+2字节不同)
源码分析
libsolidity/interface/CompilerStack.cpp
void CompilerStack::compileContract(
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
)
{
if (
_compiledContracts.count(&_contract) ||
!_contract.annotation().unimplementedFunctions.empty() ||
!_contract.constructorIsPublic()
)
return;
for (auto const* dependency: _contract.annotation().contractDependencies)
compileContract(*dependency, _compiledContracts);
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
// 此处创建出来的metadata包含了很多不确定的item, 再进行swarmHash操作, 结果自然不同
string metadata = createMetadata(compiledContract);
std::cout <<"metadata:" << __FILE__ << __LINE__ << metadata << std::endl;
// 这里是固定的前缀 65627a7a72305820 加上 swarmHash(metadata)的结果
bytes cborEncodedHash =
// CBOR-encoding of the key "bzzr0"
bytes{0x65, 'b', 'z', 'z', 'r', '0'}+
// CBOR-encoding of the hash
bytes{0x58, 0x20} + dev::swarmHash(metadata).asBytes();
std::cout <<__FILE__ << __LINE__ <<" cborEncodedHash:" << toHex(cborEncodedHash) << std::endl;
bytes cborEncodedMetadata;
if (onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures))
cborEncodedMetadata =
// CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)}
bytes{0xa1} +
cborEncodedHash;
else
cborEncodedMetadata =
// CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true}
bytes{0xa2} +
cborEncodedHash +
bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5};
std::cout << __FILE__ << __LINE__ << " cborEncodedMetadata:" << toHex(cborEncodedMetadata) << std::endl;
solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
// 16-bit big endian length
cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
std::cout << __FILE__ << __LINE__ << " cborEncodedMetadata bigEndian:" << toHex(cborEncodedMetadata) << std::endl;
compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
compiledContract.compiler = compiler;
try
{
compiledContract.object = compiler->assembledObject();
}
catch(eth::OptimizerException const&)
{
solAssert(false, "Assembly optimizer exception for bytecode");
}
catch(eth::AssemblyException const&)
{
solAssert(false, "Assembly exception for bytecode");
}
try
{
compiledContract.runtimeObject = compiler->runtimeObject();
}
catch(eth::OptimizerException const&)
{
solAssert(false, "Assembly optimizer exception for deployed bytecode");
}
catch(eth::AssemblyException const&)
{
solAssert(false, "Assembly exception for deployed bytecode");
}
compiledContract.metadata = metadata;
_compiledContracts[compiledContract.contract] = &compiler->assembly();
try
{
if (!_contract.isLibrary())
{
Compiler cloneCompiler(m_optimize, m_optimizeRuns);
cloneCompiler.compileClone(_contract, _compiledContracts);
compiledContract.cloneObject = cloneCompiler.assembledObject();
}
}
catch (eth::AssemblyException const&)
{
// In some cases (if the constructor requests a runtime function), it is not
// possible to compile the clone.
// TODO: Report error / warning
}
}
void Compiler::compileContract(
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts,
bytes const& _metadata
)
{
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
runtimeCompiler.compileContract(_contract, _contracts);
m_runtimeContext.appendAuxiliaryData(_metadata); //将一些metadata附加在尾部
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
m_context.optimise(m_optimize, m_optimizeRuns);
}
string CompilerStack::createMetadata(Contract const& _contract) const
{
Json::Value meta;
meta["version"] = 1;
meta["language"] = "Solidity";
meta["compiler"]["version"] = VersionStringStrict;
/// All the source files (including self), which should be included in the metadata.
set<string> referencedSources;
referencedSources.insert(_contract.contract->sourceUnit().annotation().path);
for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true))
referencedSources.insert(sourceUnit->annotation().path);
meta["sources"] = Json::objectValue;
for (auto const& s: m_sources)
{
if (!referencedSources.count(s.first))
continue;
solAssert(s.second.scanner, "Scanner not available");
meta["sources"][s.first]["keccak256"] =
"0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes());
if (m_metadataLiteralSources)
meta["sources"][s.first]["content"] = s.second.scanner->source();
else
{
meta["sources"][s.first]["urls"] = Json::arrayValue;
meta["sources"][s.first]["urls"].append(
"bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) // 不确定的因素
);
}
}
meta["settings"]["optimizer"]["enabled"] = m_optimize;
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
_contract.contract->annotation().canonicalName;
meta["settings"]["remappings"] = Json::arrayValue;
set<string> remappings;
for (auto const& r: m_remappings)
remappings.insert(r.context + ":" + r.prefix + "=" + r.target);
for (auto const& r: remappings)
meta["settings"]["remappings"].append(r);
meta["settings"]["libraries"] = Json::objectValue;
for (auto const& library: m_libraries)
meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes());
meta["output"]["abi"] = contractABI(_contract);
meta["output"]["userdoc"] = natspecUser(_contract);
meta["output"]["devdoc"] = natspecDev(_contract);
return jsonCompactPrint(meta);
}
template <class T>
inline bytes toCompactBigEndian(T _val, unsigned _min = 0)
{
static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift
int i = 0;
for (T v = _val; v; ++i, v >>= 8) {}
bytes ret(std::max<unsigned>(_min, i), 0);
toBigEndian(_val, ret);
return ret;
}
// Big-endian to/from host endian conversion functions.
/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection.
/// The size of the collection object will be unchanged. If it is too small, it will not represent the
/// value properly, if too big then the additional elements will be zeroed out.
/// @a Out will typically be either std::string or bytes.
/// @a T will typically by unsigned, u160, u256 or bigint.
template <class T, class Out>
inline void toBigEndian(T _val, Out& o_out)
{
static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift
for (auto i = o_out.size(); i != 0; _val >>= 8, i--)
{
T v = _val & (T)0xff;
o_out[i - 1] = (typename Out::value_type)(uint8_t)v;
}
}
测试
abc.sol
pragma solidity ^0.4.18;
contract AJCToken {
}
./solc --bin abc.sol
metadata:/home/yqq/mine/solidity/ethereum-solidity/libsolidity/interface/CompilerStack.cpp683{"compiler":{"version":"0.4.20-develop.2020.11.17+commit.3155dd80.mod"},"language":"Solidity","output":{"abi":[],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"abc.sol":"AJCToken"},"libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"abc.sol":{"keccak256":"0xe808e3b5f0e032c4f1a347995c9e634ddc96af6e2a12997a7f7bd8122c9bef63","urls":["bzzr://924d33467bd8f8751e740fb2b35990532ad31872f46a0aa456136342e9464a00"]}},"version":1}
/home/yqq/mine/solidity/ethereum-solidity/libsolidity/interface/CompilerStack.cpp690 cborEncodedHash:65627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed437
/home/yqq/mine/solidity/ethereum-solidity/libsolidity/interface/CompilerStack.cpp704 cborEncodedMetadata:a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed437
/home/yqq/mine/solidity/ethereum-solidity/libsolidity/interface/CompilerStack.cpp708 cborEncodedMetadata bigEndian:a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode: 6060604052600080fd00
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData: 6060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:6060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode: 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData: 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode: 36600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData: 36600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:36600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp661ret.bytecode: 60606040523415600e57600080fd5b603380601b6000396000f30036600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp663ret.bytecode + m_auxiliaryData: 60606040523415600e57600080fd5b603380601b6000396000f30036600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
/home/yqq/mine/solidity/ethereum-solidity/libevmasm/Assembly.cpp670ret.bytecode bigendian:60606040523415600e57600080fd5b603380601b6000396000f30036600080376020600036600073cafecafecafecafecafecafecafecafecafecafe6102c65a03f41515602d57fe5b60206000f3
Warning: This is a pre-release compiler version, please do not use it in production.
======= abc.sol:AJCToken =======
Binary:
60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
分析输出结果:
60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed4370029
第一部分: 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00
第二部分:
a165627a7a72305820 // 固定前缀
c883c4be5a32d6c09e2b5d3a2f49d69f1e5e5012d043a8a11d2fcf2db3fed437 //swarmHash(metadata)
0029 //经过toCompactBigEndian之后附加的内容
结论
最后的不同的字节是metadata相关信息, 不属于代码的bytecode