-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5e7a8df
commit 6e95068
Showing
1 changed file
with
194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
<pre> | ||
WIP: WIP-scripting | ||
Layer: Consensus (hard fork) | ||
Title: Add scripting to Witnet | ||
Authors: Tomasz Polaczyk <[email protected]> | ||
Discussions-To: `#dev-general` channel on Witnet Community's Discord server | ||
Status: Draft | ||
Type: Standards Track | ||
Created: 2022-08-09 | ||
License: BSD-2-Clause | ||
</pre> | ||
|
||
|
||
## Abstract | ||
|
||
Add scripts to value transfer transactions to allow spending UTXOs with complex conditions. | ||
|
||
## Motivation and rationale | ||
|
||
Support the following use cases: | ||
|
||
* Multisig | ||
* Atomic swap | ||
|
||
#### Multisig | ||
|
||
|
||
#### Atomic swap | ||
|
||
|
||
|
||
## Specification | ||
|
||
Scripting language, forth stack machine. | ||
|
||
Stack, dont mention alt stack because it is not used. | ||
|
||
When validating a `ValueTransferTransaction`, for each input, if that input has a `redeem_script`, do a script validation. Otherwise, if the input does not have a `redeem_script`, do a normal signature validation. Transactions can combine both script inputs and normal inputs. | ||
|
||
Script validation is described in the "Script execution" section. If the script validation is successful, the input can be spent. In case of error, the transaction is marked as invalid and cannot be included in the block. | ||
|
||
#### Types | ||
|
||
Unlike in Bitcoin, in Witnet scripts are type-safe. This means it is not possible to apply an operator to a value of a type that is not supported by that operator, any attempt to do so will result in an error and the script execution will halt. | ||
|
||
There are 3 types: `Boolean`, `Integer`, and `Bytes`. The `Bytes` type is used to encode addresses, signatures, and hashes. | ||
|
||
```rust | ||
pub enum MyValue { | ||
/// A binary value: either `true` or `false`. | ||
Boolean(bool), | ||
/// A signed integer value. | ||
Integer(i128), | ||
/// Bytes. | ||
Bytes(Vec<u8>), | ||
} | ||
``` | ||
|
||
TODO: signatures are converted to bytes using protobuf to encode a KeyedSignature. We should be able to remove the protobuf step and encode it as bytes somehow. | ||
|
||
#### Operators | ||
|
||
```rust | ||
pub enum MyOperator { | ||
/// Pop two elements from the stack, push boolean indicating whether they are equal. | ||
Equal, | ||
/// Pop bytes from the stack and apply SHA-256 truncated to 160 bits. This is the hash used in Witnet to calculate a PublicKeyHash from a PublicKey. | ||
Hash160, | ||
/// Pop bytes from the stack and apply SHA-256. | ||
Sha256, | ||
/// Pop PublicKeyHash and Signature from the stack, push boolean indicating whether the signature is valid. | ||
CheckSig, | ||
/// Pop integer "n", n PublicKeyHashes, integer "m" and m Signatures. Push boolean indicating whether the signatures are valid. | ||
CheckMultiSig, | ||
/// Pop integer "timelock" from the stack, push boolean indicating whether the block timestamp is greater than the timelock. | ||
CheckTimeLock, | ||
/// Pop element from the stack and stop script execution if that element is not "true". | ||
Verify, | ||
/// Pop boolean from the stack and conditionally execute the next If block. | ||
If, | ||
/// Flip execution condition inside an If block. | ||
Else, | ||
/// Mark end of If block. | ||
EndIf, | ||
} | ||
``` | ||
|
||
#### Script execution | ||
|
||
##### Locking script | ||
|
||
The first verification when running a script is to check if the script address (also known as locking script, `redeem_script_hash`) matches with the provided `redeem_script`. That check is performed as `hash160(redeem_script_bytes) == redeem_script_hash`: | ||
|
||
```rust | ||
// Check locking script | ||
let locking_script = &[ | ||
// Push redeem script as first argument | ||
Item::Value(MyValue::Bytes(redeem_bytes.to_vec())), | ||
// Compare hash of redeem script with value of "locking_bytes" | ||
Item::Operator(MyOperator::Hash160), | ||
Item::Value(MyValue::Bytes(locking_bytes.to_vec())), | ||
Item::Operator(MyOperator::Equal), | ||
]; | ||
``` | ||
|
||
If the result of executing the locking script is an error, the script execution stops with that error. If the result of the locking script is anything other than a boolean `true`, the script execution stops with `false`. If after executing the last instruction the number of items left in the stack is greater than one, the script execution stops with `false`. | ||
|
||
##### Redeem script | ||
|
||
To execute the redeem script, it needs to be prepended with the witness script. The witness script must not contain any operators, it must only contain values. | ||
|
||
Script execution is considered successful if the stack ends up containing exactly one item, a boolean "true". | ||
|
||
If script execution stops because of an error, such as an operator receving an invalid argument, then the script execution is unsuccessful, regardless of the contents of the stack. | ||
|
||
|
||
#### Networking changes | ||
|
||
Some network messages need to be modified to support the new script transactions. | ||
|
||
Scripts are encoded as bytes. The redeem script is a new optional field in the `Input` type: | ||
|
||
```rust | ||
pub struct Input { | ||
pub output_pointer: OutputPointer, | ||
pub redeem_script: Vec<u8>, | ||
} | ||
``` | ||
|
||
The redeem script is the part of the script that is used to create a script address. | ||
|
||
Additional inputs such as the signature are encoded separately in the `witness` field of the `VTTransaction`. | ||
|
||
|
||
```rust | ||
message VTTransaction { | ||
VTTransactionBody body = 1; | ||
repeated bytes witness = 2; | ||
} | ||
``` | ||
|
||
The previous version of the protocol had a `signatures` field instead of a witness field. Depending on the context, the value of `witness[0]` is deserialized as a script, or as a `KeyedSignature` encoded with protobuf. | ||
|
||
TODO: yes, we use protobuf inside protobuf, think of a better solution | ||
|
||
If a transaction has both a `signatures` and a `witness` field, the `witness` field must be ignored. This is for backwards compatibility because old nodes will only see the `signatures` field. | ||
|
||
#### Serialization | ||
|
||
Scripts need to be encoded as bytes, because they need to be sent over the network, and also because of the locking script check: we need a way to calculate the script hash. | ||
|
||
TODO: Currently we use JSON but that needs to change. | ||
|
||
## Backwards compatibility | ||
|
||
- Implementer: a client that implements or adopts the protocol improvements proposed in this document. | ||
- Non-implementer: a client that fails to or refuses to implement the protocol improvements proposed in this document. | ||
|
||
|
||
#### Consensus cheat sheet | ||
|
||
Upon entry into force of the proposed improvements: | ||
|
||
- Blocks and transactions that were considered valid by former versions of the protocol MUST continue to be considered valid. | ||
- Blocks and transactions produced by non-implementers MUST be validated by implementers using the same rules as with those coming from implementers, that is, the new rules. | ||
- Implementers MUST apply the proposed logic when evaluating transactions or computing their own data request eligibility. | ||
|
||
As a result: | ||
|
||
- Non-implementers MAY NOT get their blocks and transactions accepted by implementers. | ||
- Implementers MAY NOT get their valid blocks accepted by non-implementers. | ||
- Non-implementers MAY consolidate different blocks and superblocks than implementers. | ||
|
||
Due to this last point, this MUST be considered a consensus-critical protocol improvement. An adoption plan MUST be proposed, setting an activation date that gives miners enough time in advance to upgrade their existing clients. | ||
|
||
|
||
### Libraries, clients, and interfaces | ||
|
||
|
||
## Reference Implementation | ||
|
||
A reference implementation for the proposed protocol improvement can be found in the [witnet-rust] repository. | ||
|
||
|
||
## Adoption Plan | ||
|
||
An adoption plan will be proposed upon moving this document from the _Draft_ stage to the _Proposed_ stage. | ||
|
||
|
||
## Acknowledgements | ||
|
||
This proposal has been cooperatively discussed and devised by many individuals from the Witnet development community. | ||
|
||
[witnet-rust]: https://github.com/witnet/witnet-rust/ |