unsafeClient evaluation option change, Contract Manifest
Unsafe client evaluation option
Before this release, evaluationOptions.allowUnsafeClient
was a boolean value - the SDK either allowed for reading the state of an unsafe contract (i.e. such contract that is using SmartWeave.allowUnsafeClient
in its source code; evaluation of such contract is non-deterministic) or threw an exception if allowUnsafeClient
was set to false
.
This however was an issue in case the contract was interacting with a foreign contract that was using unsafeClient - and at the same time didn't want to allow for unsafeClient to be used. Any interaction with an unsafe contract was causing an exception to be thrown and evaluation to stop.
To mitigate this issue, the allowUnsafeClient
has been renamed to unsafeClient
and can now accept one of three values:
throw
- the default, same asfalse
before this release - will cause the evaluation to stop and throw an exception when a contract withunsafeClient
will be detected.allow
- same astrue
before this release - allows for evaluation of unsafe contractskip
- skips the evaluation of an unsafe contract - a new option, added in this release. It allows to skip evaluation of a foreign contract that is using unsafe client; in such case the validity of the parent contract interaction will be set to false and the error message will contain error like[SkipUnsafeError] Using unsafeClient is not allowed by default
.
NOTE: contract's evolves are also being tracked - e.g. if contract evolves from safe to unsafe - its evaluation will be skipped from that point.
NOTE: if contract evolves back to safe code (from unsafe code) - it still will be skipped. The reason is that we're unable to determine the state of the contract when it returns to the safe version.
Contract manifest
This release adds a new feature - a contract manifest. Contract manifest is a set of evaluation options that are required by the contract to properly execute.
In order to deploy a contract with a manifest, pass the manifest options in the contractData
parameter of the warp.deploy
method, e.g.:
const {contractTxId, srcTxId} = await warp.deploy({
wallet,
initState: initialState,
src: jsContractSrc,
evaluationManifest: {
evaluationOptions: {
unsafeClient: 'skip',
internalWrites: true
}
}
});
In such case - the contract deployment transaction will contain a new tag - Contract-Manifest
.
Example - oG4vBpf7IqmadALEM9XmguLTfRlztxDcX8lWdUfiHIM.
If the client's evaluation options are not compatible with the contract's manifest options - an error will be thrown, e.g.:
Error: Option {unsafeClient} differs. EvaluationOptions: [throw], manifest: [skip]. Use contract.setEvaluationOptions({unsafeClient: skip) to evaluate contract state.
Option {internalWrites} differs. EvaluationOptions: [false], manifest: [true]. Use contract.setEvaluationOptions({internalWrites: true) to evaluate contract state.
Interactions between contracts with different manifests
In case of an interaction between two contracts that both define a manifest - the idea is that evaluation of the foreign contract should not be processed with "less secure" evaluation options than those set for the main/root contract (i.e. the one that is being read by the User).
Currently, one exception to this rule are the internal writes.
Consider the examples below:
Example 1:
- The root contract blocks internal writes
- The foreign contract allows for internal writes
=> the internal writes should be allowed during evaluation of the foreign contract
Example 2:
- The root contract has the 'unsafeClient' set to 'skip'
- The foreign contract has the 'unsafeClient' to 'allow'
=> the 'unsafeClient' should be set to 'skip' for foreign contract
Example 3:
- The root contract has the 'vm2' set to 'true'
- The foreign contract has the 'vm2' set to 'false'
=> the 'vm2' for the foreign contract should be set to 'true'
Example 4:
- The root contract has the 'maxCallDepth' set to 3
- The foreign contract has the 'maxCallDepth' set to 5
=> the 'maxCallDepth' for the foreign contract should be set to '3'
NOTE: call depth is always verified from the perspective of the root contract!
Example 5:
- The root contract has the 'maxInteractionEvaluationTimeSeconds' set to 10
- The foreign contract has the 'maxInteractionEvaluationTimeSeconds' set to 60
=> the 'maxInteractionEvaluationTimeSeconds' for the foreign contract should be set to '10'
On the other hand - if the root contract has less secure options than the foreign contract -
the more secure options of the foreign contract should be respected.
Example:
- Contract "A" with 'unsafeClient' = 'allow' (and unsafeClient used in its source) is performing
write operation on Contract "B" that has 'unsafeClient' set to 'skip'.
i.e. Contract A calls SmartWeave.contracts.write on Contract B.
In this case the more secure setting of the Contract B should be reflected - and write itself
should be blocked (i.e. it should not be even created during the A.writeInteraction
- when a dry-run
is being performed, and we're evaluating a list of internal writes for a newly created interaction).
All rules are defined in https://github.com/warp-contracts/warp/blob/main/src/contract/EvaluationOptionsEvaluator.ts#L21
What's Changed
- feat: skip unsafe contracts calls by @ppedziwiatr in #259
Full Changelog: v1.2.33...1.2.35