Auction
Stateful auction with bid and close paths.
txt
# Each bid moves the auction state forward and stores the new bidder in the next script suffix.
# The old bidder must be refunded while the new state output carries a higher value.
Contract Auction:
Struct Script:
SuffixData: string
PartialHash: string
Size: number
Struct Output:
Value: number
LockingScript: Script
Struct Input:
Data: {txid: hex32, vout: hex4, sequence: hex4}
Struct PreTX:
VLIO: string
Inputs: Input[3]
UnlockingScriptHash: string
Outputs: Output[3]
Struct CurrentTX:
Outputs: Output[3]
def main(sig: hex, newBidder: hex, ctx: CurrentTX, pretx: PreTX, path: number):
if path == 1:
Delete(sig)
# The current input tells us which previous output held the state being spent.
utxoData: {txid: hex32, vout: hex4, sequence: hex4}
utxoData = Push(BVM.unlockingInput)
vout = BinToNum(utxoData.vout)
vout_copy = vout.Clone()
Delete(utxoData.sequence)
Delete(utxoData.txid)
# The previous bidder is encoded in the suffix of the spent locking script.
code_data = pretx.Outputs[vout_copy].LockingScript.SuffixData.Clone()
pre_bidder = code_data.Slice(1, 33)
SetAlt(pre_bidder)
# The spent output value is treated as the bid that must be beaten.
vout_copy = vout.Clone()
pre_value = pretx.Outputs[vout_copy].Value.Clone()
SetAlt(pre_value)
# Store the previous script identity so output 0 can be checked as the continuation.
vout_copy = vout.Clone()
pre_code_size = pretx.Outputs[vout_copy].LockingScript.Size.Clone()
SetAlt(pre_code_size)
pre_code_partialhash = pretx.Outputs[vout].LockingScript.PartialHash.Clone()
SetAlt(pre_code_partialhash)
# Recompute the previous txid from the supplied transaction pieces.
tx_data = Push(0)
SetAlt(tx_data)
for i in Range(2, -1, -1):
size_copy = pretx.Outputs[i].LockingScript.Size.Clone()
if size_copy != 0:
tx_data_temp = PartialHash(pretx.Outputs[i].LockingScript.SuffixData, pretx.Outputs[i].LockingScript.PartialHash, pretx.Outputs[i].LockingScript.Size)
tx_data_temp = Cat(pretx.Outputs[i].Value, tx_data_temp)
SetMain(tx_data)
tx_data = Cat(tx_data_temp, tx_data)
SetAlt(tx_data)
Keep(tx_data)
else:
Delete(pretx.Outputs[i].LockingScript.Size)
Delete(pretx.Outputs[i].LockingScript.PartialHash)
Delete(pretx.Outputs[i].LockingScript.SuffixData)
Delete(pretx.Outputs[i].Value)
SetMain(tx_data)
tx_data = Sha256(tx_data)
tx_data = Cat(pretx.UnlockingScriptHash, tx_data)
SetAlt(tx_data)
tx_input_data = Push(0)
for i in Range(2, -1, -1):
tx_input_data = Cat(pretx.Inputs[i].Data, tx_input_data)
tx_input_hash = Sha256(tx_input_data)
SetMain(tx_data)
tx_data = Cat(tx_input_hash, tx_data)
tx_data = Cat(pretx.VLIO, tx_data)
txid = Hash256(tx_data)
preTXID = BVM.unlockingInput.Slice(0, 32)
EqualVerify(txid, preTXID)
# Output 0 must reuse the same script identity as the spent state.
out0_size = ctx.Outputs[0].LockingScript.Size.Clone()
SetMain(pre_code_size)
EqualVerify(pre_code_size, out0_size)
out0_partialhash = ctx.Outputs[0].LockingScript.PartialHash.Clone()
SetMain(pre_code_partialhash)
EqualVerify(pre_code_partialhash, out0_partialhash)
# The next state records `newBidder` and raises the locked value.
out0_suffix = ctx.Outputs[0].LockingScript.SuffixData.Clone()
out0_suffix_bidder = out0_suffix.Slice(1, 33)
EqualVerify(out0_suffix_bidder, newBidder)
SetMain(pre_value)
pre_value_for_compare = pre_value.Clone()
new_bid_num = BinToNum(ctx.Outputs[0].Value.Clone())
pre_value_num = BinToNum(pre_value_for_compare)
NumEqualVerify(GreaterThan(new_bid_num, pre_value_num), 1)
# Output 1 returns the old value to the previous bidder's P2PKH address.
out1_suffix = ctx.Outputs[1].LockingScript.SuffixData.Clone()
{ out1_prefix, out1_suffix } = Split(out1_suffix, 3)
EqualVerify(out1_prefix, 0x76a914)
{ out1_pkh, out1_tail } = Split(out1_suffix, 20)
EqualVerify(out1_tail, 0x88ac)
SetMain(pre_bidder)
EqualVerify(out1_pkh, Hash160(pre_bidder))
out1_value = ctx.Outputs[1].Value.Clone()
EqualVerify(out1_value, pre_value)
# Rebuild the three current outputs before comparing the transaction output hash.
outputs_data = Push(0)
SetAlt(outputs_data)
for i in Range(2, -1, -1):
size_copy = ctx.Outputs[i].LockingScript.Size.Clone()
if size_copy != 0:
out_data_temp = PartialHash(ctx.Outputs[i].LockingScript.SuffixData, ctx.Outputs[i].LockingScript.PartialHash, ctx.Outputs[i].LockingScript.Size)
out_data_temp = Cat(ctx.Outputs[i].Value, out_data_temp)
SetMain(outputs_data)
outputs_data = Cat(out_data_temp, outputs_data)
SetAlt(outputs_data)
Keep(outputs_data)
else:
Delete(ctx.Outputs[i].LockingScript.Size)
Delete(ctx.Outputs[i].LockingScript.PartialHash)
Delete(ctx.Outputs[i].LockingScript.SuffixData)
Delete(ctx.Outputs[i].Value)
SetMain(outputs_data)
outputs_data = Sha256(outputs_data)
EqualVerify(outputs_data, BVM.outputsHash)
else:
# After the deadline, only the auctioneer signature is needed to close.
Delete(newBidder)
Delete(pretx)
Delete(ctx)
NumEqualVerify(GreaterOrEqual(BVM.locktime, self.auctionDeadline), 1)
CheckSigVerify(self.auctioneer, sig)
Return 1
Push(self.bidder)