diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index 56b366881..493e02346 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -3208,6 +3208,9 @@
True
+
+ True
+
True
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index 74f5d0db8..780471755 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -3738,6 +3738,9 @@
ripple\rpc\handlers
+
+ ripple\rpc\handlers
+
ripple\rpc\handlers
diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp
index f7b4b10db..e5028af5a 100644
--- a/src/ripple/app/main/Main.cpp
+++ b/src/ripple/app/main/Main.cpp
@@ -138,6 +138,9 @@ void printHelp (const po::options_description& desc)
" version\n"
" server_info\n"
" sign\n"
+#if RIPPLE_ENABLE_MULTI_SIGN
+ " sign_for\n"
+#endif // RIPPLE_ENABLE_MULTI_SIGN
" stop\n"
" submit\n"
" tx \n"
diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp
index 6ad3119d6..ef84a5e99 100644
--- a/src/ripple/app/misc/NetworkOPs.cpp
+++ b/src/ripple/app/misc/NetworkOPs.cpp
@@ -211,19 +211,16 @@ public:
Job&, STTx::pointer,
stCallback callback = stCallback ()) override;
- Transaction::pointer submitTransactionSync (
- Transaction::ref tpTrans,
- bool bAdmin, bool bLocal, bool bFailHard, bool bSubmit) override;
-
Transaction::pointer processTransactionCb (
Transaction::pointer,
- bool bAdmin, bool bLocal, bool bFailHard, stCallback) override;
+ bool bAdmin, bool bLocal, FailHard failType, stCallback) override;
+
Transaction::pointer processTransaction (
Transaction::pointer transaction,
- bool bAdmin, bool bLocal, bool bFailHard) override
+ bool bAdmin, bool bLocal, FailHard failType) override
{
return processTransactionCb (
- transaction, bAdmin, bLocal, bFailHard, stCallback ());
+ transaction, bAdmin, bLocal, failType, stCallback ());
}
// VFALCO Workaround for MSVC std::function which doesn't swallow return
@@ -232,9 +229,9 @@ public:
private:
void processTransactionCbVoid (
Transaction::pointer p,
- bool bAdmin, bool bLocal, bool bFailHard, stCallback cb)
+ bool bAdmin, bool bLocal, FailHard failType, stCallback cb)
{
- processTransactionCb (p, bAdmin, bLocal, bFailHard, cb);
+ processTransactionCb (p, bAdmin, bLocal, failType, cb);
}
public:
@@ -943,51 +940,13 @@ void NetworkOPsImp::submitTransaction (
std::make_shared (trans, Validate::NO, reason),
false,
false,
- false,
+ FailHard::no,
callback));
}
-// Sterilize transaction through serialization.
-// This is fully synchronous and deprecated
-Transaction::pointer NetworkOPsImp::submitTransactionSync (
- Transaction::ref tpTrans,
- bool bAdmin, bool bLocal, bool bFailHard, bool bSubmit)
-{
- Serializer s;
- tpTrans->getSTransaction ()->add (s);
-
- auto tpTransNew = Transaction::sharedTransaction (
- s.getData (), Validate::YES);
-
- if (!tpTransNew)
- {
- // Could not construct transaction.
- return tpTransNew;
- }
-
- if (tpTransNew->getSTransaction ()->isEquivalent (
- *tpTrans->getSTransaction ()))
- {
- if (bSubmit)
- processTransaction (tpTransNew, bAdmin, bLocal, bFailHard);
- }
- else
- {
- m_journal.fatal << "Transaction reconstruction failure";
- m_journal.fatal << tpTransNew->getSTransaction ()->getJson (0);
- m_journal.fatal << tpTrans->getSTransaction ()->getJson (0);
-
- // assert (false); "1e-95" as amount can trigger this
-
- tpTransNew.reset ();
- }
-
- return tpTransNew;
-}
-
Transaction::pointer NetworkOPsImp::processTransactionCb (
Transaction::pointer trans,
- bool bAdmin, bool bLocal, bool bFailHard, stCallback callback)
+ bool bAdmin, bool bLocal, FailHard failType, stCallback callback)
{
auto ev = m_job_queue.getLoadEventAP (jtTXN_PROC, "ProcessTXN");
int newFlags = getApp().getHashRouter ().getFlags (trans->getID ());
@@ -1065,7 +1024,7 @@ Transaction::pointer NetworkOPsImp::processTransactionCb (
}
else if (isTerRetry (r))
{
- if (bFailHard)
+ if (failType == FailHard::yes)
addLocal = false;
else
{
@@ -1088,7 +1047,8 @@ Transaction::pointer NetworkOPsImp::processTransactionCb (
trans->getSTransaction ());
}
- if (didApply || ((mMode != omFULL) && !bFailHard && bLocal))
+ if (didApply ||
+ ((mMode != omFULL) && (failType != FailHard::yes) && bLocal))
{
std::set peers;
diff --git a/src/ripple/app/misc/NetworkOPs.h b/src/ripple/app/misc/NetworkOPs.h
index 316f6074b..8627f4ae0 100644
--- a/src/ripple/app/misc/NetworkOPs.h
+++ b/src/ripple/app/misc/NetworkOPs.h
@@ -91,6 +91,16 @@ public:
omFULL = 4 // we have the ledger and can even validate
};
+ enum class FailHard : unsigned char
+ {
+ no,
+ yes
+ };
+ static inline FailHard doFailHard (bool noMeansDont)
+ {
+ return noMeansDont ? FailHard::yes : FailHard::no;
+ }
+
// VFALCO TODO Fix OrderBookDB to not need this unrelated type.
//
typedef hash_map SubMapType;
@@ -149,12 +159,10 @@ public:
typedef std::function stCallback;
virtual void submitTransaction (Job&, STTx::pointer,
stCallback callback = stCallback ()) = 0;
- virtual Transaction::pointer submitTransactionSync (Transaction::ref tpTrans,
- bool bAdmin, bool bLocal, bool bFailHard, bool bSubmit) = 0;
virtual Transaction::pointer processTransactionCb (Transaction::pointer,
- bool bAdmin, bool bLocal, bool bFailHard, stCallback) = 0;
+ bool bAdmin, bool bLocal, FailHard failType, stCallback) = 0;
virtual Transaction::pointer processTransaction (Transaction::pointer transaction,
- bool bAdmin, bool bLocal, bool bFailHard) = 0;
+ bool bAdmin, bool bLocal, FailHard failType) = 0;
virtual Transaction::pointer findTransactionByID (uint256 const& transactionID) = 0;
virtual int findTransactionsByDestination (std::list&,
RippleAddress const& destinationAccount, std::uint32_t startLedgerSeq,
diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp
index 23b2103c6..42710d163 100644
--- a/src/ripple/net/impl/RPCCall.cpp
+++ b/src/ripple/net/impl/RPCCall.cpp
@@ -420,6 +420,30 @@ private:
return jvRequest;
}
+ // sign_for
+ Json::Value parseSignFor (Json::Value const& jvParams)
+ {
+ Json::Value txJSON;
+ Json::Reader reader;
+
+ if ((4 == jvParams.size ())
+ && reader.parse (jvParams[3u].asString (), txJSON))
+ {
+ if (txJSON.type () == Json::objectValue)
+ {
+ // Return SigningFor object for the submitted transaction.
+ Json::Value jvRequest;
+ jvRequest["signing_for"] = jvParams[0u].asString ();
+ jvRequest["account"] = jvParams[1u].asString ();
+ jvRequest["secret"] = jvParams[2u].asString ();
+ jvRequest["tx_json"] = txJSON;
+
+ return jvRequest;
+ }
+ }
+ return rpcError (rpcINVALID_PARAMS);
+ }
+
// json
Json::Value parseJson (Json::Value const& jvParams)
{
@@ -605,7 +629,7 @@ private:
{
Json::Value txJSON;
Json::Reader reader;
- bool bOffline = 3 == jvParams.size () && jvParams[2u].asString () == "offline";
+ bool const bOffline = 3 == jvParams.size () && jvParams[2u].asString () == "offline";
if (1 == jvParams.size ())
{
@@ -831,6 +855,9 @@ public:
{ "random", &RPCParser::parseAsIs, 0, 0 },
{ "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 2 },
{ "sign", &RPCParser::parseSignSubmit, 2, 3 },
+#if RIPPLE_ENABLE_MULTI_SIGN
+ { "sign_for", &RPCParser::parseSignFor, 4, 4 },
+#endif // RIPPLE_ENABLE_MULTI_SIGN
{ "submit", &RPCParser::parseSignSubmit, 1, 3 },
{ "server_info", &RPCParser::parseAsIs, 0, 0 },
{ "server_state", &RPCParser::parseAsIs, 0, 0 },
diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp
index d39239513..dcb0a6cec 100644
--- a/src/ripple/overlay/impl/PeerImp.cpp
+++ b/src/ripple/overlay/impl/PeerImp.cpp
@@ -1652,7 +1652,8 @@ PeerImp::checkTransaction (Job&, int flags,
}
bool const trusted (flags & SF_TRUSTED);
- getApp().getOPs ().processTransaction (tx, trusted, false, false);
+ getApp().getOPs ().processTransaction (
+ tx, trusted, false, NetworkOPs::FailHard::no);
}
catch (...)
{
diff --git a/src/ripple/protocol/ErrorCodes.h b/src/ripple/protocol/ErrorCodes.h
index bd90fa8b3..d60f07060 100644
--- a/src/ripple/protocol/ErrorCodes.h
+++ b/src/ripple/protocol/ErrorCodes.h
@@ -100,6 +100,7 @@ enum error_code_i
rpcPAYS_AMT_MALFORMED,
rpcPORT_MALFORMED,
rpcPUBLIC_MALFORMED,
+ rpcSIGN_FOR_MALFORMED,
rpcSRC_ACT_MALFORMED,
rpcSRC_ACT_MISSING,
rpcSRC_ACT_NOT_FOUND,
diff --git a/src/ripple/protocol/HashPrefix.h b/src/ripple/protocol/HashPrefix.h
index e03e265eb..dc0cd9495 100644
--- a/src/ripple/protocol/HashPrefix.h
+++ b/src/ripple/protocol/HashPrefix.h
@@ -84,6 +84,9 @@ public:
/** inner transaction to sign */
static HashPrefix const txSign;
+ /** inner transaction to multi-sign */
+ static HashPrefix const txMultiSign;
+
/** validation for signing */
static HashPrefix const validation;
diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h
index 41f73b471..7fab19cef 100644
--- a/src/ripple/protocol/SField.h
+++ b/src/ripple/protocol/SField.h
@@ -387,7 +387,7 @@ extern SField const sfMinimumOffer;
extern SField const sfRippleEscrow;
extern SField const sfDeliveredAmount;
-// variable length
+// variable length (common)
extern TypedField const sfPublicKey;
extern SField const sfMessageKey;
extern TypedField const sfSigningPubKey;
@@ -402,6 +402,9 @@ extern SField const sfMemoType;
extern SField const sfMemoData;
extern SField const sfMemoFormat;
+// variable length (uncommon)
+extern SField const sfMultiSignature;
+
// account
extern SField const sfAccount;
extern SField const sfOwner;
@@ -430,11 +433,13 @@ extern SField const sfNewFields;
extern SField const sfTemplateEntry;
extern SField const sfMemo;
extern SField const sfSignerEntry;
+extern SField const sfSigningAccount;
+extern SField const sfSigningFor;
// array of objects
// ARRAY/1 is reserved for end of array
extern SField const sfSigningAccounts;
-extern SField const sfTxnSignatures;
+extern SField const sfMultiSigners;
extern SField const sfSignerEntries;
extern SField const sfTemplate;
extern SField const sfNecessary;
diff --git a/src/ripple/protocol/STObject.h b/src/ripple/protocol/STObject.h
index 0086228b2..503de5ad3 100644
--- a/src/ripple/protocol/STObject.h
+++ b/src/ripple/protocol/STObject.h
@@ -212,6 +212,17 @@ public:
uint256 getHash (std::uint32_t prefix) const;
uint256 getSigningHash (std::uint32_t prefix) const;
+ // Break the multi-signing hash computation into 2 parts for optimization.
+ Serializer startMultiSigningData () const;
+ void finishMultiSigningData (
+ RippleAddress const& signingForID,
+ RippleAddress const& signingID, Serializer& s) const;
+
+ // Get data to compute a complete multi-signature.
+ Serializer getMultiSigningData (
+ RippleAddress const& signingForID,
+ RippleAddress const& signingID) const;
+
const STBase& peekAtIndex (int offset) const
{
return v_[offset].get();
diff --git a/src/ripple/protocol/impl/ErrorCodes.cpp b/src/ripple/protocol/impl/ErrorCodes.cpp
index 8649a1ff5..ae6e25fc9 100644
--- a/src/ripple/protocol/impl/ErrorCodes.cpp
+++ b/src/ripple/protocol/impl/ErrorCodes.cpp
@@ -98,6 +98,7 @@ public:
add (rpcPORT_MALFORMED, "portMalformed", "Port is malformed.");
add (rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed.");
add (rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed.");
+ add (rpcSIGN_FOR_MALFORMED, "signForMalformed", "Signing for account is malformed.");
add (rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server.");
add (rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed.");
add (rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided.");
diff --git a/src/ripple/protocol/impl/HashPrefix.cpp b/src/ripple/protocol/impl/HashPrefix.cpp
index a0c3d0935..b107420b7 100644
--- a/src/ripple/protocol/impl/HashPrefix.cpp
+++ b/src/ripple/protocol/impl/HashPrefix.cpp
@@ -31,6 +31,7 @@ HashPrefix const HashPrefix::leafNode ('M', 'L', 'N');
HashPrefix const HashPrefix::innerNode ('M', 'I', 'N');
HashPrefix const HashPrefix::ledgerMaster ('L', 'W', 'R');
HashPrefix const HashPrefix::txSign ('S', 'T', 'X');
+HashPrefix const HashPrefix::txMultiSign ('S', 'M', 'T');
HashPrefix const HashPrefix::validation ('V', 'A', 'L');
HashPrefix const HashPrefix::proposal ('P', 'R', 'P');
diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp
index 2a2a8f07a..c87b9e765 100644
--- a/src/ripple/protocol/impl/SField.cpp
+++ b/src/ripple/protocol/impl/SField.cpp
@@ -175,7 +175,7 @@ SField const sfMinimumOffer = make::one(&sfMinimumOffer, STI_AMOUNT, 16, "
SField const sfRippleEscrow = make::one(&sfRippleEscrow, STI_AMOUNT, 17, "RippleEscrow");
SField const sfDeliveredAmount = make::one(&sfDeliveredAmount, STI_AMOUNT, 18, "DeliveredAmount");
-// variable length
+// variable length (common)
TypedField const sfPublicKey = make::one(&sfPublicKey, STI_VL, 1, "PublicKey");
TypedField const sfSigningPubKey = make::one(&sfSigningPubKey, STI_VL, 3, "SigningPubKey");
TypedField const sfSignature = make::one(&sfSignature, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning);
@@ -190,6 +190,9 @@ SField const sfMemoType = make::one(&sfMemoType, STI_VL
SField const sfMemoData = make::one(&sfMemoData, STI_VL, 13, "MemoData");
SField const sfMemoFormat = make::one(&sfMemoFormat, STI_VL, 14, "MemoFormat");
+// variable length (uncommon)
+SField const sfMultiSignature = make::one(&sfMultiSignature, STI_VL, 16, "MultiSignature");
+
// account
SField const sfAccount = make::one(&sfAccount, STI_ACCOUNT, 1, "Account");
SField const sfOwner = make::one(&sfOwner, STI_ACCOUNT, 2, "Owner");
@@ -219,10 +222,14 @@ SField const sfTemplateEntry = make::one(&sfTemplateEntry, STI_OBJEC
SField const sfMemo = make::one(&sfMemo, STI_OBJECT, 10, "Memo");
SField const sfSignerEntry = make::one(&sfSignerEntry, STI_OBJECT, 11, "SignerEntry");
+// inner object (uncommon)
+SField const sfSigningAccount = make::one(&sfSigningAccount, STI_OBJECT, 16, "SigningAccount");
+SField const sfSigningFor = make::one(&sfSigningFor, STI_OBJECT, 17, "SigningFor");
+
// array of objects
// ARRAY/1 is reserved for end of array
SField const sfSigningAccounts = make::one(&sfSigningAccounts, STI_ARRAY, 2, "SigningAccounts");
-SField const sfTxnSignatures = make::one(&sfTxnSignatures, STI_ARRAY, 3, "TxnSignatures", SField::sMD_Default, SField::notSigning);
+SField const sfMultiSigners = make::one(&sfMultiSigners, STI_ARRAY, 3, "MultiSigners", SField::sMD_Default, SField::notSigning);
SField const sfSignerEntries = make::one(&sfSignerEntries, STI_ARRAY, 4, "SignerEntries");
SField const sfTemplate = make::one(&sfTemplate, STI_ARRAY, 5, "Template");
SField const sfNecessary = make::one(&sfNecessary, STI_ARRAY, 6, "Necessary");
diff --git a/src/ripple/protocol/impl/STObject.cpp b/src/ripple/protocol/impl/STObject.cpp
index 0e1e0a2da..635c070a2 100644
--- a/src/ripple/protocol/impl/STObject.cpp
+++ b/src/ripple/protocol/impl/STObject.cpp
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -337,6 +338,34 @@ uint256 STObject::getSigningHash (std::uint32_t prefix) const
return s.getSHA512Half ();
}
+Serializer
+STObject::startMultiSigningData () const
+{
+ Serializer s;
+ s.add32 (HashPrefix::txMultiSign);
+ add (s, false);
+ return s;
+}
+
+void
+STObject::finishMultiSigningData (
+ RippleAddress const& signingForID,
+ RippleAddress const& signingID,
+ Serializer& s) const
+{
+ s.add160 (signingForID.getAccountID ());
+ s.add160 (signingID.getAccountID ());
+}
+
+Serializer
+STObject::getMultiSigningData (
+ RippleAddress const& signingForID, RippleAddress const& signingID) const
+{
+ Serializer s (startMultiSigningData ());
+ finishMultiSigningData (signingForID, signingID, s);
+ return s;
+}
+
int STObject::getFieldIndex (SField const& field) const
{
if (mType != nullptr)
diff --git a/src/ripple/rpc/handlers/Handlers.h b/src/ripple/rpc/handlers/Handlers.h
index f2f652d15..0ccebad63 100644
--- a/src/ripple/rpc/handlers/Handlers.h
+++ b/src/ripple/rpc/handlers/Handlers.h
@@ -62,6 +62,7 @@ Json::Value doServerState (RPC::Context&); // for machines
Json::Value doSessionClose (RPC::Context&);
Json::Value doSessionOpen (RPC::Context&);
Json::Value doSign (RPC::Context&);
+Json::Value doSignFor (RPC::Context&);
Json::Value doStop (RPC::Context&);
Json::Value doSubmit (RPC::Context&);
Json::Value doSubscribe (RPC::Context&);
diff --git a/src/ripple/rpc/handlers/Sign.cpp b/src/ripple/rpc/handlers/Sign.cpp
index b26a6a95e..9355da0b3 100644
--- a/src/ripple/rpc/handlers/Sign.cpp
+++ b/src/ripple/rpc/handlers/Sign.cpp
@@ -29,10 +29,13 @@ namespace ripple {
Json::Value doSign (RPC::Context& context)
{
context.loadType = Resource::feeHighBurdenRPC;
- bool bFailHard = context.params.isMember (jss::fail_hard)
- && context.params[jss::fail_hard].asBool ();
+ NetworkOPs::FailHard const failType =
+ NetworkOPs::doFailHard (
+ context.params.isMember (jss::fail_hard)
+ && context.params[jss::fail_hard].asBool ());
+
return RPC::transactionSign (
- context.params, false, bFailHard, context.netOps, context.role);
+ context.params, failType, context.netOps, context.role);
}
} // ripple
diff --git a/src/ripple/rpc/handlers/SignFor.cpp b/src/ripple/rpc/handlers/SignFor.cpp
new file mode 100755
index 000000000..f09aded63
--- /dev/null
+++ b/src/ripple/rpc/handlers/SignFor.cpp
@@ -0,0 +1,42 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2014 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
+
+namespace ripple {
+
+// {
+// tx_json: