ccf::jsgov::ProposalInfoSummary resolve_proposal()

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};
      }
    }