Thoughts on the token standard

Typically you would want a DAO to manage whatever service that you are thinking about from a Dapp perspective. This DAO is presumably unique to your Dapp. So how would you form a DAO? Fair launch of a token to manage the DAO would be one way.

How do you get the community rallying around the DAO? Well by building on a already existing platform with re-usable code & minimizing the chance of a “naked” rug pull. Enter SNS.

Ok ; so you buy the koolaid(SNS). How do you get investors. Enter community funds. 1.2 M ICP are already on community funds. Depending how this feature turns out, you should be able to pitch to the community to fund your project.

Sonic and infinityswap are DEXs on IC. I used Sonic to provide liquidity for OGY/WICP pair. The experience was f… awesome; with near zero gas fees (0.001ICP).

1 Like

Cryptographic verifiability of ledgers

Let me (re)start a discussion that is orthogonal to the current topics handled in the working group: Cryptographic verifiability of ledgers on the Internet Computer. In a recent call with @witter, he mentioned that cryptographic verifiability is his prime concern for ledgers. He asked me to bring this up in the Working Group again for a more thorough discussion.

Going through my notes of the Working Group discussions, I saw that @witter has voiced his opinion on the importance of this already in (one of) the first Working Group meetings on May 10 where he has flagged this as his primary concern. In a later meeting, this was coming up again and we discussed this issue within DFINITY and briefly presented the challenges in the follow-up meeting. The outcome was, that it is quite challenging and thus do not want to take any action now, but we did not give the discussion any further attention.

Following up on @witter’s request, let me now bring this up again with this forum post for discussion among the participants of the Working Group. This is indeed a very important topic and full transparency of what has been discussed is important.

The technical discussions in DFINITY are summarized next: Having cryptographic proof of integrity of the ledger can be achieved by having all blocks of the ledger cryptographically signed. Signatures could be obtained through the subnet signatures that are used for state certification. Those are BLS threshold signatures generated after every round for the purpose of state certification where parts of the state are getting certified via Merkle proofs.

The execution of messages by canisters is performed in a highly-optimized concurrent fashion and at the end of each round we obtain a deterministic state in each replica that can be certified via Merkle proofs. However, after the execution of each individual message during the round, we do not have an explicit deterministic state on which certification could act analogous to how it is done currently at the end of the round. Retrofitting execution to accomplish this would be a major task and could increase message execution latency by a constant factor (a factor of 2 or 3 seems realistic).

The essence here is that in theory it is possible to get the ledger blocks cryptographically signed, however, there are many practical obstacles and it would be a huge project in the execution layer that would lead to a significantly increased message execution latency to achieve this.

Hope this explains a bit better what the results of the technical investigations so far on the topic of certified ledger blocks have been. Further comments and ideas by the community on other ways of solving this are of course welcome.

7 Likes

It was a pleasure to have a meeting with dieter and discuss the very in-depth issues of the token standard, this summary is very detailed and appreciated dieter’s work. Currently DFT standard solves the problem of cryptographic proof by a built-in blockchain, DEFI also needs sufficient cryptographic proof to gain the trust of users (this distrust is not due to distrust of IC, it is the lack of cryptographic proof provided to users by DEFI applications)

1 Like

Perhaps can someone explain why we want cryptographic verifiability in a ledger to begin with? Are the security guarantees of the IC insufficient?

Is this more of a UX feature so that users feel that their funds are safe, or is there a legitimate security concern with the current ledger design?

Does this mean that the proof of DeFi and token transactions needs to be implemented at the application level by the developers themselves?

User can not trust a DEFI application if they cannot provide cryptographic proof that does not rely on a third party.
If IC DEFI DAPPs cannot provide cryptographic proof that does not rely on a third party. Who can trust such DEFI DAPPs if there is a risk that the DEFI applications are tampered with by third parties? How can users dare to use these DEFI DAPPs to exchange their assets? This is why I keep insisting on providing cryptographic proofs

2 Likes

Regarding SNS, I also told @dieter.sommer about my considerations.
I think SNS is a governance model that is more like a DAO for governance. DAO is great, it is valuable and has its own quadrant of problems that can be solved, but I don’t think it solves, nor is it for the underlying cryptographic proofs needed for Token standards and DEFI.

For example, while DAO can be good for resolution by voting, the underlying cryptographic proof needed for token is a tamper-evident proof, but DAO, very clearly, makes the token [modifiable] regardless of which form of resolution it produces. token underlying tamper-evidence is broken by DAO, so I don’t think DAO can replace token the underlying cryptographic proof.

Finally, to summarize, I think SNS is valuable, but I also think the underlying cryptographic proof of the token standard is very important, and the two are not contradictory or conflicting.

3 Likes

Let me clarify and answer some of the questions that have come up. There are different ways on how we can obtain verifiability of the ledger.

Integrity protected record of transfers:

This can be achieved through different means. The one outlined above ?? is by means of cryptographic signatures issued by the subnet (threshold BLS signatures and Merkle proofs). There is another, simpler, approach of having a “blockchain within a blockchain,” i.e., the blocks of the ledger forming a blockchain. There’s possibly further ways one could achieve integrity protection, but let’s remain with those for the discussion.

According to (second-hand) information, not everybody like the idea of having a blockchain within a blockchain. People in this camp think we should use a conceptually nicer mechanism to protect the integrity of the ledger entries. That’s why the approach based on signatures came up.

DelandLab’s token standard as well as the ledger use the blockchain-within-a-blockchain approach to protect the integrity of the ledger. It works well and gets the job done, but there seems to be critical comments from some sides, that’s why we wanted to have this discussion here openly to get everyone on the same page.

Having an integrity protection mechanism like this in place still does not allow for full verifiability as the ledgers as we do not have the transfer requests of users and canisters available in integrity-protected form. That is, a dishonest developer or DAO governing a ledger can still mount an attack and change token balances without a corresponding transaction, but they cannot change anything in the past any more, but only do transfers without a request at the current point in time and add integrity-protected blocks accordingly. Anything in the past is perfectly integrity protected, however.

Availability of users’ and canisters’ certified transfer requests

Full ex-post verifiability of everything regarding a ledger would require that all user’s transfer requests (ingress messages including the signatures) be retained in addition as well as all transfer requests from other canisters. Such requests from canisters on other subnets could be managed as we could use subnet signatures certified the XNet transfers already, but for requests from canisters on the same subnet it is very hard to obtain certified requests that can be later verified with a signature. This is exactly the same problem as discussed in the previous post. So full ex-post verifiability by including all signed transfer requests by both users and canisters is currently not possible. This is something that we could look at in the future.

Hope that this explains the motivation for this discussion better.

5 Likes

transfer signitures!Tho txn signitures maybe not essential for non-defi DApps, txn signitures are key for defi verifibility. Hope IC researchers came up the best mechanics to resolve it on IC. That means everything for developers and investors

1 Like

I had some thoughts on the token standard. Several payment flows have been discussed in this thread and other threads and interfaces have been proposed for the flows. Among them were, for example, 1) a 1-step push transfer initiated by the sender, 2) a pull transfer following a pre-approval by the sender (2-steps in total), etc. I thought about how to generalize the different flows into one concept with a single interface. The way to unify the flows is to create symmetry between sender and receiver. Here is what sender and receiver have in common in my model:

  1. Both have to approve of the transfer (i.e. no direct deposit into a receiver’s account without approval of the receiver).
  2. Both have a certain balance differential as a result of the transfer, positive for one, negative for the other.
  3. Both can initiate the transfer, leaving the other to accept.

I will discuss the reasons why 1. is acceptable in a subsequent post, as to not interrupt the exposition here, but briefly summarized they are as follows:

  1. A token transfer is in general an exchange in which the receiver of the transfer also delivers something to the sender at the same time when the transfer is made. It is therefore intuitive if the receiver also approves.
  2. Direct deposits into arbitrary accounts are problematic. There are reasons to disable them, at least by default.
  3. Direct deposits can be re-introduced as a special case, if desired.

Specification

type Transfer = vec Part;
type Part = record {
    of : principal;
    tokens : opt record { subaccount : Subaccount; amount : int };
    memo : opt blob
};
type Subaccount = blob;
type TransferId = nat64;

service : {
    request: (Transfer) -> (variant { Ok: TransferId ; Err });
    accept: (TransferId) -> (variant { Ok; Err });
    reject: (TransferId) -> (variant { Ok; Err });

    // queries
    transfer_details: (TransferId) -> (variant { Ok: Transfer; Err });
}

A simple transfer from principal A to B has two “parts”, one of A with a negative amount and one of B with a positive amount. The general flow is like this:

  1. Both parties negotiate out-of-band the transfer details
  2. Either party calls request to propose and initiate the transfer, providing all transfer details in the call.
  3. The party who called request transmits the TransferId to the other party out-of-band
  4. The other party calls accept with the TransferId
  5. The ledger executes the transfer

The rules are:

  • if any principal who is party to the transfer calls reject then the pending transfer is immediately deleted
  • accept calls get recorded, the transfer is executed as soon as all principals who are party to the transfer have accepted
  • calling request implicitly counts as accepting, so the initiating party does not need to also call accept

Further points:

  • transfers can have more than two parties, the generalization is straight-forward, only requirement is that all token differentials sum up to 0

  • any party who has already accepted, including the requester, can call reject if they changed their mind while the transfer is still pending

  • the principal who calls request does not necessarily have to be a party to the transfer, (however the requester should pay fees, see “Fees” below)

  • the tokens field in the record is optional. If it is omitted this means that the principal has the role of a third-party “committer” as in the “approveTransfer” proposal that came up in the ICRC-1 thread

Fees:

  • it is expected that the implementation stores the details of a pending transfer under the principal who calls request
  • that principal should pay the fees associated with the resource consumption of storing a pending transfer
  • it is up to the implementation to design fee structure and DoS prevention

Extensions:

  • a principal can set a flag called auto-accept for itself. If this flag is set then any transfer with a positive balance differential for the principal is counted as accepted without further interaction. This enables direct deposits as an opt-in feature on a per-principal basis

Payment flows:

  • 2-step push: sender requests, receiver accepts. similar to a check deposit

  • 2-step pull: receiver requests, sender accepts. similar to an e-bill or credit card payment with push notification and in-app approval

  • the previous “approveTransfer” proposal: a third-party, different from the receiver, is listed as a principal without amount, i.e. acts as a “committer”.

  • 1-step push: requires the auto-accept extension, sender deposits directly into the receiver account

  • atomic multi-party payments: A pays C but only if B also pays C

What do you think about this interface?

8 Likes

Question: Why are direct deposits without receiver approval not needed?

In the typical scenario where a customer orders from a merchant we look at the flow where the sender creates the transfer, submits it, it gets executed without receiver approval, and the receiver looks for or is notified of the incoming payment. This flow is undesirable for the merchant because of the multiple edge cases it has. The sender can pay late, to a wrong (sub)account, the wrong amount, use the wrong memo, etc. There is indeed the historical example of bitcoin payments. Exactly these kind of problems led to the development of the BIP70 payment protocol. A merchant wants to fully control the transfer details because edge cases (and the resulting refunds) can be eliminated. In Bitcoin’s case this meant that the sender sends the signed transaction to the receiver and the receiver then submits it to the Bitcoin network after double-checking it.

Question: Aren’t direct deposits without receiver approval common in traditional finance?

Not really. There is almost always an approval, even if given indirectly. For example, in the case of a bank wire the receiver has provided the account details to the sender. This can be seen as an approval to receive funds from the sender. Not an approval on a per-transfer basis but at least it is an approval on a per-sender basis.

Question: Why are unsolicited deposits bad?

There are a number of risks associated with them:

  1. They can create tax issues for the receiver. People have been criticizing Airdrops for this reason.
  2. They can create compliance issues for the receiver if an attacker intentionally sends tainted funds to the receiver.
  3. They can lead to spam attacks on the receiver.

And the problem with allowing unsolicited deposits in the standard is that they must be then be allowed by all tokens. It is not possible for a token to opt-out anymore. However, as the previous post has shown, the other way around opting in is possible: Unsolicited deposits can be disallowed in the standard but still a token can opt-in to them via the auto-accept flag.

3 Likes

The interface above easily extends to multi-coin ledgers with a modification like this:

type Part = record {
    of : principal;
    tokens : opt record { tokenid: nat64; subaccount : Subaccount; amount : int };
    memo : opt blob
};

Now different tokens can be mixed in one transfer, enabling atomic swaps. Some people have already been asking about multi-coin ledgers and atomic swaps. What is nice here is that the flow and interface is the same. It is not required to learn any new concepts for the multi-coin scenario.

2 Likes

Really thoughtful, thanks for writing this up. The only thing I’ve been wondering about is that it seems like I always need a principal to create a Part. Why is there no direct support for acount identifiers if the receiver doesnt want to share her principal?

What is the motivation? Is it a question of privacy? Like as if the account identifier could hide the principal behind it?

1 Like

Yes, this is the motivation.

Account identifiers provide only superficial privacy. Anyone who sees the raw IC blocks of the subnet, i.e. all node providers, boundary nodes or anyone else observing traffic over the internet, can see which principal is spending from a given account identifier when it is used.

Real privacy is much harder to achieve than simply introducing account identifiers.

As a user you can create a new keypair for every transfer you receive. Just like you would with Bitcoin according to Bitcoin best practices. If you do that here then you use a new principal for every transfer.

10 Likes

This is such a simple and elegant interface and yet it solves pretty much every use case :ok_hand: :ok_hand: :ok_hand:

2 Likes

This is news to me, but makes total sense. Thanks for sharing! I somehow always assumed privacy is preserved using Account Identifiers, probably because of how they are generated.

I’m very late to this particular party, but this is so elegant! Big fan of how the interface covers multiple payment models (push/pull/etc,) how it uses a double-entry-like paradigm, and how it supports atomic multi-party transactions.

5 Likes

Has this interface been considered by the working group?