Skip to content

Commit

Permalink
support relayer sign message & dynamic fee
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoch05 committed Apr 17, 2024
1 parent ccfecf1 commit 7e10b41
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 3 deletions.
4 changes: 4 additions & 0 deletions apollo/prisma/migrations/20240417090922_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "LnBridgeRelayInfo" ADD COLUMN "dynamicFee" TEXT,
ADD COLUMN "dynamicFeeExpire" TEXT,
ADD COLUMN "dynamicFeeSignature" TEXT;
3 changes: 3 additions & 0 deletions apollo/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,8 @@ model LnBridgeRelayInfo {
transferLimit String
softTransferLimit String
paused Boolean
dynamicFee String?
dynamicFeeExpire String?
dynamicFeeSignature String?
}

6 changes: 6 additions & 0 deletions apollo/src/aggregation/aggregation.history.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ type LnBridgeRelayInfo {
transferLimit: String
softTransferLimit: String
paused: Boolean
dynamicFee: String
dynamicFeeExpire: String
dynamicFeeSignature: String
}

type LnBridgeRelayInfos {
Expand Down Expand Up @@ -119,5 +122,8 @@ type Mutation {
addGuardSignature(id: String, dataHash: String, signature: String): String
updateConfirmedBlock(id: String, block: String): String
lnBridgeHeartBeat(fromChainId: String, toChainId: String, version: String, relayer: String, tokenAddress: String, softTransferLimit: String): String
signConfirmedBlock(id: String, block: String, timestamp: Int, signature: String): String
signHeartBeat(fromChainId: String, toChainId: String, version: String, relayer: String, tokenAddress: String, softTransferLimit: String, timestamp: Int, signature: String): String
signDynamicFee(dynamicFee: String, expire: String, sig: String, timestamp: Int, signature: String): String
}

146 changes: 143 additions & 3 deletions apollo/src/aggregation/aggregation.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,44 @@ import { Args, Query, Mutation, Resolver } from '@nestjs/graphql';
import { isEmpty, isNull, isUndefined } from 'lodash';
import { AggregationService } from './aggregation.service';
import { Prisma } from '@prisma/client';
import * as ethUtil from 'ethereumjs-util';
import Web3 from 'web3';

@Resolver()
export class AggregationResolver {
private readonly web3 = new Web3(Web3.givenProvider);
private readonly heartbeatTimeout = 300;
private readonly signatureExpire = 120;
// todo: move this to contract
private readonly relayerProxy = {
};
constructor(private aggregationService: AggregationService) {}

private ecrecover(hash: string, sig: string): string {
const sigObj = ethUtil.fromRpcSig(sig);
const pubkey = ethUtil.ecrecover(
Buffer.from(hash.substr(2), 'hex'),
sigObj.v,
sigObj.r,
sigObj.s
);
return ethUtil.bufferToHex(ethUtil.publicToAddress(pubkey)).toLowerCase();
}

private checkMessageSender(timestamp: number, message: string, relayer: string, sig: string): boolean {
try {
const now = Math.floor(Date.now() / 1000);
if (timestamp + this.signatureExpire < now) {
return false;
}
const messageHash = this.web3.utils.keccak256(`${timestamp}:${message}`);
const signer = this.ecrecover(messageHash, sig);
return signer === relayer || this.relayerProxy[signer] === relayer;
} catch {
return false;
}
}

@Query()
async historyRecordById(@Args('id') id: string) {
return this.aggregationService.queryHistoryRecordById({
Expand Down Expand Up @@ -161,26 +193,37 @@ export class AggregationResolver {
});
}

/**
* @deprecated instead, please use signConfirmedBlock
**/
@Mutation()
async updateConfirmedBlock(@Args('id') id: string, @Args('block') block: string) {
async updateConfirmedBlock(
@Args('id') id: string,
@Args('block') block: string
) {
await this.aggregationService.updateConfirmedBlock({
where: { id: id },
block: block,
});
}

/**
* @deprecated instead, please use signHeartBeat
**/
@Mutation()
async lnBridgeHeartBeat(
@Args('fromChainId') fromChainId: string,
@Args('toChainId') toChainId: string,
@Args('version') version: string,
@Args('relayer') relayer: string,
@Args('tokenAddress') tokenAddress: string,
@Args('softTransferLimit') softTransferLimit: string
@Args('softTransferLimit') softTransferLimit: string,
) {
const id = `${version}-${fromChainId}-${toChainId}-${relayer.toLowerCase()}-${tokenAddress.toLowerCase()}`;
const now = Math.floor(Date.now() / 1000);

let updateData = {
heartbeatTimestamp: Math.floor(Date.now() / 1000),
heartbeatTimestamp: now,
};

if (softTransferLimit !== undefined && softTransferLimit !== '0') {
Expand All @@ -206,6 +249,103 @@ export class AggregationResolver {
}
}

@Mutation()
async signConfirmedBlock(
@Args('id') id: string,
@Args('block') block: string,
@Args('timestamp') timestamp: number,
@Args('signature') signature: string
) {
const relayer = id?.split('-')[3];
const allowSetConfirmed = this.checkMessageSender(timestamp, block, relayer, signature);
if (!allowSetConfirmed) {
return;
}
await this.aggregationService.updateConfirmedBlock({
where: { id: id },
block: block,
});
}

@Mutation()
async signHeartBeat(
@Args('fromChainId') fromChainId: string,
@Args('toChainId') toChainId: string,
@Args('version') version: string,
@Args('relayer') relayer: string,
@Args('tokenAddress') tokenAddress: string,
@Args('softTransferLimit') softTransferLimit: string,
@Args('timestamp') timestamp: number,
@Args('signature') signature: string
) {
const id = `${version}-${fromChainId}-${toChainId}-${relayer.toLowerCase()}-${tokenAddress.toLowerCase()}`;
const now = Math.floor(Date.now() / 1000);

const allowHeartBeat = this.checkMessageSender(timestamp, softTransferLimit, relayer.toLowerCase(), signature);
if (!allowHeartBeat) {
return
}

let updateData = {
heartbeatTimestamp: now,
};

if (softTransferLimit !== undefined && softTransferLimit !== '0') {
// the softTransferLimit is on target chain, transfer it to source chain
const transferLimit = this.aggregationService.targetAmountToSourceAmount({
amount: softTransferLimit,
sourceChainId: Number(fromChainId),
targetChainId: Number(toChainId),
sourceToken: tokenAddress,
version
});
updateData['softTransferLimit'] = transferLimit;
}

try {
await this.aggregationService.updateLnBridgeRelayInfo({
where: { id: id },
data: updateData,
});
} catch (e) {
console.log(`heart beat failed ${id}, exception: ${e}`);
return;
}
}

@Mutation()
async signDynamicFee(
@Args('fromChainId') fromChainId: string,
@Args('toChainId') toChainId: string,
@Args('version') version: string,
@Args('relayer') relayer: string,
@Args('tokenAddress') tokenAddress: string,
@Args('dynamicFee') dynamicFee: string,
@Args('dynamicFeeExpire') dynamicFeeExpire: string,
@Args('dynamicFeeSignature') dynamicFeeSignature: string,
@Args('timestamp') timestamp: number,
@Args('signature') signature: string
) {
const id = `${version}-${fromChainId}-${toChainId}-${relayer.toLowerCase()}-${tokenAddress.toLowerCase()}`;
const allowSetDynamicFee = this.checkMessageSender(timestamp, `${dynamicFee}:${dynamicFeeExpire}:${dynamicFeeSignature}`, relayer.toLowerCase(), signature);
if (!allowSetDynamicFee) {
return
}

try {
await this.aggregationService.updateLnBridgeRelayInfo({
where: { id: id },
data: {
dynamicFee,
dynamicFeeExpire,
dynamicFeeSignature
},
});
} catch (e) {
return;
}
}

@Query()
checkLnBridgeExist(
@Args('fromChainId') fromChainId: number,
Expand Down
9 changes: 9 additions & 0 deletions apollo/src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export class LnBridgeRelayInfo {
transferLimit?: Nullable<string>;
softTransferLimit?: Nullable<string>;
paused?: Nullable<boolean>;
dynamicFee?: Nullable<string>;
dynamicFeeExpire?: Nullable<string>;
dynamicFeeSignature?: Nullable<string>;
}

export class LnBridgeRelayInfos {
Expand Down Expand Up @@ -146,6 +149,12 @@ export abstract class IMutation {
abstract updateConfirmedBlock(id?: Nullable<string>, block?: Nullable<string>): Nullable<string> | Promise<Nullable<string>>;

abstract lnBridgeHeartBeat(fromChainId?: Nullable<string>, toChainId?: Nullable<string>, version?: Nullable<string>, relayer?: Nullable<string>, tokenAddress?: Nullable<string>, softTransferLimit?: Nullable<string>): Nullable<string> | Promise<Nullable<string>>;

abstract signConfirmedBlock(id?: Nullable<string>, block?: Nullable<string>, timestamp?: Nullable<number>, signature?: Nullable<string>): Nullable<string> | Promise<Nullable<string>>;

abstract signHeartBeat(fromChainId?: Nullable<string>, toChainId?: Nullable<string>, version?: Nullable<string>, relayer?: Nullable<string>, tokenAddress?: Nullable<string>, softTransferLimit?: Nullable<string>, timestamp?: Nullable<number>, signature?: Nullable<string>): Nullable<string> | Promise<Nullable<string>>;

abstract signDynamicFee(dynamicFee?: Nullable<string>, expire?: Nullable<string>, sig?: Nullable<string>, timestamp?: Nullable<number>, signature?: Nullable<string>): Nullable<string> | Promise<Nullable<string>>;
}

export type BigInt = any;
Expand Down

0 comments on commit 7e10b41

Please sign in to comment.