Browse Source

ABI fixes and finalisation. Testing needed.

cl-refactor
Gav Wood 10 years ago
parent
commit
2d42d05f9b
  1. 283
      abi/main.cpp
  2. 12
      libdevcore/CommonData.h

283
abi/main.cpp

@ -43,8 +43,8 @@ void help()
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
<< "Input options:" << endl
<< " -p,--prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl
<< " -P,--no-prefix Require no input format to be prefixed." << endl
<< " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl
<< " -F,--no-format-prefix Require no input format to be prefixed." << endl
<< " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl
<< " -T,--no-typing Require no arguments to be typed." << endl
<< "Output options:" << endl
@ -53,8 +53,6 @@ void help()
<< " -x,--hex Display all data as hex." << endl
<< " -b,--binary Display all data as binary." << endl
<< " -p,--prefix Prefix by a base identifier." << endl
<< " -z,--no-zeroes Remove any leading zeroes from the data." << endl
<< " -n,--no-nulls Remove any trailing nulls from the data." << endl
;
exit(0);
}
@ -90,7 +88,9 @@ enum class Format
{
Binary,
Hex,
Decimal
Decimal,
Open,
Close
};
struct InvalidUserString: public Exception {};
@ -115,6 +115,12 @@ static const map<Base, string> s_bases =
{ Base::Fixed, "fixed" }
};
struct EncodingPrefs
{
Encoding e = Encoding::Auto;
bool prefix = true;
};
struct ABIType
{
Base base = Base::Unknown;
@ -204,39 +210,72 @@ struct ABIType
bool isBytes() const { return base == Base::Bytes && !size; }
string render(bytes const& _data) const
string render(bytes const& _data, EncodingPrefs _e) const
{
if (base == Base::Uint)
return toString(fromBigEndian<u256>(_data));
else if (base == Base::Int)
return toString((s256)fromBigEndian<u256>(_data));
if (base == Base::Uint || base == Base::Int)
{
if (_e.e == Encoding::Hex)
return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8))));
else
{
bigint i = fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8));
if (base == Base::Int && i > (bigint(1) << (size - 1)))
i -= (bigint(1) << size);
return toString(i);
}
}
else if (base == Base::Address)
return toString(Address(h256(_data)));
{
Address a = Address(h256(_data), Address::AlignRight);
return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a));
}
else if (isBytes())
{
return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data));
}
else if (base == Base::Bytes)
{
bytesConstRef b(&_data);
b = b.cropped(0, size);
return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b));
}
else
throw InvalidFormat();
}
bytes unrender(bytes const& _data, Format _f) const
{
if (isBytes())
{
auto ret = _data;
while (ret.size() % 32 != 0)
ret.push_back(0);
return ret;
}
else
return toHex(_data);
return aligned(_data, _f, 32);
}
void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } }
void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } }
void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } }
};
bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length)
{
(void)_t;
bytes ret = _b;
while (ret.size() < _length)
if (_f == Format::Binary)
ret.push_back(0);
else
ret.insert(ret.begin(), 0);
while (ret.size() > _length)
if (_f == Format::Binary)
ret.pop_back();
else
ret.erase(ret.begin());
return ret;
}
bytes aligned(bytes const& _b, Format _f, unsigned _length) const
{
bytes ret = _b;
while (ret.size() < _length)
if (_f == Format::Binary)
ret.push_back(0);
else
ret.insert(ret.begin(), 0);
while (ret.size() > _length)
if (_f == Format::Binary)
ret.pop_back();
else
ret.erase(ret.begin());
return ret;
}
};
tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing)
{
@ -269,25 +308,36 @@ tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix
type.noteBinaryInput();
return make_tuple(asBytes(val.substr(1)), type, Format::Binary);
}
if (val == "[")
return make_tuple(bytes(), type, Format::Open);
if (val == "]")
return make_tuple(bytes(), type, Format::Close);
}
if (_prefix != Tristate::True)
{
if (_arg.find_first_not_of("0123456789") == string::npos)
if (val.find_first_not_of("0123456789") == string::npos)
{
type.noteDecimalInput();
return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal);
}
if (_arg.find_first_not_of("0123456789abcdefABCDEF") == string::npos)
if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos)
{
type.noteHexInput(val.size());
return make_tuple(fromHex(val), type, Format::Hex);
}
if (val == "[")
return make_tuple(bytes(), type, Format::Open);
if (val == "]")
return make_tuple(bytes(), type, Format::Close);
type.noteBinaryInput();
return make_tuple(asBytes(_arg), type, Format::Binary);
return make_tuple(asBytes(val), type, Format::Binary);
}
throw InvalidUserString();
}
struct ExpectedOpen: public Exception {};
struct ExpectedClose: public Exception {};
struct ABIMethod
{
string name;
@ -355,77 +405,107 @@ struct ABIMethod
bytes encode(vector<pair<bytes, Format>> const& _params) const
{
// ALL WRONG!!!!
// INARITIES SHOULD BE HEIRARCHICAL!
bytes ret = name.empty() ? bytes() : id().asBytes();
bytes suffix;
// int int[] int
// example: 42 [ 1 2 3 ] 69
// int[2][][3]
// example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ]
unsigned pi = 0;
vector<unsigned> inArity;
for (ABIType const& i: ins)
{
unsigned arity = 1;
for (auto j: i.dims)
if (j == -1)
{
ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32);
arity *= fromBigEndian<unsigned>(_params[pi].first);
pi++;
}
else
arity *= j;
if (i.isBytes())
for (unsigned i = 0; i < arity; ++i)
inArity.push_back(arity);
}
unsigned ii = 0;
for (ABIType const& i: ins)
for (ABIType const& a: ins)
{
for (unsigned j = 0; j < inArity[ii]; ++j)
{
if (i.base == Base::Bytes && !i.size)
auto put = [&]() {
if (a.isBytes())
ret += h256(u256(_params[pi].first.size())).asBytes();
suffix += a.unrender(_params[pi].first, _params[pi].second);
pi++;
};
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) {
if (addr.size() == a.dims.size())
put();
else
{
ret += _params[pi].first;
while (ret.size() % 32 != 0)
ret.push_back(0);
if (_params[pi].second != Format::Open)
throw ExpectedOpen();
int l = a.dims[addr.size()];
if (l == -1)
{
// read ahead in params and discover the arity.
unsigned depth = 0;
l = 0;
for (unsigned pi2 = pi + 1; depth || _params[pi2].second != Format::Close;)
{
if (_params[pi2].second == Format::Open)
++depth;
if (_params[pi2].second == Format::Close)
--depth;
if (!depth)
++l;
if (++pi2 == _params.size())
throw ExpectedClose();
}
ret += h256(u256(l)).asBytes();
}
q *= l;
for (addr.push_back(0); addr.back() < l; ++addr.back())
putDim(addr, q);
if (_params[pi].second != Format::Close)
throw ExpectedClose();
}
else
ret += aligned(_params[pi].first, i, _params[pi].second, 32);
++pi;
}
++ii;
};
putDim(vector<int>(), 1);
}
return ret;
return ret + suffix;
}
string decode(bytes const& _data, int _index = -1)
string decode(bytes const& _data, int _index, EncodingPrefs _ep)
{
stringstream out;
if (_index == -1)
out << "[";
unsigned di = 0;
vector<ABIType> souts;
vector<unsigned> catDims;
for (ABIType a: outs)
for (ABIType const& a: outs)
{
unsigned q = 1;
for (auto& i: a.dims)
{
for (unsigned j = 0; j < q; ++j)
if (i == -1)
auto put = [&]() {
if (a.isBytes())
{
catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32)));
di += 32;
}
};
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) {
if (addr.size() == a.dims.size())
put();
else
{
out << "[";
int l = a.dims[addr.size()];
if (l == -1)
{
catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32)));
l = fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32));
catDims.push_back(l);
di += 32;
}
q *= i;
}
if (a.isBytes())
souts.push_back(a);
q *= l;
for (addr.push_back(0); addr.back() < l; ++addr.back())
putDim(addr, q);
out << "]";
}
};
putDim(vector<int>(), 1);
}
for (ABIType const& a: souts)
unsigned d = 0;
for (ABIType const& a: outs)
{
auto put = [&]() {
out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes()) << ", ";
di += 32;
unsigned l = 32;
if (a.isBytes())
l = (catDims[d++] + 31 / 32) * 32;
out << a.render(bytesConstRef(&_data).cropped(di, l).toBytes(), _ep) << ", ";
di += l;
};
function<void(vector<int>)> putDim = [&](vector<int> addr) {
if (addr.size() == a.dims.size())
@ -433,9 +513,11 @@ struct ABIMethod
else
{
out << "[";
auto d = addr;
addr.push_back(0);
for (addr.back() = 0; addr.back() < a.dims[addr.size() - 1]; ++addr.back())
int l = a.dims[addr.size() - 1];
if (l == -1)
l = catDims[d++];
for (addr.back() = 0; addr.back() < l; ++addr.back())
{
if (addr.back())
out << ", ";
@ -543,10 +625,9 @@ int main(int argc, char** argv)
Mode mode = Mode::Encode;
string abiFile;
string method;
Tristate prefix = Tristate::Mu;
Tristate formatPrefix = Tristate::Mu;
Tristate typePrefix = Tristate::Mu;
bool clearZeroes = false;
bool clearNulls = false;
EncodingPrefs prefs;
bool verbose = false;
int outputIndex = -1;
vector<pair<bytes, Format>> params;
@ -566,25 +647,23 @@ int main(int argc, char** argv)
else if ((arg == "-i" || arg == "--index") && argc > i)
outputIndex = atoi(argv[++i]);
else if (arg == "-p" || arg == "--prefix")
prefix = Tristate::True;
else if (arg == "-P" || arg == "--no-prefix")
prefix = Tristate::False;
prefs.prefix = true;
else if (arg == "-f" || arg == "--format-prefix")
formatPrefix = Tristate::True;
else if (arg == "-F" || arg == "--no-format-prefix")
formatPrefix = Tristate::False;
else if (arg == "-t" || arg == "--typing")
typePrefix = Tristate::True;
else if (arg == "-T" || arg == "--no-typing")
typePrefix = Tristate::False;
else if (arg == "-z" || arg == "--no-zeroes")
clearZeroes = true;
else if (arg == "-n" || arg == "--no-nulls")
clearNulls = true;
else if (arg == "-v" || arg == "--verbose")
verbose = true;
else if (arg == "-x" || arg == "--hex")
encoding = Encoding::Hex;
prefs.e = Encoding::Hex;
else if (arg == "-d" || arg == "--decimal" || arg == "--dec")
encoding = Encoding::Decimal;
prefs.e = Encoding::Decimal;
else if (arg == "-b" || arg == "--binary" || arg == "--bin")
encoding = Encoding::Binary;
prefs.e = Encoding::Binary;
else if (arg == "-v" || arg == "--verbose")
version();
else if (arg == "-V" || arg == "--version")
@ -593,7 +672,7 @@ int main(int argc, char** argv)
method = arg;
else
{
auto u = fromUser(arg, prefix, typePrefix);
auto u = fromUser(arg, formatPrefix, typePrefix);
args.push_back(get<1>(u));
params.push_back(make_pair(get<0>(u), get<2>(u)));
}
@ -648,14 +727,8 @@ int main(int argc, char** argv)
string encoded;
for (int i = cin.get(); i != -1; i = cin.get())
encoded.push_back((char)i);
cout << m.decode(fromHex(encoded));
cout << m.decode(fromHex(encoded), outputIndex, prefs);
}
// TODO: read abi to determine output format.
(void)encoding;
(void)clearZeroes;
(void)clearNulls;
(void)outputIndex;
}
return 0;

12
libdevcore/CommonData.h

@ -42,14 +42,15 @@ enum class WhenError
};
/// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte.
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
template <class _T>
std::string toHex(_T const& _data, int _w = 2)
{
std::ostringstream ret;
unsigned ii = 0;
for (auto i: _data)
ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned<decltype(i)>::type)i;
ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned<decltype(i)>::type)i;
return ret.str();
}
@ -74,6 +75,13 @@ inline std::string asString(bytes const& _b)
return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
}
/// Converts byte array ref to a string containing the same (binary) data. Unless
/// the byte array happens to contain ASCII data, this won't be printable.
inline std::string asString(bytesConstRef _b)
{
return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size()));
}
/// Converts a string to a byte array containing the string's (byte) data.
inline bytes asBytes(std::string const& _b)
{

Loading…
Cancel
Save