lukeprofits / Monero_Payment_Request_Standard

The Monero Payment Request Protocol
MIT License
13 stars 4 forks source link

Improving portability #3

Open silverpill opened 1 year ago

silverpill commented 1 year ago

I implemented subscription code generation in javascript, and here are my observations:

  1. json.dumps by default produces "pretty" json:
{"custom_label": "My Subscription", "sellers_wallet": "4At3X5rvVypTofgmueN9s9QtrzdRe5BueFrskAZi17BoYbhzysozzoMFB6zWnTKdGC6AxEAbEE5czFR3hbEEJbsm4hCeX2S", "currency": "USD", "amount": 19.99, "payment_id": "9fc88080d1d5dc09", "start_date": "2023-04-26", "billing_cycle_days": 30}

I think it would be better to use compact representation (without whitespaces): json.dumps(json_data, separators=(',', ':')). This should reduce the size of the string, and might help implementers who use other programming languages.

  1. Both JavaScript and Python keep the order of keys unchanged during serialization. However, other languages may have different defaults, so it might be a good idea to sort the keys.

  2. gzip.compress is not deterministic. The output seems to be dependent on the current time, but this could be mitigated by setting mtime parameter to 0: gzip.compress(json_str.encode('utf-8'), mtime=0) After making this adjustment I successfully generated the same monero-subscription code with JavaScript.

  3. gzip compression is tricky to implement. There's a number of parameters and I found the right ones only by trial and error. I think we can keep it as is for now, but switch to a better algorithm in a future version.

lukeprofits commented 12 months ago

Good ideas. Thanks!

silverpill commented 11 months ago

Here's my JavaScript function for generating payment requests (version 1):

import pako from "pako"
import { fromByteArray } from "base64-js"

function createMoneroPaymentRequestV1(data: PaymentData): string {
  const dataJson = JSON.stringify(data, Object.keys(data).sort())
  const encoder = new TextEncoder()
  const dataBytes = encoder.encode(dataJson)
  const dataCompressed = pako.gzip(dataBytes, { level: 9, windowBits: 31 })
  const dataEncoded = fromByteArray(dataCompressed)
  return `monero-request:1:${dataEncoded}`
}

It produces exactly the same code as the Python function in the example: https://github.com/lukeprofits/Monero_Payment_Request_Standard#example-python-function-to-create-a-monero-payment-request

One JS snippet in "Encoding A Monero Payment Request" section is not correct, I submitted a PR with a fix: #6