126 lines
8.0 KiB
Markdown
126 lines
8.0 KiB
Markdown
# `newSuggestedRoutes()`
|
|
|
|
A constructor function used to create and initialize a struct that represents a collection of suggested routes for a cross-chain transaction. This function aggregates relevant data for each route to determine the best possible paths for a transaction.
|
|
|
|
## Purpose
|
|
The primary purpose of `newSuggestedRoutes` is to assemble a structured view of all potential transaction paths based on the criteria cost, speed, network preferences, and other operational parameters.
|
|
|
|
### Builds Routes:
|
|
Collects and organises routes that meet given criteria.
|
|
|
|
### Filters and Selects Routes:
|
|
Applies business logic or algorithms to select the most efficient or cost-effective routes.
|
|
|
|
### Provides Contextual Data:
|
|
Enriches routes with additional data such as cost estimates, gas fees, and estimated transaction times.
|
|
|
|
## Component Analysis
|
|
### `amountIn *big.Int`
|
|
The amount of tokens or currency to be transferred, influencing which paths are viable based on the maximum transferable amounts and balance restrictions.
|
|
|
|
### `candidates []*Path`
|
|
A list of potential paths that have been pre-determined as possible routes for the transaction.
|
|
|
|
### `fromLockedAmount map[uint64]*hexutil.Big`
|
|
A map indicating amounts locked or reserved on specific networks, possibly affecting the viability of certain paths.
|
|
|
|
### [`filterRoutes`](#filterRoutes) and [`findBest`](#findbest)
|
|
Helper functions that might be used within `newSuggestedRoutes` to filter out infeasible paths and then select the optimal path based on the criteria. These functions represent core logic for determining the best routes from a set of candidates.
|
|
|
|
TODO Additional analysis is required for both of these.
|
|
|
|
---
|
|
|
|
## `findBest`
|
|
|
|
Aims to find the most cost-effective route among a list of potential routes, where each route is a slice of `Path` structs.
|
|
Each `Path` has an associated cost stored in `Cost`, which is of type `big.Float`.
|
|
The function searches for the route with the minimum total cost.
|
|
|
|
### Function Operation
|
|
- 1: Initialization:
|
|
- `best`: A slice of `*Path` that will store the best route found during the function's execution.
|
|
- `bestCost`: A `big.Float` initialized to positive infinity `(math.Inf(1))`, which ensures any real cost comparison will initially be lower.
|
|
- Possibly a hacky solution for the problem. It is probably totally fine.
|
|
- 2: Route Evaluation Loop:
|
|
- The outer loop iterates over each route in the provided 2D slice `routes`.
|
|
- For each `route`, it initializes currentCost to zero.
|
|
- 3: Path Cost Accumulation:
|
|
- The inner loop iterates over each `Path` within the current `route`.
|
|
- It accumulates the cost of all paths in the route into `currentCost`.
|
|
- 4: Comparison and Update:
|
|
- After calculating the total cost for a route, it compares this cost with the current `bestCost`.
|
|
- If `currentCost` is less than `bestCost` `(currentCost.Cmp(bestCost) == -1)`, it updates `bestCost` to this new lower cost and sets `best` to the current route.
|
|
- 5: Return Value:
|
|
- After processing all `routes`, it returns the route with the lowest total cost as `best`.
|
|
|
|
### Analysis
|
|
The function is straightforward, and finds the route with the lowest cost. However, I've highlighted a few potential improvements to consider:
|
|
|
|
- Precision and Performance:
|
|
- While using `big.Float` for cost calculations ensures precision, which is essential in financial applications, operations on `big.Float` are computationally more expensive than on native float types.
|
|
- We should confirm that this level of precision is necessary.
|
|
- Error Handling:
|
|
- There is no error handling in this function.
|
|
- If the route data structure is guaranteed to be well-formed (non-nil and correctly initialised), this is fine. Otherwise, we may want to add validation and/or recover from potential panics due to nil references.
|
|
- Optimisation:
|
|
- If performance is critical and the number of routes is large, we should consider parallel processing techniques.
|
|
- Divide the route slice and process each segment in a separate goroutine, then combine the results.
|
|
- This requires careful handling to avoid race conditions when updating `best` and `bestCost`.
|
|
- Flexibility:
|
|
- Currently, the function only minimises cost. If we need to optimise other metrics (e.g. speed, shortest path, etc)
|
|
- We could consider passing in a comparator function as an argument.
|
|
|
|
---
|
|
|
|
## `filterRoutes`
|
|
|
|
This function aims to filter routes based on "locked amounts" specified for each network (chain ID) and the required `amountIn`. It uses `fromLockedAmount` to constrain two main levels of filtering to determine which routes are viable.
|
|
|
|
### Function stages:
|
|
- 1: Initial Check:
|
|
- If the `fromLockedAmount` map is empty, it immediately returns all the routes as valid because there are no constraints to apply.
|
|
- 2: First Level of Filtering (`filteredRoutesLevel1`):
|
|
- It initializes `fromIncluded` and `fromExcluded` maps based on the `fromLockedAmount` values:
|
|
- If the locked amount for a chain ID is zero, that chain ID is marked as excluded.
|
|
- If the locked amount is non-zero, that chain ID needs to be included, and the route must pass through it.
|
|
- For each route, it checks:
|
|
- If any of the route's paths are on an excluded chain ID (`fromExcluded`), the route is marked **not** OK (`routeOk = false`).
|
|
- If a path's chain ID is in `fromIncluded`, it marks that chain ID as having been included in the route.
|
|
- Finally, it ensures that all required (`fromIncluded`) chain IDs are indeed part of the route. If any are missing, the route is marked **not** OK.
|
|
- Only routes that are marked OK are passed to the next level of filtering.
|
|
- 3: Second Level of Filtering (`filteredRoutesLevel2`):
|
|
- For each route that passed the first level:
|
|
- 1: checks every path in the route against the `fromLockedAmount` to determine if the remaining routes can satisfy the locked amount conditions.
|
|
- 2: calculates `requiredAmountIn` by subtracting the locked amount for the path's chain ID from the total `amountIn`.
|
|
- 3: then calculates `restAmountIn` by summing the `MaxAmountIn` of all other paths in the route, excluding the current one.
|
|
- 4: If `restAmountIn` (the total potential input from other paths) is sufficient to cover `requiredAmountIn`, it "locks" the amount for that path.
|
|
- It could be worth renaming the word "lock" to something like "allocates" or "assigns"
|
|
- 5: Else, the route is marked **not** OK (`routeOk = false`).
|
|
- Routes that pass this check are added to `filteredRoutesLevel2`.
|
|
|
|
## Analysis:
|
|
### Efficiency Concerns:
|
|
- The function performs nested loops over potentially large sets of routes and paths, which can be computationally intensive, especially with the additional checks and conditions within each loop.
|
|
|
|
### Potential Improvements:
|
|
- **Optimise the Inner Loops:**
|
|
- The calculation of `restAmountIn` could be optimized. Instead of recalculating it for each path in the route, it could be calculated once per route and then adjusted for each path.
|
|
- **Memoisation:**
|
|
- If the routes and locked amounts do not change frequently, we could use memoisation to cache results for certain input combinations and possibly improve performance.
|
|
- [The Wikipedia about memoisation](https://en.wikipedia.org/wiki/Memoization)
|
|
- **Concurrency:**
|
|
- Processing routes in parallel could speed up the filtering significantly.
|
|
|
|
### Maintainability:
|
|
The function is complex and handles a lot of logic, which might make it hard to maintain or debug.
|
|
|
|
It could benefit from being broken down into smaller, more manageable functions that handle specific parts of the logic. Example the setup of `fromIncluded` and `fromExcluded`, or the calculations of `requiredAmountIn` and `restAmountIn`.
|
|
|
|
- [see my suggested structure for `filterRoutes`](./code/filterRoutes.go)
|
|
- I've split the different nested functions apart to me the code more modular and testable.
|
|
- I've also renamed "level1Filter" and "level2Filter" to "NetworkComplianceFilter" and "CapacityValidationFilter"
|
|
- Just to make the purpose of these more understandable.
|
|
|
|
### Error Handling:
|
|
There is no error handling or validation for the inputs (checking for nil pointers, etc.). |