diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index b12cc027e..5323b32b9 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1780,6 +1780,22 @@
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+
@@ -3326,6 +3342,10 @@
+
+ True
+ True
+
True
True
@@ -4820,6 +4840,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 38f373d98..3c98ff151 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -193,6 +193,12 @@
{C94B6C51-E253-633B-0AA8-8D18CD695D5E}
+
+ {44E216F9-ACFD-B770-C6C9-BFFAD162566D}
+
+
+ {155DC1A3-8A60-BC74-A7E4-1AC1A679FFF9}
+
{235DCF23-2CF8-4F03-1A54-C159823A7E8D}
@@ -2409,6 +2415,24 @@
ripple\beast
+
+ ripple\conditions
+
+
+ ripple\conditions
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
ripple\core
@@ -3939,6 +3963,9 @@
ripple\unity
+
+ ripple\unity
+
ripple\unity
@@ -5508,6 +5535,9 @@
unity
+
+ unity
+
unity
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1cedf93cd..df2efeef1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -180,6 +180,7 @@ app_main.cpp
app_misc.cpp
app_paths.cpp
app_tx.cpp
+conditions.cpp
core.cpp
basics.cpp
crypto.cpp
@@ -199,6 +200,7 @@ src/unity/
app_test_unity.cpp
basics_test_unity.cpp
beast_test_unity.cpp
+conditions_test_unity.cpp
core_test_unity.cpp
json_test_unity.cpp
ledger_test_unity.cpp
@@ -250,6 +252,7 @@ foreach(curdir
beast/utility
app
basics
+ conditions
crypto
json
ledger
diff --git a/SConstruct b/SConstruct
index d05667a89..8b50ba0b8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -924,6 +924,7 @@ def get_classic_sources(toolchain):
append_sources(result, *list_sources('src/ripple/beast/utility', '.cpp'))
append_sources(result, *list_sources('src/ripple/app', '.cpp'))
append_sources(result, *list_sources('src/ripple/basics', '.cpp'))
+ append_sources(result, *list_sources('src/ripple/conditions', '.cpp'))
append_sources(result, *list_sources('src/ripple/crypto', '.cpp'))
append_sources(result, *list_sources('src/ripple/json', '.cpp'))
append_sources(result, *list_sources('src/ripple/ledger', '.cpp'))
@@ -986,6 +987,7 @@ def get_unity_sources(toolchain):
'src/ripple/unity/app_misc.cpp',
'src/ripple/unity/app_paths.cpp',
'src/ripple/unity/app_tx.cpp',
+ 'src/ripple/unity/conditions.cpp',
'src/ripple/unity/core.cpp',
'src/ripple/unity/basics.cpp',
'src/ripple/unity/crypto.cpp',
@@ -1003,6 +1005,7 @@ def get_unity_sources(toolchain):
'src/unity/basics_test_unity.cpp',
'src/unity/beast_test_unity.cpp',
'src/unity/core_test_unity.cpp',
+ 'src/unity/conditions_test_unity.cpp',
'src/unity/json_test_unity.cpp',
'src/unity/ledger_test_unity.cpp',
'src/unity/overlay_test_unity.cpp',
diff --git a/src/ripple/conditions/Condition.h b/src/ripple/conditions/Condition.h
new file mode 100644
index 000000000..d26bbe558
--- /dev/null
+++ b/src/ripple/conditions/Condition.h
@@ -0,0 +1,175 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_CONDITION_H
+#define RIPPLE_CONDITIONS_CONDITION_H
+
+#include // use Beast implementation
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+// NIKB-TODO: These should move to a separate file:
+std::uint16_t constexpr condition_hashlock = 0;
+std::uint16_t constexpr condition_prefix_sha256 = 1;
+std::uint16_t constexpr condition_threshold_sha256 = 2;
+std::uint16_t constexpr condition_rsa_sha256 = 3;
+std::uint16_t constexpr condition_ed25519 = 4;
+
+// NIKB-TODO: These should be `enum class : std::uint32_t`
+std::uint32_t constexpr feature_sha256 = 1;
+std::uint32_t constexpr feature_preimage = 2;
+std::uint32_t constexpr feature_prefix = 4;
+std::uint32_t constexpr feature_threshold = 8;
+std::uint32_t constexpr feature_rsa_pss = 16;
+std::uint32_t constexpr feature_ed25519 = 32;
+
+/** The list of all feature suited defined in the RFC */
+std::uint32_t constexpr definedFeatures =
+ feature_sha256 |
+ feature_preimage |
+ feature_prefix |
+ feature_threshold |
+ feature_rsa_pss |
+ feature_ed25519;
+
+/** The largest fulfillment supported by this implementation.
+
+ Fulfillments larger than this value cannot be processed
+ and will not be generated.
+*/
+constexpr std::size_t maxSupportedFulfillmentLength = 65535;
+
+struct Condition
+{
+ std::uint16_t type;
+
+ /** The maximum length of a fulfillment for this condition.
+
+ While it is possible for a fulfillment to be smaller
+ it can never be bigger than this.
+ */
+ std::uint16_t maxFulfillmentLength;
+
+ /** The features suites required to process a fulfillment. */
+ std::uint32_t featureBitmask;
+
+ /** An identifier for this condition.
+
+ This fingerprint is meant to be unique only with
+ respect to other conditions of the same type.
+ */
+ std::array fingerprint;
+
+ // Can this be deleted?
+ Condition () = default;
+
+ Condition (Condition const&) = default;
+ Condition (Condition&&) = default;
+};
+
+inline
+bool
+operator== (Condition const& lhs, Condition const& rhs)
+{
+ return
+ lhs.type == rhs.type &&
+ lhs.featureBitmask == rhs.featureBitmask &&
+ lhs.maxFulfillmentLength == rhs.maxFulfillmentLength &&
+ lhs.fingerprint == rhs.fingerprint;
+}
+
+inline
+bool
+operator!= (Condition const& lhs, Condition const& rhs)
+{
+ return !(lhs == rhs);
+}
+
+/** Determine if a given condition is valid.
+
+ @note this function checks whether it understands the
+ type of the condition, and if so, whether it meets
+ the requirements mandated by the RFC.
+*/
+inline
+bool
+validate (Condition const& c)
+{
+ // This check can never trigger because of the range of
+ // the maxFulfillmentLength type. It's here in case the
+ // type is changed in the future.
+
+ if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
+ return false;
+
+ return false;
+}
+
+/** True if condition type is specified in the RFC.
+
+ @note: this function may return true even if the type
+ of condition isn't presently supported by this
+ implementation.
+*/
+inline
+bool
+isCondition (std::uint16_t type)
+{
+ switch(type)
+ {
+ case condition_hashlock:
+ case condition_prefix_sha256:
+ case condition_threshold_sha256:
+ case condition_rsa_sha256:
+ case condition_ed25519:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/** Load a serialized condition either from its string or binary form */
+/** @{ */
+boost::optional
+loadCondition(std::string const& s);
+
+boost::optional
+loadCondition(Slice s);
+/** @} */
+
+// Convert a condition to its string form
+std::string
+to_string (Condition const& c);
+
+// Convert a condition to its binary form
+std::vector
+to_blob (Condition const& c);
+
+}
+
+}
+
+#endif
diff --git a/src/ripple/conditions/Fulfillment.h b/src/ripple/conditions/Fulfillment.h
new file mode 100644
index 000000000..e1657c5ae
--- /dev/null
+++ b/src/ripple/conditions/Fulfillment.h
@@ -0,0 +1,176 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_FULFILLMENT_H
+#define RIPPLE_CONDITIONS_FULFILLMENT_H
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+struct Fulfillment
+{
+public:
+ virtual ~Fulfillment() = default;
+
+ Fulfillment () = default;
+
+ /** Returns the size of the fulfillment's payload. */
+ virtual
+ std::size_t
+ payloadSize() const = 0;
+
+ /** Returns the fulfillment's payload */
+ virtual
+ Buffer
+ payload() const = 0;
+
+ /** Generates the condition */
+ virtual
+ Condition
+ condition() const = 0;
+
+ /** Returns the type */
+ virtual
+ std::uint16_t
+ type () const = 0;
+
+ /** Returns the features suites required.
+
+ For any given fulfillment, the result includes all
+ the feature suites that an implementation must
+ support in order to be able to successfully parse
+ the fulfillment.
+
+ @note fulfillments of the same type may require
+ different features.
+ */
+ virtual
+ std::uint32_t
+ features () const = 0;
+
+ /** Determines if this fulfillment is well-formed */
+ virtual
+ bool
+ ok () const = 0;
+
+ /** Validates a fulfillment. */
+ virtual
+ bool
+ validate (Slice data) const = 0;
+
+ /** Parses the fulfillment's payload. */
+ virtual
+ bool
+ parsePayload (Slice s) = 0;
+};
+
+inline
+bool
+operator== (Fulfillment const& lhs, Fulfillment const& rhs)
+{
+ return
+ lhs.type() == rhs.type() &&
+ lhs.ok() == rhs.ok() &&
+ lhs.payload() == rhs.payload();
+}
+
+inline
+bool
+operator!= (Fulfillment const& lhs, Fulfillment const& rhs)
+{
+ return !(lhs == rhs);
+}
+
+/** Load a fulfillment from its string serialization.
+
+ The format is specified in Section 2.5.1 of the
+ cryptoconditions RFC:
+
+ https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.1
+ */
+std::unique_ptr
+loadFulfillment (std::string const& s);
+
+/** Load a fulfillment from its binary serialization.
+
+ The format is specified in Section 2.5.2 of the
+ cryptoconditions RFC:
+
+ https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.2
+*/
+std::unique_ptr
+loadFulfillment (Slice s);
+
+// Convert a fulfillment to its string form
+std::string
+to_string (Fulfillment const& f);
+
+// Convert a fulfillment to its binary form
+std::vector
+to_blob (Fulfillment const& f);
+
+/** Determine whether a fulfillment fulfills a given condition */
+bool
+fulfills (
+ Fulfillment const& f,
+ Condition const& c);
+
+/** Verify if the given message satisfies the fulfillment.
+
+ @param f The fulfillment
+ @param c The condition
+ @param m The message; note that the message is not
+ relevant for some conditions (e.g. hashlocks)
+ and a fulfillment will successfully satisfy its
+ condition for any given message.
+*/
+bool
+validate (
+ Fulfillment const& f,
+ Condition const& c,
+ Slice m);
+
+/** Verify a cryptoconditional trigger.
+
+ A cryptoconditional trigger is a cryptocondition with
+ an empty message.
+
+ When using such triggers, it is recommended that the
+ trigger be of type preimage, prefix or threshold. If
+ a signature type is used (i.e. Ed25519 or RSA-SHA256)
+ then the Ed25519 or RSA keys should be single-use keys.
+
+ @param f The fulfillment
+ @param c The condition
+*/
+bool
+validateTrigger (
+ Fulfillment const& f,
+ Condition const& c);
+
+}
+}
+
+#endif
diff --git a/src/ripple/conditions/impl/Condition.cpp b/src/ripple/conditions/impl/Condition.cpp
new file mode 100644
index 000000000..72bbee835
--- /dev/null
+++ b/src/ripple/conditions/impl/Condition.cpp
@@ -0,0 +1,188 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+boost::optional
+loadCondition(std::string const& s)
+{
+ static boost::regex const re_current (
+ "^" // start of line
+ "cc:" // 'cc' for cryptocondition
+ "([1-9a-f][0-9a-f]{0,3}|0):" // type (hexadecimal)
+ "([1-9a-f][0-9a-f]{0,15}):" // feature bitmask (hexadecimal)
+ "([a-zA-Z0-9_-]{0,86}):" // fingerprint (base64url)
+ "([1-9][0-9]{0,17}|0)" // fulfillment length (decimal)
+ "$" // end of line
+ , boost::regex_constants::optimize
+ );
+
+ boost::smatch match;
+
+ if (!boost::regex_match (s, match, re_current))
+ return boost::none;
+
+ try
+ {
+ Condition c;
+
+ c.type = parse_hexadecimal (match[1]);
+
+ if (!isCondition (c.type))
+ return boost::none;
+
+ c.featureBitmask = parse_hexadecimal(match[2]);
+ c.maxFulfillmentLength = parse_decimal(match[4]);
+
+ if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
+ return boost::none;
+
+ // TODO: Avoid copying by decoding directly
+ // into the condition's buffer
+ auto fingerprint = base64url_decode(match[3]);
+
+ if (fingerprint.size() != c.fingerprint.size())
+ return boost::none;
+
+ std::memcpy(
+ c.fingerprint.data(),
+ fingerprint.data(),
+ fingerprint.size());
+
+ return c;
+ }
+ catch (std::exception const&)
+ {
+ return boost::none;
+ }
+}
+
+boost::optional
+loadCondition(Slice s)
+{
+ if (s.empty())
+ return boost::none;
+
+ try
+ {
+ auto start = s.data();
+ auto finish = s.data() + s.size();
+
+ Condition c;
+
+ std::tie (start, c.type) =
+ oer::decode_integer (
+ start, finish);
+
+ if (!isCondition (c.type))
+ return boost::none;
+
+ std::tie (start, c.featureBitmask) =
+ oer::decode_varuint (
+ start, finish);
+
+ {
+ std::size_t len;
+
+ std::tie (start, len) =
+ oer::decode_length (start, finish);
+
+ // Incorrect signature length
+ if (len != c.fingerprint.size())
+ return boost::none;
+
+ // Short buffer
+ if (std::distance (start, finish) < len)
+ return boost::none;
+
+ auto p = c.fingerprint.data();
+
+ while (len--)
+ *p++ = *start++;
+ }
+
+ if (start == finish)
+ return boost::none;
+
+ std::tie (start, c.maxFulfillmentLength) =
+ oer::decode_varuint (
+ start, finish);
+
+ // The maximum supported length of a fulfillment is
+ // the largest allowable value, so checking here is
+ // not helpful.
+ return c;
+ }
+ catch (std::exception const&)
+ {
+ return boost::none;
+ }
+}
+
+std::string
+to_string (Condition const& c)
+{
+ return std::string("cc:") +
+ to_hex (c.type) + ":" +
+ to_hex (c.featureBitmask) + ":" +
+ base64url_encode(c.fingerprint) + ":" +
+ to_dec (c.maxFulfillmentLength);
+}
+
+std::vector
+to_blob (Condition const& c)
+{
+ // TODO: optimize this
+ std::vector v;
+ v.reserve (48);
+
+ oer::encode_integer (
+ c.type,
+ std::back_inserter(v));
+
+ oer::encode_varuint (
+ c.featureBitmask,
+ std::back_inserter(v));
+
+ oer::encode_octetstring (
+ c.fingerprint.size(),
+ c.fingerprint.begin(),
+ c.fingerprint.end(),
+ std::back_inserter(v));
+
+ oer::encode_varuint (
+ c.maxFulfillmentLength,
+ std::back_inserter(v));
+
+ return v;
+}
+
+}
+
+}
diff --git a/src/ripple/conditions/impl/Fulfillment.cpp b/src/ripple/conditions/impl/Fulfillment.cpp
new file mode 100644
index 000000000..318f4d610
--- /dev/null
+++ b/src/ripple/conditions/impl/Fulfillment.cpp
@@ -0,0 +1,209 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+bool
+fulfills (
+ Fulfillment const& f,
+ Condition const& c)
+{
+ // Fast check: the fulfillment's type must match the
+ // conditions's type:
+ if (f.type() != c.type)
+ return false;
+
+ // Ensure that the condition is well-formed
+ if (!validate (c))
+ return false;
+
+ // The fulfillment payload can be no larger than the
+ // what the condition allows.
+ if (f.payloadSize() > c.maxFulfillmentLength)
+ return false;
+
+ return f.condition() == c;
+}
+
+bool
+validate (
+ Fulfillment const& f,
+ Condition const& c,
+ Slice m)
+{
+ return fulfills (f, c) && f.validate (m);
+}
+
+bool
+validateTrigger (
+ Fulfillment const& f,
+ Condition const& c)
+{
+ return validate (f, c, {});
+}
+
+std::unique_ptr
+loadFulfillment (std::uint16_t type, Slice payload)
+{
+ std::unique_ptr p;
+
+ switch (type)
+ {
+ default:
+ throw std::domain_error (
+ "Unknown cryptocondition type " +
+ std::to_string (type));
+ }
+
+ // If the payload can't be parsed, the load should
+ // fail.
+ if (p && !p->parsePayload(payload))
+ p.reset();
+
+ return p;
+}
+
+// Parse a condition from its string form
+std::unique_ptr
+loadFulfillment (std::string const& s)
+{
+ // CHECKME: custom parser maybe? probably faster but
+ // more work and probability of error.
+
+ // TODO: use two regex: one that accepts anything the
+ // standard supports and one which accepts only what
+ // we support. Parse with both for improved errors?
+ static boost::regex const re_current (
+ "^" // start of line
+ "cf:" // 'cf' for fulfillment
+ "([1-9a-f][0-9a-f]{0,3}|0):" // type
+ "([a-zA-Z0-9_-]*)" // fulfillment payload (base64url)
+ "$" // end of line
+ , boost::regex_constants::optimize
+ );
+
+ try
+ {
+ boost::smatch match;
+
+ if (!boost::regex_match (s, match, re_current))
+ return nullptr;
+
+ std::uint16_t const type =
+ parse_hexadecimal(match[1]);
+
+ auto payload = base64url_decode (match[2]);
+
+ if (payload.size() > maxSupportedFulfillmentLength)
+ return nullptr;
+
+ return loadFulfillment (type, makeSlice (payload));
+ }
+ catch (std::exception const&)
+ {
+ return nullptr;
+ }
+}
+
+std::unique_ptr
+loadFulfillment (Slice s)
+{
+ if (s.empty())
+ return nullptr;
+
+ try
+ {
+ auto start = s.data();
+ auto finish = s.data() + s.size();
+
+ std::uint16_t type;
+ std::size_t len;
+
+ std::tie (start, type) =
+ oer::decode_integer (
+ start, finish);
+
+ if (!isCondition (type))
+ return nullptr;
+
+ if (start == finish)
+ return nullptr;
+
+ std::tie (start, len) =
+ oer::decode_length(
+ start, finish);
+
+ if (len)
+ {
+ if (len > maxSupportedFulfillmentLength)
+ return nullptr;
+
+ if (std::distance (start, finish) < len)
+ return nullptr;
+ }
+
+ return loadFulfillment (type, Slice{ start, len });
+ }
+ catch (std::exception const&)
+ {
+ return nullptr;
+ }
+}
+
+std::string
+to_string (Fulfillment const& f)
+{
+ return std::string("cf:") + to_hex(f.type())
+ + ":" + base64url_encode (f.payload());
+}
+
+std::vector
+to_blob (Fulfillment const& f)
+{
+ // NIKB TODO optimize this
+ std::vector v;
+
+ auto const p = f.payload();
+
+ oer::encode_integer (
+ f.type(),
+ std::back_inserter(v));
+
+ oer::encode_length (
+ p.size(), std::back_inserter(v));
+
+ oer::encode_octetstring (
+ p.data(),
+ p.data() + p.size(),
+ std::back_inserter(v));
+
+ return v;
+}
+
+}
+}
diff --git a/src/ripple/conditions/impl/base64.h b/src/ripple/conditions/impl/base64.h
new file mode 100644
index 000000000..8e8edc0ed
--- /dev/null
+++ b/src/ripple/conditions/impl/base64.h
@@ -0,0 +1,204 @@
+//
+// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BEAST_DETAIL_BASE64_HPP
+#define BEAST_DETAIL_BASE64_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
+ Copyright notice:
+
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+namespace ripple {
+namespace cryptoconditions {
+
+// NIKB NOTE: This has *NOT* standard base64 - it's base64url,
+// which replaces the `+` with a `-` and the the `/` with a `_`
+// with the padding suppressed on encoding and rejected on
+// decoding.
+
+template
+std::string const&
+base64url_alphabet()
+{
+ static std::string const alphabet =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_";
+ return alphabet;
+}
+
+inline
+bool
+is_base64url(unsigned char c)
+{
+ return (std::isalnum(c) || (c == '-') || (c == '_'));
+}
+
+template
+std::string
+base64url_encode (std::uint8_t const* data,
+ std::size_t in_len)
+{
+ unsigned char c3[3], c4[4];
+ int i = 0;
+ int j = 0;
+
+ std::string ret;
+ ret.reserve (3 + in_len * 8 / 6);
+
+ char const* alphabet (base64url_alphabet().data());
+
+ while(in_len--)
+ {
+ c3[i++] = *(data++);
+ if(i == 3)
+ {
+ c4[0] = (c3[0] & 0xfc) >> 2;
+ c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
+ c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
+ c4[3] = c3[2] & 0x3f;
+ for(i = 0; (i < 4); i++)
+ ret += alphabet[c4[i]];
+ i = 0;
+ }
+ }
+
+ if(i)
+ {
+ for(j = i; j < 3; j++)
+ c3[j] = '\0';
+
+ c4[0] = (c3[0] & 0xfc) >> 2;
+ c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
+ c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
+ c4[3] = c3[2] & 0x3f;
+
+ for(j = 0; (j < i + 1); j++)
+ ret += alphabet[c4[j]];
+ }
+
+ return ret;
+
+}
+
+template
+std::string
+base64url_encode (std::array const& d)
+{
+ return base64url_encode (d.data(), d.size());
+}
+
+template
+std::string
+base64url_encode (std::vector const& d)
+{
+ return base64url_encode (d.data(), d.size());
+}
+
+template
+std::string
+base64url_encode (Buffer const& d)
+{
+ return base64url_encode (d.data(), d.size());
+}
+
+template
+std::string
+base64url_encode (Slice d)
+{
+ return base64url_encode (d.data(), d.size());
+}
+
+template
+std::vector
+base64url_decode(std::string const& data)
+{
+ int in_len = data.size();
+ std::uint8_t c4[4];
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+
+ std::vector ret;
+ ret.reserve (in_len * 6 / 8);
+
+ while(in_len-- && is_base64url(data[in_]))
+ {
+ c4[i++] = data[in_]; in_++;
+ if(i == 4) {
+ for(i = 0; i < 4; i++)
+ c4[i] = static_cast(
+ base64url_alphabet().find(c4[i]));
+
+ ret.push_back ((c4[0] << 2) + ((c4[1] & 0x30) >> 4));
+ ret.push_back (((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2));
+ ret.push_back (((c4[2] & 0x3) << 6) + c4[3]);
+
+ i = 0;
+ }
+ }
+
+ if(i)
+ {
+ for(j = i; j < 4; j++)
+ c4[j] = 0;
+
+ for(j = 0; j < 4; j++)
+ c4[j] = static_cast(
+ base64url_alphabet().find(c4[j]));
+
+ std::uint8_t c3[3];
+
+ c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
+ c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
+ c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
+
+ for(j = 0; (j < i - 1); j++)
+ ret.push_back (c3[j]);
+ }
+
+ return ret;
+}
+
+}
+}
+
+#endif
diff --git a/src/ripple/conditions/impl/utils.h b/src/ripple/conditions/impl/utils.h
new file mode 100644
index 000000000..b59ad312b
--- /dev/null
+++ b/src/ripple/conditions/impl/utils.h
@@ -0,0 +1,368 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_UTILS_H
+#define RIPPLE_CONDITIONS_UTILS_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+inline
+std::string
+hexstr (std::vector const& data)
+{
+ std::string s;
+ s.reserve (data.size() * 2);
+
+ for (auto d : data)
+ {
+ s.push_back (charHex (d >> 4));
+ s.push_back (charHex (d & 15));
+ }
+
+ return s;
+}
+
+inline
+std::vector
+hexblob (std::string const& s)
+{
+ std::vector result;
+ result.reserve (1 + (s.size () / 2));
+
+ auto iter = s.cbegin ();
+
+ if (s.size () & 1)
+ {
+ int c = charUnHex (*iter++);
+
+ if (c < 0)
+ Throw("Invalid hex in blob");
+
+ result.push_back(c);
+ }
+
+ while (iter != s.cend ())
+ {
+ int cHigh = charUnHex (*iter++);
+
+ if (cHigh < 0)
+ Throw("Invalid hex in blob");
+
+ int cLow = charUnHex (*iter);
+
+ if (cLow < 0)
+ Throw("Invalid hex in blob");
+
+ iter++;
+
+ result.push_back (
+ static_cast(cHigh << 4) |
+ static_cast(cLow));
+ }
+
+ return result;
+}
+
+template
+T parse_decimal(std::string const& s)
+{
+ T t = 0;
+
+ for (auto const c : s)
+ {
+ if (c < '0' || c > '9')
+ throw std::domain_error ("invalid decimal digit");
+
+ t = (t * 10) + (c - '0');
+ }
+
+ return t;
+}
+
+template
+T parse_hexadecimal(std::string const& s)
+{
+ T t = 0;
+
+ for (auto const c : s)
+ {
+ if (c >= '0' && c <= '9')
+ t = (t * 16) + (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ t = (t * 16) + 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ t = (t * 16) + 10 + (c - 'A');
+ else
+ throw std::domain_error ("invalid hexadecimal digit");
+ }
+
+ return t;
+}
+
+template
+std::string
+to_hex (Integer value)
+{
+ std::stringstream ss;
+ ss << std::hex << value;
+ return ss.str();
+}
+
+template
+std::string
+to_dec (Integer value)
+{
+ std::stringstream ss;
+ ss << std::dec << value;
+ return ss.str();
+}
+
+// ISO/IEC 8825/7 or ITU-T X.696: Octet Encoding Rules
+// FIXME: This assumes a little-endian architecture!
+namespace oer
+{
+
+// Simple conversion: write integer as big-endian byte stream
+// This needs to be improved and optimized:
+template
+void
+encode_integer(Integer value, OutputIt out)
+{
+ static_assert (
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value,
+ "encode_integer accepts only std::uint{8,16,32,64}_t");
+
+ std::size_t n = sizeof(Integer);
+
+ while(n--)
+ {
+ *out++ = static_cast(
+ (value >> (n * 8)) & 0xFF);
+ }
+}
+
+// Simple conversion: big-endian byte stream to integer
+template
+std::pair
+decode_integer(InputIt begin, InputIt end)
+{
+ static_assert (
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value,
+ "decode_integer accepts only std::uint{8,16,32,64}_t");
+
+ std::size_t size = std::distance (begin, end);
+
+ if (size < sizeof(Integer))
+ Throw("short integer: " + std::to_string(size));
+
+ Integer res = 0;
+
+ for (std::size_t i = 0; i < sizeof(Integer); ++i)
+ res = (res << 8) | *begin++;
+
+ return { begin, res };
+}
+
+template
+inline
+OutputIt
+encode_length (std::size_t len, OutputIt out)
+{
+ if (len <= 0x7F)
+ {
+ *out++ = static_cast(len & 0x7F);
+ return out;
+ }
+
+ // Decide how many bytes we need:
+ if (len <= 0xFFFF)
+ {
+ *out++ = 0x82;
+ *out++ = static_cast((len >> 8) & 0xFF);
+ *out++ = static_cast(len & 0xFF);
+ return out;
+ }
+
+ if (len <= 0xFFFFFF)
+ {
+ *out++ = 0x83;
+ *out++ = static_cast((len >> 16) & 0xFF);
+ *out++ = static_cast((len >> 8) & 0xFF);
+ *out++ = static_cast(len & 0xFF);
+ return out;
+ }
+
+ if (len <= 0xFFFFFFFF)
+ {
+ *out++ = 0x84;
+ *out++ = static_cast((len >> 24) & 0xFF);
+ *out++ = static_cast((len >> 16) & 0xFF);
+ *out++ = static_cast((len >> 8) & 0xFF);
+ *out++ = static_cast(len & 0xFF);
+ return out;
+ }
+
+ // Note: OER can represent lengths up to (2^1016) - 1,
+ // which is, truly, enough for everyone. We never
+ // exceed 2^32.
+ Throw("overlong encoding length: " + std::to_string(len));
+}
+
+// A "streambuf" would serve us better here - instead of the
+// crazy paired return, we consume data from it and things
+// just magically work.
+template
+std::pair
+decode_length (InputIt begin, InputIt end)
+{
+ if (begin == end)
+ Throw("empty buffer");
+
+ std::size_t bytes = *begin++;
+
+ if (bytes < 128)
+ return { begin, bytes };
+
+ bytes &= 0x7F;
+
+ if (bytes > 4)
+ Throw("overlong encoded length: " + std::to_string(bytes));
+
+ std::size_t len = 0;
+
+ if (std::distance (begin, end) < bytes)
+ Throw("short encoded length: " + std::to_string(bytes));
+
+ while (bytes--)
+ len = (len << 8) | *begin++;
+
+ return { begin, len };
+}
+
+/** Encode a fixed-size octet string: OER 2.6 (2) */
+template
+OutputIt
+encode_octetstring(InputIt begin, InputIt end, OutputIt out)
+{
+ while (begin != end)
+ *out++ = *begin++;
+
+ return out;
+}
+
+/** Encode a dynamic size octet string: OER 2.6 (1) */
+inline
+std::size_t
+predict_octetstring_size(std::size_t size)
+{
+ // Alternatively, always guess 4 + size and call it a day?
+ if (size <= 0x7F)
+ return size + 1;
+
+ // Decide how many bytes we need:
+ if (size <= 0xFFFF)
+ return size + 3;
+
+ if (size <= 0xFFFFFF)
+ return size + 4;
+
+ if (size <= 0xFFFFFFFF)
+ return size + 5;
+
+ Throw("overlong encoding length: " + std::to_string(size));
+}
+
+/** Encode an dynamic size octet string: OER 2.6 (1) */
+template
+OutputIt
+encode_octetstring(std::size_t size, InputIt begin, InputIt end, OutputIt out)
+{
+ // This will encode the length first, followed by the
+ // payload octets:
+ return encode_octetstring (
+ begin,
+ end,
+ oer::encode_length (size, out));
+}
+
+template
+std::enable_if_t::value>
+encode_varuint (Integer value, OutputIt out)
+{
+ auto count = [](Integer n)
+ {
+ std::size_t c = 0;
+
+ do
+ {
+ n >>= 8;
+ ++c;
+ } while (n);
+
+ return c;
+ };
+
+ std::size_t c = count (value);
+
+ out = encode_length (c, out);
+
+ while(c--)
+ {
+ *out++ = static_cast(
+ (value >> (c * 8)) & 0xFF);
+ }
+}
+
+template
+std::enable_if_t::value, std::pair>
+decode_varuint (InputIt begin, InputIt end)
+{
+ auto y = decode_length (begin, end);
+
+ if (y.second > sizeof(Integer))
+ Throw("Encoded integer exceeds allowable range: " + std::to_string(y.second));
+
+ Integer x = 0;
+
+ for (std::size_t i = 0; i != y.second; ++i)
+ x = (x << 8) + *y.first++;
+
+ return { y.first, x };
+}
+
+}
+}
+}
+
+#endif
diff --git a/src/ripple/unity/conditions.cpp b/src/ripple/unity/conditions.cpp
new file mode 100644
index 000000000..41bb21824
--- /dev/null
+++ b/src/ripple/unity/conditions.cpp
@@ -0,0 +1,23 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+
+#include
+#include
diff --git a/src/unity/conditions_test_unity.cpp b/src/unity/conditions_test_unity.cpp
new file mode 100644
index 000000000..398cc1921
--- /dev/null
+++ b/src/unity/conditions_test_unity.cpp
@@ -0,0 +1,19 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+