Spaces:
Runtime error
Runtime error
sidebar_position: 2 | |
# ScriptContext | |
In the UTXO model, the context of validating a smart contract is the UTXO containing it and the transaction spending it, including its inputs and outputs. In the following example, when the second of input of transaction `tx1` (2 inputs and 2 outputs) is spending the second output of `tx0` (3 inputs and 3 outputs), the context for the smart contract in the latter output is roughly the UTXO and `tx1` circled in red. | |
 | |
The context only contains local information, different from account-based blockchains whose context consists of the global state of the entire blockchain (as in Ethereum). A single shared global state across all smart contracts kills scalability, since they all have to sequentially processed due to potential race conditions. | |
This context is expressed in the `ScriptContext` interface. | |
```ts | |
export interface ScriptContext { | |
/** version number of [transaction]{@link https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction} */ | |
version: ByteString, | |
/** the specific UTXO spent by this transaction input */ | |
utxo: UTXO, | |
/** double-SHA256 hash of the serialization of some/all input outpoints, see [hashPrevouts]{@link https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashprevouts} */ | |
hashPrevouts: ByteString, | |
/** double-SHA256 hash of the serialization of some/all input sequence values, see [hashSequence]{@link https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashsequence} */ | |
hashSequence: ByteString, | |
/** sequence number of [transaction input]{@link https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#Format_of_a_Transaction_Input} */ | |
sequence: bigint, | |
/** double-SHA256 hash of the serialization of some/all output amount with its locking script, see [hashOutputs]{@link https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashoutputs} */ | |
hashOutputs: ByteString, | |
/** locktime of [transaction]{@link https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction} */ | |
locktime: bigint, | |
/** [SIGHASH flag]{@link https://wiki.bitcoinsv.io/index.php/SIGHASH_flags} used by this input */ | |
sigHashType: SigHashType, | |
} | |
export interface UTXO { | |
/** locking script */ | |
script: ByteString, | |
/** amount in satoshis */ | |
value: bigint, | |
/** outpoint referenced by this UTXO */ | |
outpoint: Outpoint, | |
} | |
export interface Outpoint { | |
/** txid of the transaction holding the output */ | |
txid: ByteString, | |
/** index of the specific output */ | |
outputIndex: bigint, | |
} | |
``` | |
The table shows the meaning of each field of the `ScriptContext` structure. | |
| Field | Description | | |
| ------------- | ------------- | | |
| version | version of the transaction | | |
| utxo.value | value of the output spent by this input | | |
| utxo.script | locking script of the UTXO | | |
| utxo.outpoint.txid | txid of the transaction being spent | | |
| utxo.outpoint.outputIndex | index of the UTXO in the outputs | | |
| hashPrevouts | If the `ANYONECANPAY` [SIGHASH type](#sighash-type) is not set, it's double SHA256 of the serialization of all input outpoints . Otherwise, it's a `unit256` of `0x0000......0000`. | | |
| hashSequence | If none of the `ANYONECANPAY`, `SINGLE`, `NONE` [SIGHASH type](#sighash-type) is set, it's double SHA256 of the serialization of sequence of all inputs. Otherwise, it's a `unit256` of `0x0000......0000`. | | |
| sequence | sequence of the input | | |
| hashOutputs | If the [SIGHASH type](#sighash-type) is neither `SINGLE` nor `NONE`, it's double SHA256 of the serialization of all outputs. If the [SIGHASH type](#sighash-type) is `SINGLE` and the input index is smaller than the number of outputs, it's the double SHA256 of the output with the same index as the input. Otherwise, it's a `unit256` of `0x0000......0000`. | | |
| locktime | locktime of the transaction | | |
| sigHashType| sighash type of the signature | | |
You can directly access the context through `this.ctx` in any public `@method`. | |
It can be considered additional information a public method gets when called, besides its function parameters. | |
The example below accesses the [locktime](https://learnmeabitcoin.com/technical/locktime) of the spending transaction. | |
```ts | |
class CheckLockTimeVerify extends SmartContract { | |
@prop() | |
readonly matureTime: bigint // Can be timestamp or block height. | |
constructor(matureTime: bigint) { | |
super(...arguments) | |
this.matureTime = matureTime | |
} | |
@method() | |
public timelock() { | |
assert(this.ctx.locktime >= this.matureTime, "locktime too low") | |
} | |
} | |
``` | |
:::note | |
Accessing `this.ctx` in **non-public** methods is not allowed. | |
::: | |
```ts | |
@method() | |
propagateState(outputs: ByteString) : boolean { | |
return this.ctx.hashOutputs == hash256(outputs); // invalid | |
} | |
``` | |
### Access inputs and outputs | |
The inputs and outpus of the spending transaction are not directly included in `ScriptContext`, but their hashes/digests. To access them, we can build them first and validate they hash to the expected digest, which ensures they are actually from the spending transaction. | |
The following example ensure both Alice and Bob get 1000 satoshis from the contract. | |
```ts | |
class DesignatedReceivers extends SmartContract { | |
@prop() | |
readonly alice: PubKeyHash | |
@prop() | |
readonly bob: PubKeyHash | |
constructor(alice: PubKeyHash, bob: PubKeyHash) { | |
super(...arguments) | |
this.alice = alice | |
this.bob = bob | |
} | |
@method() | |
public payout() { | |
const aliceOutput: ByteString = Utils.buildPublicKeyHashOutput(alice, 1000n) | |
const bobOutput: ByteString = Utils.buildPublicKeyHashOutput(bob, 1000n) | |
let outputs = aliceOutput + bobOutput | |
// require a change output | |
outputs += this.buildChangeOutput(); | |
// ensure outputs are actually from the spending transaction as expected | |
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch') | |
} | |
} | |
``` | |
### SigHash Type | |
[SigHash type](https://wiki.bitcoinsv.io/index.php/SIGHASH_flags) decides which part of the spending transaction is included in `ScriptContext`. | |
 | |
It defaults to `SigHash.ALL`, including all inputs and outputs. You can customize it by setting the argument of the `@method()` decorator, e.g., | |
```ts | |
@method(SigHash.ANYONECANPAY_SINGLE) | |
public increment() { | |
... | |
} | |
``` | |
There are a total of 6 sigHash types to choose from: | |
| SigHash Type | Functional Meaning | | |
| ------------- | ------------- | | |
| ALL | Sign all inputs and outputs | | |
| NONE | Sign all inputs and no output | | |
| SINGLE | Sign all inputs and the output with the same index | | |
| ANYONECANPAY_ALL | Sign its own input and all outputs | | |
| ANYONECANPAY_NONE | Sign its own input and no output | | |
| ANYONECANPAY_SINGLE | Sign its own input and the output with the same index | | |
### Debugging | |
See [How to Debug ScriptContext Failure](../advanced/how-to-debug-scriptcontext.md) | |