A Python SDK for sCrypt.
You can learn all about writing sCrypt smart contracts in the official docs.
To use the SDK, you need to get a copy of the sCrypt compiler. You can get it either by downloading the sCrypt IDE or executing the following command, if you have an UNIX-like OS:
curl -Ls https://scrypt.io/setup | sh -s --
This will download the latest version of the compiler.
You can also download a specific version of the compiler using the -v
flag:
curl -Ls https://scrypt.io/setup | sh -s -- -v 1.15.1
You can install the SDK as a Python package using pip
, either directly from the project folder:
pip install .
, or from the PyPI:
pip install scryptlib
The SDK is used to convert script templates, produced by the sCrypt compiler, to an object-based representation in Python. It allows for easy compilation, inspection and verification of smart contracts.
We can compile an sCrypt conract source file like so:
import scryptlib
contract = './test/res/arraydemo.scrypt'
compiler_result = scryptlib.compile_contract(contract)
This will leave us with a CompilerResult
object, that contains all of the data, returned by the compiler.
The compile_contract
method will try to automatically search for the compiler binary. You can also explicitly pass the path to the binary, using the compiler_bin
parameter.
It is also possible to pass the sCrypt source code as a string object:
contract_source = '''
contract Equals {
int x;
constructor(int x) {
this.x = x;
}
public function equals(int y) {
require(this.x == y);
}
}
'''
compiler_result = scryptlib.compile_contract(contract_source, from_string=True)
The resulting contract description file will be written to ./out
by default. That may also be changed with the out_dir
parameter.
You can access the contract description directly from the CompilerResult
with its to_desc()
method.
We can evaluate any public function of the contract locally on our machine, before broadcasting it.
First we need to create a class representation of the contract and instantiate it:
import scryptlib
from scryptlib.types import Int
EQUAL_VAL = 2021
contract = './test/res/equals.scrypt'
compiler_result = scryptlib.compile_contract(contract)
desc = compiler_result.to_desc()
Equals = scryptlib.build_contract_class(desc)
contract_obj = Equals(Int(EQUAL_VAL))
As we can see, the created class takes the contract parameters in the constructor. In the case of the Equals
contract, that is an sCrypt int
type, which we can represent in Python using an instance of scryptlib.types.Int
.
Once we have an instance of the contract class, we can evaluate its public functions:
verify_result = contract_obj.equals(Int(EQUAL_VAL)).verify()
assert verify_result == True
From the example see, that we called the contracts public function, named equals
. The actual call to equals()
in Python reutrns an instance of scryptlib.abi.FunctionCall
. That object in turn has a method, named verify
, with which we can run the function calls unlocking script against the contracts locking script.
verify
can internaly create an input evaluation context for simple contracts, but once we start using more advanced constructs, like signatures, we can pass an instance of bitconx.TxInputContext
, using the tx_input_context
parameter.
py-scryptlib leverages the bitcoinx library to deal with Bitcoin primitives.
The following is an example of a local evaluation of a P2PKH contract:
import pytest
import scryptlib
from scryptlib.types import Sig, PubKey, Ripemd160
import bitcoinx
from bitcoinx import SigHash, PrivateKey, pack_byte
key_priv = PrivateKey.from_arbitrary_bytes(b'test123')
key_pub = key_priv.public_key
pubkey_hash = key_pub.hash160()
contract = './test/res/p2pkh.scrypt'
compiler_result = scryptlib.compile_contract(contract)
desc = compiler_result.to_desc()
P2PKH = scryptlib.build_contract_class(desc)
p2pkh_obj = P2PKH(Ripemd160(pubkey_hash))
context = scryptlib.create_dummy_input_context()
sighash_flag = SigHash(SigHash.ALL | SigHash.FORKID)
input_idx = 0
utxo_satoshis = context.utxo.value
sighash = context.tx.signature_hash(input_idx, utxo_satoshis, p2pkh_obj.locking_script, sighash_flag)
sig = key_priv.sign(sighash, hasher=None)
sig = sig + pack_byte(sighash_flag)
verify_result = p2pkh_obj.unlock(Sig(sig), PubKey(key_pub)).verify(context)
assert verify_result == True
You can find many other examples under test/test_contract_*
.
The SDK has a suite of unit tests, which we can run by executing the pytest
command in the root of the project.