Update the token app to use inline authentication using unions.
Authentication
Authentication is one of two things:
Contract owned
Any predicate can have it's own account. We hash the PredicateAddress and that gives the account. What this means is if that predicate is solved then that authenticates transferring tokens from that account.
This is how it's currently done.
But basically it comes down to:
type PredicateAddress = { contract: b256, addr: b256 };
type Instance = { addr: PredicateAddress, pathway: int };
pub var key: b256;
var predicate_that_owns_account: Instance;
// Check the owning predicate is being solved in this solution.
constraint __predicate_at(predicate_that_owns_account.pathway) == predicate_that_owns_account.addr;
// Check the owning predicate is the one that owns this key
constraint __sha256(predicate_that_owns_account.addr) == key;
Signed
A user can sign over values that they want to authorize like signing over the from, to and amount. The public key of that signature is hashed and that is the account that is authorized.
// The address that the amount is being sent from.
pub var key: b256;
// The address that the amount is being sent to.
pub var to: b256;
// The amount being transfered.
pub var amount: int;
// Read the nonce from storage.
state nonce = mut storage::nonce[key];
var sig: Secp256k1Signature;
// Check that the signature over the data verifies to the key
constraint __sha256(__recover_secp256k1(__sha256({key, to, amount, nonce', { contract: __this_contract_address(), addr: __this_address() }}}), sig)) == key;
Unions
So these are the two most basic forms of authentication. There are more types (like only signing over some data but requiring other constraints) but let's focus on these two.
The challenge with including these in the token contract is that they have different data depending on the type of authentication. So we need to do something like:
type PredicateAddress = { contract: b256, addr: b256 };
type Instance = { addr: PredicateAddress, pathway: int };
type ContractAuth = { predicate_that_owns_account: Instance };
type SignedAuth = { sig: Secp256k1Signature };
union Auth = ContractAuth(ContractAuth) | SignedAuth(SignedAuth);
// The address that the amount is being sent from.
pub var key: b256;
// The address that the amount is being sent to.
pub var to: b256;
// The amount being transfered.
pub var amount: int;
// Read the nonce from storage.
state nonce = mut storage::nonce[key];
var auth: Auth;
constraint match auth {
Auth::ContractAuth(c) => {
constraint __predicate_at(c.predicate_that_owns_account.pathway) == c.predicate_that_owns_account.addr;
constraint __sha256(c.predicate_that_owns_account.addr) == key;
},
Auth::SignedAuth(s) => {
constraint __sha256(__recover_secp256k1(__sha256({key, to, amount, nonce', { contract: __this_contract_address(), addr: __this_address() }}}), s.sig)) == key;
}
}
Goal
Update the token app to use inline authentication using unions.
Authentication
Authentication is one of two things:
Contract owned
Any predicate can have it's own account. We hash the
PredicateAddress
and that gives the account. What this means is if that predicate is solved then that authenticates transferring tokens from that account. This is how it's currently done. But basically it comes down to:Signed
A user can sign over values that they want to authorize like signing over the
from, to and amount
. The public key of that signature is hashed and that is the account that is authorized.Unions
So these are the two most basic forms of authentication. There are more types (like only signing over some data but requiring other constraints) but let's focus on these two. The challenge with including these in the token contract is that they have different data depending on the type of authentication. So we need to do something like:
Resources
Sum types
section.