@ccoincash 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 ccoincash's activities
Total Economy: 0.33 USD

一种实现链上orderbook的方法

最近在断断续续的学习scrypt,想到一个通过构造bsv的交易脚本来实现链上订单表的方法。记录下来,探讨一下可行性。

假设实现一个tokenA-bsv的交易对,分别讨论构造买单和卖单的方法。

卖单

假设我们要以n satoshi的价格卖出m个tokenA,那么我们可以构造一个交易tx_s1,它的一个输出是发送m个tokenA,解锁条件是必须往某个地址A发送m * n的satoshi。这个解锁条件应该可以通过scrypt的op_push_tx技术来实现,即在解锁脚本里面检查hashOutput是否等于hash(amount + lockscript)。

如果有人想要购买这个卖单,他需要构造一个新的交易tx_s2,这个交易要以这个tx_s1及output index作为其一个input,然后在output里面必须有一个是发送m * n个satoshi到地址A。然后将tx_s1和tx_s2这两个交易一起发送到链上,这样就完成了整个卖单的交易过程。

买单

假设我们想以n satoshi的价格买入m个tokenA,那么我们构造一个交易tx_b1,交易的一个输出是m * n数量的satoshi,解锁条件则是必须往某地址B发送m个tokenA。实现方法应该类似与上面卖单的实现方法。

然后如果有人需要卖出tokenA,他需要构造一个新的交易tx_b2,交易以tx_b1及output index作为一个input,然后在output里面必须有一个发送m个tokenA到地址B。然后将tx_b1和tx_b2一起发送到链上。

撤单

上面讨论的买单和卖单在发布出去的时候有两种方式。

一是可以直接发送到链上。另外一种方式是发送给专门提供交易数据集合的中心服务器,这样做的好处是在撤单的时候节省一次交易费用。

如果有人已经发送了买单或者卖单,然后要取消这个订单。第一种情况,这个订单是发送到链上去了,那么他就需要自己去赎回,即自己完成买单或者卖单的交易过程。这个过程会耗费两次交易上链的手续费。

第二种情况,如果挂单是发送给中心服务器,则直接通知中心服务器删除这个订单,同时安全起见,将挂单交易中的一个input花费掉,这样即使有人拿到了这个挂单交易,也无法发送到链上了,因为它的一个input已经不是合法的utxo了。

其他

链上交易订单表的好处就是能够保证用户的资产安全,用户的资产完全是保存在自己的钱包,发送的挂单交易,其他人也没有办法任意窃取,必须满足解锁条件才可以获得交易里面锁定的资产。

其缺点就是需要花费一些交易上链手续费。考虑到bsv的低廉手续费,以及带来的好处,这个成本应该是可以接受的。

这里面还有很多的细节没有考虑到,留到后面再慢慢补充。

powered by powpress
link Tip
Share
tx
translate
marlab tipped:
light tipped:
tosway tipped:
xhliu tipped:
edward tipped:
我原来在scrypt上做过一个类似想法的原型,供参考。 这个最好配合Layer 1的token方案,不然就要想另外的办法让合约可以验证token的真伪。 生成的测试网交易: https://test.whatsonchain.com/tx/30cc3d743d9e76a314dd7f70aaefabd6bdd976754cd5f9bb8f6c546cb7633f7b contract文件: contract Sell { bytes bsvReceiveScript; int sellAmount; int getAmount; constructor(bytes bsvReceiveScript,int sellAmount, int getAmount) { this.bsvReceiveScript = bsvReceiveScript; this.sellAmount = sellAmount; this.getAmount = getAmount; } public function match(bytes sighashPreimage, bytes receiverScriptPubkey, int coinAmount, int bsvAmount, bytes additionalOutputs) { Tx tx = new Tx(); require(tx.validate(sighashPreimage)); require(coinAmount>0); require(bsvAmount>0); int len = length(sighashPreimage); bytes hashOutputs = sighashPreimage[len - 40 : len - 8]; // scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter bytes scriptCode = this.readVarint(sighashPreimage[104:]); int scriptLen = length(scriptCode); // last byte contains the state, i.e., counter int amount = unpack(scriptCode[scriptLen - 4 :]); require(coinAmount<=amount); // increment counter bytes scriptCode_ = scriptCode[: scriptLen - 4] ++ num2bin(amount - coinAmount, 4); //todo verify price where require(this.getAmount * coinAmount <= this.sellAmount * bsvAmount); // output: amount + scriptlen + script Sha256 hashOutputs_ = hash256( num2bin(1000, 8) ++ this.writeVarint( b"6a"++b"0453454e44"++this.writeVarint(num2bin(amount - coinAmount, 4)) ++this.writeVarint(num2bin(coinAmount, 4)) ) ++num2bin(1000, 8) ++ this.writeVarint(scriptCode_) ++num2bin(1000, 8) ++ this.writeVarint(receiverScriptPubkey) ++num2bin(bsvAmount, 8) ++ this.writeVarint(this.bsvReceiveScript) ++additionalOutputs ); // ensure output is expected: amount is same with specified // also output script is the same with scriptCode except counter incremented require(hashOutputs == hashOutputs_); } 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; } 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 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]; } 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"); } }
ccoincash replied:
嗯,我想的是必须要layer 1的方案,不然没办法保证安全。但是,Layer 1的token目前我还没看到能够完全在链上就能验证真伪的实现方法。
zhangweis replied:
你可以参考https://powping.com/posts/fe6cc987ecfc2f799ed465b41bc959e4c58f2fe8b2af477ce27da49103f26bf9 scrypt的layer1方案可以满足安全性,但没有扩展性。我提了一个方案解决数据扩展性。
my question is how to mitigate the address problem. if you want to ship peer to peer with address anonymity, how does international postal system handle delivery with mailer not knowing purchasers address?
ccoincash replied:
the purchasers need to generate tx when complete the transaction, so the output locking script is set by the purchaser which means they can set any output address they like.