函数

用户自定义函数

sCrypt允许开发者定义自己的函数,如下所示:

function sum(int a, int b): int {
    return a + b;
}

这些函数只在合约内可见,类似于 Solidity 中的 private 函数。

公共函数

如果公共函数运行完成,则返回 true,否则返回 `false。它没有返回类型和 返回 部分,返回语句是是隐式包含的。换句话说,

public function isZero(int a) {
    require(a == 0);
}

在功能上等同于

public function isZero(int a): bool {
    require(a == 0);
    return true;
}

静态函数和静态属性

可以通过合约名来直接引用静态函数/方法,而不必创建合约实例。类似于 Javascript 或 C++ 中的静态函数/属性。在定义它的合约中,静态属性/函数也可以在没有合约前缀的情况下被引用。

library Foo {
    static int N = 0;

    static function incByN(int a): int {
        // N is used with and without Foo prefix
        return a + Foo.N + N;
    }

    static function double(int x): int {
        // incByN() is called with prefix and without
        return Foo.incByN(x) + incByN(x);
    }
}

contract Bar {
    public function unlock(int y) {
        require(y == Foo.double(2));
        require(y == Foo.N);
        // N cannot be referenced without Foo prefix
        // require(y == N);
    }
}

return 语句

由于比特币脚本缺乏对 return 语义的支持,所以函数必须以 return 语句结尾,并且 return 语句只能放在函数末尾,不能放在其他位置。将来可能会放松这个限制。一般来说这不是问题,可以用如下方式避免在其他位置返回:

function abs(int a): int {
    if (a > 0) {
        return a;
    } else {
        return -a;
    }
}

可以改写为

function abs(int a): int {
    int ret = 0;

    if (a > 0) {
        ret = a;
    } else {
        ret = -a;
    }
    return ret;
}

递归

不允许递归。函数不能直接或间接地在其主体中调用自身。

库函数

sCrypt实现了如下库函数,在全局可见。

数学

  • int abs(int a)
  • int min(int a, int b)
  • int max(int a, int b)
  • bool within(int x, int min, int max)

哈希

  • Ripemd160 ripemd160(bytes b)

  • Sha1 sha1(bytes b)

  • Sha256 sha256(bytes b)

  • Ripemd160 hash160(bytes b)

    ripemd160(sha256(b))

  • Sha256 hash256(bytes b)

    sha256(sha256(b))

  • Sha256 flattenSha256(T a)

    为任何类型的给定参数 a 返回 Sha256。如果 T 是基本类型,如 bool / int / bytes ,则返回与 sha256(a) 相同。如果 T 是复合类型(即数组和结构体),它将 a 的每个展平字段的所有 sha256 值连接起来形成一个联合字节,然后对其调用 sha256 以获得最终的结果。

签名验证

  • bool checkSig(Sig sig, PubKey pk)

    如果签名与公钥匹配,则返回 true。如果签名是空字节数组,则返回 false。否则,由于 NULLFAIL 规则,整个合约立即失败。

  • bool checkMultiSig(Sig[M] sigs, PubKey[N] pks)

    如果有且仅有 M 个签名与 N 个公钥中的 M 个匹配,则返回 true。 M 和 N 可以是任意数字,只要 M <= N。如果所有签名都是空字节数组,则返回 false。否则,整个合约立即失效。

bytes 操作

  • int 之间的转换

bytes 可以使用函数 unpack 转换为 int 。使用采用小端格式的 符号-值 表示法,其中最高有效位表示符号( 0 表示正, 1 表示负)。 int 可以使用 pack 转换为 bytes

int a1 = unpack(b'36');    // 54 decimal
int a2 = unpack(b'b6');    // -54
int a3 = unpack(b'e803');  // 1000
int a4 = unpack(b'e883');  // -1000
bytes b = pack(a4);        // b'e883'
  • bytes num2bin(int num, int size)

    把数字 num 转换为字节数为 size 的字节数组,包括符号比特。如果字节数组无法容纳被转换的数字,则会转换失败。

  • len() 返回长度。

    int a = len(b'ffee11'); // a == 3
    
  • 切片操作符 - b[start:end] 返回 b 的子数组,从索引 start (包含)到 end (不包含)。 start 如果省略则为 0end 如果省略则为数组的长度。

    bytes b = b'0011223344556677';
    // b[3:6] == b'334455'
    // b[:4] == b'00112233'
    // b[5:] = b'556677'
    
  • 拼接

    bytes b = b'00112233' + b'334455'  // b == b'00112233334455'
    
  • reverseBytes(bytes b, static const int size)

    返回 b 的反向字节,sizeb 字节的大小。注意 size 必须是 编译时常量 。在小端和 大端之间转换数字时,它通常很有用。

    // returns b'6cfeea2d7a1d51249f0624ee98151bfa259d095642e253d8e2dce1e79df33f79'
    reverseBytes(b'793ff39de7e1dce2d853e24256099d25fa1b1598ee24069f24511d7a2deafe6c', 32)