Skip to content

Commit

Permalink
feat: add getSyncTxs
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Nov 4, 2024
1 parent f44e31a commit 0ffed62
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 10 deletions.
14 changes: 6 additions & 8 deletions example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,17 @@ async function main() {
const txId =
"fa89c32152bf324cd1d47d48187f977c7e0f380f6f78132c187ce27923f62fcc";
const rawTransaction = await bitcoinProvider.getRawTransaction(txId, true);
const blockHeader = await bitcoinProvider.getBlockHeader(
const header = await bitcoinProvider.getBlockHeader(
rawTransaction.blockhash
);

// Generate actual transactions
const registerBlocksTx = await utuProvider.getRegisterBlocksTx([
rawTransaction.blockhash,
]);
const canonicalChainUpdateTx = await utuProvider.getCanonicalChainUpdateTx(
blockHeader.height,
blockHeader.height,
true
const syncTransactions = await utuProvider.getSyncTxs(
starknetProvider,
header.height,
0n
);

const txInclusionProof = await utuProvider.getTxInclusionProof(txId);
} catch (error) {
console.error("Error:", error);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"ts-jest": "^29.2.5"
},
"devDependencies": {
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"rollup": "^4.24.0",
"rollup-plugin-typescript2": "^0.36.0",
Expand Down
112 changes: 112 additions & 0 deletions src/UtuProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BitcoinProvider } from "./BitcoinProvider";
import { BlockHeightProof, RegisterBlocksTx } from "@/UtuTypes";
import { BlockHeader } from "./BitcoinTypes";
import { BigNumberish, ByteArray } from "starknet";
import { Contract, RpcProvider } from "starknet";

const CONTRACT_ADDRESS =
"0x064e21f88caa162294fdda7f73d67ad09b81419e97df3409a5eb13ba39b88c31";
Expand Down Expand Up @@ -69,6 +70,15 @@ const byteArrayFromHexString = (hex: string): ByteArray => {
};
};

// Add this helper function with other utility functions at the top
const isResultEmpty = (blockStatus: string[]): boolean => {
return blockStatus.every((value: string) =>
value.startsWith("0x")
? parseInt(value.slice(2), 16) === 0
: parseInt(value, 16) === 0
);
};

export interface UtuProviderResult {
inclusionProof: string;
bitcoinRelayerTx?: string;
Expand Down Expand Up @@ -417,4 +427,106 @@ export class UtuProvider {
}
return [value, newPosition];
}

/**
* Gets the sync transactions needed to register Bitcoin blocks on Starknet.
* This is a simple version that only registers new blocks and does not handle
* chain reorganizations or attempt to override an incorrect canonical chain.
*/
async getSyncTxs(
starknetProvider: RpcProvider,
blockHeight: number,
minCPOW: bigint
) {
// Prepare the calls array
const calls = [];

// Convert bits field to target threshold and compute proof of work
function computePOWFromBits(bits: string): bigint {
// Convert bits to target threshold
// Format is: 0x1d00ffff where:
// First byte (1d) is the number of bytes
// Next byte (00) is unused
// Last bytes (ffff) are the significant digits
const exponent = parseInt(bits.slice(0, 2), 16);
const coefficient = parseInt(bits.slice(2), 16);

// Target = coefficient * 256^(exponent-3)
const target = BigInt(coefficient) * BigInt(256) ** BigInt(exponent - 3);

// Compute POW as (2^256-1)/target
const maxValue = (BigInt(1) << BigInt(256)) - BigInt(1);
return maxValue / target;
}

let cPOW = 0n;
const blocksToRegister = [];
let canonicalRewriteMin = undefined;
let canonicalRewriteMax = undefined;
let currentBlockHeight = blockHeight;

// even if we need 0 cpow, we still need at least this block
while (cPOW < minCPOW || cPOW === 0n) {
// Get block information
const blockHash = await this.bitcoinProvider.getBlockHash(
currentBlockHeight
);
const blockHeader = await this.bitcoinProvider.getBlockHeader(blockHash);
cPOW += computePOWFromBits(blockHeader.bits);

// Check if block is written
const blockStatus = await starknetProvider.callContract({
contractAddress: CONTRACT_ADDRESS,
entrypoint: "get_status",
calldata: serializedHash(blockHash),
});
if (isResultEmpty(blockStatus)) {
blocksToRegister.push(blockHash);
if (canonicalRewriteMin === undefined) {
canonicalRewriteMin = currentBlockHeight;
}
canonicalRewriteMax = currentBlockHeight;
} else {
// if the block is written, we then need to check the status on the canonical chain
const canonicalChainBlock = await starknetProvider.callContract({
contractAddress: CONTRACT_ADDRESS,
entrypoint: "get_block",
calldata: ["0x" + currentBlockHeight.toString(16)],
});
// if it is not written, then we want to update it
if (isResultEmpty(canonicalChainBlock)) {
if (canonicalRewriteMin === undefined) {
canonicalRewriteMin = currentBlockHeight;
}
canonicalRewriteMax = currentBlockHeight;
}
}

currentBlockHeight += 1;
}

// 1. Register the block itself
const registerBlocks = await this.getRegisterBlocksTx(blocksToRegister);
calls.push(registerBlocks);

// 2. Update the canonical chain to include this block
if (canonicalRewriteMin !== undefined) {
// we require a height proof if there is not already the prev block on the canonical chain
const previousChainBlock = await starknetProvider.callContract({
contractAddress: CONTRACT_ADDRESS,
entrypoint: "get_block",
calldata: ["0x" + (blockHeight - 1).toString(16)],
});

const chainUpdate = await this.getCanonicalChainUpdateTx(
canonicalRewriteMin,
// it is defined if min is defined
canonicalRewriteMax as number,
isResultEmpty(previousChainBlock)
);
calls.push(chainUpdate);
}

return calls;
}
}

0 comments on commit 0ffed62

Please sign in to comment.