English
  • 简体中文
Mainnet
BSV
  • BSV
  • mBSV
  • Bits
  • Sat

Transaction

9bdb96c4036a9d08a96e6c869825fe6f640fd828d988b8ee9dff878446114211
( - )
182,175
2020-10-25 22:13:14
1
23,628 B

3 Outputs

Total Output:
  • j"19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAutMíZ// The following "Util" contract is taken and modified from the project: // https://github.com/scrypt-sv/boilerplate/blob/master/contracts/util.scrypt // // It is included here for convenience. The MIT license is the active license // at the time of publication and covers the "Util" class below only. // A seperate GPLv3 license is provided for the SimpleAsset10 (SA10) contract // below. // MIT License // // Copyright (c) 2020 sCrypt Inc // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. contract Util { // number of bytes to denote some numeric value static int DataLen = 1; // number of bytes to denote length serialized state, including varint prefix (1 byte) + length (2 bytes), change length to 4 when you need PushData4 static int StateLen = 3; // number of bytes to denote input sequence static int InputSeqLen = 4; // 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; // convert signed integer `n` to unsigned integer of `l` bytes, in little endian static function toLEUnsigned(int n, int l): bytes { // one extra byte to accommodate possible negative sign byte bytes m = num2bin(n, l + 1); // remove sign byte return m[0 : len(m) - 1]; } // convert bytes to unsigned integer, in sign-magnitude little endian static function fromLEUnsigned(bytes b): int { // append positive sign byte. This does not hurt even when sign bit is already positive return unpack(b + b'00'); } // build P2PKH script from public key hash static function buildPublicKeyHashScript(Ripemd160 pubKeyHash): bytes { return OpCode.OP_DUP + OpCode.OP_HASH160 + pack(Util.PubKeyHashLen) /* "OP_PUSHDATA0" */ + pubKeyHash + OpCode.OP_EQUALVERIFY + OpCode.OP_CHECKSIG; } // build a tx output from its script and satoshi amount static function buildOutput(bytes outputScript, int outputSatoshis): bytes { return num2bin(outputSatoshis, Util.OutputValueLen) + Util.writeVarint(outputScript); } // wrapper for OP_PUSH_TX with customized sighash type static function checkPreimageSigHashType(SigHashPreimage txPreimage, SigHashType sigHashType): bool { // The following arguments can be generated using sample code at // https://gist.github.com/scrypt-sv/f6882be580780a88984cee75dd1564c4.js PrivKey privKey = PrivKey(0x621de38d9af72be8585d19584e3954d3fd0dc9752bb9f9fb28c4f9ed7c1e40ea); PubKey pubKey = PubKey(b'02773aca113a3217b67a95d5b78b69bb6386ed443ea5decf0ba92c00d179291921'); // invK is the modular inverse of k, the ephemeral key int invK = 0xa2103f96554aba49bbf581738d3b5a38c5a44b6238ffb54cfcca65b8c87ddc08; // r is x coordinate of R, which is kG int r = 0x00f0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60; // rBigEndian is the signed magnitude representation of r, in big endian bytes rBigEndian = b'00f0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60'; return Tx.checkPreimageAdvanced(txPreimage, privKey, pubKey, invK, r, rBigEndian, sigHashType); } // serialize state size in fixed length static function encodeStateSize(bytes state): bytes { return num2bin(len(state), Util.StateLen - 1 /* varint prefix byte */); } /* * VarInt (variable integer) is used to encode fields of variable length in a bitcoin transaction * https://learnmeabitcoin.com/technical/varint */ // read a VarInt field from the beginning of 'b' static function readVarint(bytes b): bytes { int l = 0; bytes ret = b''; byte header = b[0]; if (header == 'fd') { l = this.fromLEUnsigned(b[1:3]); ret = b[3:3+l]; } else if (header == 'fe') { l = this.fromLEUnsigned(b[1:5]); ret = b[5:5+l]; } else if (header == 'ff') { l = this.fromLEUnsigned(b[1:9]); ret = b[9:9+l]; } else { l = this.fromLEUnsigned(b[0:1]); ret = b[1:1+l]; } return ret; } // number of bytes of the VarInt field read from the beginning of 'b' static function readVarintLen(bytes b): int { int len = 0; byte header = b[0]; if (header == 'fd') { len = 3 + this.fromLEUnsigned(b[1:3]); } else if (header == 'fe') { len = 5 + this.fromLEUnsigned(b[1:5]); } else if (header == 'ff') { len = 9 + this.fromLEUnsigned(b[1:9]); } else { len = 1 + this.fromLEUnsigned(b[0:1]); } return len; } // convert 'b' to a VarInt field, including the preceding length static function writeVarint(bytes b): bytes { int n = len(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; } /* * util functions to parse every filed of a 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(SigHashPreimage preimage): bytes { return preimage[:4]; } static function hashPrevouts(SigHashPreimage preimage): bytes { return preimage[4:36]; } static function hashSequence(SigHashPreimage preimage): bytes { return preimage[36:68]; } static function outpoint(SigHashPreimage preimage): bytes { return preimage[68:104]; } // scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter static function scriptCode(SigHashPreimage preimage): bytes { return Util.readVarint(preimage[104:]); } static function valueRaw(SigHashPreimage preimage): bytes { int l = len(preimage); return preimage[l - 52 : l - 44]; } static function value(SigHashPreimage preimage): int { return Util.fromLEUnsigned(Util.valueRaw(preimage)); } static function nSequenceRaw(SigHashPreimage preimage): bytes { int l = len(preimage); return preimage[l - 44 : l - 40]; } static function nSequence(SigHashPreimage preimage): int { return Util.fromLEUnsigned(Util.nSequenceRaw(preimage)); } static function hashOutputs(SigHashPreimage preimage): bytes { int l = len(preimage); return preimage[l - 40 : l - 8]; } static function nLocktimeRaw(SigHashPreimage preimage): bytes { int l = len(preimage); return preimage[l - 8 : l - 4]; } static function nLocktime(SigHashPreimage preimage): int { return Util.fromLEUnsigned(Util.nLocktimeRaw(preimage)); } static function sigHashType(SigHashPreimage preimage): SigHashType { int l = len(preimage); return SigHashType(preimage[l - 4 :]); } // used only for testing public function testPreimageParsing(SigHashPreimage preimage) { require(Tx.checkPreimage(preimage)); SigHashPreimage preimage_ = SigHashPreimage(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_); } // The following code is contributed by MatterPool Inc. // Writes variable amount of data respecting minimal push rules static function writeVarMinimalPushdata(bytes b): bytes { int n = len(b); bytes header = b''; // Reference: https://github.com/moneybutton/bsv/blob/bsv-legacy/lib/script/script.js#L1083 if (n == 0) { } else if (n == 1) { int rawInt = this.fromLEUnsigned(b); if (rawInt >= 1 && rawInt <= 16) { // If value is between 1 and 16 then use OP_1, OP_2...OP_16 to encode header = this.toLEUnsigned(80 + rawInt, 1); } else if (n == 1 && rawInt == 0x81) { // Use OP_1NEGATE header = this.toLEUnsigned(79, 1); } } else if (n < 76) { // Use direct push header = this.toLEUnsigned(n, 1) + b; } else if (n <= 255) { header = b'4c' + this.toLEUnsigned(n, 1) + b; } else if (n <= 65535) { header = b'4d' + this.toLEUnsigned(n, 2) + b; } else { header = b'4e' + this.toLEUnsigned(n, 4) + b; } return header; } } // The following code is Copyright MatterPool Inc. // Copyright (c) 2020 MatterPool Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. // // ---------------------------------------------------------------------------------------------- // SuperAsset10 (SA10 for short) (Alpha) // // * Non-Fungible-Token (NFT) Smart Contract for Bitcoin. // ---------------------------------------------------------------------------------------------- // // Features of SA10: // - Mint, transfer, update, and melt tokens that retain their identity and satoshi balance until melted. // - Supports seperate funding inputs and change outputs with ANYONECANPAY // - Store arbitrary data payloads (HTML, JSON, Protobuf, PDFs, images, XML, etc) // - Infuse satoshis to be locked into the asset for life cycle // - Replay protection with globally unique ID passed on as an 'identity baton' // - Can destroy the token via "melt" and retrieve the satoshis // - Identical trust guarantees for the authenticity of this asset as a plain UTXO // - Users can trivially verify authenticity and title history by requesting it in it's entirety from the seller // - Wallets and indexers can proactively index these UTXO's via a blind pattern match for the minting pattern (see below) // - Simplfied Payment Verification (SPV) and 0-conf works as expected with all the properties afforded to the native satoshi // // Contract Code and State Layout: // ========================================================================== // | // SA10 STATIC CODE | OP_RETURN tokenID(36B) ownerPublicKey(33B) [ payload(varlen) ] // | // ========================================================================== // // HOW TO INITIALLY DEPLOY: // // 1. Start with the base static code part of the output: // Script Hex Static Part: // `51014001800176018801a901ac58011459790087635679597985587985615e79517920ea401e7cedf9c428fbf9b92b75c90dfdd354394e58195d58e82bf79a8de31d622102773aca113a3217b67a95d5b78b69bb6386ed443ea5decf0ba92c00d1792919212108dc7dc8b865cafc4cb5ff38624ba4c5385a3b8d7381f5bb49ba4a55963f10a20021606bfc5df21a9603c63d49e178b0620c9953d37c7ddeddfc12580925da43fcf0002100f0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60615679557955795579557955795b795679aa616100790079517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01007e81776157795679567956795679537956795479577995939521414136d08c5ed2bf3ba048afe6dcaebafeffffffffffffffffffffffffffffff0061517951795179517997527a75517a5179009f635179517993527a75517a685179777761527a75517a517951795296a0630079527994527a75517a68537982775279827754527993517993013051797e527e53797e57797e527e52797e5579517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7e56797e00797777777777777777777777776100795779ac77777777777777777761777777777777776169615e7961007901687f7700005279007f77517f75007901fd8763615379537f75517f77007901007e817761537a75527a527a5379535479937f75537f77527a75517a67007901fe8763615379557f75517f77007901007e817761537a75527a527a5379555479937f75557f77527a75517a67007901ff8763615379597f75517f77007901007e817761537a75527a527a5379595479937f75597f77527a75517a67615379517f75007f77007901007e817761537a75527a527a5379515479937f75517f77527a75517a686868517977777777617761007902a70c7f77007901247f75011379527901467f7501257f77ac69007900012480876361011179007901687f7501447f777761517a756861527902a50c7f75026a247e51797e01217e0113797e615f7900798277005179009c63675179519c63615279007901007e817761007951a2517960a19a63610150517993515179517951938000795179827751947f75007f7777777761527a75517a675279519c51790281009c9a6361014f515179517951938000795179827751947f75007f7777777761527a75517a686875675179014c9f63615179515179517951938000795179827751947f75007f777777776153797e517a7567517902ff00a163014c615279515179517951938000795179827751947f75007f77777777617e53797e517a7567517903ffff00a163014d615279525179517951938000795179827751947f75007f77777777617e53797e517a7567014e615279545179517951938000795179827751947f75007f77777777617e53797e517a7568686868680079777777617e61011279616100790079827751795179012c947f7551790134947f77777761007901007e817761776100795879806152790079827700517902fd009f63615179515179517951938000795179827751947f75007f7777777761517a75675179030000019f6301fd615279525179517951938000795179827751947f75007f77777777617e517a756751790500000000019f6301fe615279545179517951938000795179827751947f75007f77777777617e517a75675179090000000000000000019f6301ff615279585179517951938000795179827751947f75007f77777777617e517a7568686868007953797e777777617e77776161610111795b795a797e57797e51797e5b797e59797e776101117900795979806152790079827700517902fd009f63615179515179517951938000795179827751947f75007f7777777761517a75675179030000019f6301fd615279525179517951938000795179827751947f75007f77777777617e517a756751790500000000019f6301fe615279545179517951938000795179827751947f75007f77777777617e517a75675179090000000000000000019f6301ff615279585179517951938000795179827751947f75007f77777777617e517a7568686868007953797e777777617e7777617eaa61011279007982775179517958947f7551790128947f777777618777777777777777777777777777777777777777776759795187635679597985587985615d79517920ea401e7cedf9c428fbf9b92b75c90dfdd354394e58195d58e82bf79a8de31d622102773aca113a3217b67a95d5b78b69bb6386ed443ea5decf0ba92c00d1792919212108dc7dc8b865cafc4cb5ff38624ba4c5385a3b8d7381f5bb49ba4a55963f10a20021606bfc5df21a9603c63d49e178b0620c9953d37c7ddeddfc12580925da43fcf0002100f0fc43da25095812fcddde7d7cd353990c62b078e1493dc603961af25dfc6b60615679557955795579557955795b795679aa616100790079517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01007e81776157795679567956795679537956795479577995939521414136d08c5ed2bf3ba048afe6dcaebafeffffffffffffffffffffffffffffff0061517951795179517997527a75517a5179009f635179517993527a75517a685179777761527a75517a517951795296a0630079527994527a75517a68537982775279827754527993517993013051797e527e53797e57797e527e52797e5579517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7e56797e00797777777777777777777777776100795779ac77777777777777777761777777777777776169615d7961007901687f7700005279007f77517f75007901fd8763615379537f75517f77007901007e817761537a75527a527a5379535479937f75537f77527a75517a67007901fe8763615379557f75517f77007901007e817761537a75527a527a5379555479937f75557f77527a75517a67007901ff8763615379597f75517f77007901007e817761537a75527a527a5379595479937f75597f77527a75517a67615379517f75007f77007901007e817761537a75527a527a5379515479937f75517f77527a75517a6868685179777777776177616079517902a70c7f7701467f7501257f77ac6961615f79587957797e54797e51797e58797e56797e7761615f79616100790079827751795179012c947f7551790134947f77777761007901007e817761776100795679806152790079827700517902fd009f63615179515179517951938000795179827751947f75007f7777777761517a75675179030000019f6301fd615279525179517951938000795179827751947f75007f77777777617e517a756751790500000000019f6301fe615279545179517951938000795179827751947f75007f77777777617e517a75675179090000000000000000019f6301ff615279585179517951938000795179827751947f75007f77777777617e517a7568686868007953797e777777617e77776161615e79597958797e55797e51797e59797e57797e77615e7900795779806152790079827700517902fd009f63615179515179517951938000795179827751947f75007f7777777761517a75675179030000019f6301fd615279525179517951938000795179827751947f75007f77777777617e517a756751790500000000019f6301fe615279545179517951938000795179827751947f75007f77777777617e517a75675179090000000000000000019f6301ff615279585179517951938000795179827751947f75007f77777777617e517a7568686868007953797e777777617e7777617eaa615f79007982775179517958947f7551790128947f7777776187777777777777777777777777777777777767006868` // // 2. Add an OP_RETURN and 0x24 (36) to the front of the null (all zeroes) minting assetId like this: // `6a24000000000000000000000000000000000000000000000000000000000000000000000000` // // 3. Add 0x21 (33) to the front of the public key of the first initial owner: // `2102f739ee427d204830da1578b028df8b113eb0cadf51e43bc21972423bd545e7c5` // // 4. Concatenate the hex strings in Steps 1 + 2 + 3 and broadcast the transaction with the desired number of satoshis to lock up. // // Alternatively, it is more convenient to use superasset-js or scrypt boilerplate https://github.com/scrypt-sv/boilerplate // // // Behold, the Smart Contract... // contract SimpleAsset10 { // // Transfer and Update the Simple Asset // Minting is implicit after the first transfer when starting from after deploying this contract with // initial minting assetID=0x000000000000000000000000000000000000000000000000000000000000000000000000 // // @param senderSig - the signature of owner of parent input that is authorizing this transaction // @param receiver - Receiver of the NFT // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param changeAddress - Address to send change // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) // @param payload - Add and replace payload data public function transfer(Sig senderSig, PubKey receiver, SigHashPreimage txPreimage, Ripemd160 changeAddress, int changeSatoshis, bytes payload) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Bug exists that prevents it from operating currently. // Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); // Get the locking script of THIS input. bytes lockingScript = Util.scriptCode(txPreimage); // We know the static part of this contract is exactly 3237 in length (we pre-compiled and then replaced this constant). // Make sure to add 2 bytes to skip the push data for the assetID (36 bytes of the outpoint in little-endian) bytes dataState = lockingScript[ 3239: ]; bytes assetId = dataState[ : 36 ]; // Note that the assetId is in little endian to make it easier to match by outpoint // Validate and authorize the transaction (public key is 33 bytes) require(checkSig(senderSig, PubKey(dataState[ 37 : 70]))); // Was this a minting (ie: first) transfer? if (assetId == num2bin(0, 36)) { // Then the assetId must be equal to the outpoint that this input is spending. assetId = Util.outpoint(txPreimage); } // Use the constant code size of 3237 to pass along the assetId, receiver, and payload data (following all minimal push encoding rules) // Make change from the other inputs that must exist to fund this transaction // Hardcode the length of the assetId (0x24 = 36 bytes) and the public key (0x21 = 33 bytes). require( hash256( Util.buildOutput(lockingScript[ : 3237] + b'6a24' + assetId + b'21' + receiver + Util.writeVarMinimalPushdata(payload), Util.value(txPreimage)) + Util.buildOutput(Util.buildPublicKeyHashScript(changeAddress), changeSatoshis)) == Util.hashOutputs(txPreimage) ); } // // Melt the SimpleAsset back to regular satoshis. // // @param ownerSig - the signature of owner of parent input that is authorizing this transaction // @param receiverAddress - Receiver of NFT's value address // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param changeAddress - Address to send change // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function melt(Sig ownerSig, Ripemd160 receiverAddress, SigHashPreimage txPreimage, Ripemd160 changeAddress, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); // Upon the first transfer the locking bytes lockingScript = Util.scriptCode(txPreimage); // Validate and authorize the transaction require(checkSig(ownerSig, PubKey( (lockingScript[ 3239: ])[ 37 : 70]))); require(hash256( Util.buildOutput(Util.buildPublicKeyHashScript(receiverAddress), Util.value(txPreimage)) + Util.buildOutput(Util.buildPublicKeyHashScript(changeAddress), changeSatoshis)) == Util.hashOutputs(txPreimage)); } } text/markdown; charset=utf-8UTF-8'0fcb21c0-1708-11eb-b834-f39b6abd05c1.md
    https://whatsonchain.com/tx/9bdb96c4036a9d08a96e6c869825fe6f640fd828d988b8ee9dff878446114211