Skip to content

Define canonical JSON encoding for unsigned 64-bit integers#343

Open
robwoodgate wants to merge 1 commit intocashubtc:mainfrom
robwoodgate:json-int
Open

Define canonical JSON encoding for unsigned 64-bit integers#343
robwoodgate wants to merge 1 commit intocashubtc:mainfrom
robwoodgate:json-int

Conversation

@robwoodgate
Copy link
Contributor

@robwoodgate robwoodgate commented Feb 18, 2026

The NUTs define amounts as unsigned 64-bit integers, but the JSON wire encoding is ambiguous.

JSON has no integer type, and many clients parse JSON numbers via float-like defaults. This can cause cross-language divergence and precision loss for large values (e.g, in environments where JSON numbers use IEEE-754).

This PR defines a canonical UInt64Json format, which is backwards compatible and precision safe across runtimes.

Simply:

UInt64Json MAY be represented as a JSON integer number or a base-10 decimal string.

Implementations MUST accept both forms.

Values greater than 9007199254740991 (2^53-1) MUST be represented as decimal strings.

This applies to int / u64 fields across the protocol, including amounts in Proofs, Blinded Messages, and request/response payloads.

Fee fields are unchanged to keep the change minimal, but we could include all int/u64 JSON fields for consistency.

@prusnak
Copy link
Collaborator

prusnak commented Feb 18, 2026

When exactly are the amounts bigger than (2^53-1)?

>>> 21e6 * 1e8 < 2**53
True

@robwoodgate
Copy link
Contributor Author

When exactly are the amounts bigger than (2^53-1)?

>>> 21e6 * 1e8 < 2**53
True

Just look at Minibits key amounts: https://mint.minibits.cash/Bitcoin/v1/keys

Keys 9007199254740992 up to 9223372036854775808 are all over the safe limit (9007199254740991)

Amounts in millisats (eg melt quotes, amounts in unit msat) can go over the limit.

Even if not used 99.99% of the time, the wire support must be solid.

@prusnak
Copy link
Collaborator

prusnak commented Feb 18, 2026

Amounts in millisats (eg melt quotes, amounts in unit msat) can go over the limit.

Yeah, I agree that if amounts are listed in msats, then we need this.


`UInt64Json` **MAY** be represented as a JSON integer number or a base-10 decimal string.

Implementations **MUST** accept both forms.
Copy link
Contributor Author

@robwoodgate robwoodgate Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to encourage a single standard (decimal string) over time, we could tweak:

Suggested change
Implementations **MUST** accept both forms.
Implementations **MUST** accept both forms, and **SHOULD** encode values as decimal strings.

We already consistently use string amounts in the /v1/keys api, so this would be a continuation of that precedent.

@callebtc
Copy link
Contributor

I think this is too excessive. NUT-00 could state that all amounts are uint64 and we could leave the rest as is.

@SatsAndSports
Copy link

NUT-00 could state that all amounts are uint64

When you say 'uint64', do you mean non-quoted integers?

As mentioned by others, key amounts are quoted currently , but fees are non-quoted. Both of those, I think, make sense as fees can't realistically get very big

$ curl https://mint.minibits.cash/Bitcoin/v1/keys | jq .                                                                                             
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                                                                              
                                 Dload  Upload   Total   Spent    Left  Speed                                                                                                
100  5341  100  5341    0     0   1045      0  0:00:05  0:00:05 --:--:--  1100                                                                                               
{                                                                                                                                                                            
  "keysets": [                                                                                                                                                               
    {                                                                                                                                                                        
      "id": "00107937db0cc865",                                                                                                                                              
      "unit": "sat",                                                                                                                                                         
      "active": true,                                                                                                                                                        
      "input_fee_ppk": 0,                                                                                                                                                    
      "keys": {                                                                                                                                                              
        "1": "03be63a0f422f8db6297fb3018bc3e626751010434c19c9c8c990e1c4e438f03dc",  
...
        "9223372036854775808": "02d900df693b1505cc5cbdef13a7ee793b89ebfedb628f4dc5fbbe49a7ef8cd07a"

@SatsAndSports
Copy link

SatsAndSports commented Feb 26, 2026

Ignore this comment. I oversimplified. See Rob's response just below

(Maybe I've missed some detail/context, but I think this will break stuff)

The CDK code currently requires the amounts (in the amount-publickey mapping in a keyset) to be quoted, i.e. "8" not 8.

In this screenshot, 'key' means the 'amount' and 'value' means the mint's public key for that amount.

So basically, I don't think we can change anything about existing fields

=======

An AI helped me find this example. (Apologies if I'm wrong, and have been misled by AI)

image image

@robwoodgate
Copy link
Contributor Author

robwoodgate commented Feb 26, 2026

(Maybe I've missed some detail/context, but I think this will break stuff)

The CDK code currently requires the amounts (in the amount-publickey mapping in a keyset) to be quoted, i.e. "8" not 8.

In this screenshot, 'key' means the 'amount' and 'value' means the mint's public key for that amount.

So basically, I don't think we can change anything about existing fields

=======

An AI helped me find this example. (Apologies if I'm wrong, and have been misled by AI)

Key amounts ARE all strings, and that is perfect. Not suggesting a change there at all.

In a perfect world, ALL u64 amounts would be represented as strings in JSON.

The issue is "amounts" (sums of key amounts), which currently transport as numeric digits, and which CAN be > 2^53-1 because a) some keys are already larger than that, and b) combinations of proofs could go over the limit in total.

Especially with millisat (or in future, nanosat) amounts.

Fee amounts can stay as numeric digits, as it would be crazy if fees went over the limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

4 participants