mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Reply, topic message, add-poll-option, poll-vote, and create-poll now broadcast each protocol field as its own OP_RETURN script push instead of combining them into one push. The indexer requires three pushes for reply, topic message, and poll children, and accepts four for create-poll; the combined form was rejected or misparsed. hex.js builds the txid and text as separate pushes, and a new memo-multipush adapter teaches minimal-slp-wallet's single-push sendOpReturn to expand an array of fields into separate pushes. Acceptance step handlers assert the individual pushes, and unit/property tests cover the new shape. By coder.
124 lines
3.7 KiB
JavaScript
124 lines
3.7 KiB
JavaScript
/*
|
|
Unit tests for the multi-push OP_RETURN adapter.
|
|
|
|
Memo actions that carry more than one field must encode each field as its
|
|
own OP_RETURN script push. minimal-slp-wallet broadcasts a single msg push,
|
|
so the adapter wraps the wallet's sendOpReturn: an array first argument is
|
|
expanded to separate pushes, while single-field calls keep delegating to the
|
|
original wallet method.
|
|
*/
|
|
|
|
'use strict'
|
|
|
|
const test = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const {
|
|
toPushBuffer,
|
|
buildPushes,
|
|
attachMultiPushOpReturn
|
|
} = require('../../src/services/memo-multipush')
|
|
|
|
// Encode a script the way Bitcoin does: an opcode number is one byte, a
|
|
// Buffer/string becomes a length-prefixed push. Enough to observe the pushes.
|
|
function encodeScript (script) {
|
|
const parts = script.map((el) => {
|
|
if (typeof el === 'number') return Buffer.from([el])
|
|
const buf = Buffer.from(el)
|
|
return Buffer.concat([Buffer.from([buf.length]), buf])
|
|
})
|
|
return Buffer.concat(parts)
|
|
}
|
|
|
|
// A double of the minimal-slp-wallet surface the adapter reuses.
|
|
function makeSlpWalletDouble () {
|
|
const sent = []
|
|
const wallet = {
|
|
walletInfo: { cashAddress: 'bitcoincash:qtest' },
|
|
fee: 1,
|
|
walletInfoPromise: Promise.resolve(),
|
|
utxos: { utxoStore: { bchUtxos: [{ tx_hash: 'aa', tx_pos: 0, value: 100000 }] } },
|
|
calls: [],
|
|
async sendOpReturn (msg, prefix, bchOutput = []) {
|
|
this.calls.push({ msg, prefix, bchOutput })
|
|
return 'single-txid'
|
|
},
|
|
opReturn: {
|
|
bchjs: {
|
|
Script: {
|
|
opcodes: { OP_RETURN: 0x6a },
|
|
encode2: encodeScript
|
|
}
|
|
},
|
|
async createTransaction (walletInfo, bchUtxos, msg, prefix) {
|
|
this.lastScript = [
|
|
this.bchjs.Script.opcodes.OP_RETURN,
|
|
Buffer.from(prefix, 'hex'),
|
|
Buffer.from(msg)
|
|
]
|
|
this.lastEncoded = this.bchjs.Script.encode2(this.lastScript)
|
|
return { hex: 'beef' }
|
|
},
|
|
ar: {
|
|
async sendTx (hex) {
|
|
sent.push(hex)
|
|
return 'txid-1'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return { wallet, sent }
|
|
}
|
|
|
|
test('toPushBuffer encodes a string as UTF-8 and passes bytes through', () => {
|
|
assert.equal(toPushBuffer('hi').toString('hex'), '6869')
|
|
assert.equal(toPushBuffer(Uint8Array.from([1, 2])).toString('hex'), '0102')
|
|
})
|
|
|
|
test('buildPushes puts the prefix first and each field in order', () => {
|
|
const pushes = buildPushes('6d03', [Buffer.from('aa', 'hex'), 'hi'])
|
|
|
|
assert.equal(pushes.length, 3)
|
|
assert.equal(pushes[0].toString('hex'), '6d03')
|
|
assert.equal(pushes[1].toString('hex'), 'aa')
|
|
assert.equal(pushes[2].toString('utf8'), 'hi')
|
|
})
|
|
|
|
test('array fields broadcast as separate OP_RETURN pushes', async () => {
|
|
const { wallet, sent } = makeSlpWalletDouble()
|
|
attachMultiPushOpReturn(wallet)
|
|
|
|
const txid = await wallet.sendOpReturn(
|
|
[Buffer.from('aabb', 'hex'), Buffer.from('hello')],
|
|
'6d03'
|
|
)
|
|
|
|
assert.equal(txid, 'txid-1')
|
|
assert.deepEqual(sent, ['beef'])
|
|
// OP_RETURN, push(6d03), push(aabb), push(hello) — each its own push.
|
|
assert.equal(
|
|
wallet.opReturn.lastEncoded.toString('hex'),
|
|
'6a026d0302aabb0568656c6c6f'
|
|
)
|
|
})
|
|
|
|
test('a single field delegates to the original wallet sendOpReturn', async () => {
|
|
const { wallet } = makeSlpWalletDouble()
|
|
attachMultiPushOpReturn(wallet)
|
|
|
|
const txid = await wallet.sendOpReturn('single', '6d02', [])
|
|
|
|
assert.equal(txid, 'single-txid')
|
|
assert.equal(wallet.calls.length, 1)
|
|
assert.equal(wallet.calls[0].msg, 'single')
|
|
})
|
|
|
|
test('attaching twice does not double-wrap the wallet', async () => {
|
|
const { wallet } = makeSlpWalletDouble()
|
|
attachMultiPushOpReturn(wallet)
|
|
attachMultiPushOpReturn(wallet)
|
|
|
|
await wallet.sendOpReturn('single', '6d02')
|
|
|
|
assert.equal(wallet.calls.length, 1)
|
|
})
|