Scale-free kudos: brainstorming thread

Basic concept:

Scale-free kudos as discussed a bit here and in this talk:

  • Anyone at any scale can issue a kudo denomination
  • Substantial part of issuance goes towards retroactive public goods funding (hence “kudos”)
  • Kudo liquidity can be used for routing (this part is similar to Circles/CoFi)
  • New parties can issue new kudo denominations with initial RPGF / integral airdrop configurations
  • Parties can measure alignment with new denominations by similarity / difference of distributions
  • Parties can configure semi-automatic kudo issuance based on:
    • Physical co-presence (automatic kudo issuance to parties in the close vicinity, incl. parks, places where time is spent, etc.)
    • Third-party scores (e.g. automatic kudo issuance based on Givewell ratings)
    • Patronage networks (a la Patreon, but decentralized)
    • Agreements to conditionally mint each other periodic kudos

I think it will especially be important to think about and discuss what using this kind of system would feel like - kudos are not scarce in the same way that fiat cash is, you can always mint more of them - and to discuss how this might change both local and global incentive structures if actually realized.

Maybe we can also kick off with a synchronous brainstorming session & some note-taking.

cc @apriori for your context
cc @degregat did we have any other notes on this topic that should be linked?

3 Likes

Collecting notes on relevant literature here, might edit with updates:

Regarding incentive structures, I think this paper might be relevant: Barter Exchange via Friends’ Friends

Also this post by @apriori has some examples.

1 Like

Notes from sync session

Excalidraw session

The goal of this spec: give Juvix concrete enough details on stuff like resource logics, solver algorithms, etc. to be able to implement a simple version of the scale free kudos concept.

Non-goals

  • describe final scale-free kudos concept
  • mathematically model stuff
  • think about efficiency
  • worry about compatibility

Two breakdowns

  • Component- what Juvix needs to build
  • Functionality - what should a user be able to do with it

Components:

  • Resource Logic tying Issuer Identity to Denomination
    • actual public keys
    • account abstraction including multisig
  • Multi-Agent, Multi-Resource Swap
    • support TTL
    • matrix interface, edge case is single swap
      (probably don’t need to implement full generality first but
      interfaces won’t need to be refactored if general from the start)
    • example: holiday planning, map non-fungible things to Identities/Denoms
  • Preference over outcomes
    • e.g. independent weights which Resources give up first and which to receive ideally
      (linear preferences per resource)
  • Dummy interfaces:
    • sending kudos
    • check balance of self

V2:

  • intents for split bundles. e.g. partial consumption of an intent of LPs

Functionality:

  • Create a kudo denomination
  • Send a kudo to another user
  • Check their Kudo balance
  • Trade x for y (multi-resource matrix) (with preference “I want more y”)
  • Provide kudo liquidity (~ xy=k)
  • Account abstraction (create account, change pubkey)
  • Create multisig, act w/multisig

Discussion

Functionality

Users should be able to create a Kudos denomination and send a kudo to another user. For example, create a payment request like “I want 5 of x” and then another user should pay that payment request not with x but with y and the application should figure out the path necessary to create a multi-resource swap. User should be able to check their Kudo balance.

  • How many dimensions should this have? I dont see how to specify a multi-resource one. I have in mind a basic version where we are not necessarily doing Kudo trading.
  • I am offering my physical thing for 5 coffee Kudos. Maybe in this simple application concept - trade x for y and get rid of the payment request.

So we don’t want to have multi-agent resource swaps? A simple version of this application concept where the only thing a user can do is try to trade x for y, and this could involve 4 liquidity pairs.

  • I think chaining these would be harder than having one solver create a bundle
  • As a kudo LP I provide liquidity in the x for a, b for c, and as another user I submit my intent to trade x for y and solver searchers existing liquidity to route these requests.

I think we want to have preferences. If we do one for one swaps, it’s probably less important. What we would like to have is multi-resource swaps. In the end, no preferences for a one-to-one case.

  • Do we want to break this down into things that doesn’t have preferences and break down into i have 4 x and want 2 y, I will never get more than 2 y. Do we want to have utility function here to maximize welfare?

If all preferences are linear, then everything described is easy if you want this to be one for one, not sure how you would turn into a preference. Now we know these are easy to do, but should we have this? Probably not in the first version in an early version of Kudos - a preference is I want more y here

  • What you have is preference free version of the problem. You have a boolean to say if the trade went through. To express preferences, you have some multiplier that scales Booleans and scales it down-function would be dependent on the user willing to trade an a or b but rather trade an a and b. The intent solver knows it’s giving lower score if it makes a trade you don’t want but still gives some score.

  • Let’s do it and include preferences and limit it to this specific thing, trade x for more y. In our preferences we need multi-swaps. I If I have an offer for some resource then if it’s just one resource, it’s not a multi-swap or do we want to aggregate single resource offers?

We want to make the interface for the users simple. The user would submit one trade per resource with a weight, and they would be put into one batch, so we only intend to make the interface for the user easy. In reality, this is a multi-agent multi-resource swap system, assuming we are not having anti-sybil you have an intent with one weight in the anti-preference function.

  • I’m offering a and I want b. Then if I want to express my preferences over giving up a1 or a2 over b then say this with exclusionary different weights. I think we want a multi-resource swap interface for users. If I hope to disentangle these preferences, I have a and b with preferences to give up for c but not getting more than 2 c - I think the interface gets harder over time.

The full power interface would just be a matrix, and the edge case will always be the single resource trade-off. If a user wants a simple case, they will always submit a simple thing. If we want to implement this interface, it will clarify if we did the rest correctly - intents only become useful in this scenario because you are matching one for one.

  • We should at least have the interface as a matrix, and for now, we only support the single use case. If we just have the Tuple interface, we will refactor a lot of code and this will be annoying.

I want the spec to include roughly how, concrete examples of what the user will be doing - apples, train tickets, or other concrete objects. I want to stay at a hotel x for amount of time y and prefer transport z. This requires n meals and has different providers for different things, and bundle this for your ultimate dream travel.

  • This represents non-fungible kudos with calendar dependent hotel rooms and stuff. Every Hotel room has one identity per day, and they are not fungible above time slots.
  • We need account abstraction - create an account with a pubkey, we don’t need to build any multi-sig coordination infrastructure.
  • Specify a linear function for a and b is my preference for the amount of a i get and the amount of b i get.

Components

We want to come up with resource_logic that ties issuer identities to denominations because that is a Kudo. A kudo is only minted by you, which is enforced by resource_logic.

  • Multi-agent, multi-resource edge case.
  • Single swap edge case.

Linear preferences with independent weights. Which resources we would like to give up first and what we want to receive. Need versions of this resource logic for actual pub keys and account abstraction.

Multi-agent multi-resource swap needs to include provisioning, which can be an intent with an expiration time; e.g., what do I do as the hotel provider?

  • I want to everyday offer my existing hotel rooms at a given price?
  • How do I do that?
  • How is liquidity provisioning different from any request?

It’s not just the TTL is different. Maybe I want 5 apples every Monday and every time the oracle says its Monday include this intent in the solving batch but we want to support TTLs in general. Everything should be symmetric. We just need a concrete TTL that is supported by Taiaga and specified in this thing. We need concrete features, but we shouldn’t think about any intent being different from another intent. All intents are equal.

There is a distinction between offering 5 x for 5 y - choose if someone does all, or I choose who does solving.

  • Implementation-wise, this requires distinct new resources which carry over logic.
  • Want to encode this as preferences.
  • By default, what we currently have is only settle this intent until it can be settled.
  • Sometimes we will have more complex things going on.

Past Kudo thing - need to prove you held kudo previously, which is a resource that has already been consumed. Put in V2 because it doesn’t sound like a Kudo sounds like a RPGF problem.

The contours of the application concept are flexible. Does the functionality match the components?

Questions about stuff not in the resource layer sending Kudos checking account balance - sounds like querying a database, Typhon does this?

  • for v1 we just need to specify how the application reads all the resources with property x- engineering can figure out the rest from this description.
  • the client interacting with this may have a notion of their own public eternal identities - private or public?
    • don’t know how well VAMP-IR compilation target is working presently, let’s write the spec agnostic of this question

How do we want to divide labor?

Write the resource_logics in pseudocode. Organize and start writing something like this. Maybe we can do the resource logics in pseudocode and the swap in pseudocode and the preference - multiplicative factors, modeling it this way assigning a weight to every resource.

The dummy interfaces - pseudocode for resource logic and swaps, preferences and dummy APIs. CSP model tells us if the thing we come up with is compatible.

Next steps:

  • write pseudocode resource logic
  • swap in pseudocode
  • preferences are just weights on resources
  • dummy APIs

@degregat to draft pseudocode
@AHart to review pseudocode

1 Like

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
1 Like

Based on @apriori’s description of the weights being “exclusionary”, I’m going to assume

let intentA = Intent {
    input: vec![(1, kudoA1), (0.5, kudoA2)],
    ouput: vec![(1, TypeOfKudoC1), (1, TypeOfKudoB2)]
}

is saying that A wants to trade a “TypeOfKudoA1” or a “TypeOfKudoA2”, but not both, for a “TypeOfKudoC1” or a “TypeOfKudoB2”, but not both. This is not compatible with what I wrote before, as that was based on inclusive inputs and outputs. Exclusive inputs and outputs are able to be modeled. It’s easy, but it would be different from what I wrote before.

But, anyway, the way this would be modeled is by having, for every pair of possible input and output in the intent, a boolean representing that combination. The sum of these booleans must be less than or equal to 1, indicating that we select, at most, one pair.

For each pair boolean, it has an associated input and output type. We can determine how many kudos of a specific type were traded as inputs by summing the pair booleans with that input type. Similar for output types. By equating these two for each type, we can ensure conservation of resources.

Lastly, there’s the utility function. This isn’t so clear. I modeled this by scaling the boolean by the weight of the output and 1/ the weight of the input. This means more valuable inputs are less willing to be traded. Alternatively, we could interpret the input weights, not as how much they value the resource, but how much they’re willing to trade the resource, in which case the scale should just be the product of the input and output weights.

You can find an implementation of a solver doing this on your example here. Edit: I realized there’s a much more efficient encoding that simply one-hots the inputs and outputs for each intent. This scales linearly with intent size instead of quadratically. We simply require that, for each intent, the sum of input booleans is <= 1, the sum of output booleans is <= 1, and that both sums are equal. This also allows splitting utility scaling across wants and haves since there are now two true booleans for a satisfied intent. I’ve implemented this here.

You can also find my original notebook here.

There is also the catch that the solver does not keep track of the original resource owners, it just pools inputs. If the original owner of the resource doesn’t matter to the receiver, then we can just do a linear-time pass to assign owners to trades based on the order they appear in the overall intent problem. If the owner could matter, then some modification to model this is necessary. I assume this would have some implications for how solutions are modeled for later verification.

2 Likes

I think for kudos V1 if we have to choose, I would go for “inclusionary” intents as you described them, as I think they are the more interesting setting.

E.g. I want a specific thing and I have some stuff I’m willing to give up for that.

Also, we can probably model exclusionary cases just as multiple intents, some of which will fail to execute if they share input resources which where consumed already.

In kudos V2 or so, I would say we probably want to have explicit “inclusionary” and “exclusionary” connectives to use within the same intent. If that’s also easy, maybe we can have that in V1 as well (I’ll let @AHart be the judge of that).

What do you think @apriori, @cwgoes ?

@AHart Regarding the one-hot encoding, would that also give us better scaling properties for “inclusionary” intents, or do they not have the problem that “exclusionary” ones have?

Purely inclusionary conditions do not require any one-hotting; one-hot encodings only exist to specify which among an exclusive set was chosen. The inclusive implementation I gave in my original notebook already has linear scaling.

We can mix and match, though arbitrary mixing and matching may be hard. If we are going to mix, requiring some standard form on the intent along the lines of “(a | b | c) ⊕ (a | x | c) ⊕ t”, where “|” is inclusive or and “⊕” is exclusive or might be best. If we want deeper nestings, that would require more thought. Something like a Tseytin transformation might be able to make deeper nestings more efficient, but I’ll have to think about it.

That sounds great!
I think we can require a standard form, at least for now, if that enables us to have both connectives. With these and independent scaling, we already get quite general intents.

Regarding the scaling in the utility function:
I think you had the right intuition here. It seems more sensible to use the utility an agent has for a Resource especially if agents keep local measurements/estimates of their preferences over outcomes, not trades (which I think is more likely) they could just plug in the data they already have.

Can you create a notebook that implements this (both connectives in standard form, scaling as you did it first)?

Yes, give me a few hours

1 Like

Here’s the prototype for mixed inclusive-exclusive intents.

2 Likes

I just added the pseudocode from above including more elaborations to the repo here.

1 Like

This newer paper by Lewis-Pye, Naor & Shapiro paper, Grassroots Flash: A Payment System for Grassroots Cryptocurrencies, is quite interesting;

The goal of grassroots cryptocurrencies is to provide a foundation with which local digital economies can emerge independently of each other and of global digital platforms and global cryptocurrencies; can form and grow without initial capital or external credit; can trade with each other; and can gradually merge into a global digital economy. Grassroots cryptocurrencies turn mutual trust into liquidity and thus could be a powerful means for ‘banking the unbanked’.

Grassroots cryptocurrencies have not been provided yet with a payment system, which is the goal of this paper. Here, we present Grassroots Flash, a payment system for grassroots cryptocurrencies that employs the blocklace—a DAG-like counterpart of the blockchain data structure. We analyze its security (safety, liveness, and privacy) and efficiency, and that it is indeed grassroots.

The paper provides an excellent English language description of what we want to achieve with scale-free kudos. In particular, in section 2 the authors lay out the concept. Curious if it aligns with scale-free kudos?

tl;dr (my own)

  • Grassroots cryptocurrencies are permissionless tools for turning mutual trust into liquidity – coins are IOUs that can be issued and traded by anyone.
  • They operate on three social conventions and one principle:

Conventions

  • Minting: people can issue (mint) and destroy (burn) their currency at will.
  • Pricing: goods and services can be priced in the person’s own currency.
  • Mutual Credit: people exchange coins to create mutual credit lines, enhancing liquidity through trust.

Principles

  • Coin Redemption: people agree to redeem any coin they’ve issued for any other coin they hold.

Example

Consider how an isolated community may, first, achieve liquidity and, second, establish a community bank, without any external resources such as capital or external credit. Assume an autarkic village has 501 productive inhabitants that all know and trust each other to some degree, each pricing their goods and services in terms of their own personal coins. Next, assume that every two villagers exchange 100 personal coins with each other. The result would be that each villager would have a line of credit of 50,000 coins from its fellows, each with a 0.2% exposure to debt by each other villager, and that the total liquidity in the village, or coins in circulation, would be 25,000,000 coins. Certainly something to work with.

As long as all villagers are more or less balanced, and spend as much as they earn, with no villager overspending and no villager acting as a cash-hog, all will be well. Still, each villager p who wishes to purchase something from villager q will have to use its q coins, or obtain q-coins from others if it has run out of them. To avoid this hassle, the village may decide, democratically, to establish a community bank. Assume the village has a democratically elected board, members of which are given signature rights in a multisignature account for the village bank, and thus can issue a village coin via this account. Assume further that the community bank opens a mutual line of credit of 1000 coins with each villager exchanging 1000 village coins in return for 1000 of the villager’s coins, and that the villagers now price their goods and services in terms of village coins, so that its economy runs even more smoothly. One may ask what value as collateral do the 1000 villager coins the bank now holds have? The answer is the 50,000 coins the villager has received from other villagers (or whatever the villager has at present).

Thus, as a community bank, the only collateral the bank has are the personal IOUs of its members. However, it can shift its risk among villagers very easily. If villager p appears to the bank to be hemorrhaging money, the bank may try, while p is still solvent, to redeem from p some of the 1000 p-coins it holds against coins of other villagers held by p, who appear to the bank to be more financially stable. This way, the bank can reduce its exposure to p without closing down or even without reducing the 1000 coins line of credit the bank has granted p to begin with. In effect, the bank’s credit line to p is now covered by p’s friends.

1 Like