Skip to content

Instantly share code, notes, and snippets.

@mothepro
Last active April 6, 2023 02:16
Show Gist options
  • Save mothepro/78da7630fa8004e1fb660101beb5f8ce to your computer and use it in GitHub Desktop.
Save mothepro/78da7630fa8004e1fb660101beb5f8ce to your computer and use it in GitHub Desktop.
const secret = 0x801d06ddc9ed804bc3af6e90d493ba5b776383a468ee241a43257fa9a9ff13c09ab0a96d82d3072c113e3920e080e5deaa4c13fa84825281e5b9653461014931bc35aa9a29d1a624cf773851e81f4b0dd723aec85d96bb1dfee651478af664d930b0b3d53836d95dbc880815fff1b02143de18e18e7d0fcdab8240e38dee128b276n
const hint = 64
/** The next byte contains the number of bytes to be pushed onto the stack. */
const OP_PUSHDATA1 = 0x4c
/** The next 2 bytes contains the number of bytes to be pushed onto the stack. */
const OP_PUSHDATA2 = 0x4d
/** The next 4 bytes contains the number of bytes to be pushed onto the stack. */
const OP_PUSHDATA4 = 0x4e
/** The top two items on the stack are swapped. */
const OP_SWAP = 0x7c
/** Boolean and between each bit in the inputs. */
const OP_AND = 0x84
/** Boolean OR between each bit in the inputs. */
const OP_OR = 0x85
/** The input is hashed two times with SHA-256. */
const OP_HASH256 = 0xaa
/** Returns 1 if the inputs are exactly equal, 0 otherwise; runs OP_VERIFY afterward. */
const OP_EQUALVERIFY = 0x88
// Generate the OP Codes and print the ASM in hex
const opCodes = await buildOpCodes(secret, hint)
console.log(opCodesToHex(opCodes))
////////////////////
// Helper Methods //
////////////////////
/** Creates OP Codes to obfuscate the secret using the given hint. */
async function buildOpCodes(secret: bigint, hint: number) {
const secretBytes = toBytes(secret)
if (hint > secretBytes.byteLength * 8)
throw new Error('Secret is too short to be hidden sufficiently.')
// hint = 8
const hidden = 2n ** BigInt(hint) - 1n // 0x000000ff
let secretMask = 2n ** BigInt(secretBytes.byteLength * 8) - 1n // 0xffffffff
secretMask -= hidden // 0xffffff00
const visible = secret & secretMask // 0x12312300
const hiddenMask = toBytes(hidden, secretBytes.byteLength)
const visibleMask = toBytes(visible, secretBytes.byteLength)
const solutionHash = await hash256(secretBytes)
// sha256(sha256((<input> & `hidden`) | `visible`)) == `solution`
return [
solutionHash,
OP_SWAP,
hiddenMask,
OP_AND,
visibleMask,
OP_OR,
OP_HASH256,
OP_EQUALVERIFY,
]
}
/** Mini BTC script compiler. */
function opCodesToHex(opCodes: Array<number | ArrayBuffer>) {
let ret = []
for (const data of opCodes) {
if (typeof data === 'number') ret.push(data)
else if (data instanceof ArrayBuffer) {
if (data.byteLength < 0x4c) { } // push buffer directly on the stack
else if (data.byteLength < 0xff) ret.push(OP_PUSHDATA1)
else if (data.byteLength < 0xff_ff) ret.push(OP_PUSHDATA2)
else if (data.byteLength < 0xff_ff_ff_ff) ret.push(OP_PUSHDATA4)
else throw new Error('Too many bytes to add to stack')
ret.push(data.byteLength, ...new Uint8Array(data))
}
}
return ret.map(compactHex).join('')
}
/** Create little-endian (same as bitcoin) buffer from bigint. */
function toBytes(s: bigint, size = Math.ceil(s.toString(16).length / 2)) {
const result = new Uint8Array(size)
let i = size
while (s) {
result[--i] = Number(s & 0xffn)
s >>= 8n
}
return result.buffer
}
/** JS version of `OP_HASH256`, same operation used in BTC miners. */
async function hash256(data: ArrayBuffer) {
const round1 = await crypto.subtle.digest('SHA-256', data)
return await crypto.subtle.digest('SHA-256', round1)
}
function compactHex(num: number) {
const hex = num.toString(16)
return hex.padStart(hex.length + (hex.length % 2), '0')
}
@mothepro
Copy link
Author

mothepro commented Feb 28, 2023

For this example:

  • secret is the first value from the 2-2 key split with key = 'password'. (see the SSS playground).

Then use the output as the import for bytecode script in the bitcoin IDE.

@mothepro
Copy link
Author

mothepro commented Feb 28, 2023

Sample outputs

hint = 64

207698649eb600fe7fd3ccab9f3e6dbaec56ff6031903659dac0c1bb7af25202877c4c820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff844c820801d06ddc9ed804bc3af6e90d493ba5b776383a468ee241a43257fa9a9ff13c09ab0a96d82d3072c113e3920e080e5deaa4c13fa84825281e5b9653461014931bc35aa9a29d1a624cf773851e81f4b0dd723aec85d96bb1dfee651478af664d930b0b3d53836d95dbc880815fff1b02143de18e18e7d0fcdab8000000000000000085aa88

hint = 8

207698649eb600fe7fd3ccab9f3e6dbaec56ff6031903659dac0c1bb7af25202877c4c82000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff844c820801d06ddc9ed804bc3af6e90d493ba5b776383a468ee241a43257fa9a9ff13c09ab0a96d82d3072c113e3920e080e5deaa4c13fa84825281e5b9653461014931bc35aa9a29d1a624cf773851e81f4b0dd723aec85d96bb1dfee651478af664d930b0b3d53836d95dbc880815fff1b02143de18e18e7d0fcdab8240e38dee128b20085aa88

Bitcoin Playground

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