export function getContract()

in src/governance/ccf-app/js/src/endpoints/contracts.ts [108:226]


export function getContract(
  request: ccfapp.Request
): ccfapp.Response<GetContractResponse> | ccfapp.Response<ErrorResponse> {
  const id = request.params.contractId;

  // Check if the contract is already accepted.
  if (acceptedContractsStore.has(id)) {
    const contractItem = acceptedContractsStore.get(id);
    const seqno = acceptedContractsStore.getVersionOfPreviousWrite(id);
    const view = ccf.consensus.getViewForSeqno(seqno);
    if (view == null) {
      return {
        statusCode: 503,
        body: new ErrorResponse(
          "ViewNotKnown",
          "View for given sequence number not known to the node at this time."
        )
      };
    }
    const version = view + "." + seqno;
    const finalVotes: { memberId: string; vote: boolean }[] = [];
    if (contractItem.finalVotes !== undefined) {
      for (const [memberId, vote] of Object.entries(contractItem.finalVotes)) {
        finalVotes.push({
          memberId: memberId,
          vote: vote === true ? true : false
        });
      }
    }

    const body: GetContractResponse = {
      id: id,
      version: version,
      state: "Accepted",
      data: contractItem.data,
      proposalId: contractItem.proposalId,
      finalVotes: finalVotes
    };
    return { body };
  }

  // Check if the contract is currently associated with an open proposal.
  let proposedContract;
  proposalsStore.forEach((v, k) => {
    const proposal = ccf.bufToJsonCompatible(v) as ProposalStoreItem;
    proposal.actions.forEach((value) => {
      if (value.name === "set_contract") {
        const args = value.args as SetContractArgs;
        if (args.contractId === id) {
          const proposalInfo = ccf.bufToJsonCompatible(
            proposalsInfoStore.get(k)
          ) as ProposalInfoItem;
          if (proposalInfo.state == "Open") {
            const body: GetContractResponse = {
              id: id,
              state: "Proposed",
              data: args.contract.data,
              version: args.contract.version,
              proposalId: k
            };

            proposedContract = { body };
            return false; // break out of the loop.
          }
        }
      }
    });
  });

  if (proposedContract != null) {
    return proposedContract;
  }

  if (contractsStore.has(id)) {
    const contractItem = contractsStore.get(id);

    // Capture both seqno (version) and view to create version semantics.
    // Apart from getVersionOfPreviousWrite(key) we also want to call getViewForSeqno(seqno) to
    // get and incorporate the view into the version because the following situation could take place:
    // getVersionOfPreviousWrite(k) -> 5
    // Client goes to prepare write conditional on version being 5
    // Network rolls back to 3 after primary crashes, elects new leader
    // 4 and 5 happen, 5 unluckily writes to k also
    // Client request arrives, expects last write to be at 5, proceeds - but the value is now different
    // If you capture:
    // getVersionOfPreviousWrite(k) -> 5
    // getViewForSeqno(5) -> 2
    // And place (and check) the expectation that the last write for the key must be at 5 in view 2,
    // then this cannot happen.
    const seqno = contractsStore.getVersionOfPreviousWrite(id);
    const view = ccf.consensus.getViewForSeqno(seqno);
    if (view == null) {
      return {
        statusCode: 503,
        body: new ErrorResponse(
          "ViewNotKnown",
          "View for given sequence number not known to the node at this time."
        )
      };
    }
    const version = view + "." + seqno;
    const body: GetContractResponse = {
      id: id,
      state: "Draft",
      version: version,
      data: contractItem.data,
      proposalId: ""
    };
    return { body };
  }

  return {
    statusCode: 404,
    body: new ErrorResponse(
      "ContractNotFound",
      "A contract with the specified id was not found."
    )
  };
}