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