diff --git a/e2e_support/loom.yaml b/e2e_support/loom.yaml index 072ca526..10e0dc93 100644 --- a/e2e_support/loom.yaml +++ b/e2e_support/loom.yaml @@ -1,6 +1,7 @@ ChainID: 'default' QueryServerHost: "tcp://0.0.0.0:9999" RegistryVersion: 2 +ReceiptsVersion: 2 LogDestination: 'file://loom2.log' EVMAccountsEnabled: true TransferGateway: diff --git a/src/client.ts b/src/client.ts index 9655ed8b..8550ea99 100644 --- a/src/client.ts +++ b/src/client.ts @@ -448,9 +448,12 @@ export class Client extends EventEmitter { * @param txHash Transaction hash returned by call transaction. * @return EvmTxReceipt The corresponding transaction receipt. */ - async getEvmTxReceiptAsync(txHash: Uint8Array): Promise { + async getEvmTxReceiptAsync(txHashArr: Uint8Array): Promise { + const txHash = Uint8ArrayToB64(txHashArr) + debugLog(`Get EVM receipt for ${txHash}`) + const result = await this._readClient.sendAsync('evmtxreceipt', { - txHash: Uint8ArrayToB64(txHash) + txHash }) if (result) { return EvmTxReceipt.deserializeBinary(bufferToProtobufBytes(B64ToUint8Array(result))) @@ -646,8 +649,11 @@ export class Client extends EventEmitter { hashHexStr: string, full: boolean = true ): Promise { + const hash = Buffer.from(hashHexStr.slice(2), 'hex').toString('base64') + debugLog(`Evm block by hash ${hash}`) + const result = await this._readClient.sendAsync('getevmblockbyhash', { - hash: Buffer.from(hashHexStr.slice(2), 'hex').toString('base64'), + hash, full }) if (result) { diff --git a/src/internal/ws-rpc-client.ts b/src/internal/ws-rpc-client.ts index d5653619..555187a9 100644 --- a/src/internal/ws-rpc-client.ts +++ b/src/internal/ws-rpc-client.ts @@ -198,7 +198,7 @@ export class WSRPCClient extends EventEmitter { */ async sendAsync(method: string, params: object | any[]): Promise { await this.ensureConnectionAsync() - log(`Sending RPC msg to ${this.url}, method ${method}`) + log(`Sending RPC msg to ${this.url}, method ${method} with params ${JSON.stringify(params)}`) return this._client.call(method, params, this.requestTimeout) } diff --git a/src/loom-provider.ts b/src/loom-provider.ts index fd8c764f..3ee7c6cc 100644 --- a/src/loom-provider.ts +++ b/src/loom-provider.ts @@ -34,49 +34,116 @@ import { } from './crypto-utils' import { soliditySha3 } from './solidity-helpers' import { marshalBigUIntPB } from './big-uint' -import { SignedEthTxMiddleware } from './middleware' +// Based on https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt export interface IEthReceipt { transactionHash: string transactionIndex: string blockHash: string blockNumber: string - gasUsed: string + + // From and To sometimes aren't available + from?: string + to?: string + cumulativeGasUsed: string + gasUsed: string contractAddress: string logs: Array + logsBloom?: string + root?: string status: string } +// Based on https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionbyhash export interface IEthTransaction { - hash: string - nonce: string blockHash: string blockNumber: string - transactionIndex: string from: string - to: string - value: string - gasPrice: string gas: string + gasPrice: string + hash: string input: string + nonce: string + to: string + transactionIndex: string + value: string + v?: string + r?: string + s?: string } +// Based on https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbyhash export interface IEthBlock { - blockNumber: string - transactionHash: string + number: string | null + hash: string parentHash: string + nonce: string + sha3Uncles: string logsBloom: string - timestamp: number - transactions: Array + transactionsRoot: string + stateRoot: string + receiptsRoot: string + miner: string + difficulty: string + totalDifficulty: string + extraData: string + size: string + gasLimit: string + gasUsed: string + timestamp: string | number + transactions: Array + uncles: Array + + // Non strict + blockNumber?: string + transactionHash?: string } -export interface IEthRPCPayload { - id: number - method: string - params: Array +// Based on https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB#newheads +export interface IEthPubSubNewHeads { + jsonrpc: '2.0' + method: 'eth_subscription' + params: { + subscription: string + result: { + difficulty: string + extraData: string + gasLimit: string + gasUsed: string + logsBloom: string + miner: string + nonce: string + number: string + parentHash: string + receiptRoot: string + sha3Uncles: string + timestamp: string + transactionsRoot: string + } + } } +// Based on https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB#logs +export interface IEthPubLogs { + jsonrpc: '2.0' + method: 'eth_subscription' + params: { + subscription: string + result: { + address: string + blockHash: string + blockNumber: string + data: string + logIndex: string + topics: Array + transactionHash: string + transactionIndex: string + } + } +} + +// Based on https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges export interface IEthFilterLog { removed: boolean logIndex: string @@ -87,6 +154,15 @@ export interface IEthFilterLog { address: string data: string topics: Array + // blockTime is an addition isn't part of the specification + blockTime?: number +} + +// JSON RPC payload +export interface IEthRPCPayload { + id: number + method: string + params: Array } export type SetupMiddlewareFunction = ( @@ -107,6 +183,37 @@ const numberToHexLC = (num: number): string => { return numberToHex(num).toLowerCase() } +const MINED = 'mined' + +const ZEROED_HEX_256 = + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' +const ZEROED_HEX_32 = '0x0000000000000000000000000000000000000000000000000000000000000000' +const ZEROED_HEX_20 = '0x0000000000000000000000000000000000000000' +const ZEROED_HEX_8 = '0x0000000000000000' +const ZEROED_HEX = '0x0' + +const BLOCK_ZERO: IEthBlock = { + number: ZEROED_HEX, + hash: '0x0000000000000000000000000000000000000000000000000000000000000001', + parentHash: ZEROED_HEX_32, + nonce: ZEROED_HEX_8, + sha3Uncles: ZEROED_HEX_32, + logsBloom: ZEROED_HEX_256, + transactionsRoot: ZEROED_HEX_32, + stateRoot: ZEROED_HEX_32, + receiptsRoot: ZEROED_HEX_32, + miner: ZEROED_HEX_20, + difficulty: ZEROED_HEX, + totalDifficulty: ZEROED_HEX, + extraData: ZEROED_HEX, + size: ZEROED_HEX, + gasLimit: ZEROED_HEX, + gasUsed: ZEROED_HEX, + timestamp: '0x5af97a40', + transactions: [], + uncles: [] +} + /** * Web3 provider that interacts with EVM contracts deployed on Loom DAppChains. */ @@ -120,6 +227,12 @@ export class LoomProvider { protected notificationCallbacks: Array readonly accounts: Map + /** + * Strict mode true remove any param on JSON RPC that isn't compliant with the + * official Ethereum RPC docs https://en.ethereum.wiki/json-rpc + */ + private _strict: boolean = false + /** * The retry strategy that should be used to retry some web3 requests. * By default failed requested won't be resent. @@ -174,6 +287,22 @@ export class LoomProvider { this.addAccounts([privateKey]) } + // Get strict + get strict() { + return this._strict + } + + /** + * Setter to the strict mode + */ + set strict(v: boolean) { + this._strict = v + } + + get accountMiddlewares(): Map> { + return this._accountMiddlewares + } + /** * Creates new accounts by passing the private key array * @@ -205,10 +334,6 @@ export class LoomProvider { this._accountMiddlewares.set(address.toLowerCase(), middlewares) } - get accountMiddlewares(): Map> { - return this._accountMiddlewares - } - // PUBLIC FUNCTION TO SUPPORT WEB3 on(type: string, callback: any) { @@ -440,6 +565,10 @@ export class LoomProvider { const blockHash = payload.params[0] const isFull = payload.params[1] || true + if (blockHash === ZEROED_HEX_32) { + return Promise.resolve(BLOCK_ZERO) + } + const result = await this._client.getEvmBlockByHashAsync(blockHash, isFull) if (!result) { @@ -453,6 +582,11 @@ export class LoomProvider { const blockNumberToSearch = payload.params[0] === 'latest' ? payload.params[0] : hexToNumber(payload.params[0]) const isFull = payload.params[1] || true + + if (blockNumberToSearch === 0) { + return Promise.resolve(BLOCK_ZERO) + } + const result = await this._client.getEvmBlockByNumberAsync(`${blockNumberToSearch}`, isFull) if (!result) { @@ -467,13 +601,9 @@ export class LoomProvider { this._client.chainId, LocalAddress.fromHexString(payload.params[0]) ) - const result = await this._client.getEvmCodeAsync(address) - if (!result) { - throw Error('No code returned on eth_getCode') - } - - return bytesToHexAddrLC(result) + const result = await this._client.getEvmCodeAsync(address) + return result ? bytesToHexAddrLC(result) : '0x0' } private async _ethGetFilterChanges(payload: IEthRPCPayload) { @@ -535,6 +665,7 @@ export class LoomProvider { }) }) }) + return this._createReceiptResult(receipt!) } @@ -698,12 +829,13 @@ export class LoomProvider { return this._client.queryAsync(address, data, VMType.EVM, caller) } - private _createBlockInfo(blockInfo: EthBlockInfo, isFull: boolean): any { - const blockNumber = numberToHexLC(blockInfo.getNumber()) - const transactionHash = bytesToHexAddrLC(blockInfo.getHash_asU8()) - const parentHash = bytesToHexAddrLC(blockInfo.getParentHash_asU8()) + private _createBlockInfo(blockInfo: EthBlockInfo, isFull: boolean): IEthBlock { + // tslint:disable-next-line:variable-name + const number = numberToHexLC(blockInfo.getNumber()) + const hash = bytesToHexAddrLC(blockInfo.getHash_asU8()) + let parentHash = bytesToHexAddrLC(blockInfo.getParentHash_asU8()) const logsBloom = bytesToHexAddrLC(blockInfo.getLogsBloom_asU8()) - const timestamp = blockInfo.getTimestamp() + const timestamp = numberToHexLC(blockInfo.getTimestamp()) const transactions = blockInfo.getTransactionsList_asU8().map((transaction: Uint8Array) => { if (isFull) { return this._createTransactionResult( @@ -714,17 +846,45 @@ export class LoomProvider { } }) - return { - blockNumber, - transactionHash, + // Parent hash is empty for the block 0x1 so this fix it + if (parentHash === '0x' && number === '0x1') { + parentHash = '0x0000000000000000000000000000000000000000000000000000000000000001' + } + + // Some ZEROED values aren't available at the moment + const logResult = { + number, + hash, parentHash, + nonce: ZEROED_HEX_8, + sha3Uncles: ZEROED_HEX_32, logsBloom, + transactionsRoot: ZEROED_HEX_32, + stateRoot: ZEROED_HEX_32, + receiptsRoot: ZEROED_HEX_32, + miner: ZEROED_HEX_20, + difficulty: ZEROED_HEX, + totalDifficulty: ZEROED_HEX, + extraData: ZEROED_HEX, + size: ZEROED_HEX, + gasLimit: ZEROED_HEX, + gasUsed: ZEROED_HEX, timestamp, transactions, - gasLimit: '0x0', - gasUsed: '0x0', - size: '0x0', - number: '0x0' + uncles: [] + } as IEthBlock + + if (this._strict) { + return logResult + } else { + return { + ...logResult, + ...{ + blockNumber: numberToHexLC(blockInfo.getNumber()), + transactionHash: bytesToHexAddrLC(blockInfo.getHash_asU8()), + timestamp: blockInfo.getTimestamp() + } + } } } @@ -736,24 +896,39 @@ export class LoomProvider { const transactionIndex = numberToHexLC(txObject.getTransactionIndex()) const from = bytesToHexAddrLC(txObject.getFrom_asU8()) const to = bytesToHexAddrLC(txObject.getTo_asU8()) - const value = `${txObject.getValue()}` + const value = numberToHexLC(txObject.getValue()) const gas = numberToHexLC(txObject.getGas()) const gasPrice = numberToHexLC(txObject.getGasPrice()) - const input = bytesToHexAddrLC(txObject.getInput_asU8()) + let input = bytesToHexAddrLC(txObject.getInput_asU8()) - return { - hash, - nonce, + if (input === '0x') { + input = ZEROED_HEX + } + + const logResult = { blockHash, blockNumber, - transactionIndex, from, - to, - value, gas, gasPrice, - input + hash, + input, + nonce, + to, + transactionIndex, + value } as IEthTransaction + + if (this._strict) { + return logResult + } else { + return { + ...logResult, + ...{ + value: `${txObject.getValue()}` + } + } + } } private _createReceiptResult(receipt: EvmTxReceipt): IEthReceipt { @@ -761,39 +936,51 @@ export class LoomProvider { const transactionIndex = numberToHexLC(receipt.getTransactionIndex()) const blockHash = bytesToHexAddrLC(receipt.getBlockHash_asU8()) const blockNumber = numberToHexLC(receipt.getBlockNumber()) + const cumulativeGasUsed = numberToHexLC(receipt.getCumulativeGasUsed()) + const gasUsed = numberToHexLC(receipt.getGasUsed()) const contractAddress = bytesToHexAddrLC(receipt.getContractAddress_asU8()) const logs = receipt.getLogsList().map((logEvent: EventData, index: number) => { const logIndex = numberToHexLC(index) let data = bytesToHexAddrLC(logEvent.getEncodedBody_asU8()) if (data === '0x') { - data = '0x0' + data = ZEROED_HEX_32 } - return { + const blockHash = bytesToHexAddrLC(receipt.getBlockHash_asU8()) + + const log = { logIndex, address: contractAddress, blockHash, blockNumber, - blockTime: logEvent.getBlockTime(), transactionHash: bytesToHexAddrLC(logEvent.getTxHash_asU8()), transactionIndex, - type: 'mined', + type: MINED, data, topics: logEvent.getTopicsList().map((topic: string) => topic.toLowerCase()) } + + return this._strict ? log : Object.assign({}, log, { blockTime: logEvent.getBlockTime() }) }) + const status = numberToHexLC(receipt.getStatus()) + + // Commented properties aren't supported at the current version return { transactionHash, transactionIndex, blockHash, blockNumber, + // from, + // to, + cumulativeGasUsed, + gasUsed, contractAddress, - gasUsed: numberToHexLC(receipt.getGasUsed()), - cumulativeGasUsed: numberToHexLC(receipt.getCumulativeGasUsed()), logs, - status: numberToHexLC(receipt.getStatus()) + // logsBloom, + // root, + status } as IEthReceipt } @@ -814,7 +1001,7 @@ export class LoomProvider { const value = numberToHexLC(transaction.getValue()) const gasPrice = numberToHexLC(transaction.getGasPrice()) const gas = numberToHexLC(transaction.getGas()) - const input = '0x0' + const input = ZEROED_HEX return { hash, @@ -832,18 +1019,38 @@ export class LoomProvider { } private _createLogResult(log: EthFilterLog): IEthFilterLog { - return { - removed: log.getRemoved(), - blockTime: log.getBlockTime(), - logIndex: numberToHexLC(log.getLogIndex()), - transactionIndex: numberToHex(log.getTransactionIndex()), - transactionHash: bytesToHexAddrLC(log.getTransactionHash_asU8()), - blockHash: bytesToHexAddrLC(log.getBlockHash_asU8()), - blockNumber: numberToHex(log.getBlockNumber()), - address: bytesToHexAddrLC(log.getAddress_asU8()), - data: bytesToHexAddrLC(log.getData_asU8()), - topics: log.getTopicsList().map((topic: any) => String.fromCharCode.apply(null, topic)) + const removed = log.getRemoved() + const logIndex = numberToHexLC(log.getLogIndex()) + const transactionIndex = numberToHex(log.getTransactionIndex()) + const transactionHash = bytesToHexAddrLC(log.getTransactionHash_asU8()) + const blockHash = bytesToHexAddrLC(log.getBlockHash_asU8()) + const blockNumber = numberToHex(log.getBlockNumber()) + const address = bytesToHexAddrLC(log.getAddress_asU8()) + const data = bytesToHexAddrLC(log.getData_asU8()) + const topics = log.getTopicsList().map((topic: any) => String.fromCharCode.apply(null, topic)) + + const logResult = { + removed, + logIndex, + transactionIndex, + transactionHash, + blockHash, + blockNumber, + address, + data, + topics } as IEthFilterLog + + if (this._strict) { + return logResult + } else { + return { + ...logResult, + ...{ + blockTime: log.getBlockTime() + } + } + } } private async _getLogs(filterObject: Object): Promise> { @@ -863,24 +1070,29 @@ export class LoomProvider { if (msgEvent.kind === ClientEvent.EVMEvent) { log(`Socket message arrived ${JSON.stringify(msgEvent)}`) this.notificationCallbacks.forEach((callback: Function) => { + const transactionHash = bytesToHexAddrLC(msgEvent.transactionHashBytes) + const blockNumber = numberToHexLC(+msgEvent.blockHeight) + const data = bytesToHexAddrLC(msgEvent.data) + + // Some 0x0 values aren't at the moment const JSONRPCResult = { jsonrpc: '2.0', method: 'eth_subscription', params: { subscription: msgEvent.id, result: { - transactionHash: bytesToHexAddrLC(msgEvent.transactionHashBytes), - logIndex: '0x0', - transactionIndex: '0x0', - blockHash: '0x0', - blockNumber: numberToHexLC(+msgEvent.blockHeight), + transactionHash, + logIndex: ZEROED_HEX, + transactionIndex: ZEROED_HEX, + blockHash: ZEROED_HEX, + blockNumber, address: msgEvent.contractAddress.local.toString(), - type: 'mined', - data: bytesToHexAddrLC(msgEvent.data), + type: MINED, + data, topics: msgEvent.topics } } - } + } as IEthPubLogs callback(JSONRPCResult) }) diff --git a/src/tests/e2e/loom-provider-subscribe.ts b/src/tests/e2e/loom-provider-subscribe.ts index abb412ed..3e052dc0 100644 --- a/src/tests/e2e/loom-provider-subscribe.ts +++ b/src/tests/e2e/loom-provider-subscribe.ts @@ -1,39 +1,15 @@ import test from 'tape' import { LocalAddress, CryptoUtils } from '../../index' -import { createTestClient, execAndWaitForMillisecondsAsync } from '../helpers' +import { + createTestClient, + execAndWaitForMillisecondsAsync, + waitForMillisecondsAsync +} from '../helpers' import { LoomProvider } from '../../loom-provider' import { deployContract } from '../evm-helpers' -/** - * Requires the SimpleStore solidity contract deployed on a loomchain. - * go-loom/examples/plugins/evmexample/contract/SimpleStore.sol - * - * pragma solidity ^0.4.22; - * - * contract SimpleStore { - * uint value; - * - * constructor() { - * value = 10; - * } - * - * event NewValueSet(uint _value); - * - * function set(uint _value) public { - * value = _value; - * emit NewValueSet(value); - * } - * - * function get() public view returns (uint) { - * return value; - * } - * } - * - */ - -const contractData = - '608060405234801561001057600080fd5b50600a600081905550610114806100286000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c14606c575b600080fd5b606a600480360381019080803590602001909291905050506094565b005b348015607757600080fd5b50607e60df565b6040518082815260200191505060405180910390f35b806000819055507f2afa03c814297ffc234ff967b6f0863d3c358be243103f20217c8d3a4d39f9c060005434604051808381526020018281526020019250505060405180910390a150565b600080549050905600a165627a7a72305820deed812a797567167162d0af3ae5f0528c39bea0620e32b28e243628cd655dc40029' +const Web3 = require('web3') test('LoomProvider + Subscribe', async t => { let client @@ -41,12 +17,46 @@ test('LoomProvider + Subscribe', async t => { const privKey = CryptoUtils.generatePrivateKey() client = createTestClient() client.on('error', msg => console.error('error', msg)) - const fromAddr = LocalAddress.fromPublicKey( + const from = LocalAddress.fromPublicKey( CryptoUtils.publicKeyFromPrivateKey(privKey) ).toString() const loomProvider = new LoomProvider(client, privKey) - const contractDeployResult = await deployContract(loomProvider, contractData) + const ABI = [ + { + constant: false, + inputs: [{ name: '_value', type: 'uint256' }], + name: 'set', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'get', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { inputs: [], payable: false, stateMutability: 'nonpayable', type: 'constructor' }, + { + anonymous: false, + inputs: [{ indexed: true, name: '_value', type: 'uint256' }], + name: 'NewValueSet', + type: 'event' + } + ] + + const contractData = + '608060405234801561001057600080fd5b50600a600081905550610114806100286000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c14606c575b600080fd5b606a600480360381019080803590602001909291905050506094565b005b348015607757600080fd5b50607e60df565b6040518082815260200191505060405180910390f35b806000819055507f2afa03c814297ffc234ff967b6f0863d3c358be243103f20217c8d3a4d39f9c060005434604051808381526020018281526020019250505060405180910390a150565b600080549050905600a165627a7a72305820deed812a797567167162d0af3ae5f0528c39bea0620e32b28e243628cd655dc40029' + + const result = await deployContract(loomProvider, contractData) + const web3 = new Web3(loomProvider) + const contract = new web3.eth.Contract(ABI, result.contractAddress, { from }) + const id = 1 const ethSubscribeNewHardsResult = await execAndWaitForMillisecondsAsync( @@ -63,6 +73,10 @@ test('LoomProvider + Subscribe', async t => { 'Filter identification should be returned on eth_subscribe' ) + await waitForMillisecondsAsync(5000) + + await contract.methods.set(1).send({ from }) + const ethUnsubscribeNewHeadsResult = await execAndWaitForMillisecondsAsync( loomProvider.sendAsync({ id, diff --git a/src/tests/e2e/loom-provider-tests.ts b/src/tests/e2e/loom-provider-tests.ts index 4ce1a3bf..83bdebec 100644 --- a/src/tests/e2e/loom-provider-tests.ts +++ b/src/tests/e2e/loom-provider-tests.ts @@ -12,20 +12,24 @@ import { deployContract } from '../evm-helpers' import { ecrecover, privateToPublic, fromRpcSig, toBuffer } from 'ethereumjs-util' import { soliditySha3 } from '../../solidity-helpers' import { bytesToHexAddr } from '../../crypto-utils' +const Web3 = require('web3') /** * Requires the SimpleStore solidity contract deployed on a loomchain. * go-loom/examples/plugins/evmexample/contract/SimpleStore.sol * * pragma solidity ^0.4.18; + * * contract SimpleStore { - * function set(uint _value) public { - * value = _value; - * } - * function get() public constant returns (uint) { - * return value; - * } - * uint value; + * uint value; + * + * function set(uint _value) public { + * value = _value; + * } + * + * function get() public constant returns (uint) { + * return value; + * } * } * */ @@ -632,3 +636,105 @@ test('LoomProvider overwriting existing method', async t => { t.end() }) + +test('LoomProvider + BlockByNumber transaction issue', async t => { + let client + + try { + const privKey = CryptoUtils.generatePrivateKey() + client = createTestClient() + const from = LocalAddress.fromPublicKey( + CryptoUtils.publicKeyFromPrivateKey(privKey) + ).toString() + client.on('error', msg => console.error('error', msg)) + const loomProvider = new LoomProvider(client, privKey) + + const contractData = + '0x608060405234801561001057600080fd5b50600a60008190555061010e806100286000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60d9565b6040518082815260200191505060405180910390f35b806000819055506000547fb922f092a64f1a076de6f21e4d7c6400b6e55791cc935e7bb8e7e90f7652f15b60405160405180910390a250565b600080549050905600a165627a7a72305820b76f6c855a1f95260fc70490b16774074225da52ea165a58e95eb7a72a59d1700029' + + const ABI = [ + { + constant: false, + inputs: [{ name: '_value', type: 'uint256' }], + name: 'set', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: true, + inputs: [], + name: 'get', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function' + }, + { inputs: [], payable: false, stateMutability: 'nonpayable', type: 'constructor' }, + { + anonymous: false, + inputs: [{ indexed: true, name: '_value', type: 'uint256' }], + name: 'NewValueSet', + type: 'event' + } + ] + + const result = await deployContract(loomProvider, contractData) + + const web3 = new Web3(loomProvider) + const contract = new web3.eth.Contract(ABI, result.contractAddress, { from }) + + // Just call a contract to call an event on loomchain + const tx = await contract.methods.set(1).send({ from }) + + const ethGetBlockByNumberResult = await execAndWaitForMillisecondsAsync( + loomProvider.sendAsync({ + id: 1000, + method: 'eth_getBlockByNumber', + params: [`0x${tx.blockNumber.toString(16)}`] + }) + ) + + t.equal( + ethGetBlockByNumberResult.result.number, + `0x${tx.blockNumber.toString(16)}`, + 'Should block returned by the number match' + ) + + t.assert( + ethGetBlockByNumberResult.result.transactions.length > 0, + 'Should exists transactions on block' + ) + + const ehtGetBlockByHashResult = await execAndWaitForMillisecondsAsync( + loomProvider.sendAsync({ + id: 1000, + method: 'eth_getBlockByHash', + params: [tx.blockHash] + }) + ) + + t.equal( + ehtGetBlockByHashResult.result.hash, + tx.blockHash, + 'Should block returned by the hash match' + ) + + t.assert( + ehtGetBlockByHashResult.result.transactions.length > 0, + 'Should exists transactions on block' + ) + + // Waiting for newHeads events appear on log + await waitForMillisecondsAsync(5000) + } catch (err) { + console.log(err) + } + + if (client) { + client.disconnect() + } + + t.end() +}) diff --git a/src/tests/e2e/loom-provider-web3-tests.ts b/src/tests/e2e/loom-provider-web3-tests.ts index 7cfa4297..c87c565a 100644 --- a/src/tests/e2e/loom-provider-web3-tests.ts +++ b/src/tests/e2e/loom-provider-web3-tests.ts @@ -222,7 +222,11 @@ test('LoomProvider + Web3 + getBlockByNumber', async t => { try { const blockNumber = await web3.eth.getBlockNumber() const blockInfo = await web3.eth.getBlock(blockNumber, false) - t.equal(parseInt(blockInfo.blockNumber, 16), blockNumber, 'Block number should be equal') + t.equal( + parseInt(blockInfo.number, 16), + parseInt(blockNumber, 16), + 'Block number should be equal' + ) } catch (err) { console.log(err) } diff --git a/src/tests/evm-helpers.ts b/src/tests/evm-helpers.ts index d58a3e34..3a401c75 100644 --- a/src/tests/evm-helpers.ts +++ b/src/tests/evm-helpers.ts @@ -14,7 +14,7 @@ export async function deployContract(loomProvider: LoomProvider, contractData: s ).toString() const ethSendTransactionDeployResult = await loomProvider.sendAsync({ - id: 1, + id: 100, method: 'eth_sendTransaction', params: [ { @@ -28,7 +28,7 @@ export async function deployContract(loomProvider: LoomProvider, contractData: s }) const ethGetTransactionReceiptResult = await loomProvider.sendAsync({ - id: 2, + id: 200, method: 'eth_getTransactionReceipt', params: [ethSendTransactionDeployResult.result] })