Skip to content

Resource Logic

What follows is an explanation of the encoding of Kudos and Intents over them as Resources with a corresponding Resource Logic.

// Kudos
// Multi-Agent, Multi-Resource swaps over Resource Types tied to Identities
// -- Kudos as Resources --

struct KudoResource {
    predicate: Predicate,   // kudo_logic
    label: FFElement,       // (Hash of the) ExternalIdentity of the Originator, 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,
    originator_sig: Signature, // Signature over Predicate and Label (Resource Type)
}

struct ExternalIdentity {
    key: Key,
}

impl ExternalIdentity {
    fn verify(self, bytes: [Byte], sig: Signature) -> bool {
        // if sig is valid signature from self
        //    return true
        // otherwise
        //    return false
    }
}

struct PTX {
    input_resources: Vec<Resource>,
    out_resources: Vec<Resource>,
    extra_data: Vec<Byte>,
}

// Evaluation context
// The evaluation context (ctx) consists of a single PTX and some "system calls" providing extra functionality 
// we do not want to, or can not implement inside of a resource logic.
// During evaluation, the predicates from all resources get applied to the PTX one after another.
//
// "Syscalls":
// - self_hash: since some logics will need to know about their own hash/address, and trying to compute the hash from 
//   inside the logic ("self.hash(self)") is not possible since there is no fixed point, the ctx needs to provide the 
//   hash of each resource_logic to itself. 
//   The ctx would then replace the term "self_hash" in a resource_logic by the corresponding hash during evaluation.
// - verify: since we do not want to implement cryptographic primitives in the logics, the ctx should provide them by 
//   taking (Data, Key) as arguments and returning Bool. For the prototype, keys can be of type Bool with 
//   check_signature just returning the value from the key.



// The following is called at validation time (taiga to be used in the shielded case).
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.originator_sig) {
                return false // fail on invalid originator 1/2
            } // check if Value contains a valid signature of the originator, 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.originator_sig) == false {
                return false // fail on invalid Originator 2/2
            }
        }
        // Check:
        // Balance per owner and resource type should be implicit to authorized consumption + balance check per type 
    }

    // Check connective validity
    if ptx.extra_data.connectives == false {
        return false
    }

    return true
} 
// In addition to validating the resource logic, balance checks per resource type are always performed. 

// -- Example Resources --
let valueA1 = KudoValue {
    owner: agentA,
    owner_sig: sig_agentA1,
    originator_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,
    originator_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,
    originator_sig: type_sig_agentB1,
}

let kudoB1 = KudoResource {
    predicate: kudo_logic,
    label: hash(agentB)
    quantity: 1,
    value: valueB1,
}
// Analogously for B2, C1, C2

// -- Intents over Kudos --

// To encode simple preferences for independent Resources, agents can weigh each Resource. 
// This encodes how much they value having a certain Resource. 
// In other words: For Resources they already have Agents prefer to not trade ones with high weights.
//      Inversely, for Resources they want they prefer to trade for the ones with high weights.
//
// This enables, e.g. a CSP solver to optimize outcomes locally in respect to the assignment of the formula
// by scaling boolean variables.

type Weight = Float;

// A general Intent would look like this:
enum ResourceIntentElement { // TODO: Find a better name, if we think this is an important object 
    Resource,         // This could be a resource that an agent owns, or a fully specified resource they want.
    WantResourceType  // This specifies the Type (Predicate + Label) of a Resource an agent wants and requires Value.owner set to the intent creating agent.
}
// Note: Want ResourceType is only used for output resource types, to get the owner right, balance checks enforce inputs of the same type to exist.

// Note: We might also introduce HaveResourceType later, in case agents want to publish intents e.g. for 3rd parties to perform a swap.

// Agents might want to express inclusionary (OR) or exclusionary (XOR) preferences. 
// Inclusionary means they want to give or get either or all resources from a clause.
// Exclusionary means they want to give or get exactly one subclause.
// To be compatible with the CSP backend these should have a standard form as follows:
//
// let have0..2, want0..2 be ResourceIntentElements
//
// have: (have 0) XOR (have 1 OR have 2)
// want: (want 0 OR want 1) XOR (want2 )
//
// During validation of the connective, only the Resource(Type) and have/want property should be relevant, 
// i.e. validation should be invariant against the relative positions of haveI, haveJ. 
//
// TODO: We should specify this more precisely, e.g. via a BNF
type Connective = Term;

// Intents like the following (or a flat string representation as above) would be generated to be ingested by solvers, 
// which in turn produce transactions to be validated. Validation and solving time should be fully disjoint in practice. 
struct ResourceIntent {
    have: Vec<(Weight, ResourceIntentElement)>,
    want: Vec<(Weight, ResourceIntentElement)>,
    connectives: Connective,
}
// This would be encoded as a PTX as follows:
// - elements of have as input_resources
// - elements of want as ephemeral output_resource 
// - ephemeral resources required for balancing on both sides are inferred
// - solver hints encode the weights and connectives in ptx.extra_data
//
// Note: The layout here is kept close to what a PTX looks like for clarity of explanation,
// but more concise representations can be chosen for Juvix in the service of improving UX.

// 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 KudoIntentElement {
    KudoResource,
    WantKudoResourceType, // As WantResourceType, but with Predicate = kudo_logic
}

// Each Agent can produce intents over which Kudos they have and which Kudos they want, for arbitrary size vectors of both.
struct KudoIntent {
    have: Vec<(Weight, KudoIntentElement)>,
    want: Vec<(Weight, KudoIntentElement)>
    connectives: Connective,
} 
// For an example intent and solver see: https://github.com/anoma/kudos-snippets/blob/main/intents_stand_mix.ipynb

// Functionality of the VM that is needed:
// - signature scheme accessible via opcode
// - predicate.hash(self) provided via "syscall"
// - API to "send" a kudo to someone
// - API to check kudos I have

// TODOs for V2:
// - multi round solving/pathfinding, in case intents can not be satisfied locally on a single solver
// - bundle splitting, to handle resources with quantity > 1