Type mismatch in Ledger canister and Invoice canister

We are seeing a type mismatch with respect to ledger canister and the current invoice canister codebase (present here). So I believe we cannot directly plug in to the interface in the mainnet as the typings are completely different than what is used in the invoice implementation.

What do you folks at the community suggest about a way going forward?

For example, in the current invoice canister code, (As of 26/05/2022)

public type AccountIdentifier = Blob;

and in the current ledger canister types,
​public type AccountIdentifier = [Nat8];​

[Nat8] and Blob can be used interchangeably as far as I know.

It is written in the candid spec https://github.com/dfinity/candid/blob/master/spec/Candid.md#shorthand-blobs

A shorthand exists for the specific vector blob , which is an arbitrary sequence of bytes:

<constype> ::= ....
  | blob   := vec nat8

Thanks for the answer,
but for me it is giving an error at the compile time.

invoice-canister/src/invoice/Utils.mo:68.40-68.50: type error [M0096], expression of type Blob cannot produce expected type [Nat8]

In Motoko itself you can use Blob.fromArray and Blob.toArray to convert between Blob and [Nat8].

We are working on a transaction flow for an NFT project. We initially planned we will depend on the invoice canister for invoice generation and payment validation.

So now, is our only option is to go through the invoice canister code and update it to support newer typings line by line? Or is there any better way to do this? How would you suggest we go forward?

To be clear, both Motoko [Nat8] and Blob can be exported/imported as Candid vec nat8. Within Motoko itself, the type [Nat8] and Blob are distinct and require explicit conversion of values from one type to the other type.

One reason to sometimes import vec nat8 as Blob is that blobs have a much more compact in-memory representation than [Nat8] (which take 4x space over blobs), but the operations on Blobs are currently much more limited (just iteration, no map, fold, direct indexing etc)

Ok…this is interesting. If I upload a file to my canister and store it as buffer nat8 do I need to account for 8MB of storage used for a 2MB chunk?

Yes, I’d say so, given our current buffer implementation.

Even if we were to update typings in the invoice canister code, according to the repo, it says

This project is currently work in progress and it is not recommended to use it productively at this point.

So would you recommend still going forward with this implementation? or would you suggest any other solution for transactions.

To be honest, I don’t know enough about the invoice canister to recommend it or not. I think there must be a reason for that warning but don’t know what it is.
Perhaps someone else can chime in (@kpeacock perhaps).

If you can get away with it, I would just manually convert between the Blob and [Nat8] where required. If you can’t do this…

Regarding fixing the interface, one thing you could try is import the canister, extract the principal, get its textual form, and cast the textual form to the equivalent interface with the desired Motoko types.

Something along the lines of:

[Language quick reference | Internet Computer Home](https://actor references)

Doc

actor references

Example here:

(If I recall correctly, you can perform the cast on a computed text value, not just a literal as in the example above, provided you wrap the expression computing the text of the principal in parens, e.g.

   actor ( Principal.toText(Principal.ofActor(Ledger)) ) : AltLedger

But that’s just a suggestion, I haven’t tried it and the actor interface (AltLedger) you need to specify in the cast might be quite large (but could be small if you only need a subset of it’s full functionality). All terribly unsafe, too.

Forking the invoice canister and rewriting it to match the Ledger interface might be a better option, tbh.

Thanks for the reply.
It seems there are many red flags in this approach. Would you suggest any other RECOMMENDED approach for ICP transactions in Dfinity apart from the Invoice Canister Method?

Sorry for the delay, I’ve been on vacation. Here’s the latest on the invoice canister.

  1. Dfinity has decided that the invoice canister will be turned into an example of how to handle transactions in a canister, and will be moved to the examples repo. There is no official recommended approach today.
  2. In the process of moving this to the examples repo, I will fix the type mismatch and get the project ready to self-deploy
  3. The financial integrations team has indicated that their preferred approach will be to provide Motoko and Rust libraries for moving tokens, rather than a canister-based approach
1 Like

Hi @kpeacock,
Thanks for your response. Do you have any estimates on the timing of those three points?

Best,
Brian

I should be able to complete steps 1 and 2 within the month, while there is no current timeline for #3.

1 Like

Hi @kpeacock ,
I was going through invoice canister example readme ( nicely written ). And I found this line
" * There is some degree of risk in allowing the get_invoice, get_account_identifier, and get_balance queries to be left as queries"

Can you throw some more light on why “query” keyword is the reason for some degree of risk and what are those risk ?

Queries are served by a single replica and don’t go through consensus, so while they are faster and cheaper (currently free) they are less secure cause a node could modify the data.

1 Like

Zane is correct. The Dfinity security team doesn’t think anyone should use queries as they are currently implemented.

Can you elaborate on this?

Is the idea that within a single round of consensus a query can temporarily modify state on that single replica (not globally replicated) which could affect another call that is batch processed on that same replica within the same consensus round?

I think they’re talking about a rogue node operator being able (in theory) to modify a canister’s response when queried. Hence the “lack of security”.

2 Likes