Browse Source

- array validation in QML

- compliant with multi dim/dynamic array
cl-refactor
yann300 10 years ago
parent
commit
9ddf68b7f4
  1. 116
      mix/ContractCallDataEncoder.cpp
  2. 5
      mix/ContractCallDataEncoder.h
  3. 113
      mix/qml/js/InputValidator.js

116
mix/ContractCallDataEncoder.cpp

@ -20,15 +20,19 @@
* Ethereum IDE client.
*/
#include <QDebug>
#include <vector>
#include <QMap>
#include <QStringList>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <libethcore/CommonJS.h>
#include <libsolidity/AST.h>
#include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
#include "QFunctionDefinition.h"
#include "ContractCallDataEncoder.h"
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::mix;
@ -38,13 +42,18 @@ bytes ContractCallDataEncoder::encodedData()
bytes r(m_encodedData);
size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned
//apply offsets
for (auto const& p: m_offsetMap)
for (auto const& p: m_dynamicOffsetMap)
{
vector_ref<byte> offsetRef(m_dynamicData.data() + p.first, 32);
toBigEndian<size_t, vector_ref<byte>>(p.second + headerSize, offsetRef); //add header size minus signature hash
}
for (auto const& p: m_staticOffsetMap)
{
vector_ref<byte> offsetRef(r.data() + p.first, 32);
toBigEndian<size_t, vector_ref<byte>>(p.second + headerSize, offsetRef); //add header size minus signature hash
}
r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end());
if (m_dynamicData.size() > 0)
r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end());
return r;
}
@ -54,54 +63,95 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end());
}
void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, QList<int> _dim, SolidityType const& _type, bytes& _content)
{
size_t offsetStart = _content.size();
if (_dim[0] == -1)
{
bytes count = bytes(32);
toBigEndian((u256)_array.size(), count);
_content += count; //reserved space for count
}
_dim.pop_front();
int k = 0;
for (QJsonValue const& c: _array)
{
if (c.isArray())
{
if (_dim[0] == -1)
{
m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32,
m_dynamicData.size() + _content.size()));
}
encodeArray(c.toArray(), _dim, _type, _content);
}
else
{
// encode single item
if (c.isDouble())
encodeSingleItem(QString::number(c.toDouble()), _type, _content);
else if (c.isString())
encodeSingleItem(c.toString(), _type, _content);
}
k++;
}
}
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type)
{
u256 count = 1;
QStringList strList;
vector<int> dim;
if (_type.array)
{
if (_data.type() == QVariant::String)
strList = _data.toString().split(",", QString::SkipEmptyParts); //TODO: proper parsing
else
strList = _data.toStringList();
count = strList.count();
QList<int> dim = extractDimension(_type. name);
bytes content;
QJsonDocument jsonResponse = QJsonDocument::fromJson(_data.toString().toUtf8());
QJsonArray jsonObject = jsonResponse.array();
size_t size = m_encodedData.size();
if (dim[0] == -1)
{
m_encodedData += bytes(32); // reserve space for offset
m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size()));
}
encodeArray(jsonObject, dim, _type, content);
if (!_type.dynamicSize)
m_encodedData.insert(m_encodedData.end(), content.begin(), content.end());
else
m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end());
}
else
strList.append(_data.toString());
if (_type.dynamicSize)
else if (_type.dynamicSize && _type.type == SolidityType::Type::Bytes)
{
bytes empty(32);
size_t sizePos = m_dynamicData.size();
m_dynamicData += empty; //reserve space for count
if (_type.type == SolidityType::Type::Bytes)
count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
else
{
count = strList.count();
for (auto const& item: strList)
encodeSingleItem(item, _type, m_dynamicData);
}
u256 count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
vector_ref<byte> sizeRef(m_dynamicData.data() + sizePos, 32);
toBigEndian(count, sizeRef);
m_offsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos));
m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos));
m_encodedData += empty; //reserve space for offset
}
else
encodeSingleItem(_data.toString(), _type, m_encodedData);
}
QList<int> ContractCallDataEncoder::extractDimension(QString const& _type)
{
QList<int> dim;
QRegExp dimExtract("(\\[[^\\]]*\\])");
int pos = dimExtract.indexIn(_type);
while (pos != -1)
{
if (_type.array)
count = _type.count;
int c = static_cast<int>(count);
if (strList.size() > c)
strList.erase(strList.begin() + c, strList.end());
QString d = dimExtract.cap(0);
pos += d.length();
pos = dimExtract.indexIn(_type, pos);
if (d == "[]")
dim.push_front(-1);
else
while (strList.size() < c)
strList.append(QString());
for (auto const& item: strList)
encodeSingleItem(item, _type, m_encodedData);
dim.push_front(d.replace("[", "").replace("]", "").toInt());
}
return dim;
}
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest)

5
mix/ContractCallDataEncoder.h

@ -69,11 +69,14 @@ private:
QString toString(bool _b);
QString toString(dev::bytes const& _b);
bool asString(dev::bytes const& _b, QString& _str);
QList<int> extractDimension(QString const& _type);
void encodeArray(QJsonArray const& _array, QList<int> _dim, SolidityType const& _type, bytes& _content);
private:
bytes m_encodedData;
bytes m_dynamicData;
std::vector<std::pair<size_t, size_t>> m_offsetMap;
std::vector<std::pair<size_t, size_t>> m_dynamicOffsetMap;
std::vector<std::pair<size_t, size_t>> m_staticOffsetMap;
};
}

113
mix/qml/js/InputValidator.js

@ -1,27 +1,20 @@
Qt.include("QEtherHelper.js")
var nbRegEx = new RegExp('^[0-9]+$');
var arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g");
var capturenbRegEx = new RegExp("[0-9]+");
function validate(model, values)
{
var inError = [];
for (var k in model)
{
init()
if (values[model[k].name])
{
var type = model[k].type.name;
var res;
if (isContractType(type))
res = validateAddress(type, values[model[k].name]);
else if (type.indexOf("int") !== -1)
res = validateInt(type, values[model[k].name]);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, values[model[k].name]);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, values[model[k].name]);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, values[model[k].name]);
else
res.valid = true;
var value = values[model[k].name];
var res = check(type, value)
if (!res.valid)
inError.push({ type: type, value: values, message: res.message });
}
@ -29,6 +22,100 @@ function validate(model, values)
return inError;
}
function init()
{
nbRegEx = new RegExp('^[0-9]+$');
arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g");
capturenbRegEx = new RegExp("[0-9]+");
}
function check(type, value)
{
var res = { valid: true, message : "" }
if (isContractType(type))
res = validateAddress(type, value);
else if (isArray(type))
res = validateArray(type, value);
else if (type.indexOf("int") !== -1)
res = validateInt(type, value);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, value);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, value);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, value);
else
{
res.valid = true
res.message = ""
}
return res;
}
function isArray(_type)
{
if (!arrayRegEx.test(_type))
return false
else
return (_type.indexOf("int") !== -1 || _type.indexOf("bytes") !== -1 || _type.indexOf("bool") !== -1 || _type.indexOf("adress") !== -1)
}
function checkArrayRecursively(_type, _dim, _array)
{
if (_array instanceof Array)
{
if (_dim.length === 0)
return { valid: false, message: "Your input contains too many dimensions" }
var size = -1
var infinite = false
if (_dim === "[]")
infinite = true
else
size = parseInt(capturenbRegEx.exec(_dim[0]))
if (_array.length > size && !infinite)
return { valid: false, message: "Array size does not correspond. Should be " + _dim[0] }
if (_array.length > 0)
{
var _newdim = _dim.slice(0)
_newdim.splice(0, 1)
for (var i = 0; i < _array.length; i++)
{
var ret = checkArrayRecursively(_type, _newdim, _array[i])
if (!ret.valid)
return ret
}
}
return { valid: true, message: "" }
}
else
{
if (_dim.length > 0)
return { valid: false, message: "Your input contains too few dimensions" }
if (typeof(_array) === "number")
_array = '' + _array + ''
return check(_type, _array)
}
}
function validateArray(_type, _value)
{
try
{
_value = JSON.parse(_value)
}
catch (e)
{
return { valid: false, message: "Input must be JSON formatted like [1,5,3,9] or [[4,9],[4,9],[4,9],[4,9]]" }
}
var dim = _type.match(arrayRegEx)
dim.reverse();
for (var k = 0; k < dim.length; k++)
{
_type = _type.replace(dim[k], "")
}
return checkArrayRecursively(_type, dim, _value)
}
function isContractType(_type)
{
for (var k in Object.keys(codeModel.contracts))

Loading…
Cancel
Save