contract Util { // number of bytes to denote some numeric value static int DataLen = 1; // number of bytes to denote output value static int OutputValueLen = 8; // number of bytes to denote a public key (compressed) static int PubKeyLen = 33; // number of bytes to denote a public key hash static int PubKeyHashLen = 20; static function readVarint(bytes b) returns (bytes) { int len = 0; bytes ret = b''; bytes header = b[0:1]; if (header == b'fd') { len = this.fromLEUnsigned(b[1:3]); ret = b[3:3+len]; } else if (header == b'fe') { len = this.fromLEUnsigned(b[1:5]); ret = b[5:5+len]; } else if (header == b'ff') { len = this.fromLEUnsigned(b[1:9]); ret = b[9:9+len]; } else { len = this.fromLEUnsigned(b[0:1]); ret = b[1:1+len]; } return ret; } static function writeVarint(bytes b) returns (bytes) { int n = length(b); bytes header = b''; if (n < 0xfd) { header = this.toLEUnsigned(n, 1); } else if (n < 0x10000) { header = b'fd' + this.toLEUnsigned(n, 2); } else if (n < 0x100000000) { header = b'fe' + this.toLEUnsigned(n, 4); } else if (n < 0x10000000000000000) { header = b'ff' + this.toLEUnsigned(n, 8); } return header + b; } // convert signed integer `n` to unsigned integer of `len` bytes, in little endian static function toLEUnsigned(int n, int len) returns (bytes) { // one extra byte to accommodate possible negative sign byte bytes m = num2bin(n, len + 1); // remove sign byte return m[0 : length(m) - 1]; } static function fromLEUnsigned(bytes b) returns (int) { // append positive sign byte. This does not hurt even when sign bit is already positive return unpack(b + b'00'); } static function buildPublicKeyHashScript(Ripemd160 pubKeyHash) returns (bytes) { return OpCode.OP_DUP + OpCode.OP_HASH160 + pack(Util.PubKeyHashLen) /* "OP_PUSHDATA0" */ + pubKeyHash + OpCode.OP_EQUALVERIFY + OpCode.OP_CHECKSIG; } /* * parse sighash preimage * Note: only to be used after preimage is validated * spec is at https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md */ static function nVersion(bytes preimage) returns (bytes) { return preimage[:4]; } static function hashPrevouts(bytes preimage) returns (bytes) { return preimage[4:36]; } static function hashSequence(bytes preimage) returns (bytes) { return preimage[36:68]; } static function outpoint(bytes preimage) returns (bytes) { return preimage[68:104]; } // scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter static function scriptCode(bytes preimage) returns (bytes) { return Util.readVarint(preimage[104:]); } static function valueRaw(bytes preimage) returns (bytes) { int len = length(preimage); return preimage[len - 52 : len - 44]; } static function value(bytes preimage) returns (int) { return Util.fromLEUnsigned(Util.valueRaw(preimage)); } static function nSequenceRaw(bytes preimage) returns (bytes) { int len = length(preimage); return preimage[len - 44 : len - 40]; } static function nSequence(bytes preimage) returns (int) { return Util.fromLEUnsigned(Util.nSequenceRaw(preimage)); } static function hashOutputs(bytes preimage) returns (bytes) { int len = length(preimage); return preimage[len - 40 : len - 8]; } static function nLocktimeRaw(bytes preimage) returns (bytes) { int len = length(preimage); return preimage[len - 8 : len - 4]; } static function nLocktime(bytes preimage) returns (int) { return Util.fromLEUnsigned(Util.nLocktimeRaw(preimage)); } static function sigHashType(bytes preimage) returns (SigHashType) { int len = length(preimage); return SigHashType(preimage[len - 4 :]); } public function testPreimageParsing(bytes preimage) { require(Tx.checkPreimage(preimage)); bytes preimage_ = Util.nVersion(preimage) + Util.hashPrevouts(preimage) + Util.hashSequence(preimage) + Util.outpoint(preimage) + Util.writeVarint(Util.scriptCode(preimage)) + Util.valueRaw(preimage) + Util.nSequenceRaw(preimage) + Util.hashOutputs(preimage) + Util.nLocktimeRaw(preimage) + Util.sigHashType(preimage); require(preimage == preimage_); } }