@microserver is on PowPing!

PowPing is a place where you can earn Bitcoin simply by socializing, for FREE.
Never tried Bitcoin? It's OK! Just come, socialize, and earn Bitcoin.
Check out microserver's activities
Total Economy: 0.1 USD

闲着无聊改了一下spvtoken

import "util.scrypt";

/*

Copyright

1. OP_PUSH_TX是nChain的研究员Ying Chan主导发明的 - https://www.linkedin.com/in/ying-chan-10077651/

2. spvtoken 最开始是 sCrypt XiaoHui 写的 - https://github.com/sCrypt-Inc/boilerplate/blob/master/contracts/spvToken.scrypt

3. notesv 李龙先生做了指导 https://note.sv

4. 参考了感应合约 https://sensiblecontract.org/ 里面将锁定脚本做hash处理的思路。

5. microserver 修改于 2021-4-15

*/

/*

token 转账流程

1. 钱包将prevTx发送到prevTx预言机

2. 钱包在预言机的utxo里面查找刚才对应的tx的utxo,从里面获取 prevPrevTxId, prevTxHash, rabinSigPrev

3. 钱包将prePrevTx 发送到prevPrevTx预言机

4. 钱包在预言机的utxo里面查找刚才对应的tx的utxo,从里面获取 preLockingScriptHash,prevPrevTxHash,prevTokenId, rabinSigPrevprev

5. 钱包将自己拥有的token发送到目标地址

思路,xiaohui的spvtoken通俗易懂,但是有交易膨胀问题,那么就将引起膨胀的数据和代码移到预言机去处理。同时将预言机合约化。

*/

contract SPVToken {

// prevTx: tx being spent by the current tx

// prevPrevTx: tx being spent by prevTx

// 将 prevTx 发给预言机,预言机解析出 prevPrevTxId,prevTxHash,并对解析结果进行签名

// 将 prePrevTx 发给预言机,预言机解析出 preLockingScriptHash,prevPrevTxHash,prevTokenId 并对结果进行签名

// 如果脚本大小受限可以做成两个预言机,如果不受限可以做成一个预言机

bytes rabinPubkeyPrev;

bytes rabinPubkeyPrevprev;

public function transfer(Sig senderSig, PubKey receiver, int satoshiAmount, SigHashPreimage txPreimage, bytes prevPrevTxId, bytes prevTxHash, Sig rabinSigPrev, bytes preLockingScriptHash, bytes prevPrevTxHash, bytes prevTokenId, Sig rabinSigPrevprev) {

// constants

int TxIdLen = 32;

int TokenIdLen = TxIdLen;

int PrevTxIdIdx = 5;

// int UnlockingScriptIdx = 41;

// uninitialized token ID

bytes NullTokenId = num2bin(0, TokenIdLen);

require(Tx.checkPreimage(txPreimage));

require(Util.rabinVerify(prevPrevTxId+prevTxHash, rabinSigPrev, this.rabinPubkeyPrev));

require(Util.rabinVerify(preLockingScriptHash+prevPrevTxHash+prevTokenId, rabinSigPrevprev, this.rabinPubkeyPrevprev));

// read previous locking script: codePart + OP_RETURN + tokenID + ownerPublicKey

bytes lockingScript = Util.scriptCode(txPreimage);

int scriptLen = len(lockingScript);

// constant part of locking script: upto OP_RETURN

int constStart = scriptLen - TokenIdLen - Util.PubKeyLen;

bytes constPart = lockingScript[: constStart];

PubKey sender = PubKey(lockingScript[constStart + TokenIdLen : ]);

// authorize

require(checkSig(senderSig, sender));

bytes outpoint = Util.outpoint(txPreimage);

bytes prevTxId = outpoint[ : TokenIdLen];

require(prevTxHash == prevTxId);

bytes tokenId = lockingScript[constStart : constStart + TokenIdLen];

if (tokenId == NullTokenId) {

// get prevTxId and use it to initialize token ID

tokenId = prevTxId;

} else {

/*

* validate not only the parent tx (prevTx), but also its parent tx (prevPrevTx)

*/

// TODO: assume 1 input, to extend to multiple inputs

//bytes prevPrevTxId = prevTx[PrevTxIdIdx : PrevTxIdIdx + TxIdLen];

require(prevPrevTxHash == prevPrevTxId);

// int unlockingScriptLen = Util.readVarintLen(prevPrevTxWithoutUnlockScript[UnlockingScriptIdx : ]);

// TODO: only validate output 0 here, to extend to multiple outputs

// int lockingScriptIdx = UnlockingScriptIdx + unlockingScriptLen + Util.InputSeqLen + 1 /* output count length */ + Util.OutputValueLen;

// bytes prevLockingScript = Util.readVarint(prevPrevTx[lockingScriptIdx : ]);

// ensure prev tx uses the same contract code

// require(len(prevLockingScript) == len(lockingScript));

// require(prevLockingScript[: constStart] == constPart);

require(preLockingScriptHash == hash256(constPart));

// belongs to the same token

// bytes prevTokenId = prevLockingScript[constStart : constStart + TokenIdLen];

require(prevTokenId == tokenId || prevTokenId == NullTokenId);

}

// validate parent tx

bytes outputScript = constPart + tokenId + receiver;

bytes output = Util.buildOutput(outputScript, satoshiAmount);

require(hash256(output) == Util.hashOutputs(txPreimage));

}

}

contract OraclePrevTx {

// prevTx: tx being spent by the current tx

// 将 prevTx 发给预言机,预言机解析出 prevPrevTxId,prevTxHash,并对解析结果进行签名

// 如果脚本大小受限可以做成两个预言机,如果不受限可以做成一个预言机

// 钱包构造一个发往预言机地址的tx,tx的锁定脚本数据是 OP_RETURN + prevTX + prevTxLen + oracleOwner

// 预言机收到这样的tx,就解析相关信息,并生成rabin签名,将rabin签名作为解锁参数,解锁tx,拿到奖励,输出签名结果

bytes rabinPubkeyPrev;

public function sign(Sig ownerSign, SigHashPreimage txPreimage, sig rabinSigPrev, int satoshiAmount) {

// constants

int TxIdLen = 32;

int PrevTxIdIdx = 5;

// int UnlockingScriptIdx = 41;

// uninitialized token ID

require(Tx.checkPreimage(txPreimage));

// read previous locking script: codePart + OP_RETURN + prevTX + prevTxLen + oracleOwner

bytes lockingScript = Util.scriptCode(txPreimage);

int scriptLen = len(lockingScript);

// constant part of locking script: upto OP_RETURN

int constStart = scriptLen - Util.PubKeyLen;

int prevTxLen = lockingScript[constStart];

bytes codePart = lockingScript[: constStart - prevTxLen - 1];

int codePartLen = len(codePart);

bytes prevTX = lockingScript[codePartLen + 1 : codePartLen + 1 + prevTxLen];

PubKey ownerPubkey = PubKey(lockingScript[scriptLen - Util.PubKeyLen : ]);

// authorize

require(checkSig(ownerSign, ownerPubkey));

bytes prevPrevTxId = prevTx[PrevTxIdIdx : PrevTxIdIdx + TxIdLen];

bytes prevTxHash = hash256(prevTx);

require(Util.rabinVerify(prevPrevTxId + prevTxHash, rabinSigPrev, this.rabinPubkeyPrev))

// validate parent tx

bytes outputScript = OP_RETURN + prevPrevTxId + prevTxHash + rabinSigPrev;

bytes output = Util.buildOutput(outputScript, 0);

bytes output1 = Util.buildOutput(buildPublicKeyHashScript(hash160(ownerPubkey)), satoshiAmount);

require(hash256(output+output1) == Util.hashOutputs(txPreimage));

}

}

contract OraclePrevprevTx {

// prevPrevTx: tx being spent by prevTx

// 将 prevPrevTx 发给预言机,预言机解析出 preLockingScriptHash,prevPrevTxHash,prevTokenId 并对结果进行签名

// 如果脚本大小受限可以做成两个预言机,如果不受限可以做成一个预言机

// 钱包构造一个发往预言机地址的tx,tx的锁定脚本数据是 OP_RETURN + prevPrevTX + prevPrevTxLen + oracleOwner

// 预言机收到这样的tx,就解析相关信息,并生成rabin签名,将解析信息和签名作为解锁参数,解锁tx,拿到奖励,输出签名结果

bytes rabinPubkeyPrevprev;

public function sign(Sig ownerSign, SigHashPreimage txPreimage, sig rabinSigPrevprev) {

// constants

int TxIdLen = 32;

int TokenIdLen = TxIdLen;

int PrevTxIdIdx = 5;

// int UnlockingScriptIdx = 41;

// uninitialized token ID

require(Tx.checkPreimage(txPreimage));

// read previous locking script: codePart + OP_RETURN + prevprevTX + prevprevTxLen + oracleOwner

bytes lockingScript = Util.scriptCode(txPreimage);

int scriptLen = len(lockingScript);

// constant part of locking script: upto OP_RETURN

int constStart = scriptLen - Util.PubKeyLen - 2;

int prevTxLen = lockingScript[constStart];

bytes codePart = lockingScript[: constStart - prevTxLen - 1];

int codePartLen = len(codePart);

bytes prevTX = lockingScript[codePartLen + 1 : codePartLen + 1 + prevTxLen];

PubKey ownerPubkey = PubKey(lockingScript[scriptLen - Util.PubKeyLen : ]);

// authorize

require(checkSig(ownerSign, ownerPubkey));

bytes output = Util.Output(prevTX, 0);

bytes preLockingScriptHash = hash256(output[:len(output) - TokenIdLen - Util.PubKeyLen]);

bytes prevPrevTxHash = hash256(prevTx);

lockingScript = Util.Script(prevTX);

bytes prevTokenId = output[len(output) - TokenIdLen - Util.PubKeyLen : len(output) - Util.PubKeyLen];

require(Util.rabinVerify(preLockingScriptHash + prevPrevTxHash + prevTokenId, rabinSigPrevprev, this.rabinPubkeyPrevprev))

// validate parent tx

bytes outputScript = OP_RETURN + preLockingScriptHash + prevPrevTxHash + prevTokenId + rabinSigPrevprev;

bytes output = Util.buildOutput(outputScript, 0);

bytes output1 = Util.buildOutput(buildPublicKeyHashScript(hash160(ownerPubkey)), satoshiAmount);

require(hash256(output+output1) == Util.hashOutputs(txPreimage));

}

}

powered by powpress
link Tip
Share
tx
translate
改良版本 import "util.scrypt"; /** * A non-fungible token enforced by miners at layer 1 */ /* Copyright 1. OP_PUSH_TX是nChain的研究员Ying Chan主导发明的 - https://www.linkedin.com/in/ying-chan-10077651/ 2. spvtoken 最开始是 sCrypt XiaoHui 写的 - https://github.com/sCrypt-Inc/boilerplate/blob/master/contracts/spvToken.scrypt 3. notesv 李龙先生做了指导 https://note.sv 4. 参考了感应合约 https://sensiblecontract.org/ 里面将锁定脚本做hash处理的思路。 5. microserver 修改于 2021-4-15 */ /* token 转账流程 1. 钱包将prevTx发送到SigTxId预言机 2. 钱包在预言机的utxo里面查找刚才对应的tx的utxo,从里面获取 prevPrevTxId, prevTxHash, rabinSigTxId 3. 钱包将prePrevTx 发送到SignTxScript预言机 4. 钱包在预言机的utxo里面查找刚才对应的tx的utxo,从里面获取 preLockTokenScriptHash,prevPrevTxHash,rabinSigTxScript 5. 钱包将自己拥有的token发送到目标地址 思路,xiaohui的spvtoken通俗易懂,但是有交易膨胀问题,那么就将引起膨胀的数据和代码移到预言机去处理 */ contract SPVToken { // prevTx: tx being spent by the current tx // prevPrevTx: tx being spent by prevTx bytes rabinPubkeySigTx; bytes rabinPubkeySigTxScript; public function transfer(Sig senderSig, PubKey receiver, int satoshiAmount, SigHashPreimage txPreimage, bytes prevPrevTxId, bytes prevTxHash, Sig rabinSigTxId, bytes preLockTokenScriptHash, bytes prevPrevTxHash, Sig rabinSigTxScript) { // 此锁定脚本的主要目的是证明:1. 当前输入的锁定脚本跟当前输出的锁定脚本一致。2. 上一个交易的锁定脚本跟当前的锁定脚本一致。 // constants int TxIdLen = 32; int TokenIdLen = TxIdLen; int PrevTxIdIdx = 5; // int UnlockingScriptIdx = 41; // uninitialized token ID bytes NullTokenId = num2bin(0, TokenIdLen); require(Tx.checkPreimage(txPreimage)); require(Util.rabinVerify(prevPrevTxId + prevTxHash, rabinSigTxId, this.rabinPubkeySigTx)); require(Util.rabinVerify(preLockTokenScriptHash + prevPrevTxHash, rabinSigTxScript, this.rabinPubkeySigTxScript)); // read previous locking script: codePart + OP_RETURN + tokenID + ownerPublicKey bytes lockingScript = Util.scriptCode(txPreimage); int scriptLen = len(lockingScript); // constant part of locking script: upto OP_RETURN int constStart = scriptLen - TokenIdLen - Util.PubKeyLen; bytes constPart = lockingScript[: constStart]; PubKey sender = PubKey(lockingScript[constStart + TokenIdLen : ]); // authorize require(checkSig(senderSig, sender)); bytes outpoint = Util.outpoint(txPreimage); bytes prevTxId = outpoint[ : TokenIdLen]; require(prevTxHash == prevTxId); bytes tokenId = lockingScript[constStart : constStart + TokenIdLen]; if (tokenId == NullTokenId) { // get prevTxId and use it to initialize token ID tokenId = prevTxId; } else { /* * validate not only the parent tx (prevTx), but also its parent tx (prevPrevTx) */ // TODO: assume 1 input, to extend to multiple inputs //bytes prevPrevTxId = prevTx[PrevTxIdIdx : PrevTxIdIdx + TxIdLen]; require(prevPrevTxHash == prevPrevTxId); require(preLockTokenScriptHash == hash256(constPart + OP_RETURN + tokenId) || preLockTokenScriptHash == hash256(constPart + OP_RETURN + NullTokenId)); } // validate parent tx bytes outputScript = constPart + tokenId + receiver; bytes output = Util.buildOutput(outputScript, satoshiAmount); require(hash256(output) == Util.hashOutputs(txPreimage)); } } contract OracleSigTxId { // 1. 钱包将需要解析的tx发送给预言机,解锁脚本的数据区为OP_RETURN + tx + txLen + oraclePubkey // 2. 预言机收到tx后,解析出输入的TxId(prevTxId)和整个tx的txHash,对解析结果进行rabin签名。 // 3. 预言机将 prevTxId, txHash,rabin签名作为解锁参数,解锁预言机tx。 // 4. 预言机拿到奖励,同时将签名结果输出到op_return。 // 5. orcale 需要运行一个侦听tx的脚本,只要是发往自己的tx,并且锁定脚本的codePart部分等于签名脚本,而且最后是oracle的pubkey,就进行tx签名 bytes rabinPubkeySigTxId; public function sign(Sig ownerSign, SigHashPreimage txPreimage, sig rabinSigTxId, int satoshiAmount) { // constants int TxIdLen = 32; int PrevTxIdIdx = 5; require(Tx.checkPreimage(txPreimage)); // read previous locking script: codePart + OP_RETURN + tx + txLen + oraclePubkey bytes lockingScript = Util.scriptCode(txPreimage); int scriptLen = len(lockingScript); int txLen = Util.readVarintLen(lockingScript[scriptLen - Util.PubKeyLen - 2:]); bytes tx = lockingScript[scriptLen - Util.PubKeyLen - 2 - txLen : scriptLen - Util.PubKeyLen - 2]; PubKey ownerPubkey = PubKey(lockingScript[scriptLen - Util.PubKeyLen : ]); // authorize require(checkSig(ownerSign, ownerPubkey)); bytes prevTxId = tx[PrevTxIdIdx : PrevTxIdIdx + TxIdLen]; bytes txHash = hash256(tx); require(Util.rabinVerify(prevTxId + txHash, rabinSigTxId, this.rabinPubkeySigTxId)) // create output bytes output0 = Util.buildOutput(OP_RETURN + prevTxId + txHash + rabinSigTxId, 0); // 预言机的结果 bytes output1 = Util.buildOutput(buildPublicKeyHashScript(hash160(ownerPubkey)), satoshiAmount); // 预言机的奖励 require(hash256(output0 + output1) == Util.hashOutputs(txPreimage)); } } contract OracleSigTxScript { // 1. 钱包将需要解析的tx发送给预言机,解锁脚本的数据区为OP_RETURN + tx + txLen + oraclePubkey // 2. 预言机收到tx后,解析出输出的锁定脚本hash(即 lockTokenScriptHash)和整个tx的 txHash,对解析结果进行rabin签名。 // 3. 预言机将 lockTokenScriptHash, txHash,rabin签名作为解锁参数,解锁预言机tx。 // 4. 预言机拿到奖励,同时将签名结果输出到op_return。 // 5. 预言机需要运行一个侦听tx的脚本,只要是发往自己的tx,并且锁定脚本的codePart部分等于签名脚本,而且最后是oracle的pubkey,就进行tx签名 // 注意:lockTokenScriptHash = hash(codePart + OP_RETURN + tokenId) bytes rabinPubkeySigTxScript; public function sign(Sig ownerSign, SigHashPreimage txPreimage, sig rabinSigTxScript, int satoshiAmount) { // constants int TxIdLen = 32; int TokenIdLen = TxIdLen; require(Tx.checkPreimage(txPreimage)); // read previous locking script: codePart + OP_RETURN + tx + txLen + oraclePubkey bytes lockingScript = Util.scriptCode(txPreimage); int scriptLen = len(lockingScript); int txLen = Util.readVarintLen(lockingScript[scriptLen - Util.PubKeyLen - 2:]); bytes tx = lockingScript[scriptLen - Util.PubKeyLen - 2 - txLen : scriptLen - Util.PubKeyLen - 2]; PubKey ownerPubkey = PubKey(lockingScript[scriptLen - Util.PubKeyLen : ]); // authorize require(checkSig(ownerSign, ownerPubkey)); // token的锁定脚本是 codePart + OP_RETURN + tokenID + ownerPublicKey,我们从中取出 codePart + OP_RETURN + tokenID bytes output = Util.Output(tx, 0); bytes lockTokenScriptHash = hash256(output[:len(output) - Util.PubKeyLen]); bytes txHash = hash256(tx); require(Util.rabinVerify(lockTokenScriptHash + txHash, rabinSigTxScript, this.rabinPubkeyPrevprev)) // create output bytes output0 = Util.buildOutput(OP_RETURN + lockTokenScriptHash + txHash + rabinSigTxScript, 0); bytes output1 = Util.buildOutput(buildPublicKeyHashScript(hash160(ownerPubkey)), satoshiAmount); require(hash256(output0 + output1) == Util.hashOutputs(txPreimage)); } }
xhliu tipped: