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