Merge remote-tracking branch 'refs/remotes/origin/main'
commit
9eb5de216a
136
Gajumaru.md
Normal file
136
Gajumaru.md
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# The Gajumaru
|
||||||
|
|
||||||
|
The word "Gajumaru" comes from southern Japan and means "the walking tree". This type of tree expands its branch and root system endlessly as circumstances require such that an entire copse or even small forest may consist of a single tree and many interconnected boles. Early contemplation of an effective architecture that solves the various problems faced by [DLTs](Distributed-Ledger-Technologies-(DLTs)) inspired the name.
|
||||||
|
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
The Gajumaru consists of a tree of ledgers with the central ledger, the [Gajumaru Root](Groot) ("groot"), being a distributed blockchain that uses a proof-of-work (PoW) consensus mechanism acting as a system-wide currency mint and registrar. Child ledgers called [“associate chains” (ACs)](ACs) can be created and registered with groot. ACs can themselves have their own subordinate ACs registered to them in turn. Users can transfer funds and resources between Gajumaru chains via the common parent, without having to involve third-party gateways.
|
||||||
|
|
||||||
|
Groot is the only chain in the system required to have a [[trustless]] [consensus](consensus) mechanism and is therefore the only chain permitted to [[mint]] the [[currency]] that underlies the Gajumaru. ACs are connected to a parent chain via a permissionless (on Groot) connection point. The ACs must comply with the inter-chain protocol for funds transfers. Beyond that, they may choose to be public or private, and can use any blockchain platform, or even a non-blockchain system. This is similar to how a central gold reserve is the sole authority for the money supply, but subordinate banks across the financial system are trusted entities that enable specific forms of business that may not concern the central bank. Whether to allow transfers to and from an AC is fully within the purview of the AC operators, simplifying regulatory compliance.
|
||||||
|
|
||||||
|
The structure of the Gajumaru, a homogeneous protocol and runtime substrate coupled with heterogeneous consensus protocols, permits almost limitless flexibility for operators and users of Gajumaru associate chains without suffering the problems faced by other solutions.
|
||||||
|
|
||||||
|
As user can move funds between Gajumaru chains, Gajumaru wallets are typically designed to track account balances across multiple chains. In this way a wallet needing to move coins from AC X to AC Y can walk the path between them and make the transfers necessary to accomplish the overall transaction desired.
|
||||||
|
|
||||||
|
|
||||||
|
## Consensus and Finality
|
||||||
|
|
||||||
|
The block structure of the Gajumaru Root chain is somewhat novel and is derived from its consensus mechanism, [[Bitcoin-NG]] ([Eyal et al 2016](https://arxiv.org/pdf/1510.02037)) 1. This means that instead of single blocks which contain proof of work as well as ledger entries, "[[blocks]]" are split into "[[keyblocks]]" and "[[microblocks]]", where the keyblock contains the proof (e.g. mined proof of work) that determines the [[current network leader]] and the [[transactions included]] in the ledger at that height are added to a list of microblocks. A keyblock along with all its relevant microblocks is called a "[[generation]]".
|
||||||
|
|
||||||
|
Not all blockchains offer actual [[finality]], i.e. a point where a transaction is guaranteed to be impossible to evict from the chain. The traditional protection is that for each additional block, the cost of re-writing the history on-chain increases exponentially. Nowadays, several blockchains claim some form of finality, often even within a few seconds. We claim that a proper finality mechanism needs to be irreversible once claimed, even in the face of [[network splits]] (which tend to take more than a few seconds to even detect). Ideally, it should also offer levels of fallback, allowing traffic to (cautiously) proceed, even when the network is compromised.
|
||||||
|
|
||||||
|
In addition to the built-in classical [[fork protection]] of the Bitcoin-NG consensus mechanism, the Gajumaru also greatly accelerates finality by employing a [[witness]] system among significant [[stakeholders]] within the system. This witness system works very similar to other witnessing systems in DLTs, but rather than being the sole basis for consensus, it is merely a fast [[fork]] protection and finality mechanism in the context of the Gajumaru. Unlike systems such as Algorand, the Gajumaru can continue to function on a purely PoW basis even if no majority of witnesses are reachable, but once a generation has accrued a majority of witnesses it will be a universally recognized basis for the true fork.
|
||||||
|
|
||||||
|
A particular feature of the witnessing scheme is that its progress is readily observable. The Gajumaru blockchain explorer tracks witnessing status in real-time. This way, once a transaction appears in a microblock you can typically see immediately if the enclosing generation is on its way to persistency: A new keyblock typically receives [[testimonies]] immediately, and if it is on the majority fork, a majority of testimonies will accrue very quickly. These testimonies will then normally be represented in the next keyblock, thereby lending finality to the preceding keyblock. For actual finality of our transaction, its following keyblock also needs to be finalized, leading to a normal finality time of 2-4 minutes (2 minutes being the configured [[keyblock interval]]).
|
||||||
|
|
||||||
|
A further feature of the witnessing scheme is that the keyblocks being witnessed are content-free, i.e. they carry no transactions. This means that the witnessing scheme is clearly limited to the purely structural aspect: does the keyblock in question follow the current top block of the witnessing node? This way, witnesses are safe from the risk of accidentally 'approving' questionable content.
|
||||||
|
|
||||||
|
Every time Groot opens a new generation the new height can be propagated down to all subordinate ACs. This provides for a very low-resolution synchronization method for the system as well as necessary sources of entropy and information upon which various provable consensus mechanisms can be built on subordinate ledgers.
|
||||||
|
|
||||||
|
A ledger other than groot can in theory implement any consensus mechanism desired by its [[operator]] (permissioned, [[proof-of-stake]], [[proof-of-useful-work]], witnessing schemes, etc.) and may implement any storage or distribution scheme required. It does not necessarily have to be a proper blockchain, for example, so long as the concept of height is maintained, and it can “speak Gajumaru” (adhere to the protocol definition). For full interoperability, this means it implements the [FATE virtual machine](FATE), comply with blockchain addressing schemes, and is capable of communicating asset commits and remits between its children, itself, and its parent. Ledgers other than Groot can also implement whatever [[fee]], [[gas]] or other processing rules suit them best.
|
||||||
|
|
||||||
|
At a minimum, an Associate Chain implements only the connection protocol, allowing [[Gajus]] to be transferred in and out of the chain. This could be used to connect an existing financial actor, giving it access to Gajus, while using its own internal systems (legacy, perhaps) to maintain the assets.
|
||||||
|
|
||||||
|
While ACs can implement any consensus mechanism desired as long as it can be made to comply with Gajumaru’s protocol, the default options for AC operators using the Gajumaru platform at Gajumaru’s initial launch will be [[Proof-of-Stake]] and [[Permissioned]] schemes. The Proof-of-Stake mechanism involved is based on leader elections, where various stakers vote to agree on the leader for an upcoming generation.
|
||||||
|
|
||||||
|
The Gajumaru consensus engine is implemented as a pluggable architecture upon which a number of different modes have already been implemented. In particular, a [[Smart Contract]] Consensus model is supported, where key decision points are delegated to a Sophia smart contract. The contract is selected and upgraded using a governance process, and review of the upgrades is made much easier as the logic is written in the canonical language of the blockchain. Examples of decisions that can be made by such a contract are: leader election, stake management (if PoS), difficulty calculation, and reward payout.
|
||||||
|
|
||||||
|
A parent ledger can commit assets from an account on itself to the same account on its child ledger. It can accept commits from an account on a child ledger back to the same account on itself. It is not possible for a child ledger to commit more assets back to the parent than were committed to the child ledger. This means that if a faithless child ledger were to falsely report a transfer of funds from one account to another and commit them back to the parent, or falsely mint tokens within its own scope, the maximum damage to the system is limited to the sum of the commits to that child ledger.
|
||||||
|
|
||||||
|
Placing assets on to a child ledger from a parent ledger implies trust in that child ledger's operation. Likewise, founding a child ledger on a parent ledger implies trust in the parent ledger. Schemes for proving valid ownership and transferal on a subordinate ledger are an open area of research, but no system will ever be able to completely automate the trust function without incurring the impractical and extreme penalties faced by trustless, distributed blockchain ledgers already.
|
||||||
|
|
||||||
|
In addition to ACs, the Gajumaru offers a mechanism called “[[state channels]]” in which two parties can open a sort of mini-chain, the channel, similarly to how an AC is created, and in that context can run any number transactions as rapidly as they can sign them (perhaps with automated assistance), completely free of gas fees or mining constraints. At the conclusion of their business this channel can be closed by committing only the final state back to the parent chain from which that state channel was spawned. The entire history of transitions that occurred in the state channel need never affect the parent chain, only the final commit transaction. Additionally, it is possible to checkpoint a state channel on the parent or commit additional funds or other resources to an existing state channel, allowing state channels to represent ongoing two-party relationships with checkpoints in and out of the channel along the way. In a sense this can be thought of as the ultra-performant, near-zero cost way of creating a temporary, two-party AC, and ACs can be thought of as a way of creating a “mega state channel” open to multiple users.
|
||||||
|
|
||||||
|
The structure of the Gajumaru resolves many outstanding problems in
|
||||||
|
blockchain:
|
||||||
|
- Prevents bad faith actors from wreaking systemic damage
|
||||||
|
- Blocks the possibility of currency debasement
|
||||||
|
- Allows diverse consensus mechanisms to be applied on ACs where appropriate
|
||||||
|
- Partitions performance issues
|
||||||
|
- Enables incredibly flexible two-party operation
|
||||||
|
- Naturally incentivizes the creation of performance extending ACs whenever a parent chain is nearing its performance limits
|
||||||
|
- Provides an opening for existing financial service providers to provide the services they are best at in the context of an AC (Visa, for example, could
|
||||||
|
implement a fraud prevention family of ACs for buyers and sellers with unique rules)
|
||||||
|
- Breaks blockchain out of the false dichotomy of “all trust” vs “zero trust” that fails to reflect how humans make decisions in the real world
|
||||||
|
|
||||||
|
|
||||||
|
## On-Chain Data
|
||||||
|
|
||||||
|
The primitive types of objects that can be found on chain are split into three categories:
|
||||||
|
|
||||||
|
### Universal Objects
|
||||||
|
- Plain Old Accounts (aka [[PLAs]]) are implicitly created and therefore transferable among ledgers
|
||||||
|
|
||||||
|
### Non-transferable Objects
|
||||||
|
- Contracts
|
||||||
|
- State channels
|
||||||
|
- Generalized Accounts
|
||||||
|
- Subordinate ledgers
|
||||||
|
|
||||||
|
### Transferable Objects
|
||||||
|
- Currency (Gajus or other coins defined by an AC)
|
||||||
|
|
||||||
|
As on other systems with smart contract languages available, additional asset classes can be defined in terms of the primitive types above.
|
||||||
|
|
||||||
|
|
||||||
|
## Data Time-to-Live ([[TTL]])
|
||||||
|
|
||||||
|
All data created on-ledger is subject to an expiration date governed by a time-to-live (TTL) value carried by that object.
|
||||||
|
|
||||||
|
When data is stored on the chain the calculation of gas fees is a result of the storage cost per height multiplied by the time (in number of heights) that
|
||||||
|
storage is required. The time interval between generations (keyblocks) is, on average, three minutes. This means storing an object on the chain for one year will set its TTL at the current height plus 175,200.
|
||||||
|
|
||||||
|
Existing objects on the chain can have their life extended by committing a [[TTL transaction]] to the ledger whereby the signing account pays the gas fees
|
||||||
|
necessary to extend the life of an object.
|
||||||
|
|
||||||
|
Given that subordinate ledgers can implement arbitrary storage rules and TTL requirements (including 'infinity' as a valid TTL) it can be advantageous to
|
||||||
|
transfer objects intended for long-term storage to a subordinate ledger, or even implement subordinate ledgers whose sole purpose is to act as bulk data archives for long-term storage.
|
||||||
|
|
||||||
|
All objects created on a state channel have a TTL of 'infinity' within the context of that state channel.
|
||||||
|
|
||||||
|
If an open state channel expires the last valid state committed to its parent (last snapshot) is honored and an automatic commit back to the parent is made that closes the state channel.
|
||||||
|
|
||||||
|
(For state channel authors it is therefore important to understand state channel force-progress rules, important for users of state channel applications to understand state channel TTLs, and important for authors of tools that interact with state channels such as wallets and interface apps to clearly express TTLs to end users.)
|
||||||
|
|
||||||
|
It is possible for contracts to call other contract code within the scope of a single ledger.
|
||||||
|
|
||||||
|
It may occur that a newly deployed contract is intended to outlive another contract upon which it depends.
|
||||||
|
|
||||||
|
It is the task of external tooling (and the responsibility of contract authors and owners) to check whether dependency code will live long enough to satisfy the requirements of a new contract or extend its TTL.
|
||||||
|
|
||||||
|
A contract call that attempts to access expired code will abort with an error that indicates which call to what contract address failed.
|
||||||
|
|
||||||
|
|
||||||
|
## Contract Source Visibility
|
||||||
|
|
||||||
|
Two contract deployment modes are available:
|
||||||
|
1. Visible
|
||||||
|
2. Incognito
|
||||||
|
|
||||||
|
Visible contract creation means that the source code of the contract is deployed as a part of the transaction, and an incognito creation means that the source code is omitted from the contract deployment.
|
||||||
|
|
||||||
|
This ensures that if the contract author desires, it is always possible for the caller of a contract to check what a given contract call is doing (though it is possible that the source is deliberately obfuscated).
|
||||||
|
|
||||||
|
This enables wallets to provide more information about a given call before a user signs a transaction.
|
||||||
|
|
||||||
|
This also means that library code deployed to the chain is researchable from within the chain, even if the off-chain sources are lost.
|
||||||
|
|
||||||
|
A contract clone inherits the visibility of its parent.
|
||||||
|
|
||||||
|
|
||||||
|
## Garbage Collection
|
||||||
|
|
||||||
|
At a predetermined height dictated by governance the current data on the chain can be snapshotted and committed to a fresh genesis block. In the same way that the span of blocks between two keyblocks is a “generation”, the span between two genesis blocks is called an "[[epoch]]". The transition between two epochs is called a "ledger closeout". This is, in essence, similar to closing a fiscal year and opening a new one.
|
||||||
|
|
||||||
|
Nodes operating in a ledger's network can operate in a garbage collected or archive mode. Nodes can be asked which mode they are in as part of a "status" query.
|
||||||
|
|
||||||
|
A garbage collected node may clear out intermediate data and state and objects that do not apply to the current height or have expired (past their TTL). Archive nodes will retain all intermediate data so that any lookups that apply to any height in the past can be referenced.
|
||||||
|
|
||||||
|
All nodes retain the entire transaction log for the current epoch, however, so it is possible even for a garbage collected node to reconstruct whatever intermediate data states have been garbage collected (not currently implemented, but possible). (There are fast-sync implications to this, of course, but that's not the point of this current description.)
|
||||||
|
|
||||||
|
Nodes may retain intermediate data and transaction logs prior to the new genesis, but this is not required.
|
||||||
|
|
||||||
|
The significant thing about a ledger closeout is that it permits garbage collecting the previous epoch's transaction log, regardless of whether a given node is acting in garbage collected or archive mode.
|
||||||
|
|
||||||
|
An archive node that retains the entire ledger's history, regardless of epoch, is called a "[[full archive node]]". An archive node that retains the current and some past epochs, but not all, is a "partial archive node". An archive node that retains the complete current epoch only is a "current archive node". Any combination of modes is valid so long as the node maintains protocol compatibility, to include accurately reporting the mode it is operating in as well as correct query return values when data is inaccessible due to garbage collection (rather than missing due to error or corruption).
|
||||||
3
Home.md
3
Home.md
@ -32,8 +32,9 @@ Title | Brief Description
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Principles
|
## Aspirational Principles
|
||||||
|
|
||||||
|
- **JUST WRITE** - imperfect content is infinitely better than nonexistent content, all else notwithstanding
|
||||||
- **PICTURES:** ~Every article should have a "YouTube Thumbnail" diagram at the
|
- **PICTURES:** ~Every article should have a "YouTube Thumbnail" diagram at the
|
||||||
top that visually illustrates whatever is explained in the page
|
top that visually illustrates whatever is explained in the page
|
||||||
- **QUICKREF:** Every page should have a "I just need to get a thing to work
|
- **QUICKREF:** Every page should have a "I just need to get a thing to work
|
||||||
|
|||||||
78
State-Channels.md
Normal file
78
State-Channels.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Gajumaru State Channels
|
||||||
|
|
||||||
|
State Channels on Gajumaru are an evolution of [Aeternity State Channels](https://github.com/aeternity/protocol/tree/master/channels). They are 1-to-1 messaging sessions anchored on-chain, supporting the following types of interaction:
|
||||||
|
|
||||||
|
* Co-signed coin transfers
|
||||||
|
* Deposit and withdraw coins
|
||||||
|
* Loading and calling Smart Contracts
|
||||||
|
* Sending plain messages
|
||||||
|
|
||||||
|
A channel, once set up, can basically be kept open forever. The participants can disconnect and later reconnect and resume the session. The channel state can be checkpointed to the chain, and eventually, when the channel is closed, the coin balances in the channel are distributed accordingly to the corresponding on-chain accounts. As each state change is co-signed, transactions on a channel are settled immediately. The on-chain protocol provides clear dispute logic for cases where the parties do not agree on the closing state. Each endpoint can monitor the chain in real-time, and detect if the opposing party tries to close the channel subversively. Third-party monitor support is also supported.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
Each Gajumaru node has the capacity to act as a State Channel proxy, providing a simple JSON-RPC interface for clients. One node can service at least a thousand concurrent channels. As channels only touch the chain very rarely (when opened, closed, and if checkpointing the state), the number of state channels that can be open simultaneously basically scales linearly with the number of nodes deployed.
|
||||||
|
|
||||||
|
On one state channel session, you can roughly execute:
|
||||||
|
* 40 contract calls per second
|
||||||
|
* 500+ coin transfers per second
|
||||||
|
* 3000+ plain messages per second
|
||||||
|
|
||||||
|
(or a mix of the above). These are estimates based on a rudimentary benchmark suite, running on an M4 Macbook, and can be updated on request. It's important to note that these interactions come with an ordering guarantee, i.e. they are all performed sequentially. Multiple concurrent channels allow for much higher throughput when transactions can be serviced in parallel.
|
||||||
|
|
||||||
|
An intuition for the number "500+ coin transfers per second" would be that given that the test measured round-trip time for both parties of a coin transfer (2 ms), one might estimate the processing overhead of serving one side of a State Channel payment to be **less than 1 ms**.
|
||||||
|
|
||||||
|
For real-world business cases, one would also need to consider network transfer delays, manual signing delays, business logic overhead, etc., but the raw processing cost is an important metric when considering how much a solution can scale.
|
||||||
|
|
||||||
|
Running a small scaling benchmark, similar to the single-channel benchmark above, using 50 concurrent channels:
|
||||||
|
* 1,900 contract calls per second (47.5x)
|
||||||
|
* 3,500 coin transfers per second (6.5x)
|
||||||
|
* 30,400 plain messages per second (11.5x)
|
||||||
|
|
||||||
|
We have not yet spent much time optimizing this, but were able to push the benchmark to 10k concurrent channels, and the total throughput numbers remained stable. This indicates that the system is mostly free from internal bottlenecks that would degrade performance as more channels are added. However, the 10 performance cores were saturated already with 50 channels putting the pedal to the metal, so further scaling was not possible on that particular hardware. Adding more nodes, the total throughput will scale linearly, i.e. if one node can deliver a certain throughput, `N` nodes will deliver `N` times as much, at least during the time when channels do not post transactions to the chain (the whole point of State Channels is to do this as seldom as possible).
|
||||||
|
|
||||||
|
The State Channel implementation is capable of running in server mode, where the application listens on a given port and services any request that comes in. A State Channel Market demo exists, where a market provider services merchants and customers, facilitating customer-to-merchant payments atomically across channels, for a low service fee, and at a round-trip cost of ca 15 ms + network transfer delays and, as mentioned above, with immediate settlement.
|
||||||
|
|
||||||
|
In the context of Gajumaru, such a State Channel Market could allow for immediate transfer of funds between Associate Chains. In this scenario, the Market provider uses the on-chain mechanism for balancing collateral in bulk between chains.
|
||||||
|
|
||||||
|
### On-chain performance
|
||||||
|
Opening a channel involves an on-chain transaction. On Aeternity, the rule of thumb was to wait for a requisite number of keyblock confirmations before funds allocated to the channel became usable. We have since implemented support for deferring funding confirmation, in order to make the channel immediately usable as soon as the opening transaction is seen on-chain. With Gajumaru's witnessing-based finality, the time to full confidence in a channel is even shorter. But since a channel can be suspended and resumed without on-chain activity, a channel often has to be set up only once, would be usable within seconds, and activated on-demand within fractions of a second.
|
||||||
|
|
||||||
|
## Transaction fees
|
||||||
|
|
||||||
|
There is no mandatory fee for transactions within a State Channel, although fees could certainly be defined for a given service. The extremely low processing overhead of State Channel interactions allows for minimal to no fees.
|
||||||
|
|
||||||
|
In the case of cross-AC State Channel Markets, service fees might be higher to reflect the regulatory compliance costs of intermediating transfers between markets, as well as the market value of facilitating near-instant cross-market transactions.
|
||||||
|
|
||||||
|
## Execution models
|
||||||
|
|
||||||
|
### Peer-to-peer
|
||||||
|
The fundamental model for State Channels is **peer-to-peer**: User A and user B agree to set up a channel by posting an `ChannelCreateTx` transaction on-chain. In this channel, they can send each other coins, load and execute contracts, deposit to and withdraw from the channel, and snapshot current state to the chain. The channel is finally closed using (ideally) a `ChannelCloseMutualTx`, indicating that both agree on the final state. This model can offer the following benefits:
|
||||||
|
* Aggregation, if the transaction volume is greater than 2
|
||||||
|
* Instant finality, as all transactions are co-signed
|
||||||
|
* In-band messaging enriches the interaction model beyond signed transactions
|
||||||
|
* Privacy: transactions inside a channel are not visible - only the closing state is.
|
||||||
|
|
||||||
|
### Client-to-Server
|
||||||
|
For scenarios like Scooter Rental, Casino, Coffee Shop Card, you can set up a server listening on some port, and servicing client state channels. The establishment provides business logic on the server side, and the State Channel framework provides support for spawning multiple acceptors on a single port, very similarly to how e.g. a web server works. Clients can disconnect and reconnect to the same channel at any time. Each session is private and encrypted.
|
||||||
|
|
||||||
|
## Planned extensions
|
||||||
|
|
||||||
|
Work is underway to provide native multi-currency support on Associate Chains. This will also be reflected in State Channels, such that they will support efficient transfers of different currencies.
|
||||||
|
|
||||||
|
## Use cases
|
||||||
|
|
||||||
|
* Instant payment along the lines of Swish (Scandinavia) or Venmo are clear use cases. Larger events, transportation, etc.
|
||||||
|
* A generalized version of 'loyalty cards', like e.g. coffee shop cards.
|
||||||
|
* Open tab at bars and restaurants. Customers can pay as they order by scanning a QR code. The owner is protected from walkouts, as they can unilaterally close the tab and get paid. Smart Contracts can offer mutual protection, with pre-pay locked into the contract until the food/beer is served.
|
||||||
|
* Micro-paywalls, e.g. paying for the next page of text.
|
||||||
|
* Pay-as-you-go AI bot interaction.
|
||||||
|
* Pay-as-you-go media streaming. The in-band plain message support can be used to prompt users when it's nearing time to top up.
|
||||||
|
* "Follow-the-money" social media content promotion, where State Channels can be used to widgetize a monetary tip rather than just a 'like' (which costs nothing, and therefore is easily gamed.)
|
||||||
|
|
||||||
|
For practical widgetization, we have added support for adding a proxy keypair for automatic signing. This could be used e.g. such that the user application pre-approves a certain number of transactions below a certain threshold, which can all be automatically signed before a manual signature is requested to approve the next batch. This is fully under the user's control, and risk is also limited further through the amount of money the user decides to deposit into the channel.
|
||||||
|
|
||||||
|
|
||||||
|
## Implementation status
|
||||||
|
|
||||||
|
End-to-end demonstrations of practical State Channel use cases, including State Channel Markets, are planned during the Spring or early Summer after Mainnet Launch. Partly dependent on funding. Basic support for Gajumaru's State Channel design was in place already in 2019, as exemplified by [CoinFabrik](https://coinfabrik.com) building a [merchant demo system](https://www.youtube.com/watch?v=tuGTeipXUeY). The demo inspired several performance and robustness improvements of the code base.
|
||||||
Loading…
x
Reference in New Issue
Block a user