mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-indexer.git
synced 2026-09-21 16:52:02 -07:00
Fixing issue with indexing
This commit is contained in:
@@ -81,6 +81,29 @@ All routes are under `/level` with a consistent CRUD pattern generated from `ENT
|
|||||||
| `processerror` | `txid` | `errorData` | txid |
|
| `processerror` | `txid` | `errorData` | txid |
|
||||||
| `ptx` | `txid` | `ptxData` | txid |
|
| `ptx` | `txid` | `ptxData` | txid |
|
||||||
|
|
||||||
|
### Read routes (query use cases)
|
||||||
|
|
||||||
|
| Method | Path | Query params |
|
||||||
|
|--------|------|----------------|
|
||||||
|
| `GET` | `/profile/recent` | `limit` (default 100, max 100), `offset` (default 0) |
|
||||||
|
|
||||||
|
Returns profiles sorted by **block height** (newest first), using each profile’s `txid` to look up `blockHeight` in `ptxs`. Tie-breaker: `seen` timestamp descending.
|
||||||
|
|
||||||
|
Response shape:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"profiles": [
|
||||||
|
{ "addr": "bitcoincash:q...", "text": "...", "txid": "...", "seen": 123, "blockHeight": 600000 }
|
||||||
|
],
|
||||||
|
"pagination": { "limit": 100, "offset": 0, "total": 42, "hasMore": false }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementation: `profile-query` adapter (LevelDB scan) → `list-recent-profiles` use case → `/profile` REST controller.
|
||||||
|
|
||||||
|
**Tradeoff:** Full scan of `profiles` on each request; suitable for moderate corpus sizes. A height-indexed store would be needed for very large archives.
|
||||||
|
|
||||||
### Status (special case)
|
### Status (special case)
|
||||||
|
|
||||||
Matches SLP status semantics:
|
Matches SLP status semantics:
|
||||||
|
|||||||
+50
-8
@@ -111,19 +111,61 @@ export function findMemoOutputs (txDetails) {
|
|||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract compressed or uncompressed pubkey from a P2PKH unlocking script.
|
||||||
|
* Mirrors Go wallet.GetP2pkhAddrFromUnlockScript (pubkey push after signature).
|
||||||
|
*/
|
||||||
|
export function getPubkeyFromUnlockScript (scriptBuf) {
|
||||||
|
if (!scriptBuf || !scriptBuf.length) return null
|
||||||
|
|
||||||
|
let decoded
|
||||||
|
try {
|
||||||
|
decoded = getBchjs().Script.decode(scriptBuf)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decoded || !decoded.length) return null
|
||||||
|
|
||||||
|
for (let i = decoded.length - 1; i >= 0; i--) {
|
||||||
|
const chunk = decoded[i]
|
||||||
|
if (Buffer.isBuffer(chunk) && (chunk.length === 33 || chunk.length === 65)) {
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive cash address from unlocking script hex (P2PKH spend).
|
||||||
|
*/
|
||||||
|
export function getAddressFromUnlockScriptHex (scriptHex) {
|
||||||
|
if (!scriptHex || typeof scriptHex !== 'string') return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const scriptBuf = Buffer.from(scriptHex, 'hex')
|
||||||
|
const pubkey = getPubkeyFromUnlockScript(scriptBuf)
|
||||||
|
if (!pubkey) return null
|
||||||
|
|
||||||
|
const hash160 = getBchjs().Crypto.hash160(pubkey)
|
||||||
|
return getBchjs().Address.hash160ToCash(hash160)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getSignerAddress (txDetails) {
|
export function getSignerAddress (txDetails) {
|
||||||
if (!txDetails || !txDetails.vin) return null
|
if (!txDetails || !txDetails.vin) return null
|
||||||
|
|
||||||
for (const vin of txDetails.vin) {
|
for (const vin of txDetails.vin) {
|
||||||
if (!vin.scriptSig || !vin.scriptSig.hex) continue
|
const scriptHex = vin.scriptSig && vin.scriptSig.hex
|
||||||
try {
|
if (!scriptHex) continue
|
||||||
const scriptBuf = Buffer.from(vin.scriptSig.hex, 'hex')
|
|
||||||
const addr = getBchjs().Script.getAddressFromScriptSig(scriptBuf)
|
const addr = getAddressFromUnlockScriptHex(scriptHex)
|
||||||
if (addr) return addr
|
if (addr) return addr
|
||||||
} catch (err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { assert } from 'chai'
|
|||||||
import {
|
import {
|
||||||
decodeMemoOpReturn,
|
decodeMemoOpReturn,
|
||||||
parseScriptPushDatas,
|
parseScriptPushDatas,
|
||||||
isMemoScript
|
isMemoScript,
|
||||||
|
getSignerAddress,
|
||||||
|
getAddressFromUnlockScriptHex
|
||||||
} from '../../../src/lib/memo-parser.js'
|
} from '../../../src/lib/memo-parser.js'
|
||||||
import { CODE_POST, CODE_PREFIX } from '../../../src/lib/memo-codes.js'
|
import { CODE_POST, CODE_PREFIX } from '../../../src/lib/memo-codes.js'
|
||||||
|
|
||||||
@@ -38,4 +40,49 @@ describe('#memo-parser', () => {
|
|||||||
assert.equal(isMemoScript(POST_OP_RETURN_HEX), true)
|
assert.equal(isMemoScript(POST_OP_RETURN_HEX), true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('#getAddressFromUnlockScriptHex', () => {
|
||||||
|
const SCRIPT_SIG_1 =
|
||||||
|
'483045022100e15e18048b78e744fe8e440eb2687b5d4eb7009140d5e71950d4351c6e3bf2ef022031b0dab7326846a58e16f6a753e0d011514f827d2adacde63912daa3f85636c5412103833927b98fab40d5f857214d802ba42ea21087105b81e9bff57bf2486c9ff171'
|
||||||
|
const SCRIPT_SIG_2 =
|
||||||
|
'483045022100aa35ee315859b63f92e7878304364635943b28b2334dfd796a51cb2e0b284fe3022033e852b7c965f36c7d6eecc577b01218d611450c80e75af1fd41e76cfc7af81c4121035c5970c98aa4543271348bb18de4fb04122af87555fc4411c376493cc85594c3'
|
||||||
|
|
||||||
|
it('should derive cash address from real memo tx scriptSig', () => {
|
||||||
|
const addr = getAddressFromUnlockScriptHex(SCRIPT_SIG_1)
|
||||||
|
assert.isString(addr)
|
||||||
|
assert.include(addr, 'bitcoincash:')
|
||||||
|
assert.equal(
|
||||||
|
addr,
|
||||||
|
'bitcoincash:qzd7s66p0mh97s2tqkrwacx4dazwataul5m9f9yw68'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should derive cash address from second sample scriptSig', () => {
|
||||||
|
const addr = getAddressFromUnlockScriptHex(SCRIPT_SIG_2)
|
||||||
|
assert.equal(
|
||||||
|
addr,
|
||||||
|
'bitcoincash:qrnr8q2ndfmzgfehz5txel9jpq3lrwhvt5f270fnd4'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#getSignerAddress', () => {
|
||||||
|
const SCRIPT_SIG_1 =
|
||||||
|
'483045022100e15e18048b78e744fe8e440eb2687b5d4eb7009140d5e71950d4351c6e3bf2ef022031b0dab7326846a58e16f6a753e0d011514f827d2adacde63912daa3f85636c5412103833927b98fab40d5f857214d802ba42ea21087105b81e9bff57bf2486c9ff171'
|
||||||
|
|
||||||
|
it('should return signer from txDetails with vin scriptSig', () => {
|
||||||
|
const txDetails = {
|
||||||
|
vin: [{ scriptSig: { hex: SCRIPT_SIG_1 } }]
|
||||||
|
}
|
||||||
|
const addr = getSignerAddress(txDetails)
|
||||||
|
assert.equal(
|
||||||
|
addr,
|
||||||
|
'bitcoincash:qzd7s66p0mh97s2tqkrwacx4dazwataul5m9f9yw68'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null when no vin', () => {
|
||||||
|
assert.equal(getSignerAddress({}), null)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user