Skip to content

Commit

Permalink
Refactor Lamport sigs to use bytestrings instead of arrays for effici…
Browse files Browse the repository at this point in the history
…ency.
  • Loading branch information
msinkec committed Dec 15, 2023
1 parent b620dc5 commit 953ff11
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 19 deletions.
14 changes: 9 additions & 5 deletions src/contracts/lamportSig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import {
lshift,
method,
prop,
slice,
SmartContract,
} from 'scrypt-ts'

// 512 * 256 bit random byte strings
export type LamportPubKey = FixedArray<ByteString, 512>
export type LamportPubKey = ByteString

// For msg of 256 bits.
export type LamportSig = FixedArray<ByteString, 256>
export type LamportSig = ByteString

export class LamportP2PK extends SmartContract {
@prop()
Expand All @@ -34,11 +35,14 @@ export class LamportP2PK extends SmartContract {
for (let i = 0; i < 256; i++) {
let offset = 0n
if (and(lshift(m, BigInt(i)), 1n) == 0n) {
offset = 256n
offset = 256n * 32n
}

const sigChunk = sig[i]
const pkChunk = this.pubKey[Number(offset) + i]
const start = BigInt(i) * 32n
const sigChunk = slice(sig, start, start + 32n)

const pkChunkStart = offset + start
const pkChunk = slice(this.pubKey, pkChunkStart, pkChunkStart + 32n)
assert(hash256(sigChunk) == pkChunk, `sig chunk ${i} hash mismatch`)
}
}
Expand Down
41 changes: 27 additions & 14 deletions tests/lamportSig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
hash256,
lshift,
sha256,
slice,
toByteString,
} from 'scrypt-ts'
import {
Expand All @@ -19,9 +20,10 @@ import {
import { getDefaultSigner } from './utils/helper'
import chaiAsPromised from 'chai-as-promised'
import { and, getPreimage } from 'scryptlib'
import { off } from 'process'
use(chaiAsPromised)

type LamportSecretKey = FixedArray<ByteString, 512>
type LamportSecretKey = ByteString

describe('Heavy: Test SmartContract `LamportSig`', () => {
let sk: LamportSecretKey
Expand All @@ -32,10 +34,12 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {
before(async () => {
await LamportP2PK.loadArtifact()

sk = fill(bsv.PrivateKey.fromRandom().toByteString(), 512)
pk = fill(toByteString(''), 512)
sk = toByteString('')
pk = toByteString('')
for (let i = 0; i < 512; i++) {
pk[i] = hash256(sk[i])
const skChunk = bsv.PrivateKey.fromRandom().toByteString()
sk += skChunk
pk += hash256(skChunk)
}

instance = new LamportP2PK(pk)
Expand All @@ -49,15 +53,15 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {
console.log(`Deployed contract "LamportSig": ${deployTx.id}`)

// Create unsigned TX to get sighHash preimage.
const dummySig: LamportSig = fill(toByteString(''), 256)
const dummySig: LamportSig = toByteString('')
const dummyCallRes = await instance.methods.unlock(dummySig, {
partiallySigned: true,
exec: false,
autoPayFee: false,
} as MethodCallOptions<LamportP2PK>)

// Sign tx.
const sig: LamportSig = fill(toByteString(''), 256)
let sig: LamportSig = toByteString('')
const txSigHashPreimage = getPreimage(
dummyCallRes.tx,
instance.lockingScript,
Expand All @@ -68,9 +72,12 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {
for (let i = 0; i < 256; i++) {
let offset = 0n
if (and(lshift(m, BigInt(i)), 1n) == 0n) {
offset = 256n
offset = 256n * 32n
}
sig[i] = sk[Number(offset) + i]

const start = BigInt(i) * 32n
const skChunkStart = start + offset
sig += slice(sk, skChunkStart, skChunkStart + 32n)
}

// Execute actual contract call.
Expand All @@ -92,15 +99,15 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {
console.log(`Deployed contract "LamportSig": ${deployTx.id}`)

// Create unsigned TX to get sighHash preimage.
const dummySig: LamportSig = fill(toByteString(''), 256)
const dummySig: LamportSig = toByteString('')
const dummyCallRes = await instance.methods.unlock(dummySig, {
partiallySigned: true,
exec: false,
autoPayFee: false,
} as MethodCallOptions<LamportP2PK>)

// Sign tx.
const sig: LamportSig = fill(toByteString(''), 256)
let sig: LamportSig = toByteString('')
const txSigHashPreimage = getPreimage(
dummyCallRes.tx,
instance.lockingScript,
Expand All @@ -111,12 +118,18 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {
for (let i = 0; i < 256; i++) {
let offset = 0n
if (and(lshift(m, BigInt(i)), 1n) == 0n) {
offset = 256n
offset = 256n * 32n
}
sig[i] = sk[Number(offset) + i]

const start = BigInt(i) * 32n
const skChunkStart = start + offset
sig += slice(sk, skChunkStart, skChunkStart + 32n)
}

sig[3] = toByteString(hash256(toByteString('wrong data')))
sig =
slice(sig, 0n, 32n) +
toByteString('00').repeat(32) +
slice(sig, 64n)

// Execute actual contract call.
const call = async () => {
Expand All @@ -127,6 +140,6 @@ describe('Heavy: Test SmartContract `LamportSig`', () => {

console.log(`Called "unlock" method: ${callRes.tx.id}`)
}
await expect(call()).to.be.rejectedWith(/sig chunk 3 hash mismatch/)
await expect(call()).to.be.rejectedWith(/sig chunk 1 hash mismatch/)
})
})

0 comments on commit 953ff11

Please sign in to comment.