diff --git a/abi/main.cpp b/abi/main.cpp index e895152cc..cdae81f1e 100644 --- a/abi/main.cpp +++ b/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 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(_data)); - else if (base == Base::Int) - return toString((s256)fromBigEndian(_data)); + if (base == Base::Uint || base == Base::Int) + { + if (_e.e == Encoding::Hex) + return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian(bytesConstRef(&_data).cropped(32 - size / 8)))); + else + { + bigint i = fromBigEndian(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 fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) { @@ -269,25 +308,36 @@ tuple 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> 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 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(_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, unsigned)> putDim = [&](vector 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(), 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 souts; vector 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(bytesConstRef(&_data).cropped(di, 32))); + di += 32; + } + }; + function, unsigned)> putDim = [&](vector 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(bytesConstRef(&_data).cropped(di, 32))); + l = fromBigEndian(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(), 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)> putDim = [&](vector 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> 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; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1c7a4cd61..6468e250f 100644 --- a/libdevcore/CommonData.h +++ b/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 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::type)i; + ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned::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) {