in src/node/rpc/member_frontend.h [202:411]
ccf::jsgov::ProposalInfoSummary resolve_proposal(
kv::Tx& tx,
const ProposalId& proposal_id,
const std::vector<uint8_t>& proposal,
const std::string& constitution)
{
auto pi = tx.rw<ccf::jsgov::ProposalInfoMap>(Tables::PROPOSALS_INFO);
auto pi_ = pi->get(proposal_id);
std::vector<std::pair<MemberId, bool>> votes;
std::optional<ccf::jsgov::Votes> final_votes = std::nullopt;
std::optional<ccf::jsgov::VoteFailures> vote_failures = std::nullopt;
for (const auto& [mid, mb] : pi_->ballots)
{
js::Runtime rt;
js::Context context(rt);
rt.add_ccf_classdefs();
js::TxContext txctx{&tx, js::TxAccess::GOV_RO};
js::populate_global(
&txctx,
nullptr,
nullptr,
std::nullopt,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
context);
auto ballot_func = context.function(
mb,
"vote",
fmt::format(
"public:ccf.gov.proposal_info[{}].ballots[{}]", proposal_id, mid));
std::vector<js::JSWrappedValue> argv = {
context.new_string_len((const char*)proposal.data(), proposal.size()),
context.new_string_len(
pi_->proposer_id.data(), pi_->proposer_id.size())};
auto val = context.call(ballot_func, argv);
if (!JS_IsException(val))
{
votes.emplace_back(mid, JS_ToBool(context, val));
}
else
{
if (!vote_failures.has_value())
{
vote_failures = ccf::jsgov::VoteFailures();
}
auto [reason, trace] = js::js_error_message(context);
vote_failures.value()[mid] = ccf::jsgov::Failure{reason, trace};
}
}
{
js::Runtime rt;
js::Context js_context(rt);
rt.add_ccf_classdefs();
js::TxContext txctx{&tx, js::TxAccess::GOV_RO};
js::populate_global(
&txctx,
nullptr,
nullptr,
std::nullopt,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
js_context);
auto resolve_func = js_context.function(
constitution, "resolve", "public:ccf.gov.constitution[0]");
std::vector<js::JSWrappedValue> argv;
argv.push_back(js_context.new_string_len(
(const char*)proposal.data(), proposal.size()));
argv.push_back(js_context.new_string_len(
pi_->proposer_id.data(), pi_->proposer_id.size()));
auto vs = js_context.new_array();
size_t index = 0;
for (auto& [mid, vote] : votes)
{
auto v = JS_NewObject(js_context);
auto member_id = JS_NewStringLen(js_context, mid.data(), mid.size());
JS_DefinePropertyValueStr(
js_context, v, "member_id", member_id, JS_PROP_C_W_E);
auto vote_status = JS_NewBool(js_context, vote);
JS_DefinePropertyValueStr(
js_context, v, "vote", vote_status, JS_PROP_C_W_E);
JS_DefinePropertyValueUint32(
js_context, vs, index++, v, JS_PROP_C_W_E);
}
argv.push_back(vs);
auto val = js_context.call(resolve_func, argv);
std::optional<jsgov::Failure> failure = std::nullopt;
if (JS_IsException(val))
{
pi_.value().state = ProposalState::FAILED;
auto [reason, trace] = js::js_error_message(js_context);
failure = ccf::jsgov::Failure{
fmt::format("Failed to resolve(): {}", reason), trace};
}
else if (JS_IsString(val))
{
auto status = js_context.to_str(val).value_or("");
if (status == "Open")
{
pi_.value().state = ProposalState::OPEN;
}
else if (status == "Accepted")
{
pi_.value().state = ProposalState::ACCEPTED;
}
else if (status == "Withdrawn")
{
pi_.value().state = ProposalState::FAILED;
}
else if (status == "Rejected")
{
pi_.value().state = ProposalState::REJECTED;
}
else if (status == "Failed")
{
pi_.value().state = ProposalState::FAILED;
}
else if (status == "Dropped")
{
pi_.value().state = ProposalState::DROPPED;
}
else
{
pi_.value().state = ProposalState::FAILED;
failure = ccf::jsgov::Failure{
fmt::format(
"resolve() returned invalid status value: \"{}\"", status),
std::nullopt};
}
}
else
{
pi_.value().state = ProposalState::FAILED;
failure = ccf::jsgov::Failure{
"resolve() returned invalid status value", std::nullopt};
}
if (pi_.value().state != ProposalState::OPEN)
{
remove_all_other_non_open_proposals(tx, proposal_id);
final_votes = std::unordered_map<ccf::MemberId, bool>();
for (auto& [mid, vote] : votes)
{
final_votes.value()[mid] = vote;
}
if (pi_.value().state == ProposalState::ACCEPTED)
{
js::Runtime rt;
js::Context js_context(rt);
rt.add_ccf_classdefs();
js::TxContext txctx{&tx, js::TxAccess::GOV_RW};
js::populate_global(
&txctx,
nullptr,
nullptr,
std::nullopt,
nullptr,
&context.get_node_state(),
nullptr,
&network,
nullptr,
this,
js_context);
auto apply_func = js_context.function(
constitution, "apply", "public:ccf.gov.constitution[0]");
std::vector<js::JSWrappedValue> argv = {
js_context.new_string_len(
(const char*)proposal.data(), proposal.size()),
js_context.new_string_len(
proposal_id.c_str(), proposal_id.size())};
auto val = js_context.call(apply_func, argv);
if (JS_IsException(val))
{
pi_.value().state = ProposalState::FAILED;
auto [reason, trace] = js::js_error_message(js_context);
failure = ccf::jsgov::Failure{
fmt::format("Failed to apply(): {}", reason), trace};
}
}
}
return jsgov::ProposalInfoSummary{
proposal_id,
pi_->proposer_id,
pi_.value().state,
pi_.value().ballots.size(),
final_votes,
vote_failures,
failure};
}
}