Skip to content

Commit

Permalink
Refactor staging
Browse files Browse the repository at this point in the history
  • Loading branch information
gitzhou committed Jan 16, 2025
1 parent ce1ae3c commit 95655d1
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 131 deletions.
9 changes: 9 additions & 0 deletions l1/src/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const INPUT_MAX_COUNT = 6;
export const OUTPUT_MAX_COUNT = 6;

export const INPUT_COUNT_BYTE_LEN = 4n;
export const OUTPUT_COUNT_BYTE_LEN = 4n;

export const TX_HASH_BYTE_LEN = 32n;
export const OUTPUT_INDEX_BYTE_LEN = 4n;
export const PREVOUT_BYTE_LEN = TX_HASH_BYTE_LEN + OUTPUT_INDEX_BYTE_LEN;
37 changes: 17 additions & 20 deletions l1/src/contracts/aggregatorUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, ByteString, hash256, len, method, sha256, Sha256, SmartContractLib, toByteString } from 'scrypt-ts';
import { AggregatorTx, DepositData } from './types';
import { AggregatorTx, DepositData } from '../types';
import { GeneralUtils } from './generalUtils';

export class AggregatorUtils extends SmartContractLib {
Expand All @@ -9,33 +9,33 @@ export class AggregatorUtils extends SmartContractLib {
const contractInputLen: bigint = isLeaf ? 0n : 37n;

assert(len(tx.version) == 4n);
assert(len(tx.contractInput0) == contractInputLen);
assert(len(tx.contractInput1) == contractInputLen);
assert(len(tx.aggregatorInput0) == contractInputLen);
assert(len(tx.aggregatorInput1) == contractInputLen);
assert(len(tx.feeInput) >= 37n);
assert(tx.contractOutputAmount > 0n);
assert(len(tx.contractOutputLocking) == 35n);
assert(tx.aggregatorOutputAmount > 0n);
assert(len(tx.aggregatorOutputLocking) == 35n);
assert(len(tx.dataHash) == 32n);
assert(len(tx.locktime) == 4n);

const inputs = isLeaf
? toByteString('01') + tx.feeInput
: toByteString('03') + tx.contractInput0 + tx.contractInput1 + tx.feeInput;
: toByteString('03') + tx.aggregatorInput0 + tx.aggregatorInput1 + tx.feeInput;

return hash256(
tx.version +
inputs +
toByteString('02') +
GeneralUtils.buildContractOutput(tx.aggregatorOutputAmount, tx.aggregatorOutputLocking) +
GeneralUtils.buildStateOutput(tx.dataHash) +
GeneralUtils.buildContractOutput(tx.contractOutputAmount, tx.contractOutputLocking) +
tx.locktime,
);
}

@method()
static buildPrevout(prevTx: AggregatorTx, isLeaf: boolean): ByteString {
const prevTxId = this.getTxId(prevTx, isLeaf);
// prevTxId + outputIndex (01000000)
return prevTxId + toByteString('01000000');
// prevTxId + outputIndex (00000000)
return prevTxId + toByteString('00000000');
}

@method()
Expand All @@ -54,9 +54,8 @@ export class AggregatorUtils extends SmartContractLib {
isPrevTxLeaf: boolean,
depositData: DepositData,

ancestorTx0: AggregatorTx,
ancestorTx1: AggregatorTx,
isAncestorLeaf: boolean,
firstAncestorTx: AggregatorTx,
isFirstAncestorLeaf: boolean,
) {
// check prevouts if the passed prevTx are unlocked by the currently executing tx
const prevouts = prevoutsPrefix + this.buildPrevout(prevTx, isPrevTxLeaf) + prevoutsSuffix;
Expand All @@ -65,20 +64,18 @@ export class AggregatorUtils extends SmartContractLib {
if (isPrevTxLeaf) {
// if prevTx is a leaf
// check that the data hash in its state output corresponds to the data passed in as witness
assert(prevTx.contractOutputAmount == depositData.amount);
assert(prevTx.aggregatorOutputAmount == depositData.amount);
assert(prevTx.dataHash == this.hashDepositData(depositData));
} else {
// if we're higher up the aggregation tree, we need to check the ancestor
// transactions to inductively validate the whole tree
// check prevTx unlocks ancestorTx0 and ancestorTx1
assert(prevTx.contractInput0 == this.buildInput(ancestorTx0, isAncestorLeaf));
assert(prevTx.contractInput1 == this.buildInput(ancestorTx1, isAncestorLeaf));
// check the first input of prevTx unlocks firstAncestorTx
assert(prevTx.aggregatorInput0 == this.buildInput(firstAncestorTx, isFirstAncestorLeaf));

// check ancestors have the same contract locking as prevTx
// check the ancestor has the same contract locking as prevTx
// this completes the inductive step, since the successful evaluation
// of the ancestors' contract locking also checked its ancestors
assert(prevTx.contractOutputLocking == ancestorTx0.contractOutputLocking);
assert(prevTx.contractOutputLocking == ancestorTx1.contractOutputLocking);
// of the ancestor's contract locking also checked its ancestor
assert(prevTx.aggregatorOutputLocking == firstAncestorTx.aggregatorOutputLocking);
}
}

Expand Down
43 changes: 43 additions & 0 deletions l1/src/contracts/bridgeUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
assert,
ByteString,
hash256,
int2ByteString,
len,
method,
Sha256,
SmartContractLib,
toByteString,
} from 'scrypt-ts';
import { BridgeTx } from '../types';

export class BridgeUtils extends SmartContractLib {
@method()
static getTxId(tx: BridgeTx): Sha256 {
assert(len(tx.version) == 4n);
assert(len(tx.bridgeInput) == 0n || len(tx.bridgeInput) == 37n);
assert(len(tx.aggregatorInput) == 0n || len(tx.aggregatorInput) == 37n);
assert(len(tx.feeInput) >= 37n);
assert(tx.bridgeOutputAmount > 0n);
assert(len(tx.bridgeOutputLocking) == 35n);
assert(len(tx.dataHash) == 32n);
assert(len(tx.locktime) == 4n);

let inputCount = 1n;
if (len(tx.bridgeInput) > 0n) {
inputCount += 1n;
}
if (len(tx.aggregatorInput) > 0n) {
inputCount += 1n;
}

return hash256(tx.version + int2ByteString(inputCount));
}

@method()
static buildPrevout(prevTx: BridgeTx): ByteString {
const prevTxId = this.getTxId(prevTx);
// prevTxId + outputIndex (00000000)
return prevTxId + toByteString('00000000');
}
}
73 changes: 58 additions & 15 deletions l1/src/contracts/depositAggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {
SmartContract,
toByteString,
} from 'scrypt-ts';
import { AggregatorTx, DepositData, SHPreimage } from './types';
import { AggregatorTx, BridgeTx, DepositData, SHPreimage } from '../types';
import { AggregatorUtils } from './aggregatorUtils';
import { SigHashUtils } from './sigHashUtils';
import { SigHashUtils } from '../utils/sigHashUtils';
import { GeneralUtils } from './generalUtils';
import { BridgeUtils } from './bridgeUtils';

export class DepositAggregator extends SmartContract {
@prop()
Expand Down Expand Up @@ -44,9 +45,8 @@ export class DepositAggregator extends SmartContract {
siblingPrevTx: AggregatorTx,
isSiblingPrevTxLeaf: boolean,

ancestorTx0: AggregatorTx,
ancestorTx1: AggregatorTx,
isAncestorLeaf: boolean,
firstAncestorTx: AggregatorTx,
isFirstAncestorLeaf: boolean,
) {
// check sighash preimage
const s = SigHashUtils.checkSHPreimage(shPreimage);
Expand All @@ -61,20 +61,17 @@ export class DepositAggregator extends SmartContract {

// require this method is called at the first or second input
if (isFirstInput) {
assert(shPreimage.inputNumber == toByteString('00000000'));
assert(shPreimage.inputIndex == toByteString('00000000'));
prevoutsPrefix = toByteString('');
prevoutsSuffix = AggregatorUtils.buildPrevout(siblingPrevTx, isSiblingPrevTxLeaf) + feePrevouts;
newDataHash = hash256(prevTx.dataHash + siblingPrevTx.dataHash);
} else {
assert(shPreimage.inputNumber == toByteString('01000000'));
assert(shPreimage.inputIndex == toByteString('01000000'));
prevoutsPrefix = AggregatorUtils.buildPrevout(siblingPrevTx, isSiblingPrevTxLeaf);
prevoutsSuffix = feePrevouts;
newDataHash = hash256(siblingPrevTx.dataHash + prevTx.dataHash);
}

// require this tx has two contract inputs, and these two contracts have the same locking
assert(prevTx.contractOutputLocking == siblingPrevTx.contractOutputLocking);

// backtrace validation for the currently unlocking input
AggregatorUtils.backtrace(
prevoutsPrefix,
Expand All @@ -83,18 +80,64 @@ export class DepositAggregator extends SmartContract {
prevTx,
isPrevTxLeaf,
depositData,
ancestorTx0,
ancestorTx1,
isAncestorLeaf,
firstAncestorTx,
isFirstAncestorLeaf,
);

// require this tx has two contract inputs, and these two contracts have the same locking
assert(prevTx.aggregatorOutputLocking == siblingPrevTx.aggregatorOutputLocking);

// confine outputs for this tx
const stateOutput = GeneralUtils.buildStateOutput(newDataHash);

const contractOutputAmount = prevTx.contractOutputAmount + siblingPrevTx.contractOutputAmount;
const contractOutput = GeneralUtils.buildContractOutput(contractOutputAmount, prevTx.contractOutputLocking);
const contractOutputAmount = prevTx.aggregatorOutputAmount + siblingPrevTx.aggregatorOutputAmount;
const contractOutput = GeneralUtils.buildContractOutput(contractOutputAmount, prevTx.aggregatorOutputLocking);

const hashOutputs = sha256(stateOutput + contractOutput);
assert(shPreimage.hashOutputs == hashOutputs);
}

@method()
public finalize(
shPreimage: SHPreimage,

sigOperator: Sig,
bridgePrevTx: BridgeTx,
feePrevouts: ByteString,

prevTx: AggregatorTx,
isPrevTxLeaf: boolean,
depositData: DepositData,

firstAncestorTx: AggregatorTx,
isFirstAncestorLeaf: boolean,
) {
// check sighash preimage
const s = SigHashUtils.checkSHPreimage(shPreimage);
assert(this.checkSig(s, SigHashUtils.Gx));

// check operator sig
assert(this.checkSig(sigOperator, this.operatorPubKey));

// require this method is called at the second input
assert(shPreimage.inputIndex == toByteString('01000000'));

const prevoutsPrefix = BridgeUtils.buildPrevout(bridgePrevTx);
const prevoutsSuffix = feePrevouts;

// backtrace validation for the currently unlocking input
AggregatorUtils.backtrace(
prevoutsPrefix,
prevoutsSuffix,
shPreimage.hashPrevouts,
prevTx,
isPrevTxLeaf,
depositData,
firstAncestorTx,
isFirstAncestorLeaf,
);

// check bridge locking matches the prop
assert(this.bridgeLocking == bridgePrevTx.bridgeOutputLocking);
}
}
2 changes: 1 addition & 1 deletion l1/src/contracts/generalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
len,
OpCode,
} from 'scrypt-ts';
import { int32 } from './types';
import { int32 } from '../types';

export class GeneralUtils extends SmartContractLib {
@method()
Expand Down
57 changes: 0 additions & 57 deletions l1/src/contracts/sigHashUtils.ts

This file was deleted.

38 changes: 0 additions & 38 deletions l1/src/contracts/types.ts

This file was deleted.

Loading

0 comments on commit 95655d1

Please sign in to comment.