Пример #1
0
std::pair<Blob, bool> strUnHex (std::string const& strSrc)
{
    std::string strTmp;

    if (strUnHex (strTmp, strSrc) == -1)
        return std::make_pair (Blob (), false);

    return std::make_pair(strCopy (strTmp), true);
}
Пример #2
0
    void testUnHexFailure (std::string strIn)
    {
        std::string strOut;

        expect (strUnHex (strOut, strIn) == -1,
            "strUnHex: parsing incorrect input succeeded");

        expect (strOut.empty (),
            "strUnHex: parsing incorrect input returned data");
    }
Пример #3
0
    void testUnHexSuccess (std::string strIn, std::string strExpected)
    {
        std::string strOut;

        expect (strUnHex (strOut, strIn) == strExpected.length (),
            "strUnHex: parsing correct input failed");

        expect (strOut == strExpected,
            "strUnHex: parsing doesn't produce expected result");
    }
Пример #4
0
/**
 * Decode a JSON value representing a ledger (as found in a dump file) into
 * a 3-tuple of: #1 a Ledger object, #2 a SHAMap holding a set of
 * transactions, and #3 a vector of transaction IDs indicating the order
 * that the transactions in the SHAMap were applied in the Ledger.
 */
static std::tuple<Ledger::pointer, SHAMap::pointer, std::vector<uint256> >
loadLedgerAndTransactionsFromJSON(Application& app, Json::Value const& j)
{
    require (j.isMember ("raw") && j["raw"].isString (),
             "JSON ledger \"raw\" entry missing or non-string");

    std::pair<Blob, bool> ledgerBlob = strUnHex (j["raw"].asString ());
    require (ledgerBlob.second, "Error decoding ledger \"raw\" field ");

    Ledger::pointer ledger = boost::make_shared<Ledger> (ledgerBlob.first, true);

    require (j.isMember ("txs") && j["txs"].isArray(),
             "JSON ledger \"txs\" entry missing or non-array");

    // Fill a SHAMap full of the current transactions.
    SHAMap::pointer txSet =
        boost::make_shared<SHAMap> (smtTRANSACTION, app.getFullBelowCache());

    std::vector<uint256> txOrder;

    for (auto const& jtx : j["txs"])
    {
        std::pair<Blob, bool> txBlob = strUnHex (jtx.asString ());
        require (txBlob.second, "Error decoding tx");

        Serializer ser (txBlob.first);
        SerializerIterator sit (ser);
        SerializedTransaction stx (sit);

        auto txID = stx.getTransactionID();
        require (txSet->addItem (SHAMapItem (txID, ser), true, true),
                 "Error adding transaction");
        txOrder.push_back (txID);
    }
    return std::make_tuple (ledger, txSet, txOrder);
}
Пример #5
0
boost::optional<ValidatorToken>
ValidatorToken::make_ValidatorToken(std::vector<std::string> const& tokenBlob)
{
    try
    {
        std::string tokenStr;
        tokenStr.reserve (
            std::accumulate (tokenBlob.cbegin(), tokenBlob.cend(), std::size_t(0),
                [] (std::size_t init, std::string const& s)
                {
                    return init + s.size();
                }));

        for (auto const& line : tokenBlob)
            tokenStr += beast::rfc2616::trim(line);

        tokenStr = beast::detail::base64_decode(tokenStr);

        Json::Reader r;
        Json::Value token;
        if (! r.parse (tokenStr, token))
            return boost::none;

        if (token.isMember("manifest") && token["manifest"].isString() &&
            token.isMember("validation_secret_key") &&
            token["validation_secret_key"].isString())
        {
            auto const ret = strUnHex (token["validation_secret_key"].asString());
            if (! ret.second || ! ret.first.size ())
                return boost::none;

            return ValidatorToken(
                token["manifest"].asString(),
                SecretKey(Slice{ret.first.data(), ret.first.size()}));
        }
        else
        {
            return boost::none;
        }
    }
    catch (std::exception const&)
    {
        return boost::none;
    }
}
Пример #6
0
// {
//   public_key: <public_key>
//   channel_id: 256-bit channel id
//   drops: 64-bit uint (as string)
//   signature: signature to verify
// }
Json::Value doChannelVerify (RPC::Context& context)
{
    auto const& params (context.params);
    for (auto const& p :
        {jss::public_key, jss::channel_id, jss::amount, jss::signature})
        if (!params.isMember (p))
            return RPC::missing_field_error (p);

    std::string const strPk = params[jss::public_key].asString ();
    auto const pk =
        parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk);
    if (!pk)
        return rpcError (rpcPUBLIC_MALFORMED);

    uint256 channelId;
    if (!channelId.SetHexExact (params[jss::channel_id].asString ()))
        return rpcError (rpcCHANNEL_MALFORMED);

    std::uint64_t drops = 0;
    try
    {
        drops = std::stoul (params[jss::amount].asString ());
    }
    catch (std::exception const&)
    {
        return rpcError (rpcCHANNEL_AMT_MALFORMED);
    }

    std::pair<Blob, bool> sig(strUnHex (params[jss::signature].asString ()));
    if (!sig.second || !sig.first.size ())
        return rpcError (rpcINVALID_PARAMS);

    Serializer msg;
    serializePayChanAuthorization (msg, channelId, XRPAmount (drops));

    Json::Value result;
    result[jss::signature_verified] =
        verify (*pk, msg.slice (), makeSlice (sig.first), /*canonical*/ true);
    return result;
}
Пример #7
0
static
bool
isMemoOkay (STObject const& st, std::string& reason)
{
    if (!st.isFieldPresent (sfMemos))
        return true;

    auto const& memos = st.getFieldArray (sfMemos);

    // The number 2048 is a preallocation hint, not a hard limit
    // to avoid allocate/copy/free's
    Serializer s (2048);
    memos.add (s);

    // FIXME move the memo limit into a config tunable
    if (s.getDataLength () > 1024)
    {
        reason = "The memo exceeds the maximum allowed size.";
        return false;
    }

    for (auto const& memo : memos)
    {
        auto memoObj = dynamic_cast <STObject const*> (&memo);

        if (!memoObj || (memoObj->getFName() != sfMemo))
        {
            reason = "A memo array may contain only Memo objects.";
            return false;
        }

        for (auto const& memoElement : *memoObj)
        {
            auto const& name = memoElement.getFName();

            if (name != sfMemoType &&
                name != sfMemoData &&
                name != sfMemoFormat)
            {
                reason = "A memo may contain only MemoType, MemoData or "
                         "MemoFormat fields.";
                return false;
            }

            // The raw data is stored as hex-octets, which we want to decode.
            auto data = strUnHex (memoElement.getText ());

            if (!data.second)
            {
                reason = "The MemoType, MemoData and MemoFormat fields may "
                         "only contain hex-encoded data.";
                return false;
            }

            if (name == sfMemoData)
                continue;

            // The only allowed characters for MemoType and MemoFormat are the
            // characters allowed in URLs per RFC 3986: alphanumerics and the
            // following symbols: -._~:/?#[]@!$&'()*+,;=%
            static std::array<char, 256> const allowedSymbols = []
            {
                std::array<char, 256> a;
                a.fill(0);

                std::string symbols (
                    "0123456789"
                    "-._~:/?#[]@!$&'()*+,;=%"
                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                    "abcdefghijklmnopqrstuvwxyz");

                for(char c : symbols)
                    a[c] = 1;
                return a;
            }();

            for (auto c : data.first)
            {
                if (!allowedSymbols[c])
                {
                    reason = "The MemoType and MemoFormat fields may only "
                             "contain characters that are allowed in URLs "
                             "under RFC 3986.";
                    return false;
                }
            }
        }
    }

    return true;
}
Пример #8
0
bool STParsedJSON::parse (std::string const& json_name,
    Json::Value const& json, SField::ref inName, int depth,
        std::unique_ptr <STObject>& sub_object)
{
    if (! json.isObject ())
    {
        error = not_an_object (json_name);
        return false;
    }

    SField::ptr name (&inName);

    boost::ptr_vector<SerializedType> data;
    Json::Value::Members members (json.getMemberNames ());

    for (Json::Value::Members::iterator it (members.begin ());
        it != members.end (); ++it)
    {
        std::string const& fieldName = *it;
        Json::Value const& value = json [fieldName];

        SField::ref field = SField::getField (fieldName);

        if (field == sfInvalid)
        {
            error = unknown_field (json_name, fieldName);
            return false;
        }

        switch (field.fieldType)
        {
        case STI_UINT8:
            try
            {
                if (value.isString ())
                {
                    // VFALCO TODO wtf?
                }
                else if (value.isInt ())
                {
                    if (value.asInt () < 0 || value.asInt () > 255)
                    {
                        error = out_of_range (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STUInt8 (field,
                        range_check_cast <unsigned char> (
                            value.asInt (), 0, 255)));
                }
                else if (value.isUInt ())
                {
                    if (value.asUInt () > 255)
                    {
                        error = out_of_range (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STUInt8 (field,
                        range_check_cast <unsigned char> (
                            value.asUInt (), 0, 255)));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT16:
            try
            {
                if (value.isString ())
                {
                    std::string strValue = value.asString ();

                    if (! strValue.empty () &&
                        ((strValue[0] < '0') || (strValue[0] > '9')))
                    {
                        if (field == sfTransactionType)
                        {
                            TxType const txType (TxFormats::getInstance()->
                                findTypeByName (strValue));

                            data.push_back (new STUInt16 (field,
                                static_cast <std::uint16_t> (txType)));

                            if (*name == sfGeneric)
                                name = &sfTransaction;
                        }
                        else if (field == sfLedgerEntryType)
                        {
                            LedgerEntryType const type (LedgerFormats::getInstance()->
                                findTypeByName (strValue));

                            data.push_back (new STUInt16 (field,
                                static_cast <std::uint16_t> (type)));

                            if (*name == sfGeneric)
                                name = &sfLedgerEntry;
                        }
                        else
                        {
                            error = invalid_data (json_name, fieldName);
                            return false;
                        }
                    }
                    else
                    {
                        data.push_back (new STUInt16 (field,
                            beast::lexicalCastThrow <std::uint16_t> (strValue)));
                    }
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt16 (field,
                        range_check_cast <std::uint16_t> (
                            value.asInt (), 0, 65535)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt16 (field,
                        range_check_cast <std::uint16_t> (
                            value.asUInt (), 0, 65535)));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT32:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STUInt32 (field,
                        beast::lexicalCastThrow <std::uint32_t> (value.asString ())));
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt32 (field,
                        range_check_cast <std::uint32_t> (value.asInt (), 0u, 4294967295u)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt32 (field,
                        static_cast <std::uint32_t> (value.asUInt ())));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT64:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STUInt64 (field,
                        uintFromHex (value.asString ())));
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt64 (field,
                        range_check_cast<std::uint64_t> (
                            value.asInt (), 0, 18446744073709551615ull)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt64 (field,
                        static_cast <std::uint64_t> (value.asUInt ())));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH128:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash128 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH160:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash160 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH256:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash256 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_VL:
            if (! value.isString ())
            {
                error = bad_type (json_name, fieldName);
                return false;
            }

            try
            {
                std::pair<Blob, bool> ret(strUnHex (value.asString ()));

                if (!ret.second)
                    throw std::invalid_argument ("invalid data");

                data.push_back (new STVariableLength (field, ret.first));
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_AMOUNT:
            try
            {
                data.push_back (new STAmount (field, value));
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_VECTOR256:
            if (! value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }
            
            try
            {
                data.push_back (new STVector256 (field));
                STVector256* tail (dynamic_cast <STVector256*> (&data.back ()));
                assert (tail);

                for (Json::UInt i = 0; !json.isValidIndex (i); ++i)
                {
                    uint256 s;
                    s.SetHex (json[i].asString ());
                    tail->addValue (s);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_PATHSET:
            if (!value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }

            try
            {
                data.push_back (new STPathSet (field));
                STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ());
                assert (tail);

                for (Json::UInt i = 0; value.isValidIndex (i); ++i)
                {
                    STPath p;

                    if (!value[i].isArray ())
                    {
                        std::stringstream ss;
                        ss << fieldName << "[" << i << "]";
                        error = array_expected (json_name, ss.str ());
                        return false;
                    }

                    for (Json::UInt j = 0; value[i].isValidIndex (j); ++j)
                    {
                        std::stringstream ss;
                        ss << fieldName << "[" << i << "][" << j << "]";
                        std::string const element_name (
                            json_name + "." + ss.str());

                        // each element in this path has some combination of account,
                        // currency, or issuer

                        Json::Value pathEl = value[i][j];

                        if (!pathEl.isObject ())
                        {
                            error = not_an_object (element_name);
                            return false;
                        }

                        const Json::Value& account  = pathEl["account"];
                        const Json::Value& currency = pathEl["currency"];
                        const Json::Value& issuer   = pathEl["issuer"];
                        bool hasCurrency            = false;
                        uint160 uAccount, uCurrency, uIssuer;

                        if (! account.isNull ())
                        {
                            // human account id
                            if (! account.isString ())
                            {
                                error = string_expected (element_name, "account");
                                return false;
                            }

                            std::string const strValue (account.asString ());

                            if (value.size () == 40) // 160-bit hex account value
                                uAccount.SetHex (strValue);

                            {
                                RippleAddress a;

                                if (! a.setAccountID (strValue))
                                {
                                    error = invalid_data (element_name, "account");
                                    return false;
                                }

                                uAccount = a.getAccountID ();
                            }
                        }

                        if (!currency.isNull ())
                        {
                            // human currency
                            if (!currency.isString ())
                            {
                                error = string_expected (element_name, "currency");
                                return false;
                            }

                            hasCurrency = true;

                            if (currency.asString ().size () == 40)
                            {
                                uCurrency.SetHex (currency.asString ());
                            }
                            else if (!STAmount::currencyFromString (
                                uCurrency, currency.asString ()))
                            {
                                error = invalid_data (element_name, "currency");
                                return false;
                            }
                        }

                        if (!issuer.isNull ())
                        {
                            // human account id
                            if (!issuer.isString ())
                            {
                                error = string_expected (element_name, "issuer");
                                return false;
                            }

                            if (issuer.asString ().size () == 40)
                            {
                                uIssuer.SetHex (issuer.asString ());
                            }
                            else
                            {
                                RippleAddress a;

                                if (!a.setAccountID (issuer.asString ()))
                                {
                                    error = invalid_data (element_name, "issuer");
                                    return false;
                                }

                                uIssuer = a.getAccountID ();
                            }
                        }

                        p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency));
                    }

                    tail->addPath (p);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_ACCOUNT:
        {
            if (! value.isString ())
            {
                error = bad_type (json_name, fieldName);
                return false;
            }

            std::string strValue = value.asString ();

            try
            {
                if (value.size () == 40) // 160-bit hex account value
                {
                    uint160 v;
                    v.SetHex (strValue);
                    data.push_back (new STAccount (field, v));
                }
                else
                {
                    // ripple address
                    RippleAddress a;

                    if (!a.setAccountID (strValue))
                    {
                        error = invalid_data (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STAccount (field, a.getAccountID ()));
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }
        }
        break;

        case STI_OBJECT:
        case STI_TRANSACTION:
        case STI_LEDGERENTRY:
        case STI_VALIDATION:
            if (! value.isObject ())
            {
                error = not_an_object (json_name, fieldName);
                return false;
            }

            if (depth > 64)
            {
                error = too_deep (json_name, fieldName);
                return false;
            }

            try
            {
                std::unique_ptr <STObject> sub_object_;
                bool const success (parse (json_name + "." + fieldName,
                    value, field, depth + 1, sub_object_));
                if (! success)
                    return false;
                data.push_back (sub_object_.release ());
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_ARRAY:
            if (! value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }

            try
            {
                data.push_back (new STArray (field));
                STArray* tail = dynamic_cast<STArray*> (&data.back ());
                assert (tail);

                for (Json::UInt i = 0; value.isValidIndex (i); ++i)
                {
                    bool const isObject (value[i].isObject());
                    bool const singleKey (isObject
                        ? value [i].size() == 1
                        : true);

                    if (!isObject || !singleKey)
                    {
                        std::stringstream ss;
                        ss << json_name << "." << fieldName << "[" << i << "]";
                        error = singleton_expected (ss.str ());
                        return false;
                    }

                    // TODO: There doesn't seem to be a nice way to get just the
                    // first/only key in an object without copying all keys into
                    // a vector
                    std::string const objectName (value[i].getMemberNames()[0]);;
                    SField::ref       nameField (SField::getField(objectName));

                    if (nameField == sfInvalid)
                    {
                        error = unknown_field (json_name, objectName);
                        return false;
                    }

                    Json::Value const objectFields (value[i][objectName]);

                    std::unique_ptr <STObject> sub_object_;
                    {
                        std::stringstream ss;
                        ss << json_name << "." << fieldName <<
                            "[" << i << "]." << objectName;
                        bool const success (parse (ss.str (), objectFields,
                            nameField, depth + 1, sub_object_));
                        if (! success)
                            return false;
                    }
                    tail->push_back (*sub_object_);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        default:
            error = bad_type (json_name, fieldName);
            return false;
        }
    }

    sub_object.reset (new STObject (*name, data));
    return true;
}
Пример #9
0
bool
ValidatorList::load (
    PublicKey const& localSigningKey,
    std::vector<std::string> const& configKeys,
    std::vector<std::string> const& publisherKeys)
{
    static boost::regex const re (
        "[[:space:]]*"            // skip leading whitespace
        "([[:alnum:]]+)"          // node identity
        "(?:"                     // begin optional comment block
        "[[:space:]]+"            // (skip all leading whitespace)
        "(?:"                     // begin optional comment
        "(.*[^[:space:]]+)"       // the comment
        "[[:space:]]*"            // (skip all trailing whitespace)
        ")?"                      // end optional comment
        ")?"                      // end optional comment block
    );

    boost::unique_lock<boost::shared_mutex> read_lock{mutex_};

    JLOG (j_.debug()) <<
        "Loading configured trusted validator list publisher keys";

    std::size_t count = 0;
    for (auto key : publisherKeys)
    {
        JLOG (j_.trace()) <<
            "Processing '" << key << "'";

        auto const ret = strUnHex (key);

        if (! ret.second || ! ret.first.size ())
        {
            JLOG (j_.error()) <<
                "Invalid validator list publisher key: " << key;
            return false;
        }

        auto id = PublicKey(Slice{ ret.first.data (), ret.first.size() });

        if (validatorManifests_.revoked (id))
        {
            JLOG (j_.warn()) <<
                "Configured validator list publisher key is revoked: " << key;
            continue;
        }

        if (publisherLists_.count(id))
        {
            JLOG (j_.warn()) <<
                "Duplicate validator list publisher key: " << key;
            continue;
        }

        publisherLists_[id].available = false;
        ++count;
    }

    JLOG (j_.debug()) <<
        "Loaded " << count << " keys";

    localPubKey_ = validatorManifests_.getMasterKey (localSigningKey);

    // Treat local validator key as though it was listed in the config
    if (localPubKey_.size())
        keyListings_.insert ({ localPubKey_, 1 });

    JLOG (j_.debug()) <<
        "Loading configured validator keys";

    count = 0;
    PublicKey local;
    for (auto const& n : configKeys)
    {
        JLOG (j_.trace()) <<
            "Processing '" << n << "'";

        boost::smatch match;

        if (!boost::regex_match (n, match, re))
        {
            JLOG (j_.error()) <<
                "Malformed entry: '" << n << "'";
            return false;
        }

        auto const id = parseBase58<PublicKey>(
            TokenType::TOKEN_NODE_PUBLIC, match[1]);

        if (!id)
        {
            JLOG (j_.error()) << "Invalid node identity: " << match[1];
            return false;
        }

        // Skip local key which was already added
        if (*id == localPubKey_ || *id == localSigningKey)
            continue;

        auto ret = keyListings_.insert ({*id, 1});
        if (! ret.second)
        {
            JLOG (j_.warn()) << "Duplicate node identity: " << match[1];
            continue;
        }
        auto it = publisherLists_.emplace(
            std::piecewise_construct,
            std::forward_as_tuple(local),
            std::forward_as_tuple());
        // Config listed keys never expire
        if (it.second)
            it.first->second.expiration = TimeKeeper::time_point::max();
        it.first->second.list.emplace_back(std::move(*id));
        it.first->second.available = true;
        ++count;
    }

    JLOG (j_.debug()) <<
        "Loaded " << count << " entries";

    return true;
}
Пример #10
0
ListDisposition
ValidatorList::verify (
    Json::Value& list,
    PublicKey& pubKey,
    std::string const& manifest,
    std::string const& blob,
    std::string const& signature)
{
    auto m = Manifest::make_Manifest (beast::detail::base64_decode(manifest));

    if (! m || ! publisherLists_.count (m->masterKey))
        return ListDisposition::untrusted;

    pubKey = m->masterKey;
    auto const revoked = m->revoked();

    auto const result = publisherManifests_.applyManifest (
        std::move(*m));

    if (revoked && result == ManifestDisposition::accepted)
    {
        removePublisherList (pubKey);
        publisherLists_.erase (pubKey);
    }

    if (revoked || result == ManifestDisposition::invalid)
        return ListDisposition::untrusted;

    auto const sig = strUnHex(signature);
    auto const data = beast::detail::base64_decode (blob);
    if (! sig.second ||
        ! ripple::verify (
            publisherManifests_.getSigningKey(pubKey),
            makeSlice(data),
            makeSlice(sig.first)))
        return ListDisposition::invalid;

    Json::Reader r;
    if (! r.parse (data, list))
        return ListDisposition::invalid;

    if (list.isMember("sequence") && list["sequence"].isInt() &&
        list.isMember("expiration") && list["expiration"].isInt() &&
        list.isMember("validators") && list["validators"].isArray())
    {
        auto const sequence = list["sequence"].asUInt();
        auto const expiration = TimeKeeper::time_point{
            TimeKeeper::duration{list["expiration"].asUInt()}};
        if (sequence < publisherLists_[pubKey].sequence ||
            expiration <= timeKeeper_.now())
            return ListDisposition::stale;
        else if (sequence == publisherLists_[pubKey].sequence)
            return ListDisposition::same_sequence;
    }
    else
    {
        return ListDisposition::invalid;
    }

    return ListDisposition::accepted;
}
Пример #11
0
ListDisposition
ValidatorList::applyList (
    std::string const& manifest,
    std::string const& blob,
    std::string const& signature,
    std::uint32_t version)
{
    if (version != requiredListVersion)
        return ListDisposition::unsupported_version;

    boost::unique_lock<boost::shared_mutex> lock{mutex_};

    Json::Value list;
    PublicKey pubKey;
    auto const result = verify (list, pubKey, manifest, blob, signature);
    if (result != ListDisposition::accepted)
        return result;

    // Update publisher's list
    Json::Value const& newList = list["validators"];
    publisherLists_[pubKey].available = true;
    publisherLists_[pubKey].sequence = list["sequence"].asUInt ();
    publisherLists_[pubKey].expiration = TimeKeeper::time_point{
        TimeKeeper::duration{list["expiration"].asUInt()}};
    std::vector<PublicKey>& publisherList = publisherLists_[pubKey].list;

    std::vector<PublicKey> oldList = publisherList;
    publisherList.clear ();
    publisherList.reserve (newList.size ());
    std::vector<std::string> manifests;
    for (auto const& val : newList)
    {
        if (val.isObject () &&
            val.isMember ("validation_public_key") &&
            val["validation_public_key"].isString ())
        {
            std::pair<Blob, bool> ret (strUnHex (
                val["validation_public_key"].asString ()));

            if (! ret.second || ! ret.first.size ())
            {
                JLOG (j_.error()) <<
                    "Invalid node identity: " <<
                    val["validation_public_key"].asString ();
            }
            else
            {
                publisherList.push_back (
                    PublicKey(Slice{ ret.first.data (), ret.first.size() }));
            }

            if (val.isMember ("manifest") && val["manifest"].isString ())
                manifests.push_back(val["manifest"].asString ());
        }
    }

    // Update keyListings_ for added and removed keys
    std::sort (
        publisherList.begin (),
        publisherList.end ());

    auto iNew = publisherList.begin ();
    auto iOld = oldList.begin ();
    while (iNew != publisherList.end () ||
        iOld != oldList.end ())
    {
        if (iOld == oldList.end () ||
            (iNew != publisherList.end () &&
            *iNew < *iOld))
        {
            // Increment list count for added keys
            ++keyListings_[*iNew];
            ++iNew;
        }
        else if (iNew == publisherList.end () ||
            (iOld != oldList.end () && *iOld < *iNew))
        {
            // Decrement list count for removed keys
            if (keyListings_[*iOld] <= 1)
                keyListings_.erase (*iOld);
            else
                --keyListings_[*iOld];
            ++iOld;
        }
        else
        {
            ++iNew;
            ++iOld;
        }
    }

    if (publisherList.empty())
    {
        JLOG (j_.warn()) <<
            "No validator keys included in valid list";
    }

    for (auto const& valManifest : manifests)
    {
        auto m = Manifest::make_Manifest (
            beast::detail::base64_decode(valManifest));

        if (! m || ! keyListings_.count (m->masterKey))
        {
            JLOG (j_.warn()) <<
                "List for " << strHex(pubKey) <<
                " contained untrusted validator manifest";
            continue;
        }

        auto const result = validatorManifests_.applyManifest (std::move(*m));
        if (result == ManifestDisposition::invalid)
        {
            JLOG (j_.warn()) <<
                "List for " << strHex(pubKey) <<
                " contained invalid validator manifest";
        }
    }

    return ListDisposition::accepted;
}
Пример #12
0
 void testUnHexFailure (std::string const& strIn)
 {
     auto rv = strUnHex (strIn);
     BEAST_EXPECT(! rv.second);
     BEAST_EXPECT(rv.first.empty());
 }
Пример #13
0
 void testUnHexSuccess (std::string const& strIn, std::string const& strExpected)
 {
     auto rv = strUnHex (strIn);
     BEAST_EXPECT(rv.second);
     BEAST_EXPECT(makeSlice(rv.first) == makeSlice(strExpected));
 }
Пример #14
0
// {
//   tx_json: <object>,
//   secret: <secret>
// }
Json::Value doSubmit (RPC::Context& context)
{
    context.loadType = Resource::feeMediumBurdenRPC;

    if (!context.params.isMember (jss::tx_blob))
    {
        auto const failType = getFailHard (context);

        if (context.role != Role::ADMIN && !context.app.config().canSign())
            return RPC::make_error (rpcNOT_SUPPORTED,
                "Signing is not supported by this server.");

        auto ret = RPC::transactionSubmit (
            context.params, failType, context.role,
            context.ledgerMaster.getValidatedLedgerAge(),
            context.app, RPC::getProcessTxnFn (context.netOps));

        ret[jss::deprecated] = "Signing support in the 'submit' command has been "
                               "deprecated and will be removed in a future version "
                               "of the server. Please migrate to a standalone "
                               "signing tool.";

        return ret;
    }

    Json::Value jvResult;

    std::pair<Blob, bool> ret(strUnHex (context.params[jss::tx_blob].asString ()));

    if (!ret.second || !ret.first.size ())
        return rpcError (rpcINVALID_PARAMS);

    SerialIter sitTrans (makeSlice(ret.first));

    std::shared_ptr<STTx const> stpTrans;

    try
    {
        stpTrans = std::make_shared<STTx const> (std::ref (sitTrans));
    }
    catch (std::exception& e)
    {
        jvResult[jss::error]        = "invalidTransaction";
        jvResult[jss::error_exception] = e.what ();

        return jvResult;
    }


    {
        if (!context.app.checkSigs())
            forceValidity(context.app.getHashRouter(),
                stpTrans->getTransactionID(), Validity::SigGoodOnly);
        auto validity = checkValidity(context.app.getHashRouter(),
            *stpTrans, context.ledgerMaster.getCurrentLedger()->rules(),
                context.app.config());
        if (validity.first != Validity::Valid)
        {
            jvResult[jss::error] = "invalidTransaction";
            jvResult[jss::error_exception] = "fails local checks: " + validity.second;

            return jvResult;
        }
    }

    std::string reason;
    auto tpTrans = std::make_shared<Transaction> (
        stpTrans, reason, context.app);
    if (tpTrans->getStatus() != NEW)
    {
        jvResult[jss::error]            = "invalidTransaction";
        jvResult[jss::error_exception] = "fails local checks: " + reason;

        return jvResult;
    }

    try
    {
        auto const failType = getFailHard (context);

        context.netOps.processTransaction (
            tpTrans, isUnlimited (context.role), true, failType);
    }
    catch (std::exception& e)
    {
        jvResult[jss::error]           = "internalSubmit";
        jvResult[jss::error_exception] = e.what ();

        return jvResult;
    }


    try
    {
        jvResult[jss::tx_json] = tpTrans->getJson (0);
        jvResult[jss::tx_blob] = strHex (
            tpTrans->getSTransaction ()->getSerializer ().peekData ());

        if (temUNCERTAIN != tpTrans->getResult ())
        {
            std::string sToken;
            std::string sHuman;

            transResultInfo (tpTrans->getResult (), sToken, sHuman);

            jvResult[jss::engine_result]           = sToken;
            jvResult[jss::engine_result_code]      = tpTrans->getResult ();
            jvResult[jss::engine_result_message]   = sHuman;
        }

        return jvResult;
    }
    catch (std::exception& e)
    {
        jvResult[jss::error]           = "internalJson";
        jvResult[jss::error_exception] = e.what ();

        return jvResult;
    }
}