EspressoSystems / HotShot

http://hotshot.docs.espressosys.com/
101 stars 25 forks source link

[CX-Marketplace] - Define the AuctionResults Trait #3386

Closed jparr721 closed 1 week ago

jparr721 commented 1 week ago

Closes #3369

This PR:

Creates and implements the AuctionResults trait within HotShot to ensure that the results from the Solver are accessible to HotShot in a way which obscures it’s need to know the details of the data types of the Solver. The trait is defined roughly as the following:

/// This trait guarantees that a particular type has a url associated with it. This trait
/// essentially ensures that the results returned by the [`AuctionResults`] trait includes a URL
/// for the builder that HotShot must request from.
pub trait HasUrl {
    /// Returns the builer url associated with the datatype
    fn url(&self) -> Url;
}

/// The AuctionResults trait is the sole source of Solver-originated allocations, and returns
/// the results via its own custom type.
#[async_trait]
pub trait AuctionResults<TYPES: NodeType>: Send + Sync {
    /// The AuctionSolverResult is a type that holds the data associated with a particular solver
    /// run, for a particular view.
    type AuctionSolverResult: HasUrl;

    /// Fetches the auction result for a view. Does not cache the result,
    /// subsequent calls will invoke additional wasted calls.
    async fn fetch_auction_result(
        self,
        view_number: TYPES::Time,
    ) -> Result<Vec<Self::AuctionSolverResult>>;
}

This trait is also added to the system context handle to allow for the state to be cleanly passed down to the HotShot instance, so that's also in this. This PR is low-touch, and mostly just adds some things in more spots.

We need the HasBuilderUrl trait, but I'm willing to move it somewhere else if people disagree on its location. This is because we have no way of knowing how the fields on the associated type AuctionSolverResult are laid out and, as a result, we cannot get the URLs from the vector of results without that method being available.

This trait depends on making async calls to the solver over HTTP, collecting the results into whatever the Sequencer defines AuctionResult as, and then sending it down to HotShot. So we also needed to implement a test implementation of this type, which is laid out as the following:

/// A mock result for the auction solver. This type is just a pointer to a URL.
#[derive(Debug)]
pub struct TestAuctionSolverResult {
    /// The URL of the builder to reach out to.
    pub url: Url,
}

impl HasUrl for TestAuctionSolverResult {
    fn url(&self) -> Url {
        self.url.clone()
    }
}

/// The test auction results type is used to mimic the results from the Solver.
#[derive(Debug, Default)]
pub struct TestAuctionResults {
    /// We intentionally allow for the results to be pre-cooked for the unit test to gurantee a
    /// particular outcome is met.
    pub solver_results: Vec<TestAuctionSolverResult>,

    /// A canned type to ensure that an error is thrown in absence of a true fault-injectible
    /// system for logical tests. This will guarantee that `fetch_auction_result` always throws an
    /// error.
    pub should_return_err: bool,
}

#[async_trait]
impl<TYPES: NodeType> AuctionResults<TYPES> for TestAuctionResults {
    type AuctionSolverResult = TestAuctionSolverResult;

    /// Mock fetching the auction results, with optional error injection to simulate failure cases
    /// in the solver.
    async fn fetch_auction_result(
        self,
        _view_number: TYPES::Time,
    ) -> Result<Vec<Self::AuctionSolverResult>> {
        if self.should_return_err {
            bail!("Something went wrong")
        }

        // Otherwise, return our pre-made results
        Ok(self.solver_results)
    }
}

And this should be all that’s needed to support this within HotShot for testing purposes. Once integration of the later work in this macro task is done, we'll have a more comprehensive usage of this.

This PR does not:

Key places to review: