mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Ticket issuing (RIPD-368):
* New CreateTicket transactor to create tickets * New CancelTicket transactor to cancel tickets * Ledger entries for tickets & associated functions * First draft of M-of-N documentation
This commit is contained in:
@@ -2389,6 +2389,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CancelOffer.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CancelTicket.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\Change.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@@ -2401,6 +2404,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CreateOfferDirect.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CreateTicket.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\Payment.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
||||
@@ -3492,6 +3492,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CancelOffer.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CancelTicket.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\Change.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
@@ -3504,6 +3507,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CreateOfferDirect.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\CreateTicket.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\module\app\transactors\Payment.cpp">
|
||||
<Filter>ripple\module\app\transactors</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -1758,7 +1758,7 @@ uint256 Ledger::getOfferIndex (Account const& account, std::uint32_t uSequence)
|
||||
Serializer s (26);
|
||||
|
||||
s.add16 (spaceOffer); // 2
|
||||
s.add160 (account); // 20
|
||||
s.add160 (account); // 20
|
||||
s.add32 (uSequence); // 4
|
||||
|
||||
return s.getSHA512Half ();
|
||||
@@ -1791,6 +1791,18 @@ uint256 Ledger::getRippleStateIndex (
|
||||
return s.getSHA512Half ();
|
||||
}
|
||||
|
||||
uint256 Ledger::getTicketIndex (
|
||||
Account const& account, std::uint32_t uSequence)
|
||||
{
|
||||
Serializer s (26);
|
||||
|
||||
s.add16 (spaceTicket); // 2
|
||||
s.add160 (account); // 20
|
||||
s.add32 (uSequence); // 4
|
||||
|
||||
return s.getSHA512Half ();
|
||||
}
|
||||
|
||||
bool Ledger::walkLedger ()
|
||||
{
|
||||
std::vector <SHAMapMissingNode> missingNodes1;
|
||||
|
||||
@@ -414,6 +414,13 @@ public:
|
||||
Currency const& uTakerGetsCurrency, Account const& uTakerGetsIssuer,
|
||||
const std::uint64_t & uRate);
|
||||
|
||||
//
|
||||
// Tickets
|
||||
//
|
||||
|
||||
static uint256 getTicketIndex (
|
||||
Account const& account, std::uint32_t uSequence);
|
||||
|
||||
//
|
||||
// Ripple functions : credit lines
|
||||
//
|
||||
|
||||
92
src/ripple/module/app/transactors/CancelTicket.cpp
Normal file
92
src/ripple/module/app/transactors/CancelTicket.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class CancelTicket
|
||||
: public Transactor
|
||||
{
|
||||
public:
|
||||
CancelTicket (
|
||||
SerializedTransaction const& txn,
|
||||
TransactionEngineParams params,
|
||||
TransactionEngine* engine)
|
||||
: Transactor (
|
||||
txn,
|
||||
params,
|
||||
engine,
|
||||
deprecatedLogs().journal("CancelTicket"))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TER doApply () override
|
||||
{
|
||||
assert (mTxnAccount);
|
||||
|
||||
uint256 const ticketId = mTxn.getFieldH256 (sfTicketID);
|
||||
|
||||
SLE::pointer sleTicket = mEngine->view ().entryCache (ltTICKET, ticketId);
|
||||
|
||||
if (!sleTicket)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
Account const ticket_owner (sleTicket->getFieldAccount160 (sfAccount));
|
||||
|
||||
bool authorized (mTxnAccountID == ticket_owner);
|
||||
|
||||
// The target can also always remove a ticket
|
||||
if (!authorized && sleTicket->isFieldPresent (sfTarget))
|
||||
authorized = (mTxnAccountID == sleTicket->getFieldAccount160 (sfTarget));
|
||||
|
||||
// And finally, anyone can remove an expired ticket
|
||||
if (!authorized && sleTicket->isFieldPresent (sfExpiration))
|
||||
{
|
||||
std::uint32_t const expiration = sleTicket->getFieldU32 (sfExpiration);
|
||||
|
||||
if (mEngine->getLedger ()->getParentCloseTimeNC () >= expiration)
|
||||
authorized = true;
|
||||
}
|
||||
|
||||
if (!authorized)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
std::uint64_t const hint (sleTicket->getFieldU64 (sfOwnerNode));
|
||||
|
||||
TER const result = mEngine->view ().dirDelete (false, hint,
|
||||
Ledger::getOwnerDirIndex (ticket_owner), ticketId, false, (hint == 0));
|
||||
|
||||
mEngine->view ().ownerCountAdjust (ticket_owner, -1);
|
||||
mEngine->view ().entryDelete (sleTicket);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TER
|
||||
transact_CancelTicket (
|
||||
SerializedTransaction const& txn,
|
||||
TransactionEngineParams params,
|
||||
TransactionEngine* engine)
|
||||
{
|
||||
return CancelTicket (txn, params, engine).apply ();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
28
src/ripple/module/app/transactors/CancelTicket.h
Normal file
28
src/ripple/module/app/transactors/CancelTicket.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_CANCELTICKET_H_INCLUDED
|
||||
#define RIPPLE_TX_CANCELTICKET_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
129
src/ripple/module/app/transactors/CreateTicket.cpp
Normal file
129
src/ripple/module/app/transactors/CreateTicket.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class CreateTicket
|
||||
: public Transactor
|
||||
{
|
||||
public:
|
||||
CreateTicket (
|
||||
SerializedTransaction const& txn,
|
||||
TransactionEngineParams params,
|
||||
TransactionEngine* engine)
|
||||
: Transactor (
|
||||
txn,
|
||||
params,
|
||||
engine,
|
||||
deprecatedLogs().journal("CreateTicket"))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TER doApply () override
|
||||
{
|
||||
assert (mTxnAccount);
|
||||
|
||||
// A ticket counts against the reserve of the issuing account, but we check
|
||||
// the starting balance because we want to allow dipping into the reserve to
|
||||
// pay fees.
|
||||
auto const accountReserve (mEngine->getLedger ()->getReserve (
|
||||
mTxnAccount->getFieldU32 (sfOwnerCount) + 1));
|
||||
|
||||
if (mPriorBalance.getNValue () < accountReserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
std::uint32_t expiration (0);
|
||||
|
||||
if (mTxn.isFieldPresent (sfExpiration))
|
||||
{
|
||||
expiration = mTxn.getFieldU32 (sfExpiration);
|
||||
|
||||
if (!expiration)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed ticket requestion: bad expiration";
|
||||
|
||||
return temBAD_EXPIRATION;
|
||||
}
|
||||
|
||||
if (mEngine->getLedger ()->getParentCloseTimeNC () >= expiration)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
SLE::pointer sleTicket = mEngine->entryCreate (ltTICKET,
|
||||
Ledger::getTicketIndex (mTxnAccountID, mTxn.getSequence ()));
|
||||
|
||||
sleTicket->setFieldAccount (sfAccount, mTxnAccountID);
|
||||
sleTicket->setFieldU32 (sfSequence, mTxn.getSequence ());
|
||||
|
||||
if (expiration != 0)
|
||||
sleTicket->setFieldU32 (sfExpiration, expiration);
|
||||
|
||||
if (mTxn.isFieldPresent (sfTarget))
|
||||
{
|
||||
Account const target_account (mTxn.getFieldAccount160 (sfTarget));
|
||||
|
||||
SLE::pointer sleTarget = mEngine->entryCache (ltACCOUNT_ROOT,
|
||||
Ledger::getAccountRootIndex (target_account));
|
||||
|
||||
// Destination account does not exist.
|
||||
if (!sleTarget)
|
||||
return tecNO_TARGET;
|
||||
|
||||
// The issuing account is the default account to which the ticket
|
||||
// applies so don't bother saving it if that's what's specified.
|
||||
if (target_account != mTxnAccountID)
|
||||
sleTicket->setFieldAccount (sfTarget, target_account);
|
||||
}
|
||||
|
||||
std::uint64_t hint;
|
||||
|
||||
TER result = mEngine->view ().dirAdd (hint,
|
||||
Ledger::getOwnerDirIndex (mTxnAccountID), sleTicket->getIndex (),
|
||||
std::bind (&Ledger::ownerDirDescriber,
|
||||
std::placeholders::_1, std::placeholders::_2,
|
||||
mTxnAccountID));
|
||||
|
||||
m_journal.trace <<
|
||||
"Creating ticket " << to_string (sleTicket->getIndex ()) <<
|
||||
": " << transHuman (result);
|
||||
|
||||
if (result != tesSUCCESS)
|
||||
return result;
|
||||
|
||||
sleTicket->setFieldU64(sfOwnerNode, hint);
|
||||
|
||||
// If we succeeded, the new entry counts agains the creator's reserve.
|
||||
mEngine->view ().ownerCountAdjust (mTxnAccountID, 1, mTxnAccount);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TER
|
||||
transact_CreateTicket (
|
||||
SerializedTransaction const& txn,
|
||||
TransactionEngineParams params,
|
||||
TransactionEngine* engine)
|
||||
{
|
||||
return CreateTicket (txn, params, engine).apply ();
|
||||
}
|
||||
|
||||
}
|
||||
27
src/ripple/module/app/transactors/CreateTicket.h
Normal file
27
src/ripple/module/app/transactors/CreateTicket.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_CREATETICKET_H_INCLUDED
|
||||
#define RIPPLE_TX_CREATETICKET_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,8 @@ TER transact_CreateOffer (SerializedTransaction const& txn, TransactionEnginePar
|
||||
TER transact_CancelOffer (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
TER transact_AddWallet (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
TER transact_Change (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
TER transact_CreateTicket (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
TER transact_CancelTicket (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
|
||||
TER
|
||||
Transactor::transact (
|
||||
@@ -63,6 +65,12 @@ Transactor::transact (
|
||||
case ttFEE:
|
||||
return transact_Change (txn, params, engine);
|
||||
|
||||
case ttTICKET_CREATE:
|
||||
return transact_CreateTicket (txn, params, engine);
|
||||
|
||||
case ttTICKET_CANCEL:
|
||||
return transact_CancelTicket (txn, params, engine);
|
||||
|
||||
default:
|
||||
return temUNKNOWN;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,14 @@ LedgerFormats::LedgerFormats ()
|
||||
<< SOElement (sfReserveBase, SOE_REQUIRED)
|
||||
<< SOElement (sfReserveIncrement, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
add ("Ticket", ltTICKET)
|
||||
<< SOElement (sfAccount, SOE_REQUIRED)
|
||||
<< SOElement (sfSequence, SOE_REQUIRED)
|
||||
<< SOElement (sfOwnerNode, SOE_REQUIRED)
|
||||
<< SOElement (sfTarget, SOE_OPTIONAL)
|
||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||
;
|
||||
}
|
||||
|
||||
void LedgerFormats::addCommonFields (Item& item)
|
||||
|
||||
@@ -52,14 +52,12 @@ enum LedgerEntryType
|
||||
|
||||
/** Describes a trust line.
|
||||
*/
|
||||
// VFALCO TODO Rename to TrustLine or something similar.
|
||||
ltRIPPLE_STATE = 'r',
|
||||
|
||||
/** Deprecated.
|
||||
*/
|
||||
ltOFFER = 'o',
|
||||
ltTICKET = 'T',
|
||||
|
||||
ltNotUsed01 = 'c',
|
||||
/* Deprecated. */
|
||||
ltOFFER = 'o',
|
||||
|
||||
ltLEDGER_HASHES = 'h',
|
||||
|
||||
@@ -70,13 +68,14 @@ enum LedgerEntryType
|
||||
// No longer used or supported. Left here to prevent accidental
|
||||
// reassignment of the ledger type.
|
||||
ltNICKNAME = 'n',
|
||||
|
||||
ltNotUsed01 = 'c',
|
||||
};
|
||||
|
||||
/**
|
||||
@ingroup protocol
|
||||
*/
|
||||
// Used as a prefix for computing ledger indexes (keys).
|
||||
// VFALCO TODO Why are there a separate set of prefixes? i.e. class HashPrefix
|
||||
enum LedgerNameSpace
|
||||
{
|
||||
spaceAccount = 'a',
|
||||
@@ -90,6 +89,7 @@ enum LedgerNameSpace
|
||||
spaceSkipList = 's',
|
||||
spaceAmendment = 'f',
|
||||
spaceFee = 'e',
|
||||
spaceTicket = 'T',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space and
|
||||
// avoid accidental reuse of the space.
|
||||
|
||||
@@ -173,6 +173,7 @@ SField const sfBookDirectory = make::one(&sfBookDirectory, STI_HASH256, 16, "Boo
|
||||
SField const sfInvoiceID = make::one(&sfInvoiceID, STI_HASH256, 17, "InvoiceID");
|
||||
SField const sfNickname = make::one(&sfNickname, STI_HASH256, 18, "Nickname");
|
||||
SField const sfAmendment = make::one(&sfAmendment, STI_HASH256, 19, "Amendment");
|
||||
SField const sfTicketID = make::one(&sfTicketID, STI_HASH256, 20, "TicketID");
|
||||
|
||||
// 160-bit (common)
|
||||
SField const sfTakerPaysCurrency = make::one(&sfTakerPaysCurrency, STI_HASH160, 1, "TakerPaysCurrency");
|
||||
|
||||
@@ -333,6 +333,7 @@ extern SField const sfBookDirectory;
|
||||
extern SField const sfInvoiceID;
|
||||
extern SField const sfNickname;
|
||||
extern SField const sfAmendment;
|
||||
extern SField const sfTicketID;
|
||||
|
||||
// 160-bit (common)
|
||||
extern SField const sfTakerPaysCurrency;
|
||||
|
||||
@@ -52,6 +52,10 @@ bool transResultInfo (TER terCode, std::string& strToken, std::string& strHuman)
|
||||
{ tecNO_LINE, "tecNO_LINE", "No such line." },
|
||||
{ tecINSUFF_FEE, "tecINSUFF_FEE", "Insufficient balance to pay fee." },
|
||||
{ tecFROZEN, "tecFROZEN", "Asset is frozen." },
|
||||
{ tecNO_TARGET, "tecNO_TARGET", "Target account does not exist." },
|
||||
{ tecNO_PERMISSION, "tecNO_PERMISSION", "No permission to perform requested operation." },
|
||||
{ tecNO_ENTRY, "tecNO_ENTRY", "No matching entry found." },
|
||||
{ tecINSUFFICIENT_RESERVE,"tecINSUFFICIENT_RESERVE", "Insufficient reserve to complete requested operation." },
|
||||
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger." },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
|
||||
@@ -185,6 +185,10 @@ enum TER // aka TransactionEngineResult
|
||||
tecNO_LINE = 135,
|
||||
tecINSUFF_FEE = 136,
|
||||
tecFROZEN = 137,
|
||||
tecNO_TARGET = 138,
|
||||
tecNO_PERMISSION = 139,
|
||||
tecNO_ENTRY = 140,
|
||||
tecINSUFFICIENT_RESERVE = 141,
|
||||
};
|
||||
|
||||
inline bool isTelLocal(TER x)
|
||||
|
||||
@@ -22,56 +22,65 @@ namespace ripple {
|
||||
TxFormats::TxFormats ()
|
||||
{
|
||||
add ("AccountSet", ttACCOUNT_SET)
|
||||
<< SOElement (sfEmailHash, SOE_OPTIONAL)
|
||||
<< SOElement (sfWalletLocator, SOE_OPTIONAL)
|
||||
<< SOElement (sfWalletSize, SOE_OPTIONAL)
|
||||
<< SOElement (sfMessageKey, SOE_OPTIONAL)
|
||||
<< SOElement (sfDomain, SOE_OPTIONAL)
|
||||
<< SOElement (sfTransferRate, SOE_OPTIONAL)
|
||||
<< SOElement (sfSetFlag, SOE_OPTIONAL)
|
||||
<< SOElement (sfClearFlag, SOE_OPTIONAL)
|
||||
;
|
||||
<< SOElement (sfEmailHash, SOE_OPTIONAL)
|
||||
<< SOElement (sfWalletLocator, SOE_OPTIONAL)
|
||||
<< SOElement (sfWalletSize, SOE_OPTIONAL)
|
||||
<< SOElement (sfMessageKey, SOE_OPTIONAL)
|
||||
<< SOElement (sfDomain, SOE_OPTIONAL)
|
||||
<< SOElement (sfTransferRate, SOE_OPTIONAL)
|
||||
<< SOElement (sfSetFlag, SOE_OPTIONAL)
|
||||
<< SOElement (sfClearFlag, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("TrustSet", ttTRUST_SET)
|
||||
<< SOElement (sfLimitAmount, SOE_OPTIONAL)
|
||||
<< SOElement (sfQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement (sfQualityOut, SOE_OPTIONAL)
|
||||
;
|
||||
<< SOElement (sfLimitAmount, SOE_OPTIONAL)
|
||||
<< SOElement (sfQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement (sfQualityOut, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("OfferCreate", ttOFFER_CREATE)
|
||||
<< SOElement (sfTakerPays, SOE_REQUIRED)
|
||||
<< SOElement (sfTakerGets, SOE_REQUIRED)
|
||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||
<< SOElement (sfOfferSequence, SOE_OPTIONAL)
|
||||
;
|
||||
<< SOElement (sfTakerPays, SOE_REQUIRED)
|
||||
<< SOElement (sfTakerGets, SOE_REQUIRED)
|
||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||
<< SOElement (sfOfferSequence, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("OfferCancel", ttOFFER_CANCEL)
|
||||
<< SOElement (sfOfferSequence, SOE_REQUIRED)
|
||||
;
|
||||
<< SOElement (sfOfferSequence, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
add ("SetRegularKey", ttREGULAR_KEY_SET)
|
||||
<< SOElement (sfRegularKey, SOE_OPTIONAL)
|
||||
;
|
||||
<< SOElement (sfRegularKey, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("Payment", ttPAYMENT)
|
||||
<< SOElement (sfDestination, SOE_REQUIRED)
|
||||
<< SOElement (sfAmount, SOE_REQUIRED)
|
||||
<< SOElement (sfSendMax, SOE_OPTIONAL)
|
||||
<< SOElement (sfPaths, SOE_DEFAULT)
|
||||
<< SOElement (sfInvoiceID, SOE_OPTIONAL)
|
||||
<< SOElement (sfDestinationTag, SOE_OPTIONAL)
|
||||
;
|
||||
<< SOElement (sfDestination, SOE_REQUIRED)
|
||||
<< SOElement (sfAmount, SOE_REQUIRED)
|
||||
<< SOElement (sfSendMax, SOE_OPTIONAL)
|
||||
<< SOElement (sfPaths, SOE_DEFAULT)
|
||||
<< SOElement (sfInvoiceID, SOE_OPTIONAL)
|
||||
<< SOElement (sfDestinationTag, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("EnableAmendment", ttAMENDMENT)
|
||||
<< SOElement (sfAmendment, SOE_REQUIRED)
|
||||
;
|
||||
<< SOElement (sfAmendment, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
add ("SetFee", ttFEE)
|
||||
<< SOElement (sfBaseFee, SOE_REQUIRED)
|
||||
<< SOElement (sfReferenceFeeUnits, SOE_REQUIRED)
|
||||
<< SOElement (sfReserveBase, SOE_REQUIRED)
|
||||
<< SOElement (sfReserveIncrement, SOE_REQUIRED)
|
||||
;
|
||||
<< SOElement (sfBaseFee, SOE_REQUIRED)
|
||||
<< SOElement (sfReferenceFeeUnits, SOE_REQUIRED)
|
||||
<< SOElement (sfReserveBase, SOE_REQUIRED)
|
||||
<< SOElement (sfReserveIncrement, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
add ("TicketCreate", ttTICKET_CREATE)
|
||||
<< SOElement (sfTarget, SOE_OPTIONAL)
|
||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
add ("TicketCancel", ttTICKET_CANCEL)
|
||||
<< SOElement (sfTicketID, SOE_REQUIRED)
|
||||
;
|
||||
}
|
||||
|
||||
void TxFormats::addCommonFields (Item& item)
|
||||
|
||||
@@ -42,6 +42,8 @@ enum TxType
|
||||
ttOFFER_CREATE = 7,
|
||||
ttOFFER_CANCEL = 8,
|
||||
no_longer_used = 9,
|
||||
ttTICKET_CREATE = 10,
|
||||
ttTICKET_CANCEL = 11,
|
||||
|
||||
ttTRUST_SET = 20,
|
||||
|
||||
|
||||
83
src/ripple/multisign/README.md
Normal file
83
src/ripple/multisign/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
# M-of-N / Multi-Signature Support on Ripple
|
||||
|
||||
In order to enhance the flexibility of Ripple and provide support for enhanced security of accounts, native support for "multi-signature" or "multisign" accounts is required.
|
||||
|
||||
Transactions on an account which is designated as multisign can be authorized either by using the master or regular keys (unless those are disabled) or by being signed by a certain number (a quorum) of preauthorized accounts.
|
||||
|
||||
Some technical details, including tables indicating some of the Ripple commands and ledger entries to be used for implementing multi-signature, are currently listed on the [wiki](https://ripple.com/wiki/Multisign) but will eventually be migrated into this document as well.
|
||||
|
||||
## Steps to MultiSign
|
||||
|
||||
The implementation of multisign is a protocol breaking change which will require the coordinated rollout and deployment of the feature on the Ripple network.
|
||||
|
||||
Critical components for MultiSign are:
|
||||
|
||||
* Ticket Support
|
||||
* Authorized Signer List Management
|
||||
* Verification of Multiple Signatures during TX processing.
|
||||
|
||||
### Ticket Support
|
||||
|
||||
**Associated JIRA task is [RIPD-368](https://ripplelabs.atlassian.net/browse/RIPD-368)**
|
||||
|
||||
Currently transactions on the Ripple network require the use of sequence numbers and sequence numbers must monotonically increase. Since the sequence number is part of the transaction, it is "covered" by the signature that authorizes the transaction, which means that the sequence number would have to be decided at the time of transaction issuance. This would mean that multi-signature transactions could only be processed in strict "first-in, first-out" order which is not practical.
|
||||
|
||||
Tickets can be used in lieu of sequence number. A ticket is a special token which, through a transaction, can be issued by any account and can be configured with an optional expiry date and an optional associated account.
|
||||
|
||||
#### Specifics
|
||||
|
||||
The expiry date can be used to constrain the validity of the ticket. If specified, the ticket will be considered invalid and unusable if the closing time of the last *validated* ledger is greater than or equal to the expiration time of the ticket.
|
||||
|
||||
The associated account can be used to specify an account, other than the issuing account, that is allowed to "consume" the ticket. Consuming a ticket means to use the ticket in a transaction that is accepted by the network and makes its way into a validated ledger. If not present, the ticket can only be consumed by the issuing account.
|
||||
|
||||
*Corner Case:* It is possible that two or more transactions reference the same ticket and that both go into the same consensus set. During final application of transactions from the consensus set at most one of these transactions may succeed; others must fail with the indication that the ticket has been consumed.
|
||||
|
||||
*Reserve:* While a ticket is outstanding, it should count against the reserve of the *issuer*.
|
||||
|
||||
##### Issuance
|
||||
We should decide whether, in the case of multi-signature accounts, any single authorized signer can issue a ticket on the multisignature accounts' behalf. This approach has both advantages and disadvantages.
|
||||
|
||||
Advantages include:
|
||||
|
||||
+ Simpler logic for tickets and reduced data - no need to store or consider an associated account.
|
||||
+ Owner reserves for issued tickets count against the multi-signature account instead of the account of the signer proposing a transaction.
|
||||
+ Cleaner meta-data: easier to follow who issued a ticket and how many tickets are outstanding and associated with a particular account.
|
||||
|
||||
Disadvantages are:
|
||||
|
||||
+ Any single authorized signer can issue multiple tickets, each counting against the account's reserve.
|
||||
+ Special-case logic for authorizing tickets on multi-sign accounts.
|
||||
|
||||
I believe that the disadvantages outweigh the advantages, but debate is welcome.
|
||||
|
||||
##### Proposed Transaction Cancelation
|
||||
A transaction that has been proposed against a multi-sign account using a ticket can be positively cancelled if a quorum of authorized signers sign and issue a transaction that consumes that ticket.
|
||||
|
||||
### Authorized Signer List Management
|
||||
|
||||
For accounts which are designated as multi-sign, there must be a list which specifies which accounts are authorized to sign and the quorum threshold.
|
||||
|
||||
The quorum threshold indicates the minimum required weight that the sum of the weights of all signatures must have before a transaction can be authorized. It is an unsigned integer that is at least 2.
|
||||
|
||||
Each authorized account can be given a weight, from 1 to 32 inclusive. The weight is used when to determine whether a given number of signatures are sufficient for a quorum.
|
||||
|
||||
### Verification of Multiple Signatures during TX processing
|
||||
The current approach to adding multi-signature support is to require that a transaction is to be signed outside the Ripple network and only submitted after the quorum has been reached.
|
||||
|
||||
This reduces the implementation footprint and the load imposed on the network, and mirrors the way transaction signing is currently handled. It will require some messaging mechanism outside the Ripple network to disseminate the proposed transaction to the authorized signers and to allow them apply signatures.
|
||||
|
||||
Supporting in-ledger voting should be considered, but it has both advantages and disadvantages.
|
||||
|
||||
One of the advantages is increased transparency - transactions are visible as are the "votes" (the authorized accounts signing the transaction) on the ledger. However, transactions may also languish for a long time in the ledger, never reaching a quorum and consuming network resources.
|
||||
|
||||
### Signature Format
|
||||
We should not develop a new format for multi-sign signatures. Instead each signer should extract and sign the transaction as he normally would if this were a regular transaction. The resulting signature will be stored as a pair of { signing-account, signature } in an array of signatures associated with this transaction.
|
||||
|
||||
The advantage of this is that we can reuse the existing signing and verification code, and leverage the work that will go towards implementing support for the Ed25519 elliptic curve.
|
||||
|
||||
|
||||
### Fees
|
||||
Multi-signature transactions impose a heavier load on the network and should claim higher fees.
|
||||
|
||||
The fee structure is not yet decided, but an escalating fee structure is laid out and discussed on the [wiki](https://ripple.com/wiki/Multisign). This might need to be revisited and designed in light of discussions about changing how fees are handled.
|
||||
@@ -38,3 +38,5 @@
|
||||
#include <ripple/module/app/transactors/CreateOffer.cpp>
|
||||
#include <ripple/module/app/transactors/CreateOfferDirect.cpp>
|
||||
#include <ripple/module/app/transactors/CreateOfferBridged.cpp>
|
||||
#include <ripple/module/app/transactors/CreateTicket.cpp>
|
||||
#include <ripple/module/app/transactors/CancelTicket.cpp>
|
||||
|
||||
Reference in New Issue
Block a user