@atomiqlabs/chain-evm
Version:
EVM specific base implementation
116 lines (95 loc) • 4.14 kB
text/typescript
import {IClaimHandler} from "../ClaimHandlers";
import {BigIntBufferUtils, ChainSwapType, RelaySynchronizer} from "@atomiqlabs/base";
import {EVMBtcRelay} from "../../../../btcrelay/EVMBtcRelay";
import { EVMBtcStoredHeader } from "../../../../btcrelay/headers/EVMBtcStoredHeader";
import {EVMTx} from "../../../../chain/modules/EVMTransactions";
import {getLogger} from "../../../../../utils/Utils";
import {keccak256} from "ethers";
import {Buffer} from "buffer";
import {EVMSwapData} from "../../../EVMSwapData";
export type BitcoinCommitmentData = {
btcRelay: EVMBtcRelay<any>,
confirmations: number
}
export type BitcoinWitnessData = {
tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
requiredConfirmations: number,
commitedHeader?: EVMBtcStoredHeader,
btcRelay?: EVMBtcRelay<any>,
synchronizer?: RelaySynchronizer<EVMBtcStoredHeader, EVMTx, any>
};
const logger = getLogger("IBitcoinClaimHandler: ");
export abstract class IBitcoinClaimHandler<C, W extends BitcoinWitnessData> implements IClaimHandler<C & BitcoinCommitmentData, W> {
public readonly address: string;
constructor(address: string) {
this.address = address;
}
public static readonly address = "";
public static readonly type: ChainSwapType = ChainSwapType.CHAIN_TXID;
public static readonly gas: number = 10_000;
protected serializeCommitment(data: BitcoinCommitmentData): Buffer {
const buffer = Buffer.alloc(24);
buffer.writeUint32BE(data.confirmations, 0);
Buffer.from(data.btcRelay.contractAddress.substring(2), "hex").copy(buffer, 4, 0, 20);
return buffer;
}
getCommitment(data: C & BitcoinCommitmentData): string {
return keccak256(this.serializeCommitment(data));
}
protected async _getWitness(
signer: string,
swapData: EVMSwapData,
{tx, btcRelay, commitedHeader, synchronizer, requiredConfirmations}: BitcoinWitnessData,
commitment: C,
feeRate?: string
): Promise<{
initialTxns: EVMTx[];
witness: Buffer,
commitment: Buffer,
blockheader: Buffer,
merkleProof: Buffer
}> {
const serializedCommitment: Buffer = this.serializeCommitment({
...commitment,
btcRelay,
confirmations: requiredConfirmations
});
const commitmentHash = keccak256(serializedCommitment);
if(!swapData.isClaimData(commitmentHash)) throw new Error("Invalid commit data");
const merkleProof = await btcRelay.bitcoinRpc.getMerkleProof(tx.txid, tx.blockhash);
logger.debug("getWitness(): merkle proof computed: ", merkleProof);
const txs: EVMTx[] = [];
if(commitedHeader==null) {
const headers = await EVMBtcRelay.getCommitedHeadersAndSynchronize(
signer, btcRelay,
[{blockheight: tx.height, requiredConfirmations, blockhash: tx.blockhash}],
txs, synchronizer, feeRate
);
if(headers==null) throw new Error("Cannot fetch committed header!");
commitedHeader = headers[tx.blockhash];
}
const serializedHeader = commitedHeader.serialize();
const serializedMerkleProof = Buffer.concat([
BigIntBufferUtils.toBuffer(BigInt(merkleProof.pos), "be", 4),
BigIntBufferUtils.toBuffer(BigInt(merkleProof.merkle.length), "be", 32),
...merkleProof.merkle
]);
return {
initialTxns: txs,
witness: Buffer.concat([
serializedCommitment,
serializedHeader,
serializedMerkleProof
]),
commitment: serializedCommitment,
blockheader: serializedHeader,
merkleProof: serializedMerkleProof
};
}
abstract getWitness(signer: string, data: EVMSwapData, witnessData: W, feeRate?: string): Promise<{
initialTxns: EVMTx[];
witness: Buffer
}>;
abstract getGas(data: EVMSwapData): number;
abstract getType(): ChainSwapType;
}