Pseudocode Draft
Here is a first draft for the pseudocode for the Resource Logic
and Intents
for the swap. Regarding implementation of the swap, see end of this post.
struct KudoResource {
predicate: Predicate, // kudo_logic
label: FFElement, // (Hash of the) ExternalIdentity of the Issuer, might need to be resolved to the actual ExtID
quantity: FFElement, // Encoding of Nat? Or are they also be native FFElements?
value: KudoValue, // Application Data
} // Predicate and Value will be encoded as Program Field Elements
// Kudos are fungible iff (kudoA1.predicate == kudoA2.predicate && kudoA1.label == kudoA2.label)
struct KudoValue {
owner: ExternalIdentity,
owner_sig: Signature,
issuer_sig: Signature, // Signature over Predicate and Label (Resource Type)
}
struct ExternalIdentity {
key: Key,
}
impl ExternalIdentity {
fn verify(self, bytes: [Byte], sig: Signature) -> bool {
// return true is sig is valid signature from self
// return false otherwise
}
}
// Note: the execution environment (env) needs to provide the hash of each resource_logic to the respective predicates (hash_self), since they need it for verification, but we can't hash(self) within self, because there is no fixpoint
fn kudo_logic(ptx: Ptx) -> bool {
for ri in ptx.resources.input {
if ri.predicate == env.hash_self { // if the Resource is a Kudo
if not ri.label.verify([ri.predicate.as_bytes()++ri.label.as_bytes()], r.value.issuer_sig) {
return false // fail on invalid Issuer 1/2
} // check if Value contains a valid signature of the Issuer, denoted by the label
// since label is external Identity, we can call verification of a signature
if not ri.value.owner.verify(ri.as_bytes, ri.value.owner_sig) {
return false // if sig is valid, owner has authorized consumption
}
}
// Check:
// If a kudo without owner_sig is consumed, the only kudo that can be created is one that has the same contents + owner_sig
}
for ro in ptx.resources.output {
if ro.predicate == env.hash_self {
if ro.label.verify(r.value.issuer_sig) == false {
return false // fail on invalid Issuer 2/2
}
}
// Check:
// Balance per owner and denom should be implicit to authorized consumption + balance check per denom
}
return true
} // in addition to that, balance checks per denomination are always performed
let valueA1 = KudoValue {
owner: agentA,
owner_sig: sig_agentA1,
issuer_sig: type_sig_agentA1,
}
let kudoA1 = KudoResource {
predicate: kudo_logic,
label: hash(agentA)
quantity: 1,
value: valueA1,
}
let valueA2 = KudoValue {
owner: agentA,
owner_sig: sig_agentA2,
issuer_sig: type_sig_agentA2,
}
let kudoA2 = KudoResource {
predicate: kudo_logic,
label: hash(agentA)
quantity: 1,
value: valueA2,
}
let valueB1 = KudoValue {
owner: agentB,
owner_sig: sig_agentB1,
issuer_sig: type_sig_agentB1,
}
let kudoB1 = KudoResource {
predicate: kudo_logic,
label: hash(agentB)
quantity: 1,
value: valueB1,
}
let valueB2 = KudoValue {
owner: agentB,
owner_sig: sig_agentB2,
issuer_sig: type_sig_agentB2,
}
let kudoB2 = KudoResource {
predicate: kudo_logic,
label: hash(agentB)
quantity: 1,
value: valueB2,
}
// Analogously for C1, C2
// To encode simple preferences for independent Kudos, agents can weigh each kudo. This encodes:
// how much they want to give up a certain Kudo, compared to the ones they offer,
// and how much they want a certain kudo, compared to the others they seek.
//
// This enables, e.g. a CSP solver to optimize outcomes locally.
type Weight = Float;
// Since Agents might know which type of Kudo they seek, but not all the details of the Resource,
// we want to enable intents over Kudo Resource Types.
enum KudoIntent {
KudoResource,
KudoResourceType, // We assume this to be a Kudo with the desired Predicate and Label + Value.owner set to the issuer of the intent.
// These should only be used for output resource types, to get the owner right.
// Balance checks enforce inputs of the same type to exist.
}
// Each Agent can produce intents over which Kudos they offer and which Kudos they seek, for arbitrary amounts of input and output kudos.
struct Intent {
input: Vec<(Weight, KudoIntent)>,
output: Vec<(Weight, KudoIntent)>
} // this is encoded as a Ptx + solver hints to encode the weights
// For simplicities sake we only talk about Kudo Resources of quantity 1, but they could be arbitrary size.
// If bundles are split, the remainder is sent back to the owner before consumption.
// Example intents:
let intentA = Intent {
input: vec![(1, kudoA1), (0.5, kudoA2)],
ouput: vec![(1, TypeOfKudoC1), (1, TypeOfKudoB2)]
}
let intentB = Intent {
input: vec![(0.5, kudoB1), (1, kudoB2)],
ouput: vec![(1, TypeOfKudoC2), (0.2, TypeOfKudoA2)]
}
let intentC = Intent {
input: vec![(1, kudoC1), (1, kudoC2)],
ouput: vec![(1, TypeOfKudoB1), (1, TypeOfKudoA1)]
}
// Extra functionality the simulator needs:
// - signature scheme accessible via opcode
// - API to "send" a kudo to someone
// - API to check kudos I have
@cwgoes does the kudo_logic
make sense to you?
Also, should we specify the APIs or leave the choice up to the simulator devs?
@AHart would this structure of Intents be compatible with your rudimentary solver?
If so, could you upload the notebook where you implemented it somewhere, I guess it would be more informative to implement the swap with an actual solver.
We could also make a git repo for (kudos) snippets and drafts if people think that would make sense, or use e.g. the anoma research repo.
TODO
- account abstraction
- multisigs
- splitting bundles