::kj::Promise<void> swv::ContestGenerator::getContest(ContestGenerator::Server::GetContestContext context)
{
    KJ_REQUIRE(generated < contestCount, "No more contests available.");
    auto contest = context.initResults().initNextContest();
    contest.initContestId().setOperationId(contestCount - generated++);
    contest.setTracksLiveResults(false);
    contest.setVotingStake(0);
    return kj::READY_NOW;
}
swv::ContestGenerator::ContestGenerator(int contestCount)
    : contestCount(contestCount) {
    KJ_REQUIRE(contestCount <= std::numeric_limits<char>::max());
}
kj::Promise<OwningWrapper<DecisionWrapper>*> ChainAdaptorWrapper::_getDecision(QString owner, QString contestId)
{
    if (!hasAdaptor()) return KJ_EXCEPTION(FAILED, "No blockchain adaptor is set.");

    using Reader = ::Balance::Reader;
    auto promise = m_adaptor->getContest(QByteArray::fromHex(contestId.toLocal8Bit())).then([=](::Contest::Reader c) {
        return m_adaptor->getBalancesForOwner(owner).then([c](kj::Array<Reader> balances) {
            return std::make_tuple(c, kj::mv(balances));
        });
    }).then([=](std::tuple<::Contest::Reader, kj::Array<Reader>> contestAndBalances) {
        ::Contest::Reader contest;
        kj::Array<Reader> balances;
        std::tie(contest, balances) = kj::mv(contestAndBalances);

        auto newEnd = std::remove_if(balances.begin(), balances.end(), [contest](Reader balance) {
            return balance.getType() != contest.getContest().getCoin();
        });
        KJ_REQUIRE(newEnd - balances.begin() > 0, "No balances found in the contest's coin, so no decision exists.");

        // This struct is basically a lambda on steroids. I'm using it to transform the array of balances into an array
        // of datagrams containing the decision I want on each of those balances. Also, make sure the newest balance's
        // decision is in the front (I don't care about preserving order) so that the newest decision will be returned.
        struct {
            // Capture this
            ChainAdaptorWrapper* wrapper;
            // Capture contestId
            QString contestId;
            // For each balance, look up the datagram containing the relevant decision. Store the promises in this array
            kj::ArrayBuilder<kj::Promise<kj::Maybe<::Datagram::Reader>>> datagramPromises;

            Reader newestBalance;
            void operator() (Reader balance) {
                if (datagramPromises.size() == 0) {
                    // First balance. Nothing to compare.
                    newestBalance = balance;
                }

                // Start a lookup for the datagram and store the promise.
                auto promise = wrapper->m_adaptor->getDatagram(convertBlob(balance.getId()),
                                                               Datagram::DatagramType::DECISION,
                                                               contestId);
                datagramPromises.add(promise.then([](::Datagram::Reader r) -> kj::Maybe<::Datagram::Reader> {
                        return r;
                    }, [](kj::Exception e) -> kj::Maybe<::Datagram::Reader> {
                        qInfo() << "Got exception when fetching datagram on balance:" << e.getDescription().cStr();
                        return {};
                    })
                );

                // If this balance is newer than the previous newest, move its promise to the front
                if (balance.getCreationOrder() > newestBalance.getCreationOrder()) {
                    newestBalance = balance;
                    std::swap(datagramPromises.front(), datagramPromises.back());
                }
            }
        } accumulator{this, contestId,
                    kj::heapArrayBuilder<kj::Promise<kj::Maybe<::Datagram::Reader>>>(newEnd - balances.begin()), {}};

        // Process all of the balances. Fetch the appropriate decision for each.
        qDebug() << "Looking for decisions on" << newEnd - balances.begin() << "balances.";
        accumulator = std::for_each(balances.begin(), newEnd, kj::mv(accumulator));

        return kj::joinPromises(accumulator.datagramPromises.finish());
    }).then([=](kj::Array<kj::Maybe<::Datagram::Reader>> datagrams) {
        std::unique_ptr<OwningWrapper<swv::DecisionWrapper>> decision;
        for (auto datagramMaybe : datagrams) {
            KJ_IF_MAYBE(datagram, datagramMaybe) {
                decision.reset(OwningWrapper<DecisionWrapper>::deserialize(convertBlob(datagram->getContent())));
                break;
            }
        }
예제 #4
0
void TlsPskAdaptor::shutdownWrite() {
    KJ_REQUIRE(!!channel, "Adaptor cannot be used without a channel. Call setChannel first");
    channel->close();
    stream->shutdownWrite();
}
kj::Promise<std::unique_ptr<data::Decision>> BlockchainWalletApi::_getDecision(QString owner, QString contestId) {
    using Reader = ::Balance::Reader;
    using DatagramResponse = capnp::Response<BlockchainWallet::GetDatagramByBalanceResults>;

    // Fetch the contest object; everything this function does is continuations of that promise
    return getContestImpl(contestId).then([=](capnp::Response<BlockchainWallet::GetContestByIdResults> r) {
        // Fetch all balances belonging to this owner so we can search those balances for datagrams containing
        // decisions on our contest
        return getBalancesBelongingToImpl(owner).then([contestResponse = kj::mv(r)](auto response) mutable {
            return std::make_tuple(kj::mv(contestResponse), kj::mv(response));
        });
    }).then([=](auto contestAndBalances) {
        ::Signed<Contest>::Reader contest = std::get<0>(contestAndBalances).getContest();
        auto tmpBalances = std::get<1>(contestAndBalances).getBalances();
        kj::Array<::Balance::Reader> balances = kj::heapArray<::Balance::Reader>(tmpBalances.begin(),
                                                                                 tmpBalances.end());

        // Filter out all balances which are not in the coin this contest is weighted in; only balances in the coin the
        // contest is weighted in can have valid decisions on that contest
        {
            auto newEnd = std::remove_if(balances.begin(), balances.end(), [contest](Reader balance) {
                 return balance.getType() != contest.getValue().getCoin();
            });
            KJ_REQUIRE(newEnd != balances.begin(), "No balances found in the contest's coin, so no decision exists.");
            balances = kj::heapArray<::Balance::Reader>(balances.begin(), newEnd);
        }
        KJ_DBG("Looking for decisions on balances", balances);

        // This struct is basically a lambda on steroids. I'm using it to transform the array of balances into an array
        // of datagrams by looking up relevant datagrams on each balance. Also, make sure the newest balance's decision
        // is in the front (I don't care about preserving order)
        struct {
            // Capture this
            BlockchainWalletApi* wrapper;
            // Capture contestId
            QString contestId;
            // For each balance, look up the datagram containing the relevant decision. Store the promises in this array
            kj::ArrayBuilder<kj::Promise<kj::Maybe<DatagramResponse>>> datagramPromises;

            Reader newestBalance;
            void operator() (Reader balance) {
                if (datagramPromises.size() == 0) {
                    // First balance. Nothing to compare.
                    newestBalance = balance;
                }

                // Start a lookup for the datagram and store the promise.
                auto request = wrapper->m_chain.getDatagramByBalanceRequest();
                request.setBalanceId(balance.getId());
                auto contestIdReader = convertSerialStruct<::ContestId>(contestId);
                request.initKey().initKey().initDecisionKey().setContestId(*contestIdReader);
                datagramPromises.add(request.send().then([](auto response) -> kj::Maybe<DatagramResponse> {
                        return kj::mv(response);
                    }, [](kj::Exception e) -> kj::Maybe<DatagramResponse> {
                        qInfo() << "Got exception when fetching datagram on balance:" << e.getDescription().cStr();
                        return {};
                    })
                );

                // If this balance is newer than the previous newest, move its promise to the front
                if (balance.getCreationOrder() > newestBalance.getCreationOrder()) {
                    newestBalance = balance;
                    std::swap(datagramPromises.front(), datagramPromises.back());
                }
            }
        } accumulator{this, contestId,
                      kj::heapArrayBuilder<kj::Promise<kj::Maybe<DatagramResponse>>>(balances.size()), {}};

        // Process all of the balances. Fetch the appropriate decision for each.
        qDebug() << "Looking for decisions on" << balances.size() << "balances.";
        accumulator = std::for_each(balances.begin(), balances.end(), kj::mv(accumulator));

        return kj::joinPromises(accumulator.datagramPromises.finish());
    }).then([=](kj::Array<kj::Maybe<DatagramResponse>> datagrams) {
        std::unique_ptr<swv::data::Decision> decision;
        // Take the first valid decision as the official decision
        for (auto& datagramMaybe : datagrams) {
            KJ_IF_MAYBE(datagram, datagramMaybe) {
                BlobMessageReader reader(datagram->getDatagram().getContent());
                if (decision)
                    decision->updateFields(reader->getRoot<::Decision>());
                else
                    decision.reset(new data::Decision(reader->getRoot<::Decision>()));
                break;
            }
        }