HeliosLang/compiler

Tx::is_signed_by can't check for staking key signatures

Closed this issue ยท 6 comments

There are cases where we require transactions to be signed by staking keys instead of, or aside from, spending keys. But Tx::is_signed_by() right now only allows checking for signatures matching PubKeyHashes.

If we try checking for a staking key signature like for example:

spending myValidator

struct Datum {
    acct: StakeKeyHash
}

func main(datum: Datum, _, ctx: ScriptContext) -> Bool {
    ctx.tx.is_signed_by(datum.acct).trace("signed by holder: ")
}

...an error like the following is thrown:

...node_modules/@hyperionbt/helios/helios.js:1587
                return new UserError(msg, src, startPos, endPos);
         ^
UserError: TypeError on line 25: expected 'PubKeyHash' for arg 1, got 'StakeKeyHash'
    at Function.new (...node_modules/@hyperionbt/helios/helios.js:1587:10)
    at Function.typeError (...node_modules/@hyperionbt/helios/helios.js:1633:30)
    at Site.typeError (...node_modules/@hyperionbt/helios/helios.js:1508:20)
    at FuncType.checkCall (...node_modules/@hyperionbt/helios/helios.js:18592:10)
    at FuncEntity.call (...node_modules/@hyperionbt/helios/helios.js:19590:28)
    at CallExpr.evalInternal (...node_modules/@hyperionbt/helios/helios.js:32561:24)
    at CallExpr.eval (...node_modules/@hyperionbt/helios/helios.js:29831:23)
    at MemberExpr.evalInternal (...node_modules/@hyperionbt/helios/helios.js:32839:33)
    at MemberExpr.eval (...node_modules/@hyperionbt/helios/helios.js:29831:23)
    at CallExpr.evalInternal (...node_modules/@hyperionbt/helios/helios.js:32492:30)

Tx::signatories and Tx.addSigner in the api should probably be updated also..

https://www.hyperion-bt.org/helios-book/lang/builtins/tx.html#signatories
https://www.hyperion-bt.org/helios-book/api/reference/classes/Tx.html#addsigner

I just saw this now, and responded in discord. Here are my comments below.

I think the trick is to add the staking key hash as a signer to the tx in the offchain code, and then onchain you can check the staking key as if it were a public key hash. Both staking key and public key are just opaque types around a bytestring hash. So from a cryptographic perspective, it is simply a matter of confirming that the hash matches the signature.

image

And in your example, instead of using StakeKeyHash in the datum, use a bytestring to pass the hash, and then create a PubKeyHash in the onchain code.

struct Datum {
acct: ByteArray
}

... inside main somewhere...

stakingHash: PubKeyHash = PubKeyHash::new(datum.acct);

instead of using StakeKeyHash in the datum, use a bytestring to pass the hash, and then create a PubKeyHash in the onchain code.

Thanks for this! Definitely a work-around for now.

Still, it'd be better if we can just do tx.is_signed_by(stakeKeyHash) in future versions; makes code intent more apparent.

I've been giving this some thought, and maybe the best option is to simply get rid of StakeKeyHash and represent it with a PubKeyHash instead?

reposting here my comment on discord:

I can't say I know much about the low-level stuff, but if StakeKeyHash has the same structure as PubKeyHash, then it makes sense to just use PubKeyHash and get rid of StakeKeyHash entirely.

Then perhaps it's just the docs that will need updating to make it clear, especially to newcomers, that these two types are treated the same by Helios.

v0.15.11 removes all occurences of StakeKeyHash in favor of PubKeyHash