Properly read and clean value types read from storage.

This commit is contained in:
chriseth 2019-03-18 11:39:15 +01:00
parent dce27bb452
commit 68d4a85575
3 changed files with 106 additions and 3 deletions

View File

@ -346,6 +346,39 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
});
}
string ABIFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
{
solAssert(_type.isValueType(), "");
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier();
return createFunction(functionName, [&] {
Whiskers templ(R"(
function <functionName>(value) -> cleaned {
<body>
}
)");
templ("functionName", functionName);
unsigned storageBytes = _type.storageBytes();
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
if (type->isSigned() && storageBytes != 32)
{
templ("body", "cleaned := signextend(" + to_string(storageBytes - 1) + ", value)");
return templ.render();
}
if (storageBytes == 32)
templ("body", "cleaned := value");
else if (_type.category() == Type::Category::Function || _type.category() == Type::Category::FixedBytes)
templ("body", "cleaned := " + m_utils.shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
else
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
return templ.render();
});
}
string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
{
string functionName =
@ -778,6 +811,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
subOptions.encodeFunctionFromStack = false;
subOptions.padded = true;
templ("encodeToMemoryFun", abiEncodeAndReturnUpdatedPosFunction(*_from.baseType(), *_to.baseType(), subOptions));
// There is only one element per slot, so sload suffices, no need to use "readFromStorage".
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", m_utils.nextArrayElementFunction(_from));
return templ.render();
@ -900,7 +934,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
{
let data := sload(srcPtr)
<#items>
<encodeToMemoryFun>(<shiftRightFun>(data), pos)
<encodeToMemoryFun>(<extractFromSlot>(data), pos)
pos := add(pos, <elementEncodedSize>)
</items>
srcPtr := add(srcPtr, 1)
@ -934,7 +968,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
for (size_t i = 0; i < itemsPerSlot; ++i)
items[i]["shiftRightFun"] = m_utils.shiftRightFunction(i * storageBytes * 8);
items[i]["extractFromSlot"] = extractFromStorageValue(*_from.baseType(), i * storageBytes, false);
templ("items", items);
return templ.render();
}
@ -1020,7 +1054,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))";
previousSlotOffset = storageSlotOffset;
}
members.back()["retrieveValue"] = m_utils.shiftRightFunction(intraSlotOffset * 8) + "(slotValue)";
members.back()["retrieveValue"] = extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)";
}
else
{
@ -1509,6 +1543,52 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
});
}
string ABIFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
{
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName =
"read_from_storage_" +
string(_splitFunctionTypes ? "split_" : "") +
"offset_" +
to_string(_offset) +
_type.identifier();
return m_functionCollector->createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, "");
return Whiskers(R"(
function <functionName>(slot) -> value {
value := <extract>(sload(slot))
}
)")
("functionName", functionName)
("extract", extractFromStorageValue(_type, _offset, false))
.render();
});
}
string ABIFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes)
{
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName =
"extract_from_storage_value_" +
string(_splitFunctionTypes ? "split_" : "") +
"offset_" +
to_string(_offset) +
_type.identifier();
return m_functionCollector->createFunction(functionName, [&] {
return Whiskers(R"(
function <functionName>(slot_value) -> value {
value := <cleanupStorage>(<shr>(slot_value))
}
)")
("functionName", functionName)
("shr", m_utils.shiftRightFunction(_offset * 8))
("cleanupStorage", cleanupFromStorageFunction(_type, false))
.render();
});
}
string ABIFunctions::arrayStoreLengthForEncodingFunction(ArrayType const& _type, EncodingOptions const& _options)
{
string functionName = "array_storeLengthForEncoding_" + _type.identifier() + _options.toFunctionNameSuffix();

View File

@ -134,6 +134,15 @@ private:
/// otherwise an assertion failure.
std::string cleanupFunction(Type const& _type, bool _revertOnFailure = false);
/// Performs cleanup after reading from a potentially compressed storage slot.
/// The function does not perform any validation, it just masks or sign-extends
/// higher order bytes or left-aligns (in case of bytesNN).
/// The storage cleanup expects the value to be right-aligned with potentially
/// dirty higher order bytes.
/// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable.
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
/// @returns the name of the function that converts a value of type @a _from
/// to a value of type @a _to. The resulting vale is guaranteed to be in range
/// (i.e. "clean"). Asserts on failure.
@ -233,6 +242,19 @@ private:
/// Part of @a abiDecodingFunction for array types.
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack);
/// @returns a function that reads a value type from storage.
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
/// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable.
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
/// @returns a function that extracts a value type from storage slot that has been
/// retrieved already.
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
/// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable.
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
/// @returns the name of a function used during encoding that stores the length
/// if the array is dynamically sized (and the options do not request in-place encoding).
/// It returns the new encoding position.

View File

@ -68,6 +68,7 @@ public:
std::string shiftLeftFunction(size_t _numBits);
std::string shiftRightFunction(size_t _numBits);
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
std::string roundUpFunction();