@0x:contracts-integrations Removed the Cache

This commit is contained in:
Alex Towle 2019-10-16 17:25:25 -07:00
parent c87364f86b
commit 03e35846fb
5 changed files with 32 additions and 266 deletions

View File

@ -1,4 +1,4 @@
/*import { blockchainTests, expect } from '@0x/contracts-test-utils'; import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { artifacts, TestFrameworkContract } from '../../src'; import { artifacts, TestFrameworkContract } from '../../src';
@ -73,4 +73,3 @@ blockchainTests('TestFramework', env => {
}); });
}); });
}); });
*/

View File

@ -1,10 +1,20 @@
/*import { blockchainTests, constants, expect, filterLogsToArguments, hexRandom } from '@0x/contracts-test-utils'; import {
import { BigNumber, generatePseudoRandom256BitNumber } from '@0x/utils'; blockchainTests,
constants,
expect,
filterLogsToArguments,
getRandomInteger,
hexRandom,
} from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { artifacts, TestFrameworkContract, TestFrameworkSomeEventEventArgs, TestFrameworkEvents } from '../../src'; import { artifacts, TestFrameworkContract, TestFrameworkSomeEventEventArgs, TestFrameworkEvents } from '../../src';
import { FunctionAssertion, Result } from '../utils/function_assertions'; import { FunctionAssertion, Result } from '../utils/function_assertions';
const ZERO = constants.ZERO_AMOUNT;
const MAX_UINT256 = constants.MAX_UINT256;
blockchainTests.resets('FunctionAssertion Unit Tests', env => { blockchainTests.resets('FunctionAssertion Unit Tests', env => {
let exampleContract: TestFrameworkContract; let exampleContract: TestFrameworkContract;
@ -19,7 +29,7 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
describe('runAsync', () => { describe('runAsync', () => {
it('should call the before function with the provided arguments', async () => { it('should call the before function with the provided arguments', async () => {
let sideEffectTarget = constants.ZERO_AMOUNT; let sideEffectTarget = ZERO;
const assertion = new FunctionAssertion(exampleContract.noEffect, { const assertion = new FunctionAssertion(exampleContract.noEffect, {
before: async (inputValue: BigNumber) => { before: async (inputValue: BigNumber) => {
sideEffectTarget = inputValue; sideEffectTarget = inputValue;
@ -27,13 +37,13 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {}, after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {},
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
expect(sideEffectTarget).bignumber.to.be.eq(randomInput); expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
}); });
it('should call the after function with the provided arguments', async () => { it('should call the after function with the provided arguments', async () => {
let sideEffectTarget = constants.ZERO_AMOUNT; let sideEffectTarget = ZERO;
const assertion = new FunctionAssertion(exampleContract.noEffect, { const assertion = new FunctionAssertion(exampleContract.noEffect, {
before: async (inputValue: BigNumber) => {}, before: async (inputValue: BigNumber) => {},
after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => { after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {
@ -41,7 +51,7 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
}, },
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
expect(sideEffectTarget).bignumber.to.be.eq(randomInput); expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
}); });
@ -52,12 +62,12 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
before: async (inputValue: BigNumber) => {}, before: async (inputValue: BigNumber) => {},
after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {}, after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {},
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
}); });
it('should pass the return value from "before" to "after"', async () => { it('should pass the return value from "before" to "after"', async () => {
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
let sideEffectTarget = constants.ZERO_AMOUNT; let sideEffectTarget = constants.ZERO_AMOUNT;
const assertion = new FunctionAssertion(exampleContract.noEffect, { const assertion = new FunctionAssertion(exampleContract.noEffect, {
before: async (inputValue: BigNumber) => { before: async (inputValue: BigNumber) => {
@ -79,13 +89,13 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
sideEffectTarget = result.data; sideEffectTarget = result.data;
}, },
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
expect(sideEffectTarget).bignumber.to.be.eq(randomInput); expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
}); });
it('should pass the receipt from the function call to "after"', async () => { it('should pass the receipt from the function call to "after"', async () => {
let sideEffectTarget: TransactionReceiptWithDecodedLogs = {} as TransactionReceiptWithDecodedLogs; let sideEffectTarget = {} as TransactionReceiptWithDecodedLogs;
const assertion = new FunctionAssertion(exampleContract.revertSideEffect, { const assertion = new FunctionAssertion(exampleContract.revertSideEffect, {
before: async (inputValue: BigNumber) => {}, before: async (inputValue: BigNumber) => {},
after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => { after: async (beforeInfo: any, result: Result, returnValue: BigNumber) => {
@ -96,7 +106,7 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
} }
}, },
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
// Ensure that the correct events were emitted. // Ensure that the correct events were emitted.
@ -116,11 +126,10 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
sideEffectTarget = result.data; sideEffectTarget = result.data;
}, },
}); });
const randomInput = generatePseudoRandom256BitNumber(); const randomInput = getRandomInteger(ZERO, MAX_UINT256);
await assertion.runAsync(randomInput); await assertion.runAsync(randomInput);
const errorMessage = 'VM Exception while processing transaction: revert Revert'; const errorMessage = 'VM Exception while processing transaction: revert Revert';
expect(sideEffectTarget.message).to.be.eq(errorMessage); expect(sideEffectTarget.message).to.be.eq(errorMessage);
}); });
}); });
}); });
*/

View File

@ -1,155 +0,0 @@
import { blockchainTests, constants, expect, hexSlice } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { BlockParam, CallData } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import { artifacts, TestFrameworkContract } from '../../src';
import { GetterCache } from '../utils/cache';
blockchainTests.resets('Cache Tests', env => {
let exampleContract: TestFrameworkContract;
before(async () => {
exampleContract = await TestFrameworkContract.deployFrom0xArtifactAsync(
artifacts.TestFramework,
env.provider,
env.txDefaults,
artifacts,
);
});
describe('callAsync', () => {
describe('() => uint', () => {
let cache: GetterCache;
beforeEach(async () => {
cache = new GetterCache(exampleContract.numberSideEffect);
});
it('should return 0 when "counter" == 0', async () => {
expect(await cache.callAsync()).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return 1 when "counter" == 1', async () => {
// Update the counter to 1.
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// Ensure that the returned value is the updated counter.
expect(await cache.callAsync()).bignumber.to.be.eq(new BigNumber(1));
});
it('should return the cached counter', async () => {
// Cache a value.
expect(await cache.callAsync()).bignumber.to.be.eq(constants.ZERO_AMOUNT);
// Update the counter to 1.
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// This should return "0" because a value was cached by the first call to "callAsync"
expect(await cache.callAsync()).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
});
describe('uint => boolean', () => {
let cache: GetterCache;
beforeEach(async () => {
cache = new GetterCache(exampleContract.equalsSideEffect);
});
it('should return true when "possiblyZero" == 0 && "counter" == 0', async () => {
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.true();
});
it('should return false when "possiblyZero" == 0 && "counter" != 0', async () => {
// Update "counter" to "1", which will cause all calls to return false.
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.false();
});
it('should return the cached value', async () => {
// Cache a "true" value when "possiblyZero" == 0 && "counter" == 0
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.true();
// Update "counter" to "1", which will cause all calls of "isZeroOrFalse" to return "false".
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// This should return "true" because a value was by the first call to "callAsync"
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.true();
});
});
describe('(uint, bytes32) => bytes32', () => {
let cache: GetterCache;
beforeEach(async () => {
cache = new GetterCache(exampleContract.hashSideEffect);
});
it('should return correct hash when counter == 0', async () => {
// Get the calldata for the function call, which includes the abi-encoded data to hash.
const hashData = exampleContract.hashSideEffect.getABIEncodedTransactionData(
new BigNumber(1),
ethUtil.bufferToHex(ethUtil.sha3(0)),
);
// Ensure that the correct hash was returned from the cache.
expect(await cache.callAsync(new BigNumber(1), ethUtil.bufferToHex(ethUtil.sha3(0)))).to.be.eq(
ethUtil.bufferToHex(ethUtil.sha3(hexSlice(hashData, 4))),
);
});
it('should return the null hash when counter != 0', async () => {
// Update "counter" to "1", which will cause all calls to return the null hash.
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// Ensure that the cache returns the correct value.
expect(await cache.callAsync(new BigNumber(1), ethUtil.bufferToHex(ethUtil.sha3(0)))).to.be.eq(
ethUtil.bufferToHex(ethUtil.sha3('0x')),
);
});
it('should return the cached hash', async () => {
// Get the calldata for the function call, which includes the abi-encoded data to hash.
const hashData = exampleContract.hashSideEffect.getABIEncodedTransactionData(
new BigNumber(1),
ethUtil.bufferToHex(ethUtil.sha3(0)),
);
const hash = ethUtil.bufferToHex(ethUtil.sha3(hexSlice(hashData, 4)));
// Ensure that the cache returns the correct value.
expect(await cache.callAsync(new BigNumber(1), ethUtil.bufferToHex(ethUtil.sha3(0)))).to.be.eq(hash);
// Update "counter" to "1", which will cause all calls to return the null hash.
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// The cache should return the same value as the first call to `callAsync` since a value was cached.
expect(await cache.callAsync(new BigNumber(1), ethUtil.bufferToHex(ethUtil.sha3(0)))).to.be.eq(hash);
});
});
});
describe('flush', () => {
describe('uint => boolean', () => {
let cache: GetterCache;
beforeEach(async () => {
cache = new GetterCache(exampleContract.equalsSideEffect);
});
it('should return false when the cache was flushed && "possiblyZero" == 0 && "counter" != 0', async () => {
// Cache a "true" value when "possiblyZero" == 0 && "counter" == 0
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.true();
// Update "counter" to "1", which will cause all calls of "isZeroOrFalse" to return "false".
await exampleContract.setCounter.awaitTransactionSuccessAsync(new BigNumber(1));
// Flush the entire cache.
cache.flush();
// This should return "false" because the value was flushed.
expect(await cache.callAsync(constants.ZERO_AMOUNT)).to.be.false();
});
});
});
});

View File

@ -1,84 +0,0 @@
import { ContractGetterFunction } from './function_assertions';
import * as _ from 'lodash';
export class GetterCache<TCallArgs extends any[]> {
// The getter function whose values will be cached.
public getter: ContractGetterFunction<TCallArgs>;
// The cache that will be used to store values. This has to use a "string" for indexing
// because the "Map" datastructure uses reference equality when the keys are objects,
// which was unsuitable for our purposes.
private cache: {
[key: string]: any;
};
constructor(getter: ContractGetterFunction<TCallArgs>) {
this.getter = getter;
this.cache = {};
}
/**
* Calls the contract getter and caches the result if there is not a cached value. Otherwise,
* this returns the cached value.
* @param ...args A variadic list of args to use when calling "callAsync". Due
* to the fact that we need to serialize the arguments to "callAsync" these
* arguments must be valid arguments to "getABIEncodedTransactionData".
* @return Either a cached value or the queried value.
*/
public async callAsync(...args: TCallArgs): Promise<any> {
const key = this.getter.getABIEncodedTransactionData(...args);
const cachedResult = this.cache[key];
if (cachedResult !== undefined) {
return cachedResult;
} else {
const result = await this.getter.callAsync(...args);
this.cache[key] = result;
return result;
}
}
/**
* Flushes the entire cache so that future calls to "callAsync" call the contract getter.
*/
public flush(): void {
this.cache = {};
}
}
export interface GetterCacheSet {
[getter: string]: GetterCache<any[]>;
}
export class GetterCacheCollection {
// A dictionary of getter cache's that allow the user of the collection to reference
// the getter functions by name.
public getters: GetterCacheSet;
/**
* Constructs a getter collection with pre-seeded getter names and getters.
* @param getterNames The names of the getter functions to register.
* @param getters The getter functions to register.
*/
constructor(getters: GetterCacheSet) {
this.getters = getters;
}
/**
* Registers a new getter in the collection.
* @param getterName The name of the contract getter function.
* @param getter The actual contract getter function.
*/
public registerGetter(getterName: string, getter: ContractGetterFunction<any[]>): void {
this.getters[getterName] = new GetterCache(getter);
}
/**
* Flushes all of the registered caches.
*/
public flushAll(): void {
for (const getter of Object.keys(this.getters)) {
this.getters[getter].flush();
}
}
}

View File

@ -1,16 +1,13 @@
import { PromiseWithTransactionHash } from '@0x/base-contract'; import { PromiseWithTransactionHash } from '@0x/base-contract';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
export interface ContractGetterFunction<TCallArgs extends any[]> { export interface ContractGetterFunction {
callAsync: (...args: TCallArgs) => Promise<any>; callAsync: (...args: any[]) => Promise<any>;
getABIEncodedTransactionData: (...args: TCallArgs) => string; getABIEncodedTransactionData: (...args: any[]) => string;
} }
export interface ContractWrapperFunction<TAwaitArgs extends any[], TCallArgs extends any[]> export interface ContractWrapperFunction extends ContractGetterFunction {
extends ContractGetterFunction<TCallArgs> { awaitTransactionSuccessAsync?: (...args: any[]) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
awaitTransactionSuccessAsync?: (
...args: TAwaitArgs
) => PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>;
} }
export interface Condition { export interface Condition {
@ -18,14 +15,14 @@ export interface Condition {
after: (beforeInfo: any, result: Result, ...args: any[]) => Promise<any>; after: (beforeInfo: any, result: Result, ...args: any[]) => Promise<any>;
} }
export class FunctionAssertion<TAwaitArgs extends any[], TCallArgs extends any[]> { export class FunctionAssertion {
// A before and an after assertion that will be called around the wrapper function. // A before and an after assertion that will be called around the wrapper function.
public condition: Condition; public condition: Condition;
// The wrapper function that will be wrapped in assertions. // The wrapper function that will be wrapped in assertions.
public wrapperFunction: ContractWrapperFunction<TAwaitArgs, TCallArgs>; public wrapperFunction: ContractWrapperFunction;
constructor(wrapperFunction: ContractWrapperFunction<TAwaitArgs, TCallArgs>, condition: Condition) { constructor(wrapperFunction: ContractWrapperFunction, condition: Condition) {
this.condition = condition; this.condition = condition;
this.wrapperFunction = wrapperFunction; this.wrapperFunction = wrapperFunction;
} }
@ -34,7 +31,7 @@ export class FunctionAssertion<TAwaitArgs extends any[], TCallArgs extends any[]
* Runs the wrapped function and fails if the before or after assertions fail. * Runs the wrapped function and fails if the before or after assertions fail.
* @param ...args The args to the contract wrapper function. * @param ...args The args to the contract wrapper function.
*/ */
public async runAsync(args: TAwaitArgs & TCallArgs): Promise<{ beforeInfo: any; afterInfo: any }> { public async runAsync(...args: any[]): Promise<{ beforeInfo: any; afterInfo: any }> {
// Call the before condition. // Call the before condition.
const beforeInfo = await this.condition.before(...args); const beforeInfo = await this.condition.before(...args);