Skip to content

Commit

Permalink
Auction bid rpc and consensus implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Fieroni <[email protected]>
  • Loading branch information
bvbfan committed Aug 31, 2021
1 parent 2d423f0 commit c6d3e92
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 9 deletions.
51 changes: 51 additions & 0 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ std::string ToString(CustomTxType type) {
case CustomTxType::Vault: return "Vault";
case CustomTxType::UpdateVault: return "UpdateVault";
case CustomTxType::DepositToVault: return "DepositToVault";
case CustomTxType::AuctionBid: return "AuctionBid";
case CustomTxType::None: return "None";
}
return "None";
Expand Down Expand Up @@ -149,6 +150,7 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) {
case CustomTxType::Vault: return CVaultMessage{};
case CustomTxType::UpdateVault: return CUpdateVaultMessage{};
case CustomTxType::DepositToVault: return CDepositToVaultMessage{};
case CustomTxType::AuctionBid: return CAuctionBidMessage{};
case CustomTxType::None: return CCustomTxMessageNone{};
}
return CCustomTxMessageNone{};
Expand Down Expand Up @@ -455,6 +457,11 @@ class CCustomMetadataParseVisitor : public boost::static_visitor<Res>
return !res ? res : serialize(obj);
}

Res operator()(CAuctionBidMessage& obj) const {
auto res = isPostFortCanningFork();
return !res ? res : serialize(obj);
}

Res operator()(CCustomTxMessageNone&) const {
return Res::Ok();
}
Expand Down Expand Up @@ -2150,6 +2157,50 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
return Res::Ok();
}

Res operator()(const CAuctionBidMessage& obj) const {
// owner auth
if (!HasAuth(obj.from)) {
return Res::Err("tx must have at least one input from token owner");
}
// vault exists
auto vault = mnview.GetVault(obj.vaultId);
if (!vault)
return Res::Err("Cannot find existing vault with id %s", obj.vaultId.GetHex());

// vault under liquidation
if (!vault.val->isUnderLiquidation)
return Res::Err("Cannot bid to vault which is not under liquidation");

auto data = mnview.GetAuction(obj.vaultId, height);
if (!data)
return Res::Err("No auction data to vault %s", obj.vaultId.GetHex());

auto batch = mnview.GetAuctionBatch(obj.vaultId, obj.index);
if (!batch)
return Res::Err("No batch to vault/index %s/%d", obj.vaultId.GetHex(), obj.index);

if (obj.amount.nTokenId != batch->loanAmount.nTokenId)
return Res::Err("Bid token does not match auction one");

auto bid = mnview.GetAuctionBid(obj.vaultId, obj.index);
if (!bid) {
auto amount = MultiplyAmounts(batch->loanAmount.nValue, COIN + data->liquidationPenalty);
if (amount > obj.amount.nValue)
return Res::Err("First bid should include liquidation penalty of %d%%", data->liquidationPenalty * 100 / COIN);
} else {
auto amount = MultiplyAmounts(bid->second.nValue, COIN + (COIN / 100));
if (amount > obj.amount.nValue)
return Res::Err("Bid override should be at least 1%% higher than current one");
// immediate refund previous bid
CalculateOwnerRewards(bid->first);
mnview.AddBalance(bid->first, bid->second);
}
//check balance
CalculateOwnerRewards(obj.from);
auto res = mnview.SubBalance(obj.from, obj.amount);
return !res ? res : mnview.StoreAuctionBid(obj.vaultId, obj.index, {obj.from, obj.amount});
}

Res operator()(const CCustomTxMessageNone&) const {
return Res::Ok();
}
Expand Down
7 changes: 5 additions & 2 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ enum class CustomTxType : uint8_t
DestroyLoanScheme = 'D',
Vault = 'V',
UpdateVault = 'v',
DepositToVault = 'S'
DepositToVault = 'S',
AuctionBid = 'I'
};

inline CustomTxType CustomTxCodeToType(uint8_t ch) {
Expand Down Expand Up @@ -126,6 +127,7 @@ inline CustomTxType CustomTxCodeToType(uint8_t ch) {
case CustomTxType::Vault:
case CustomTxType::UpdateVault:
case CustomTxType::DepositToVault:
case CustomTxType::AuctionBid:
case CustomTxType::None:
return type;
}
Expand Down Expand Up @@ -282,7 +284,8 @@ typedef boost::variant<
CDestroyLoanSchemeMessage,
CVaultMessage,
CUpdateVaultMessage,
CDepositToVaultMessage
CDepositToVaultMessage,
CAuctionBidMessage
> CCustomTxMessage;

CCustomTxMessage customTypeToMessage(CustomTxType txType);
Expand Down
8 changes: 8 additions & 0 deletions src/masternodes/rpc_customtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ class CCustomTxRpcVisitor : public boost::static_visitor<void>
rpcInfo.pushKV("from", ScriptToString(obj.from));
rpcInfo.pushKV("amount", obj.amount.ToString());
}

void operator()(const CAuctionBidMessage& obj) const {
rpcInfo.pushKV("vaultid", obj.vaultId.GetHex());
rpcInfo.pushKV("index", int64_t(obj.index));
rpcInfo.pushKV("from", ScriptToString(obj.from));
rpcInfo.pushKV("amount", obj.amount.ToString());
}

void operator()(const CCustomTxMessageNone&) const {
}
};
Expand Down
91 changes: 91 additions & 0 deletions src/masternodes/rpc_vault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,96 @@ UniValue deposittovault(const JSONRPCRequest& request) {
return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex();
}

UniValue auctionbid(const JSONRPCRequest& request) {
CWallet *const pwallet = GetWallet(request);

RPCHelpMan{"auctionbid",
"Bid to vault in auction\n" +
HelpRequiringPassphrase(pwallet) + "\n",
{
{"vaultid", RPCArg::Type::STR, RPCArg::Optional::NO, "Vault id"},
{"index", RPCArg::Type::NUM, RPCArg::Optional::NO, "Auction index"},
{"from", RPCArg::Type::STR, RPCArg::Optional::NO, "Address to get tokens"},
{"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "Amount of amount@symbol format"},
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
},
},
},
}
},
RPCResult{
"\"txid\" (string) The transaction id.\n"
},
RPCExamples{
HelpExampleCli("auctionbid",
"84b22eee1964768304e624c416f29a91d78a01dc5e8e12db26bdac0670c67bb2i 0 mwSDMvn1Hoc8DsoB7AkLv7nxdrf5Ja4jsF 100@TSLA") +
HelpExampleRpc("deposittovault",
"84b22eee1964768304e624c416f29a91d78a01dc5e8e12db26bdac0670c67bb2i 0 mwSDMvn1Hoc8DsoB7AkLv7nxdrf5Ja4jsF 1@DTSLA")
},
}.Check(request);

RPCTypeCheck(request.params,
{UniValue::VSTR, UniValue::VNUM, UniValue::VSTR, UniValue::VSTR, UniValue::VARR},
false);

if (pwallet->chain().isInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot make auction bid while still in Initial Block Download");

pwallet->BlockUntilSyncedToCurrentChain();
LockedCoinsScopedGuard lcGuard(pwallet);

// decode vaultid
CVaultId vaultId = ParseHashV(request.params[0], "vaultid");
uint32_t index = request.params[1].get_int();
auto from = DecodeScript(request.params[2].get_str());
CTokenAmount amount = DecodeAmount(pwallet->chain(), request.params[3].get_str(), "amount");

CAuctionBidMessage msg{vaultId, index, from, amount};
CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
markedMetadata << static_cast<unsigned char>(CustomTxType::DepositToVault)
<< msg;
CScript scriptMeta;
scriptMeta << OP_RETURN << ToByteVector(markedMetadata);

int targetHeight = chainHeight(*pwallet->chain().lock()) + 1;

const auto txVersion = GetTransactionVersion(targetHeight);
CMutableTransaction rawTx(txVersion);

rawTx.vout.push_back(CTxOut(0, scriptMeta));

CTransactionRef optAuthTx;
std::set<CScript> auths{from};
rawTx.vin = GetAuthInputsSmart(pwallet, rawTx.nVersion, auths, false /*needFoundersAuth*/, optAuthTx, request.params[4]);

CCoinControl coinControl;

// Set change to from address
CTxDestination dest;
ExtractDestination(from, dest);
if (IsValidDestination(dest)) {
coinControl.destChange = dest;
}

fund(rawTx, pwallet, optAuthTx, &coinControl);

// check execution
{
LOCK(cs_main);
CCoinsViewCache coins(&::ChainstateActive().CoinsTip());
if (optAuthTx)
AddCoins(coins, *optAuthTx, targetHeight);
auto metadata = ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, msg});
execTestTx(CTransaction(rawTx), targetHeight, metadata, CAuctionBidMessage{}, coins);
}
return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex();
}

static const CRPCCommand commands[] =
{
// category name actor (function) params
Expand All @@ -465,6 +555,7 @@ static const CRPCCommand commands[] =
{"vault", "getvault", &getvault, {"id"}},
{"vault", "updatevault", &updatevault, {"id", "parameters", "inputs"}},
{"vault", "deposittovault", &deposittovault, {"id", "from", "amount", "inputs"}},
{"vault", "auctionbid", &auctionbid, {"id", "index", "from", "amount", "inputs"}},
};

void RegisterVaultRPCCommands(CRPCTable& tableRPC) {
Expand Down
18 changes: 13 additions & 5 deletions src/masternodes/vault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ Res CVaultView::EraseAuction(const CVaultId& vaultId, uint32_t height)
return Res::Err("Auction for vault <%s> not found", vaultId.GetHex());
}

boost::optional<CAuctionData> CVaultView::GetAuction(const CVaultId& vaultId, uint32_t height)
{
auto it = LowerBound<AuctionHeightKey>(std::make_pair(height, vaultId));
for (; it.Valid(); it.Next()) {
if (it.Key().second == vaultId) {
return it.Value().as<CAuctionData>();
}
}
return {};
}

Res CVaultView::StoreAuctionBatch(const CVaultId& vaultId, uint32_t id, const CAuctionBatch& batch)
{
WriteBy<AuctionBatchKey>(std::make_pair(vaultId, id), batch);
Expand All @@ -121,13 +132,10 @@ boost::optional<CAuctionBatch> CVaultView::GetAuctionBatch(const CVaultId& vault
return ReadBy<AuctionBatchKey, CAuctionBatch>(std::make_pair(vaultId, id));
}

void CVaultView::ForEachVaultAuction(std::function<bool(const CVaultId&, const CAuctionData&)> callback, uint32_t height)
void CVaultView::ForEachVaultAuction(std::function<bool(const CVaultId&, uint32_t, const CAuctionData&)> callback, uint32_t height)
{
ForEach<AuctionHeightKey, std::pair<uint32_t, CVaultId>, CAuctionData>([&](const std::pair<uint32_t, CVaultId>& pair, const CAuctionData& data) {
if (pair.first != height) {
return false;
}
return callback(pair.second, data);
return callback(pair.second, pair.first, data);
}, std::make_pair(height, CVaultId{}));
}

Expand Down
21 changes: 20 additions & 1 deletion src/masternodes/vault.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ struct CDepositToVaultMessage {
}
};

struct CAuctionBidMessage {
CVaultId vaultId;
uint32_t index;
CScript from;
CTokenAmount amount;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(vaultId);
READWRITE(index);
READWRITE(from);
READWRITE(amount);
}
};

struct CAuctionData {
uint32_t batchCount;
CAmount liquidationPenalty;
Expand Down Expand Up @@ -103,10 +121,11 @@ class CVaultView : public virtual CStorageView

Res StoreAuction(const CVaultId& vaultId, uint32_t height, const CAuctionData& data);
Res EraseAuction(const CVaultId& vaultId, uint32_t height);
boost::optional<CAuctionData> GetAuction(const CVaultId& vaultId, uint32_t height);
Res StoreAuctionBatch(const CVaultId& vaultId, uint32_t id, const CAuctionBatch& batch);
Res EraseAuctionBatch(const CVaultId& vaultId, uint32_t id);
boost::optional<CAuctionBatch> GetAuctionBatch(const CVaultId& vaultId, uint32_t id);
void ForEachVaultAuction(std::function<bool(const CVaultId&, const CAuctionData&)> callback, uint32_t height);
void ForEachVaultAuction(std::function<bool(const CVaultId&, uint32_t, const CAuctionData&)> callback, uint32_t height = 0);

using COwnerTokenAmount = std::pair<CScript, CTokenAmount>;
Res StoreAuctionBid(const CVaultId& vaultId, uint32_t id, COwnerTokenAmount amount);
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "updatevault", 1, "parameters" },
{ "updatevault", 2, "inputs" },
{ "deposittovault", 3, "inputs" },
{ "auctionbid", 4, "inputs" },

{ "spv_sendrawtx", 0, "rawtx" },
{ "spv_createanchor", 0, "inputs" },
Expand Down
5 changes: 4 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2982,7 +2982,10 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
return true;
});
}
cache.ForEachVaultAuction([&](const CVaultId& vaultId, const CAuctionData& data) {
cache.ForEachVaultAuction([&](const CVaultId& vaultId, uint32_t height, const CAuctionData& data) {
if (int(height) != pindex->nHeight) {
return false;
}
std::set<DCT_ID> tokensLooseInterest;
for (uint32_t i = 0; i < data.batchCount; i++) {
auto batch = cache.GetAuctionBatch(vaultId, i);
Expand Down

0 comments on commit c6d3e92

Please sign in to comment.