diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 526470a6d..3d79afca7 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1802,6 +1802,8 @@
+
+
@@ -4555,6 +4557,9 @@
True
+
+ True
+
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 52ba9c06e..fb679895d 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -2442,6 +2442,9 @@
ripple\conditions\impl
+
+ ripple\conditions
+
ripple\conditions
@@ -5337,6 +5340,9 @@
test\conditions
+
+ test\conditions
+
test\conditions
diff --git a/src/ripple/conditions/Condition.h b/src/ripple/conditions/Condition.h
index e36ed25c7..017bbcb08 100644
--- a/src/ripple/conditions/Condition.h
+++ b/src/ripple/conditions/Condition.h
@@ -127,6 +127,29 @@ validate (Condition const& c)
if (c.type == condition_hashlock)
return (c.featureBitmask == (feature_sha256 | feature_preimage));
+ // A prefix condition contains a subfulfillment; it
+ // requires all the features its child may require.
+ if (c.type == condition_prefix_sha256)
+ {
+ auto const mask = (feature_sha256 | feature_prefix);
+
+ // We need to have at least our own feature suites:
+ auto const cf1 = c.featureBitmask & mask;
+
+ if (cf1 != mask)
+ return false;
+
+ // And at least one more feature suite for our
+ // subfulfillment (since you need to terminate a
+ // chain of prefix conditions with a non-prefix)
+ auto const cf2 = c.featureBitmask & ~mask;
+
+ if (cf2 == 0)
+ return false;
+
+ return (cf2 & definedFeatures) == cf2;
+ }
+
if (c.type == condition_ed25519)
return (c.featureBitmask == feature_ed25519);
diff --git a/src/ripple/conditions/PrefixSha256.h b/src/ripple/conditions/PrefixSha256.h
new file mode 100644
index 000000000..643e2c600
--- /dev/null
+++ b/src/ripple/conditions/PrefixSha256.h
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+/*
+ 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_PREFIX_SHA256_H
+#define RIPPLE_CONDITIONS_PREFIX_SHA256_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+class PrefixSha256 final
+ : public Fulfillment
+{
+ Buffer prefix_;
+ std::unique_ptr subfulfillment_;
+
+public:
+ PrefixSha256 () = default;
+
+ std::size_t
+ payloadSize () const override
+ {
+ return
+ oer::predict_octetstring_size(prefix_.size()) +
+ subfulfillment_->payloadSize();
+ }
+
+ Buffer
+ payload() const override
+ {
+ // We should never have a condition in a state that
+ // isn't ok to call payload on.
+ if (!ok())
+ return {};
+
+ auto const subpayload = to_blob (*subfulfillment_);
+
+ Buffer b (subpayload.size() +
+ oer::predict_octetstring_size (prefix_.size()));
+
+ auto out = oer::encode_octetstring (
+ prefix_.size(),
+ prefix_.data(),
+ prefix_.data() + prefix_.size(),
+ b.data());
+
+ std::memcpy (out, subpayload.data(), subpayload.size());
+
+ return b;
+ }
+
+ Condition
+ condition() const override
+ {
+ auto const sc = subcondition();
+ auto const blob = to_blob (sc);
+
+ Buffer b (blob.size() +
+ oer::predict_octetstring_size (prefix_.size()));
+
+ auto out = oer::encode_octetstring (
+ prefix_.size(),
+ prefix_.data(),
+ prefix_.data() + prefix_.size(),
+ b.data());
+
+ std::memcpy (out, blob.data(), blob.size());
+
+ sha256_hasher h;
+ h (b.data(), b.size());
+
+ Condition cc;
+ cc.type = type();
+ cc.featureBitmask = features();
+ cc.maxFulfillmentLength = payloadSize();
+ cc.fingerprint = static_cast(h);
+
+ return cc;
+ }
+
+ std::uint16_t
+ type () const override
+ {
+ return condition_prefix_sha256;
+ }
+
+ std::uint32_t
+ features () const override
+ {
+ return
+ feature_sha256 |
+ feature_prefix |
+ subfulfillment_->features();
+ }
+
+ bool
+ ok () const override
+ {
+ return static_cast(subfulfillment_);
+ }
+
+ bool
+ validate (Slice m) const override
+ {
+ if (!ok())
+ return false;
+
+ // Prepend the prefix to the message:
+ Buffer b (prefix_.size() + m.size());
+
+ if (prefix_.size())
+ std::memcpy (b.data(), prefix_.data(), prefix_.size());
+
+ if (m.size())
+ std::memcpy (b.data() + prefix_.size(), m.data(), m.size());
+
+ return subfulfillment_->validate (b);
+ }
+
+ Fulfillment const&
+ subfulfillment () const
+ {
+ return *subfulfillment_;
+ }
+
+ Condition
+ subcondition () const
+ {
+ return subfulfillment_->condition();
+ }
+
+ bool
+ parsePayload (Slice s) override
+ {
+ // The payload consists of the prefix, followed by
+ // a subfulfillment. It cannot be empty:
+ if (s.empty())
+ return false;
+
+ auto start = s.data();
+ auto finish = s.data() + s.size();
+
+ std::size_t len;
+
+ std::tie (start, len) = oer::decode_length (
+ start, finish);
+
+ if (len != 0)
+ {
+ if (std::distance (start, finish) < len)
+ return false;
+
+ std::memcpy (prefix_.alloc (len), start, len);
+ std::advance (start, len);
+ }
+
+ s += std::distance (s.data(), start);
+
+ // The remaining bytes in the slice are a fulfillment
+ // so we parse it as such. If we can, then we've
+ // succeeded.
+ subfulfillment_ = loadFulfillment (s);
+
+ if (!subfulfillment_)
+ {
+ prefix_.clear();
+ return false;
+ }
+
+ return true;
+ }
+
+ void setPrefix (Slice prefix)
+ {
+ prefix_ = prefix;
+ }
+
+ Slice prefix() const
+ {
+ return prefix_;
+ }
+
+ void setSubfulfillment (std::unique_ptr subfulfillment)
+ {
+ subfulfillment_ = std::move (subfulfillment);
+ }
+};
+
+}
+
+}
+
+#endif
diff --git a/src/ripple/conditions/impl/Fulfillment.cpp b/src/ripple/conditions/impl/Fulfillment.cpp
index b04a7b071..9584d2f5e 100644
--- a/src/ripple/conditions/impl/Fulfillment.cpp
+++ b/src/ripple/conditions/impl/Fulfillment.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -80,6 +81,10 @@ loadFulfillment (std::uint16_t type, Slice payload)
p = std::make_unique();
break;
+ case condition_prefix_sha256:
+ p = std::make_unique();
+ break;
+
case condition_ed25519:
p = std::make_unique();
break;
diff --git a/src/test/conditions/PrefixSha256_test.cpp b/src/test/conditions/PrefixSha256_test.cpp
new file mode 100644
index 000000000..6bad71c85
--- /dev/null
+++ b/src/test/conditions/PrefixSha256_test.cpp
@@ -0,0 +1,562 @@
+//------------------------------------------------------------------------------
+/*
+ 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
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+class PrefixSha256_test : public beast::unit_test::suite
+{
+ void check (
+ Fulfillment const& f,
+ Condition const& c,
+ Slice test,
+ Slice good)
+ {
+ BEAST_EXPECT (validate (f, c, test) ==
+ ((test == good) && (f.condition() == c)));
+ }
+
+ void testMalformedCondition ()
+ {
+ testcase ("Malformed Condition");
+
+ // This is malformed and will not load because a
+ // feature suite of 0 is not supported.
+ auto c1 = loadCondition (
+ "cc:1:0:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
+ BEAST_EXPECT (!c1);
+
+ // The following will load but fail in different ways
+ auto c2 = loadCondition ( // only sha256
+ "cc:1:1:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
+ BEAST_EXPECT (c2 && !validate(*c2));
+
+ auto c3 = loadCondition ( // only preimage
+ "cc:1:4:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
+ BEAST_EXPECT (c3 && !validate(*c3));
+
+ auto c4 = loadCondition ( // only sha256+preimage
+ "cc:1:5:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
+ BEAST_EXPECT (c4 && !validate(*c4));
+ }
+
+ void testPrefix ()
+ {
+ testcase ("Prefix");
+
+ std::string const prefix1 = "prefix1";
+ std::string const prefix2 = "prefix2";
+
+ std::uint8_t msg[8];
+ std::iota (std::begin(msg), std::end(msg), std::uint8_t(39));
+
+ std::array const sk =
+ {{
+ 0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
+ 0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
+ 0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
+ 0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
+ }};
+
+ {
+ PrefixSha256 f1;
+ f1.setPrefix(makeSlice (prefix1));
+ f1.setSubfulfillment(std::make_unique (
+ makeSlice (prefix1)));
+
+ PrefixSha256 f2;
+ f2.setPrefix(makeSlice (prefix2));
+ f2.setSubfulfillment(std::make_unique (
+ makeSlice (prefix1)));
+
+ BEAST_EXPECT (f1 != f2);
+ BEAST_EXPECT (f1.condition() != f2.condition());
+
+ // Validating with own condition should succeed.
+ BEAST_EXPECT (validate (f1, f1.condition(), {}));
+ BEAST_EXPECT (validate (f2, f2.condition(), {}));
+
+ for (std::size_t i = 1; i != sizeof(msg); ++i)
+ {
+ BEAST_EXPECT (validate (f1, f1.condition(),
+ Slice{msg, i}));
+ BEAST_EXPECT (validate (f2, f2.condition(),
+ Slice{msg, i}));
+ }
+
+ // The rest should fail:
+ BEAST_EXPECT (! validate (f1, f2.condition(), {}));
+ BEAST_EXPECT (! validate (f2, f1.condition(), {}));
+
+ for (std::size_t i = 1; i != sizeof(msg); ++i)
+ {
+ BEAST_EXPECT (! validate (f1, f2.condition(),
+ Slice{msg, i}));
+ BEAST_EXPECT (! validate (f2, f1.condition(),
+ Slice{msg, i}));
+ }
+ }
+
+ {
+ PrefixSha256 f1;
+ f1.setPrefix(makeSlice (prefix1));
+ f1.setSubfulfillment(std::make_unique (
+ makeSlice (prefix1)));
+
+ PrefixSha256 f2;
+ f2.setPrefix(makeSlice (prefix2));
+ f2.setSubfulfillment(std::make_unique (
+ makeSlice (prefix2)));
+
+ BEAST_EXPECT (f1 != f2);
+ BEAST_EXPECT (f1.condition() != f2.condition());
+ BEAST_EXPECT (validate (f1, f1.condition(), {}));
+ BEAST_EXPECT (validate (f2, f2.condition(), {}));
+ BEAST_EXPECT (! validate (f1, f2.condition(), {}));
+ BEAST_EXPECT (! validate (f2, f1.condition(), {}));
+
+ // For preimage conditions, the message shouldn't
+ // matter, so verify that it does not:
+ for (std::size_t i = 1; i != sizeof(msg); ++i)
+ {
+ BEAST_EXPECT (validate (f1, f1.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (validate (f2, f2.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (! validate (f1, f2.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (! validate (f2, f1.condition(),
+ Slice(msg, i)));
+ }
+ }
+
+ {
+ PrefixSha256 f1;
+ f1.setPrefix(makeSlice (prefix1));
+ f1.setSubfulfillment(std::make_unique (
+ SecretKey{ sk }, makeSlice (prefix1)));
+
+ PrefixSha256 f2;
+ f2.setPrefix(makeSlice (prefix2));
+ f2.setSubfulfillment(std::make_unique (
+ SecretKey{ sk }, makeSlice (prefix2)));
+
+ BEAST_EXPECT (f1 != f2);
+ BEAST_EXPECT (f1.condition() != f2.condition());
+ BEAST_EXPECT (validate (f1, f1.condition(), {}));
+ BEAST_EXPECT (validate (f2, f2.condition(), {}));
+ BEAST_EXPECT (! validate (f1, f2.condition(), {}));
+ BEAST_EXPECT (! validate (f2, f1.condition(), {}));
+
+ // For non-prefix conditions, the message matters
+ // so verify that it does:
+ for (std::size_t i = 1; i < sizeof(msg); ++i)
+ {
+ BEAST_EXPECT (! validate (f1, f1.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (! validate (f2, f2.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (! validate (f1, f2.condition(),
+ Slice(msg, i)));
+ BEAST_EXPECT (! validate (f2, f1.condition(),
+ Slice(msg, i)));
+ }
+ }
+
+ { // Test signing with non-empty prefix and non-empty
+ // message to ensure that the prefix is properly
+ // prepended to the message:
+ std::string const m = prefix1 + prefix2;
+
+ // Construct a prefix condition with the prefix
+ // prefix1, containing a Ed25519 signature for prefix1+prefix2
+ // and check that it passing prefix2 validates, while
+ // passing anything else fails:
+ PrefixSha256 f;
+ f.setPrefix(makeSlice (prefix1));
+ f.setSubfulfillment(std::make_unique (
+ SecretKey{ sk }, makeSlice (m)));
+
+ BEAST_EXPECT (to_string(f) ==
+ "cf:1:B3ByZWZpeDEABGBEKZMGUASqHkxI9N0BWBlMA"
+ "jSbzGZM2W5ADqJpkYqNUTiaLmMYVDHrc-tKqXcmRIT"
+ "RFqtYxru4rMSIplCYRP71H9tD09mnfqw4eu5FAJZw1"
+ "wa_NOmw78ADIlB4_ENJWAo");
+
+ auto const c = f.condition();
+
+ BEAST_EXPECT (validate (f, c, makeSlice(prefix2)));
+
+ BEAST_EXPECT (! validate (f, c, {}));
+ BEAST_EXPECT (! validate (f, c, makeSlice(prefix1)));
+ BEAST_EXPECT (! validate (f, c, makeSlice(m)));
+
+ for (std::size_t i = 1; i < sizeof(msg); ++i)
+ BEAST_EXPECT (! validate (f, c, Slice(msg, i)));
+ }
+ }
+
+ void testKnown ()
+ {
+ testcase ("Known");
+
+ Slice const empty {};
+ Slice const abc { "abc", 3 };
+ Slice const abcd { "abcd", 4 };
+ Slice const vwxyz { "vwxyz", 5 };
+
+ { // empty prefix with an empty PREIMAGE-SHA256 subfulfillment
+ auto f = loadFulfillment ("cf:1:AAAAAA");
+ BEAST_EXPECT (f);
+
+ {
+ auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
+ BEAST_EXPECT (f2);
+ BEAST_EXPECT (*f == *f2);
+ }
+
+ auto c = loadCondition ("cc:1:7:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
+ BEAST_EXPECT (c);
+
+ {
+ auto c1 = loadCondition (to_string(*c));
+ BEAST_EXPECT (c1);
+ BEAST_EXPECT (*c == *c1);
+
+ auto c2 = loadCondition (makeSlice(to_blob(*c)));
+ BEAST_EXPECT (c2);
+ BEAST_EXPECT (*c == *c2);
+ }
+
+ // Ensure that it has the correct features set
+ BEAST_EXPECT (f->features() ==
+ (feature_sha256 | feature_prefix | feature_preimage));
+
+ // Test manual construction
+ {
+ PrefixSha256 f2;
+ f2.setPrefix({});
+ f2.setSubfulfillment(loadFulfillment ("cf:0:"));
+
+ BEAST_EXPECT (f2 == *f);
+ BEAST_EXPECT (f2.condition() == *c);
+ }
+
+ // The PREIMAGE-SHA256 we contain validates for
+ // any message. So, this condition should work
+ // with any buffer:
+ check (*f, c.get(), empty, empty);
+ check (*f, c.get(), abc, abc);
+ check (*f, c.get(), abcd, abcd);
+ check (*f, c.get(), vwxyz, vwxyz);
+ }
+
+ { // A PREFIX-SHA256 with an empty prefix, wrapping
+ // the PREFIX-SHA256 condition we created above
+ // which contains a PREIMAGE-SHA256
+ auto f = loadFulfillment ("cf:1:AAABBAAAAAA");
+ BEAST_EXPECT (f);
+
+ {
+ auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
+ BEAST_EXPECT (f2);
+ BEAST_EXPECT (*f == *f2);
+ }
+
+ auto c = loadCondition ("cc:1:7:Mp5A0CLrJOMAUMe0-qFb-_5U2C0X-iuwwfvumOT0go8:2");
+ BEAST_EXPECT (c);
+
+ {
+ auto c1 = loadCondition (to_string(*c));
+ BEAST_EXPECT (c1);
+ BEAST_EXPECT (*c == *c1);
+
+ auto c2 = loadCondition (makeSlice(to_blob(*c)));
+ BEAST_EXPECT (c2);
+ BEAST_EXPECT (*c == *c2);
+ }
+
+ // Ensure that it has the correct features set
+ BEAST_EXPECT (f->features() ==
+ (feature_sha256 | feature_prefix | feature_preimage));
+
+ // Test manual construction
+ {
+ PrefixSha256 f2;
+ f2.setPrefix({});
+ f2.setSubfulfillment(loadFulfillment ("cf:1:AAAAAA"));
+
+ BEAST_EXPECT (f2 == *f);
+ BEAST_EXPECT (f2.condition() == *c);
+ }
+
+ // The PREIMAGE-SHA256 we contain validates for
+ // any message. So, this condition should work
+ // with any buffer:
+ check (*f, c.get(), empty, empty);
+ check (*f, c.get(), abc, abc);
+ check (*f, c.get(), abcd, abcd);
+ check (*f, c.get(), vwxyz, vwxyz);
+ }
+
+ { // A PREFIX-SHA256, with the prefix set to 'abc'
+ // that wraps around an ED25519 condition signing
+ // the message 'abc':
+ auto f = loadFulfillment (
+ "cf:1:A2FiYwAEYHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1rsara"
+ "pEir_D33Llmf_YTE2iUcytueMJvW2cxAeJn_i4rZfpNU9rUeKGtpk1Q_R39t9l"
+ "JINw-GlZKZHscujVgAQ");
+ BEAST_EXPECT (f);
+
+ {
+ auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
+ BEAST_EXPECT (f2);
+ BEAST_EXPECT (*f == *f2);
+ }
+
+ auto c = loadCondition ("cc:1:25:KHqL2K2uisoMhxznwl-6pai-ENDk2x9Wru6Ls63O5Vs:100");
+ BEAST_EXPECT (c);
+
+ {
+ auto c1 = loadCondition (to_string(*c));
+ BEAST_EXPECT (c1);
+ BEAST_EXPECT (*c == *c1);
+
+ auto c2 = loadCondition (makeSlice(to_blob(*c)));
+ BEAST_EXPECT (c2);
+ BEAST_EXPECT (*c == *c2);
+ }
+
+ // Ensure that it has the correct features set
+ BEAST_EXPECT (f->features() ==
+ (feature_sha256 | feature_prefix | feature_ed25519));
+
+ // Test manual construction
+ {
+ PrefixSha256 f2;
+ f2.setPrefix(abc);
+ f2.setSubfulfillment(loadFulfillment (
+ "cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8"
+ "PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Uk"
+ "g3D4aVkpkexy6NWAB"));
+
+ // Check the subfulfillment directly:
+ auto sc = f2.subcondition();
+
+ check (f2.subfulfillment(), sc, empty, abc);
+ check (f2.subfulfillment(), sc, abc, abc);
+ check (f2.subfulfillment(), sc, abcd, abc);
+ check (f2.subfulfillment(), sc, vwxyz, abc);
+
+ // This may seem counterintuitive, but it's
+ // not: the subfulfillment signed the message
+ // "abc"; our prefix is also "abc" so in order
+ // to verify this condition successfully, the
+ // message must be empty:
+ check (f2, c.get(), empty, empty);
+ check (f2, c.get(), abc, empty);
+ check (f2, c.get(), abcd, empty);
+ check (f2, c.get(), vwxyz, empty);
+ }
+
+ // Like before, the ED25519 condition we contain
+ // signed the message 'abc' which is our prefix
+ // which means that this will only validate with
+ // an empty message:
+ check (*f, c.get(), empty, empty);
+ check (*f, c.get(), abc, empty);
+ check (*f, c.get(), abcd, empty);
+ check (*f, c.get(), vwxyz, empty);
+ }
+ }
+
+ void testBinaryCodec()
+ {
+ testcase ("Binary Encoding");
+
+ // A sample prefix+Ed25519 fulfillment and its
+ // associated condition:
+ std::string const xf =
+ "cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STL"
+ "HDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYu"
+ "cSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAP"
+ "qny6Kwc7IalHUUhJ6vboJ0bbzMcBwo";
+
+ std::string const xc =
+ "cc:1:25:1EMtp3YUOBZgeW3lX1lOIoAbUjx9maUty9TMJpMgXo4:110";
+
+ // The subfulfillment for the above, along with its
+ // associated condition:
+ std::string const xsf =
+ "cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZa"
+ "DRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9"
+ "fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG"
+ "28zHAcK";
+
+ std::string const xsc =
+ "cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96";
+
+ auto f = loadFulfillment (xf);
+ BEAST_EXPECT (f);
+ BEAST_EXPECT (to_string(*f) == xf);
+
+ auto c = loadCondition (xc);
+ BEAST_EXPECT (c);
+ BEAST_EXPECT (to_string(*c) == xc);
+
+ BEAST_EXPECT (f->condition() == c);
+ BEAST_EXPECT (to_string(f->condition()) == xc);
+
+ auto subf = loadFulfillment (xsf);
+ BEAST_EXPECT (subf);
+ BEAST_EXPECT (to_string(*subf) == xsf);
+
+ auto subc = loadCondition (xsc);
+ BEAST_EXPECT (subc);
+ BEAST_EXPECT (to_string(*subc) == xsc);
+
+ // Now generate the binary versions and ensure
+ // that they match what we expect. Then load them
+ // and ensure they're identical:
+ {
+ auto const fblob1 = hexblob(
+ "0001710d48656c6c6f20576f726c642120000460ec172b93ad5e563bf4"
+ "932c70e1245034c35467ef2efd4d64ebf819683467e2bfb62291fad943"
+ "2f8f298b9c4a4895dbe293f6ffda1a68dadf0ccdef5f47a0c7212a5fea"
+ "3cda97a3f4c03ea9f2e8ac1cec86a51d452127abdba09d1b6f331c070a");
+
+ auto const fblob2 = to_blob(*f);
+ BEAST_EXPECT (fblob1 == fblob2);
+
+ auto f2 = loadFulfillment(makeSlice(fblob2));
+ BEAST_EXPECT (f2);
+ BEAST_EXPECT (*f == *f2);
+ }
+
+ {
+ auto const cblob1 = hexblob (
+ "0001012520d4432da77614381660796de55f594e22801b523c7d99a52d"
+ "cbd4cc2693205e8e016e");
+
+ auto const cblob2 = to_blob(*c);
+ BEAST_EXPECT (cblob1 == cblob2);
+
+ auto c2 = loadCondition(makeSlice(cblob2));
+ BEAST_EXPECT (c2);
+ BEAST_EXPECT (*c == c2);
+ }
+ }
+
+ void testNested()
+ {
+ testcase ("Nested");
+
+ std::string const abc = "abc";
+ std::string const def = "def";
+ std::string const abcdef = abc + def;
+
+ { // prefix ("abc", prefix ("def", ed25519 (..., "abcdef")))
+ std::array sk =
+ {{
+ 0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
+ 0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
+ 0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
+ 0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
+ }};
+
+ auto edf = std::make_unique (
+ SecretKey{ sk }, makeSlice (abcdef));
+
+ // Inner
+ auto pif = std::make_unique();
+ pif->setPrefix(makeSlice (abc));
+ pif->setSubfulfillment (std::move(edf));
+
+ // Outer
+ auto pof = std::make_unique();
+ pof->setPrefix(makeSlice (def));
+ pof->setSubfulfillment (std::move (pif));
+
+ auto const c = pof->condition();
+
+ // The condition should validate with an empty
+ // message, since the nested prefixes contain
+ // the full message.
+ check (*pof, c, {}, {});
+
+ // It should fail with anything else.
+ check (*pof, c, makeSlice (abc), {});
+ check (*pof, c, makeSlice (def), {});
+ check (*pof, c, makeSlice (abcdef), {});
+ }
+
+ { // prefix ("abc", prefix ("def", preimage (...)))
+ auto const v = hexblob (
+ "6B62BA0A77D5C7A423A5FC937EE5FF09");
+
+ auto img = std::make_unique (
+ makeSlice (v));
+
+ // Inner
+ auto pif = std::make_unique();
+ pif->setPrefix(makeSlice (abc));
+ pif->setSubfulfillment (std::move(img));
+
+ // Outer
+ auto pof = std::make_unique();
+ pof->setPrefix(makeSlice (def));
+ pof->setSubfulfillment (std::move (pif));
+
+ auto const c = pof->condition();
+
+ // The condition should validate with any message
+ // since it terminates at a preimage, which
+ // validates for any message:
+ check (*pof, c, {}, {});
+ check (*pof, c, makeSlice (abc), makeSlice (abc));
+ check (*pof, c, makeSlice (def), makeSlice (def));
+ check (*pof, c, makeSlice (abcdef), makeSlice (abcdef));
+ }
+ }
+
+ void run ()
+ {
+ testKnown ();
+ testNested ();
+ testPrefix ();
+ testBinaryCodec ();
+ testMalformedCondition ();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE (PrefixSha256, conditions, ripple);
+
+}
+
+}
diff --git a/src/unity/conditions_test_unity.cpp b/src/unity/conditions_test_unity.cpp
index 2387ee7e6..eea946a98 100644
--- a/src/unity/conditions_test_unity.cpp
+++ b/src/unity/conditions_test_unity.cpp
@@ -18,4 +18,5 @@
//==============================================================================
#include
+#include
#include