Skip to content

Commit

Permalink
fix(sdk): chunk witness data wrt. bytes instead of str length (#80)
Browse files Browse the repository at this point in the history
* feat: add const value for max witness stack size

* fix: chunk witness data wrt. bytes instead of str length

* refactor: chunk by default in `opPush`

also, rearrange variables and replace `do..while` w/ `while`
  • Loading branch information
iamcrazycoder authored Oct 6, 2023
1 parent 06d8ab6 commit 75a3ce0
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 44 deletions.
3 changes: 3 additions & 0 deletions packages/sdk/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export const MINIMUM_AMOUNT_IN_SATS = 600

// Fee calculated by the fee estimator cannot be greater than 0.05 BTC in any case
export const MAXIMUM_FEE = 5000000

// Maximum number of bytes pushable to the witness stack
export const MAXIMUM_SCRIPT_ELEMENT_SIZE = 520
79 changes: 35 additions & 44 deletions packages/sdk/src/inscription/witness.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,16 @@
import * as ecc from "@bitcoinerlab/secp256k1"
import * as bitcoin from "bitcoinjs-lib"

import { MAXIMUM_SCRIPT_ELEMENT_SIZE } from "../constants"

export function buildWitnessScript({ recover = false, ...options }: WitnessScriptOptions) {
bitcoin.initEccLib(ecc)
if (!options.mediaType || !options.mediaContent || !options.xkey) {
throw new Error("Failed to build witness script")
}

const contentChunks = chunkContent(options.mediaContent)

const metaStackElements: (number | Buffer)[] = []

if (typeof options.meta === "object") {
metaStackElements.push(
...[
bitcoin.opcodes.OP_FALSE,
bitcoin.opcodes.OP_IF,
opPush("ord"),
1,
1,
opPush("application/json;charset=utf-8"),
bitcoin.opcodes.OP_0
]
)
const metaChunks = chunkContent(JSON.stringify(options.meta))

metaChunks &&
metaChunks.forEach((chunk) => {
metaStackElements.push(opPush(chunk))
})
metaChunks && metaStackElements.push(bitcoin.opcodes.OP_ENDIF)
if (recover) {
return bitcoin.script.compile([Buffer.from(options.xkey, "hex"), bitcoin.opcodes.OP_CHECKSIG])
}

const baseStackElements = [
Expand All @@ -44,40 +25,50 @@ export function buildWitnessScript({ recover = false, ...options }: WitnessScrip
bitcoin.opcodes.OP_0
]

const contentStackElements: (number | Buffer)[] = []
const contentStackElements = opPush(options.mediaContent, !options.mediaType.includes("text") ? "base64" : "utf8")

if (contentChunks) {
contentChunks.forEach((chunk) => {
let encoding: BufferEncoding = "utf8"
if (options.mediaType.indexOf("text") < 0) {
encoding = "base64"
}
contentStackElements.push(opPush(chunk, encoding))
})
}
const metaStackElements: (number | Buffer)[] = []
if (typeof options.meta === "object") {
metaStackElements.push(
...[
bitcoin.opcodes.OP_FALSE,
bitcoin.opcodes.OP_IF,
opPush("ord"),
1,
1,
opPush("application/json;charset=utf-8"),
bitcoin.opcodes.OP_0
]
)

if (recover) {
return bitcoin.script.compile([Buffer.from(options.xkey, "hex"), bitcoin.opcodes.OP_CHECKSIG])
metaStackElements.push(opPush(JSON.stringify(options.meta)))
options.meta && metaStackElements.push(bitcoin.opcodes.OP_ENDIF)
}

return bitcoin.script.compile([
...baseStackElements,
...contentStackElements,
contentStackElements,
bitcoin.opcodes.OP_ENDIF,
...metaStackElements
])
}

function opPush(str: string, encoding: BufferEncoding = "utf8") {
const buff = Buffer.from(str, encoding)
const obj = [buff]
const push = Buffer.concat(obj)
return push
function opPush(data: string, encoding: BufferEncoding = "utf8") {
return Buffer.concat(chunkContent(data, encoding))
}

const chunkContent = function (str: string) {
const chunkList = str.match(/.{1,520}/g)
return chunkList
export const chunkContent = function (str: string, encoding: BufferEncoding = "utf8") {
const contentBuffer = Buffer.from(str, encoding)
const chunks: Buffer[] = []
let chunkedBytes = 0

while (chunkedBytes < contentBuffer.byteLength) {
const chunk = contentBuffer.subarray(chunkedBytes, chunkedBytes + MAXIMUM_SCRIPT_ELEMENT_SIZE)
chunkedBytes += chunk.byteLength
chunks.push(chunk)
}

return chunks
}

export type WitnessScriptOptions = {
Expand Down

0 comments on commit 75a3ce0

Please sign in to comment.