docs: readme and tsdocs updated

This commit is contained in:
ppedziwiatr
2021-09-17 15:05:38 +02:00
committed by Piotr Pędziwiatr
parent df52478bd9
commit a6596f7244
12 changed files with 205 additions and 117 deletions

127
README.md
View File

@@ -1,6 +1,7 @@
# RedStone SmartContracts SDK
RedStone SmartContracts SDK is the new, written from scratch, implementation of
the [SmartWeave](https://github.com/ArweaveTeam/SmartWeave) Protocol.
RedStone SmartContracts SDK is the new, rewritten from scratch, SDK implementation proposal for interaction with [SmartWeave](https://github.com/ArweaveTeam/SmartWeave) Contracts.
It has been built with performance (e.g. caching at multiple layers, Arweave calls optimization)
and modularity (e.g. ability to use different types of caches, imported from external libraries) in mind.
@@ -8,24 +9,47 @@ and modularity (e.g. ability to use different types of caches, imported from ext
We're already using the new SDK on production, both in our webapp and nodes.
However, if you'd like to use it in production as well, please contact us on [discord](https://discord.com/invite/PVxBZKFr46) to ensure a smooth transition and get help with testing.
The base motivation behind rewriting SmartWeave SDK (and roadmap proposal) has been described [here](https://github.com/redstone-finance/redstone-smartweave/blob/main/docs/ROAD_MAP.md).
To further improve contract state evaluation time, one can additionally use AWS CloudFront based Arweave cache described in [here](https://github.com/redstone-finance/redstone-smartweave-contracts/blob/main/docs/CACHE.md).
The base motivation behind rewriting the original SDK (and roadmap proposal) has been described [here](https://github.com/redstone-finance/redstone-smartweave/blob/main/docs/ROAD_MAP.md).
To further improve contract state evaluation time, one can additionally use AWS CloudFront based Arweave cache described [here](https://github.com/redstone-finance/redstone-smartweave-contracts/blob/main/docs/CACHE.md).
- [Architecture](#architecture)
- [Development](#development)
- [Installation and import](#installation-and-import)
- [Examples](#examples)
- [Missing features](#missing-features)
- [Source code structure](#source-code-structure)
- [core package](#core-package)
- [contract package](#contract-package)
- [cache package](#cache-package)
- [plugins package](#plugins-package)
- [legacy package](#legacy-package)
- [logger package](#logger-package)
- [Installation and import](#installation-and-import)
- [Examples](#examples)
- [Migration guide](#migration-guide)
- [Documentation](#documentation)
- [Missing features](#missing-features)
### Development
PRs are welcome! :-) Also, feel free to submit issues - with both bugs and feature proposals.
Please use [semantic commit messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716)
## Architecture
RedStone SmartContracts SDK consists of main 3 layers:
<img src="https://smartweave.redstone.finance/assets/img/illustrations/architecture.svg" height='50%' width='50%'>
1. The `Core Protocol` layer is the implementation of the original SmartWeave protocol and is responsible for communication with the SmartWeave smart contracts deployed on Arweave. It consists of 5 modules:
1. `Interactions Loader` - this module is responsible for loading from Arweave all the interaction transactions registered
for given contract.
2. `Interactions Sorter` - responsible for sorting the interactions according to the protocol specification. This is crucial operation for the deterministic contract state evaluation.
3. `Definition Loader` - this module loads all the data related to the given SmartWeave contract - its source code, initial state, etc.
4. `Executor Factory` - this module is responsible for creating "handles" to the SmartWeave contract. These handles are then used by the SDK to call SmartWeave contract methods.
5. `State Evaluator` - this module is responsible for evaluating SmartWeave contract state up to the requested block height.
2. The `Caching` layer - is build on top of the `Core Protocol` layer and allows caching results of each of the `Core Protocol` modules separately.
The main interfaces of this layer are the:
1. `SwCache` - simple key-value cache, useful for modules like `Definition Loader`
2. `BlockHeightSwCache` - a block height aware cache, crucial for modules like `Interactions Loader` and `State Evaluator`.
These interfaces - used in conjunction with cache-aware versions of the core modules (like `CacheableContractInteractionsLoader` or `CacheableStateEvaluator`)
allow to greatly improve performance and SmartWeave contract's state evaluation time - especially for contracts that heavily interact with other contracts.
3. The `Extensions` layer - includes everything that can be built on top of the core SDK - including Command Line Interface, Debugging tools, different logging implementations,
so called "dry-runs" (i.e. actions that allow to quickly verify the result of given contract interaction - without writing anything on Arweave).
This modular architecture has several advantages:
1. Each module can be separately tested and developed.
2. The SmartWeave client can be customized depending on user needs (e.g. different type of caches for web and node environment)
3. It makes it easier to add new features on top of the core protocol - without the risk of breaking the functionality of the core layer.
## Development
PRs are welcome! :-) Also, feel free to submit [issues](https://github.com/redstone-finance/redstone-smartcontracts/issues) - with both bugs and feature proposals.
In case of creating a PR - please use [semantic commit messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716).
### Installation and import
@@ -45,69 +69,28 @@ import * as SmartWeaveSdk from 'redstone-smartweave'
import { SmartWeave, Contract, ... } from 'redstone-smartweave'
```
The SDK is available in both the ESM and CJS format - to make it possible for web bundlers (like webpack) to effectively
perform tree-shaking.
### Examples
Usage examples can be found in
a dedicated [repository](https://github.com/redstone-finance/redstone-smartcontracts-examples).
Please follow instructions in its README.md (and detail-ish comments in the examples files) to learn more.
There is also a separate repository with a web application [example](https://github.com/redstone-finance/redstone-smartcontracts-app).
We've also created a [tutorial](https://github.com/redstone-finance/smartweave-loot/blob/main/docs/LOOT_CONTRACT_TUTORIAL.md) that introduces to the process of writing your own SmartWeave contract from scratch
and describes how to interact with it using RedStone SmartContracts SDK.
### Migration Guide
If you're already using Arweave smartweave.js SDK and would like to smoothly migrate to RedStone SmartContracts SDK -
check out the [migration guide](https://github.com/redstone-finance/redstone-smartweave/blob/main/docs/MIGRATION_GUIDE.md).
### Documentation
TSDocs can be found [here](https://smartweave.docs.redstone.finance/).
### Missing features
The features below are not yet implemented. They will be either added soon to the core SDK, or as
Some features from the original Arweave's smartweave.js are not yet implemented. They will be either added soon to the core SDK, or as
a separate libraries, built on top of the SDK:
- "dry-runs" (though not sure if this should be part of the "core" SDK)
- CLI (though not sure if that is a necessary - even if, it should be
probably a separate lib built on top of the base SDK).
### Source code structure
SDK's source code is divided into few main modules.
#### Core package
Code located in the `core` package contains all the main modules of the reference SDK v2 implementation.
These modules are used to create instances of `SmartWeave` - main class that allows to connect to contracts.
There are currently 5 core interfaces:
1. `DefinitionLoader` - it is responsible for loading contract's definition (i.e. its source code, initial state, etc.)
Its reference implementation is `ContractDefinitionLoader`.
2. `ExecutorFactory` - factory responsible for creating executors that run contract's source code. Its reference implementation is
`HandlerExecutorFactory` - which produces handlers that run contracts written using the "handle" function.
In the future - more advanced `ExecutorFactory`ies can be implemented - e.g. such that will allow
code exception isolation or running contracts written in a more `OOP` style.
Please **note** that new SDK version allows calling `viewState` (`interactRead` from the current version) from within the contract source code.
3. `InteractionsLoader` - responsible for loading interaction transactions, with reference implementation in `ContractInteractionsLoader`
4. `InteractionsSorter` - self-explanatory ;-) Two implementations - `LexicographicalInteractionsSorter` - same, as in
current SDK, and `LexicographicalInteractionsSorter` - based on a PR [https://github.com/ArweaveTeam/SmartWeave/pull/82](https://github.com/ArweaveTeam/SmartWeave/pull/82)
5. `StateEvaluator` - responsible for evaluating the state for a given set of transactions, with reference `DefaultStateEvaluator`.
Please **note** that `DefaultStateEvaluator` currently by default doest **not stop** processing in case of any `exception` result type from state evaluation (to be backwards compatible
with current SDK version) - though we still can't decide whether it is a proper behaviour.
You can change this behaviour by modifying `EvaluationOptions`.
Additionally, the core package contains the definition of all the tags used by the protocol - `SmartWeaveTags`.
All interfaces and implementations are further described in TSDocs.
#### Contract package
Code located in the `contract` package contains base contract interface - `Contract` and its
"reference" implementation - `HandlerBasedContract` - that allows to interact with contracts.
To connect to a contract, first an instance of the `SmartWeave` must be created.
Refer the TSDocs for more information.
#### Cache package
Code located in the `cache` package contains base interfaces - `SwCache` and `BlockHeightSwCache`
and some example implementations. These caches can be used while configuring `SmartWeave`
instance - to greatly improve processing speed (i.e. contract's state evaluation) .
Refer the TSDocs for more information.
#### Plugins package
This package contains some example extensions to base implementation, adding features like "Evolve", caching
capabilities to `InteractionsLoader`, `ExecutorFactory` and `StateEvaluator`, etc.
One cool plugin is the `DebuggableExecutorFactor` - it's a wrapper over `ExecutorFactory` that adds a feature
of changing the contract's code "on the fly" (while evaluating the state) - without the need of deploying anything on Arweave.
This is really useful while debugging contracts (e.g. quickly adding some console.logs in contract's source code)
or quickly testing new features.
#### Legacy package
This package contains some code from the current SDK implementation - most of
this code will be probably remove with the future releases of the new SDK implementation.
#### Logger package
TODO: add description

View File

@@ -5,7 +5,6 @@
- [`setEvaluationOptions`](#setevaluationoptions)
- [`readState`](#readstate)
- [`viewState`](#viewstate)
- [`viewStateForTx`](#viewstatefortx)
- [`writeInteraction`](#writeinteraction)
## Contract Methods

View File

@@ -1,4 +1,4 @@
# Migration Guide from SmartWeave v1 to SmartWeave v2
# Migration Guide from Arweave's SmartWeave SDL to RedStone SmartContracts
This guide describes <strong>the simplest</strong> way to switch to the new version of SmartWeave. It uses `SmartWeaveNodeFactory` for Node and `SmartWeaveWebFactory` for Web to quickly obtain fully configured, mem-cacheable SmartWeave instance. To see a more detailed explanation of all the core modules visit the [SmartWeave v2 documentation](https://smartweave.docs.redstone.finance/) or check out the [source code.](https://github.com/redstone-finance/redstone-smartweave)

View File

@@ -1,26 +1,25 @@
# SmartWeave v2
# RedStone SmartContracts SDK
### The issues with the current implementation
### The issues with original smartweave.js SDK
* low performance (unnecessary calls to Arweave http api, no easy option to add caching layer, etc.)
* no clearly defined base protocol
* implementation that is really hard (if not impossible...) to unit test
* no tests... :-(((
* no tests, very prone to errors (eg. recent issue with input tags format)
* many c/p in the code (like the recent "evolve" feature)
* any change in the base function (ie. adding cache) basically requires to make a c/p of the base function
* any change in the base function (i.e. adding cache) basically requires to make a c/p of the base function
and add required changes (eg. Kyve's own version of readContract/interactRead functions)
- this of course makes it really hard to keep the copy-pasted version up to date with base implementation
* sometimes a bit "chaotic" implementation (ie. not sticking to one naming convention, multiple optional function arguments, etc.)
- this of course makes it really to maintain and keep the code up to date with base implementation
* sometimes a bit "chaotic" implementation (i.e. not sticking to one naming convention, multiple optional function arguments, etc.)
### The "v2" approach
1. Clearly defined core protocol layers/interfaces/tags, etc.
All of this is kept in the "core" directory.
### The "RedStone SmartContracts" approach
1. Clearly defined core protocol layers/interfaces/tags.
2. OOP implementation
3. Each of the base protocol interface can be easily and independently tested
4. Keep the "core" layer at the bare minimum
5. All additional features (like "evolve" feature or caching or "dry-runs") should build on top of the core layer ("plugins" directory)
4. The "core" layer should be kept at a bare minimum - to reduce the risk of mistakes in core protocol implementation.
5. All additional features (like "evolve" feature or caching or "dry-runs") should build on top of the core layer ("plugins")
6. Option to easily substitute different implementations of the core layers (ie. with or without caching, different ExecutorFactories, etc.)
7. proper use of types in Typescript
8. strongly typed state and handler's api (generics)
8. strongly typed state and handler's api (i.e. generics)
### Roadmap
@@ -29,23 +28,28 @@ All of this is kept in the "core" directory.
2. Description of the core building blocks (in the source code) - done
3. Example caching capabilities implementation - done
4. Example "evolve" implementation - done (my concerns re. this feature described in Evolve.ts and in [this analysis](EVOLVE_analysis.md)))
5. Example usage with one of RedStone's contracts - done (call `yarn run v2`)
5. Example usage with one of RedStone's contracts - done
6. new readContract and interactWrite implementations - done
7. updating the SmartWeaveGlobal definition (with updated readContract version) - done
8. Kyve cache implementation (probably with collaboration with Kyve team) - TODO
9. Verifying output results for X currently "most popular" (with most interactions?) contracts - done for all contracts
10. unit tests for all the core layers and plugins/caches (because screw TDD...;-)) - TODO
11. even more unit tests - TODO
12. did I mention unit tests? TODO
13. release as a separate npm library?
7. release as a separate npm library - done
8. updating the SmartWeaveGlobal definition (with updated readContract version) - done
9. Adding ability to call "interactRead" from the contract's source code. - done
10. Kyve cache implementation (probably with collaboration with Kyve team) - done
11. Verifying output results for X currently "most popular" (with most interactions?) contracts - done for all contracts
12. regression tests - done
13. integration tests - done
14. documentation, migration guide, usage examples for node and web env., tutorial - done
15. unit tests for all the core layers and plugins/caches - in progress
#### Phase 2 - TODO
1. Contract's source code versioning (using standard semantic versioning) - sth. similar to "pragma solidity ^0.8.2;"
2. Adding ability to call "interactRead" from the contract's source code.
1. Contract's execution environment isolation
2. Generating a stack trace from all the contract interactions
#### Phase 3 - TODO
1. Contract's execution environment isolation
1. Contract's source code versioning (using standard semantic versioning) - sth. similar to "pragma solidity ^0.8.2;"
2. Alternation ExecutorFactory implementations - the one that would allow create handlers for contract's written
in a more OOP approach (so instead one "big" handle function, contract can define its interface and class-based implementation);
#### Phase 4 - TODO
1. Alternation ExecutorFactory implementations - the one that would allow create handlers for contract's written
in a more OOP approach (so instead one "big" handle function, contract can define its interface and class-based implementation);
Thing on top of the SDK eg:
1. Custom Gateway, optimized for interactions with SmartWeave contracts
2. Custom Viewblock-like contracts explorer

15
src/cache/SwCache.ts vendored
View File

@@ -8,13 +8,28 @@
* @typeParam V - type of the cache value, default to `any`.
*/
export interface SwCache<K = string, V = any> {
/**
* gets value by its key
*/
get(key: K): V;
/**
* checks whether cache contains entry for given key
*/
contains(key: K): boolean;
/**
* puts new value under specified key
*/
put(key: K, value: V);
/**
* clears the whole cache
*/
clearAll();
/**
* remove entry in cache for given key
*/
remove(key: K);
}

View File

@@ -14,26 +14,37 @@ import {
*/
export interface Contract<State = unknown> {
/**
* Returns transaction id of this contract.
* Returns the Arweave transaction id of this contract.
*/
txId(): string;
/**
* Allows to connect wallet to a contract.
* Allows to connect {@link ArWallet} to a contract.
* Connecting a wallet MAY be done before "viewState" (depending on contract implementation,
* ie. whether called contract's function required "caller" info)
* Connecting a wallet MUST be done before "writeInteraction".
*
* @param wallet - {@link ArWallet} that will be connected to this contract
*/
connect(wallet: ArWallet): Contract<State>;
/**
* Allows to set ({@link EvaluationOptions})
*
* @param options - a set of {@link EvaluationOptions} that will overwrite current configuration
*/
setEvaluationOptions(options: Partial<EvaluationOptions>): Contract<State>;
/**
* Returns state of the contract at required blockHeight.
* Similar to {@link readContract} from the current version.
*
* @param blockHeight - block height at which state should be read. If not passed
* current Arweave block height from the network info will be used.
*
* @param currentTx - a set of currently evaluating interactions, that should
* be skipped during contract inner calls - to prevent the infinite call loop issue
* (mostly related to contracts that use the Foreign Call Protocol)
*/
readState(
blockHeight?: number,
@@ -45,6 +56,17 @@ export interface Contract<State = unknown> {
* ie. object that is a derivative of a current state and some specific
* smart contract business logic.
* Similar to the "interactRead" from the current SDK version.
*
* This method firstly evaluates the contract state to the requested block height.
* Having the contract state on this block height - it then calls the contract's code
* with specified input.
*
* @param input - the input to the contract - eg. function name and parameters
* @param blockHeight - the height at which the contract state will be evaluated
* before applying last interaction transaction - ie. transaction with 'input'
* @param tags - a set of tags that can be added to the interaction transaction
* @param transfer - additional {@link ArTransfer} data that can be attached to the interaction
* transaction
*/
viewState<Input = unknown, View = unknown>(
input: Input,
@@ -72,6 +94,10 @@ export interface Contract<State = unknown> {
/**
* Writes a new "interaction" transaction - ie. such transaction that stores input for the contract.
*
* @param input - new input to the contract that will be assigned with this interactions transaction
* @param tags - additional tags that can be attached to the newly created interaction transaction
* @param transfer - additional {@link ArTransfer} than can be attached to the interaction transaction
*/
writeInteraction<Input = unknown>(input: Input, tags?: Tags, transfer?: ArTransfer): Promise<string | null>;
}

View File

@@ -30,8 +30,18 @@ import { NetworkInfoInterface } from 'arweave/node/network';
export class HandlerBasedContract<State> implements Contract<State> {
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
/**
* wallet connected to this contract
* @protected
*/
protected wallet?: ArWallet;
private evaluationOptions: EvaluationOptions = new DefaultEvaluationOptions();
/**
* current Arweave networkInfo that will be used for all operations of the SmartWeave protocol.
* Only the 'root' contract call should read this data from Arweave - all the inner calls ("child" contracts)
* should reuse this data from the parent ("calling) contract.
*/
public networkInfo?: NetworkInfoInterface = null;
constructor(
@@ -86,7 +96,6 @@ export class HandlerBasedContract<State> implements Contract<State> {
return result as EvalStateResult<State>;
}
// TODO: use tags and transfer params
async viewState<Input, View>(
input: Input,
blockHeight?: number,
@@ -190,7 +199,6 @@ export class HandlerBasedContract<State> implements Contract<State> {
);
}
// TODO: this basically calls previous version, to be refactored.
async writeInteraction<Input>(
input: Input,
tags: Tags = [],

View File

@@ -10,28 +10,45 @@ export interface BalanceResult {
}
/**
* Interface for all contracts the implement the {@link Evolve} feature
* Interface for all contracts the implement the {@link Evolve} feature.
* Evolve is a feature that allows to change contract's source
* code, without having to deploy a new contract.
* See ({@link Evolve})
*/
export interface EvolvingContract {
/**
* allows to post new contract source on Arweave
* @param newContractSource - new contract source...
*/
saveNewSource(newContractSource: string): Promise<string | null>;
/**
* effectively evolves the contract to the source.
* This requires the {@link saveNewSource} to be called first
* and its transaction to be confirmed by the network.
* @param newSrcTxId - result of the {@link saveNewSource} method call.
*/
evolve(newSrcTxId: string): Promise<string | null>;
}
/**
* Interface describing state for all Evolve-compatible contracts.
* Evolve is a feature that allows to change contract's source
* code, without deploying a new contract.
* See ({@link Evolve})
*/
export interface EvolveState {
settings: any[] | unknown | null;
canEvolve: boolean; // whether contract is allowed to evolve. seems to default to true..
evolve: string; // the transaction id of the Arweave transaction with the updated source code. odd naming convention..
/**
* whether contract is allowed to evolve. seems to default to true..
*/
canEvolve: boolean;
/**
* the transaction id of the Arweave transaction with the updated source code.
*/
evolve: string;
}
/**
* Interface describing state for all PST contracts.
* Interface describing base state for all PST contracts.
*/
export interface PstState extends EvolveState {
ticker: string;
@@ -51,12 +68,23 @@ export interface TransferInput {
/**
* A type of {@link Contract} designed specifically for the interaction with
* Profit Sharing Tokens.
* Profit Sharing Token contract.
*/
export interface PstContract extends Contract, EvolvingContract {
/**
* return the current balance for the given wallet
* @param target - wallet address
*/
currentBalance(target: string): Promise<BalanceResult>;
/**
* returns the current contract state
*/
currentState(): Promise<PstState>;
/**
* allows to transfer PSTs between wallets
* @param transfer - data required to perform a transfer, see {@link transfer}
*/
transfer(transfer: TransferInput): Promise<string | null>;
}

View File

@@ -1,5 +1,5 @@
/**
* contains all data and meta-data of the given contact.
* This type contains all data and meta-data of the given contact.
*/
export type ContractDefinition<State> = {
txId: string;

View File

@@ -7,16 +7,43 @@ import { BlockData } from 'arweave/node/blocks';
* that are required to call contract's code.
* This has been created to prevent some operations from loading certain data (eg.
* contract's definition - which is very time consuming) multiple times
* (eg. multiple calls to "loadContract" in "interactRead" in the current version of the SW SDK).
* (eg. multiple calls to "loadContract" in "interactRead" in the current version of the Arweave's smartweave.js).
*/
export type ExecutionContext<State, Api = unknown> = {
/**
* {@link SmartWeave} client currently being used
*/
smartweave: SmartWeave;
/**
* {@link Contract} related to this execution context
*/
contract: Contract<State>;
/**
* The full {@link ContractDefinition} of the {@link Contract}
*/
contractDefinition: ContractDefinition<State>;
/**
* block height used for all operations - either requested block height or current network block height
*/
blockHeight: number;
/**
* all the interactions registered for this contract
*/
interactions: GQLEdgeInterface[];
/**
* interaction sorted using either {@link LexicographicalInteractionsSorter} or {@link BlockHeightInteractionsSorter}
* - crucial for proper and deterministic state evaluation
*/
sortedInteractions: GQLEdgeInterface[];
/**
* evaluation options currently being used
* TODO: this can be removed, as it should be accessible from the {@link Contract}
*/
evaluationOptions: EvaluationOptions;
/**
* A handle to the contract's "handle" function - ie. main function of the given SWC - that actually
* performs all the computation.
*/
handler: Api;
currentNetworkInfo?: NetworkInfoInterface;
currentBlockData?: BlockData;

View File

@@ -1,10 +1,8 @@
import { ExecutionContext, HandlerApi } from '@smartweave';
/**
* really not a fan of this feature...
*
* This adds ability to modify current execution context based
* on state - example (and currently only) use case is the "evolve" feature...
* on state - example (and currently only) use case is the "evolve" feature.
*/
export interface ExecutionContextModifier {
modify<State>(

View File

@@ -14,7 +14,7 @@ import { Contract, HandlerBasedContract, PstContract, PstContractImpl } from '@s
/**
* The SmartWeave "motherboard" ;-).
* This is the base class that supplies the implementation of the SmartWeave SDK.
* This is the base class that supplies the implementation of the SmartWeave protocol
* Allows to plug-in different implementation of all the modules defined in the constructor.
*
* After being fully configured, it allows to "connect" to