Skip to content

Commit

Permalink
Merge 2b9d448 into 797decd
Browse files Browse the repository at this point in the history
  • Loading branch information
josipbagaric committed Nov 20, 2018
2 parents 797decd + 2b9d448 commit 5e80aef
Show file tree
Hide file tree
Showing 17 changed files with 908 additions and 536 deletions.
709 changes: 408 additions & 301 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"clean-built": "rm -rf built",
"clean": "npm run clean-backups && npm run clean-built",
"deploy-eac": "sh scripts/deploy_eac.sh",
"ganache": "ganache-cli -m \"shove afford modify census bridge good random error fault floor fringe oblige\" -i 1002 -b 1",
"ganache": "ganache-cli -m \"shove afford modify census bridge good random error fault floor fringe oblige\" -i 1002 -b 1 --noVMErrorsOnRPCResponse",
"lint": "tslint --project .",
"lint-fix": "tslint --fix --project .",
"precommit": "lint-staged",
Expand Down
3 changes: 3 additions & 0 deletions src/Cache/Cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import BigNumber from 'bignumber.js';
import { TxStatus } from '../Enum';

export interface ICachedTxDetails {
bounty: BigNumber;
temporalUnit: number;
claimedBy: string;
wasCalled: boolean;
windowStart: BigNumber;
claimWindowStart: BigNumber;
status: TxStatus;
}

Expand Down
4 changes: 2 additions & 2 deletions src/EconomicStrategy/EconomicStrategyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ export class EconomicStrategyManager {
return currentNetworkPrice.greaterThan(maxGasPrice)
? maxGasPrice
: currentNetworkPrice.lessThan(minGasPrice)
? minGasPrice
: currentNetworkPrice;
? minGasPrice
: currentNetworkPrice;
}

public async shouldExecuteTx(txRequest: ITxRequest, targetGasPrice: BigNumber): Promise<boolean> {
Expand Down
108 changes: 100 additions & 8 deletions src/Scanner/CacheScanner.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import BigNumber from 'bignumber.js';

import { IntervalId, ITxRequest } from '../Types';
import BaseScanner from './BaseScanner';
import IRouter from '../Router';
import Config from '../Config';
import { TxStatus } from '../Enum';

import { ICachedTxDetails } from '../Cache';

export default class CacheScanner extends BaseScanner {
public cacheInterval: IntervalId;
public avgBlockTime: number;

private routes: Set<string> = new Set<string>();

constructor(config: Config, router: IRouter) {
Expand All @@ -17,16 +23,82 @@ export default class CacheScanner extends BaseScanner {
return;
}

return this.config.cache
.stored()
.map(address => this.config.eac.transactionRequest(address))
.sort((a, b) => this.prioritize(a, b))
.forEach(txRequest => this.route(txRequest));
this.avgBlockTime = await this.config.util.getAverageBlockTime();

let txRequests = this.getCacheTxRequests();
txRequests = this.prioritizeTransactions(txRequests);
txRequests.forEach((txRequest: ITxRequest) => this.route(txRequest));
}

public getCacheTxRequests(): ITxRequest[] {
return this.config.cache.stored().map(address => this.config.eac.transactionRequest(address));
}

private prioritize(a: ITxRequest, b: ITxRequest): number {
const statusA = this.config.cache.get(a.address).status;
const statusB = this.config.cache.get(b.address).status;
/*
* Prioritizes transactions in the following order:
* 1. Transactions in FreezePeriod come first
* 2. Sorts sorted by windowStart
* 3. If some 2 transactions have windowStart set to the same block,
* it sorts those by whichever has the highest bounty.
*/
private prioritizeTransactions(txRequests: ITxRequest[]): ITxRequest[] {
const getTxFromCache = (address: string) => this.config.cache.get(address);

const blockTransactions = txRequests.filter(
(tx: ITxRequest) => getTxFromCache(tx.address).temporalUnit === 1
);
const timestampTransactions = txRequests.filter(
(tx: ITxRequest) => getTxFromCache(tx.address).temporalUnit === 2
);

blockTransactions.sort((currentTx, nextTx) =>
this.claimWindowStartSort(
getTxFromCache(currentTx.address).claimWindowStart,
getTxFromCache(nextTx.address).claimWindowStart
)
);
blockTransactions.sort((currentTx, nextTx) =>
this.higherBountySortIfInSameBlock(
getTxFromCache(currentTx.address),
getTxFromCache(nextTx.address)
)
);

timestampTransactions.sort((currentTx, nextTx) =>
this.claimWindowStartSort(
getTxFromCache(currentTx.address).claimWindowStart,
getTxFromCache(nextTx.address).claimWindowStart
)
);
timestampTransactions.sort((currentTx, nextTx) =>
this.higherBountySortIfInSameBlock(
getTxFromCache(currentTx.address),
getTxFromCache(nextTx.address)
)
);

txRequests = blockTransactions
.concat(timestampTransactions)
.sort((currentTx, nextTx) => this.prioritizeFreezePeriod(currentTx, nextTx));

return txRequests;
}

private claimWindowStartSort(
currentClaimWindowStart: BigNumber,
nextClaimWindowStart: BigNumber
): number {
if (currentClaimWindowStart.lessThan(nextClaimWindowStart)) {
return -1;
} else if (currentClaimWindowStart.greaterThan(nextClaimWindowStart)) {
return 1;
}
return 0;
}

private prioritizeFreezePeriod(currentTx: ITxRequest, nextTx: ITxRequest): number {
const statusA = this.config.cache.get(currentTx.address).status;
const statusB = this.config.cache.get(nextTx.address).status;

if (statusA === statusB) {
return 0;
Expand All @@ -35,6 +107,26 @@ export default class CacheScanner extends BaseScanner {
return statusA === TxStatus.FreezePeriod && statusB !== TxStatus.FreezePeriod ? -1 : 1;
}

private higherBountySortIfInSameBlock(
currentTx: ICachedTxDetails,
nextTx: ICachedTxDetails
): number {
const blockTime = currentTx.temporalUnit === 1 ? 1 : this.avgBlockTime;

const blockDifference = currentTx.windowStart.minus(nextTx.windowStart).abs();
const isInSameBlock = blockDifference.lessThanOrEqualTo(blockTime);

if (isInSameBlock) {
if (currentTx.bounty.lessThan(nextTx.bounty)) {
return 1;
} else if (currentTx.bounty.greaterThan(nextTx.bounty)) {
return -1;
}
}

return 0;
}

private async route(txRequest: ITxRequest): Promise<void> {
const address = txRequest.address;
if (!this.routes.has(address)) {
Expand Down
11 changes: 10 additions & 1 deletion src/Scanner/ChainScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,19 @@ export default class ChainScanner extends CacheScanner {
}

private store(txRequest: ITxRequestRaw) {
const windowStart = txRequest.params[7];
const freezePeriod = txRequest.params[3];
const claimWindowSize = txRequest.params[2];

const claimWindowStart = windowStart.minus(freezePeriod).minus(claimWindowSize);

this.config.cache.set(txRequest.address, {
bounty: txRequest.params[1],
temporalUnit: txRequest.params[5].toNumber(),
claimedBy: null,
wasCalled: false,
windowStart: txRequest.params[7],
windowStart,
claimWindowStart,
status: TxStatus.BeforeClaimWindow
});
}
Expand Down
37 changes: 34 additions & 3 deletions src/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,38 @@ export default class W3Util {
});
}

/*
* Takes an average of the last 100 blocks and estimates the
* blocktime.
*/
public async getAverageBlockTime(): Promise<number> {
const numLookbackBlocks: number = 100;
const times: number[] = [];

const blockPromises: Promise<IBlock>[] = [];
const currentBlockNumber: number = await this.getBlockNumber();
const firstBlock: IBlock = await this.getBlock(currentBlockNumber - numLookbackBlocks);

for (let i = firstBlock.number; i < currentBlockNumber; i++) {
blockPromises.push(this.getBlock(i));
}

const resolvedBlocks: IBlock[] = await Promise.all(blockPromises);

let prevTimestamp = firstBlock.timestamp;
resolvedBlocks.forEach((block: IBlock) => {
const time = block.timestamp - prevTimestamp;
prevTimestamp = block.timestamp;
times.push(time);
});

if (times.length === 0) {
return 1;
}

return Math.round(times.reduce((a, b) => a + b) / times.length);
}

public isWatchingEnabled(): Promise<boolean> {
return W3Util.isWatchingEnabled(this.web3);
}
Expand Down Expand Up @@ -174,9 +206,8 @@ export default class W3Util {

public sendRawTransaction(transaction: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.web3.eth.sendRawTransaction(
transaction,
(e: any, r: any) => (e ? reject(e) : resolve(r))
this.web3.eth.sendRawTransaction(transaction, (e: any, r: any) =>
e ? reject(e) : resolve(r)
);
});
}
Expand Down
10 changes: 5 additions & 5 deletions test/unit/UnitTestCache.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { expect, assert } from 'chai';
import Cache from '../../src/Cache';

describe('Cache unit tests', () => {
let cache: Cache<any>;
let cache: Cache<any>;

beforeEach(() => {
cache = new Cache(null);
});
beforeEach(() => {
cache = new Cache(null);
});

describe('Cache unit tests', () => {
describe('get()', () => {
it('get the key value', () => {
cache.set('key', 'value');
Expand Down

0 comments on commit 5e80aef

Please sign in to comment.