Browse Source

ABI fixes and finalisation. Testing needed.

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

253
abi/main.cpp

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

Loading…
Cancel
Save