-
-
Save mothepro/78da7630fa8004e1fb660101beb5f8ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample outputs
hint = 64
hint = 8
Bitcoin Playground