Skip to content

Instantly share code, notes, and snippets.

@xhliu
Last active April 26, 2023 18:28
Show Gist options
  • Save xhliu/4b9c7bf4a3f9ace51538e6bb9ca412e7 to your computer and use it in GitHub Desktop.
Save xhliu/4b9c7bf4a3f9ace51538e6bb9ca412e7 to your computer and use it in GitHub Desktop.
StatefulMultiSig
export type Owner = {
pubKey: PubKey
validated: boolean
}
export class StatefulMultiSig extends SmartContract {
// N of M signatures required.
static readonly N = 2
static readonly M = 3
// Payment destination once signature threshold is reached.
@prop()
dest: PubKeyHash
// Public keys of the owners along with boolean flags, that
// indicate if their sig was already validated.
@prop(true)
owners: FixedArray<Owner, typeof StatefulMultiSig.M>
constructor(
dest: PubKeyHash,
owners: FixedArray<Owner, typeof StatefulMultiSig.M>
) {
super(...arguments)
this.dest = dest
this.owners = owners
}
@method(SigHash.ANYONECANPAY_SINGLE)
public pay() {
// Check if threshold was reached.
let nValid = 0n
for (let i = 0; i < StatefulMultiSig.M; i++) {
if (this.owners[i].validated) {
nValid += 1n
}
}
assert(
nValid >= BigInt(StatefulMultiSig.N),
'Not enough valid signatures.'
)
// Make sure balance in the contract does not change.
const amount: bigint = this.ctx.utxo.value
// Pay destination address
const output: ByteString = Utils.buildPublicKeyHashOutput(
this.dest,
amount
)
// Verify unlocking tx has this output.
assert(this.ctx.hashOutputs == hash256(output), 'hashOutputs mismatch')
}
@method(SigHash.ANYONECANPAY_SINGLE)
public add(sig: Sig, pubKeyIdx: bigint) {
let added = false
for (let i = 0; i < StatefulMultiSig.M; i++) {
if (BigInt(i) == pubKeyIdx) {
const owner = this.owners[i]
const valid = this.checkSig(sig, owner.pubKey)
if (valid && !owner.validated) {
// Toggle flag.
this.owners[i].validated = true
added = true
}
}
}
// Make sure at least one new valid sig was added.
assert(added, 'No new valid signature was provided.')
// Make sure balance in the contract does not change.
const amount: bigint = this.ctx.utxo.value
// Output containing the latest state.
const output: ByteString = this.buildStateOutput(amount)
// Verify unlocking tx has this single output.
assert(this.ctx.hashOutputs == hash256(output), 'hashOutputs mismatch')
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment