mirror of https://github.com/embarklabs/embark.git
add contract logs
This commit is contained in:
parent
c7f9a374f2
commit
031ccc37ee
|
@ -76,6 +76,13 @@ export const processLogs = {
|
|||
failure: (error) => action(PROCESS_LOGS[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const CONTRACT_LOGS = createRequestTypes('CONTRACT_LOGS');
|
||||
export const contractLogs = {
|
||||
request: () => action(CONTRACT_LOGS[REQUEST]),
|
||||
success: (contractLogs) => action(CONTRACT_LOGS[SUCCESS], {contractLogs}),
|
||||
failure: (error) => action(CONTRACT_LOGS[FAILURE], {error})
|
||||
};
|
||||
|
||||
export const CONTRACTS = createRequestTypes('CONTRACTS');
|
||||
export const contracts = {
|
||||
request: () => action(CONTRACTS[REQUEST]),
|
||||
|
|
|
@ -56,6 +56,10 @@ export function fetchProcessLogs(payload) {
|
|||
return get(`/process-logs/${payload.processName}`);
|
||||
}
|
||||
|
||||
export function fetchContractLogs() {
|
||||
return get(`/contracts/logs`);
|
||||
}
|
||||
|
||||
export function fetchContracts() {
|
||||
return get('/contracts');
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "tabler-react";
|
||||
|
||||
import ContractContainer from '../containers/ContractContainer';
|
||||
import ContractLoggerContainer from '../containers/ContractLoggerContainer';
|
||||
import ContractProfileContainer from '../containers/ContractProfileContainer';
|
||||
|
||||
const ContractLayout = (props) => (
|
||||
|
@ -48,11 +49,20 @@ const ContractLayout = (props) => (
|
|||
>
|
||||
Profile
|
||||
</List.GroupItem>
|
||||
<List.GroupItem
|
||||
className="d-flex align-items-center"
|
||||
to={`/embark/contracts/${props.match.params.contractName}/logger`}
|
||||
icon="chevrons-right"
|
||||
RootComponent={withRouter(NavLink)}
|
||||
>
|
||||
Logger
|
||||
</List.GroupItem>
|
||||
</List.Group>
|
||||
</div>
|
||||
</Grid.Col>
|
||||
<Grid.Col md={9}>
|
||||
<Switch>
|
||||
<Route exact path="/embark/contracts/:contractName/logger" component={ContractLoggerContainer} />
|
||||
<Route exact path="/embark/contracts/:contractName/profiler" component={ContractProfileContainer} />
|
||||
<ContractContainer />
|
||||
</Switch>
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import PropTypes from "prop-types";
|
||||
import React from 'react';
|
||||
import {
|
||||
Page,
|
||||
Grid, Table
|
||||
} from "tabler-react";
|
||||
|
||||
const ContractLogger = ({contractName, contractLogs}) => (
|
||||
<Page.Content title={contractName + ' Logger'}>
|
||||
<Grid.Row>
|
||||
<Grid.Col>
|
||||
<Table
|
||||
responsive
|
||||
className="card-table table-vcenter text-nowrap"
|
||||
headerItems={[
|
||||
{content: "Function name"},
|
||||
{content: "Params"},
|
||||
{content: "Transaction hash"},
|
||||
{content: "Gas Used"},
|
||||
{content: "Block number"},
|
||||
{content: "Status"}
|
||||
]}
|
||||
bodyItems={
|
||||
contractLogs.map((log) => {
|
||||
return ([
|
||||
{content: log.functionName},
|
||||
{content: log.paramString},
|
||||
{content: log.transactionHash},
|
||||
{content: log.gasUsed},
|
||||
{content: log.blockNumber},
|
||||
{content: log.status}
|
||||
]);
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid.Row>
|
||||
</Page.Content>
|
||||
);
|
||||
|
||||
ContractLogger.propTypes = {
|
||||
contractName: PropTypes.string.isRequired,
|
||||
contractLogs: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
export default ContractLogger;
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {withRouter} from 'react-router-dom';
|
||||
import {contractLogs as contractLogsAction} from '../actions';
|
||||
|
||||
import ContractLogger from '../components/ContractLogger';
|
||||
// import DataWrapper from "../components/DataWrapper";
|
||||
import {getContractLogsByContract} from "../reducers/selectors";
|
||||
|
||||
class ContractProfileContainer extends Component {
|
||||
componentDidMount() {
|
||||
if (this.props.contractLogs.length === 0) {
|
||||
this.props.fetchContractLogs(this.props.match.params.contractName);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ContractLogger contractLogs={this.props.contractLogs} contractName={this.props.match.params.contractName}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
contractLogs: getContractLogsByContract(state, props.match.params.contractName)
|
||||
};
|
||||
}
|
||||
|
||||
ContractProfileContainer.propTypes = {
|
||||
contractLogs: PropTypes.array,
|
||||
fetchContractLogs: PropTypes.func,
|
||||
match: PropTypes.object
|
||||
};
|
||||
|
||||
export default withRouter(connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
fetchContractLogs: contractLogsAction.request
|
||||
}
|
||||
)(ContractProfileContainer));
|
|
@ -11,6 +11,7 @@ const entitiesDefaultState = {
|
|||
processLogs: [],
|
||||
contracts: [],
|
||||
contractProfiles: [],
|
||||
contractLogs: [],
|
||||
commands: [],
|
||||
messages: [],
|
||||
messageChannels: [],
|
||||
|
@ -27,6 +28,9 @@ const sorter = {
|
|||
processLogs: function(a, b) {
|
||||
return a.timestamp - b.timestamp;
|
||||
},
|
||||
contractLogs: function(a, b) {
|
||||
return a.timestamp - b.timestamp;
|
||||
},
|
||||
messages: function(a, b) {
|
||||
return a.time - b.time;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,11 @@ export function getProcessLogsByProcess(state, processName) {
|
|||
return state.entities.processLogs.filter((processLog => processLog.name === processName));
|
||||
}
|
||||
|
||||
export function getContractLogsByContract(state, contractName) {
|
||||
return state.entities.contractLogs;
|
||||
// return state.entities.processLogs.filter((processLog => processLog.name === processName));
|
||||
}
|
||||
|
||||
export function getContracts(state) {
|
||||
return state.entities.contracts;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import {eventChannel} from 'redux-saga';
|
|||
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects';
|
||||
|
||||
const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs,
|
||||
contracts, contract, contractProfile, messageSend, messageVersion, messageListen} = actions;
|
||||
contracts, contract, contractProfile, messageSend, messageVersion, messageListen, contractLogs} = actions;
|
||||
|
||||
function *doRequest(entity, apiFn, payload) {
|
||||
const {response, error} = yield call(apiFn, payload);
|
||||
|
@ -24,6 +24,7 @@ export const fetchTransactions = doRequest.bind(null, transactions, api.fetchTra
|
|||
export const fetchProcesses = doRequest.bind(null, processes, api.fetchProcesses);
|
||||
export const postCommand = doRequest.bind(null, commands, api.postCommand);
|
||||
export const fetchProcessLogs = doRequest.bind(null, processLogs, api.fetchProcessLogs);
|
||||
export const fetchContractLogs = doRequest.bind(null, contractLogs, api.fetchContractLogs);
|
||||
export const fetchContracts = doRequest.bind(null, contracts, api.fetchContracts);
|
||||
export const fetchContract = doRequest.bind(null, contract, api.fetchContract);
|
||||
export const fetchContractProfile = doRequest.bind(null, contractProfile, api.fetchContractProfile);
|
||||
|
@ -64,6 +65,10 @@ export function *watchFetchProcessLogs() {
|
|||
yield takeEvery(actions.PROCESS_LOGS[actions.REQUEST], fetchProcessLogs);
|
||||
}
|
||||
|
||||
export function *watchFetchContractLogs() {
|
||||
yield takeEvery(actions.CONTRACT_LOGS[actions.REQUEST], fetchContractLogs);
|
||||
}
|
||||
|
||||
export function *watchFetchContract() {
|
||||
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
|
||||
}
|
||||
|
@ -146,6 +151,7 @@ export default function *root() {
|
|||
fork(watchFetchAccount),
|
||||
fork(watchFetchProcesses),
|
||||
fork(watchFetchProcessLogs),
|
||||
fork(watchFetchContractLogs),
|
||||
fork(watchListenToProcessLogs),
|
||||
fork(watchFetchBlock),
|
||||
fork(watchFetchTransactions),
|
||||
|
|
|
@ -8,120 +8,122 @@ let receipts = {};
|
|||
|
||||
const {canonicalHost, defaultHost} = require('../utils/host');
|
||||
|
||||
const parseRequest = function(reqBody){
|
||||
let jsonO;
|
||||
try {
|
||||
jsonO = JSON.parse(reqBody);
|
||||
} catch(e){
|
||||
return; // Request is not a json. Do nothing
|
||||
}
|
||||
if(jsonO.method === "eth_sendTransaction"){
|
||||
commList[jsonO.id] = {
|
||||
type: 'contract-log',
|
||||
address: jsonO.params[0].to,
|
||||
data: jsonO.params[0].data
|
||||
};
|
||||
} else if(jsonO.method === "eth_getTransactionReceipt"){
|
||||
if(transactions[jsonO.params[0]]){
|
||||
transactions[jsonO.params[0]].receiptId = jsonO.id;
|
||||
receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
|
||||
}
|
||||
const parseRequest = function(reqBody) {
|
||||
let jsonO;
|
||||
try {
|
||||
jsonO = JSON.parse(reqBody);
|
||||
} catch (e) {
|
||||
return; // Request is not a json. Do nothing
|
||||
}
|
||||
if (jsonO.method === "eth_sendTransaction") {
|
||||
commList[jsonO.id] = {
|
||||
type: 'contract-log',
|
||||
address: jsonO.params[0].to,
|
||||
data: jsonO.params[0].data
|
||||
};
|
||||
} else if (jsonO.method === "eth_getTransactionReceipt") {
|
||||
if (transactions[jsonO.params[0]]) {
|
||||
transactions[jsonO.params[0]].receiptId = jsonO.id;
|
||||
receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const parseResponse = function(ipc, resBody){
|
||||
let jsonO;
|
||||
try {
|
||||
jsonO = JSON.parse(resBody);
|
||||
} catch(e) {
|
||||
return; // Response is not a json. Do nothing
|
||||
const parseResponse = function(ipc, resBody) {
|
||||
let jsonO;
|
||||
try {
|
||||
jsonO = JSON.parse(resBody);
|
||||
} catch (e) {
|
||||
return; // Response is not a json. Do nothing
|
||||
}
|
||||
|
||||
if (commList[jsonO.id]) {
|
||||
commList[jsonO.id].transactionHash = jsonO.result;
|
||||
transactions[jsonO.result] = {commListId: jsonO.id};
|
||||
} else if (receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber) {
|
||||
commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
|
||||
commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
|
||||
commList[receipts[jsonO.id]].status = jsonO.result.status;
|
||||
|
||||
if (ipc.connected && !ipc.connecting) {
|
||||
ipc.request('log', commList[receipts[jsonO.id]]);
|
||||
} else {
|
||||
ipc.connecting = true;
|
||||
ipc.connect(() => {
|
||||
ipc.connecting = false;
|
||||
});
|
||||
}
|
||||
|
||||
if(commList[jsonO.id]){
|
||||
commList[jsonO.id].transactionHash = jsonO.result;
|
||||
transactions[jsonO.result] = {commListId: jsonO.id};
|
||||
} else if(receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber){
|
||||
commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
|
||||
commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
|
||||
commList[receipts[jsonO.id]].status = jsonO.result.status;
|
||||
|
||||
if(ipc.connected && !ipc.connecting){
|
||||
ipc.request('log', commList[receipts[jsonO.id]]);
|
||||
} else {
|
||||
ipc.connecting = true;
|
||||
ipc.connect(() => {
|
||||
ipc.connecting = false;
|
||||
});
|
||||
}
|
||||
|
||||
delete transactions[commList[receipts[jsonO.id]].transactionHash];
|
||||
delete receipts[jsonO.id];
|
||||
delete commList[jsonO.id];
|
||||
}
|
||||
delete transactions[commList[receipts[jsonO.id]].transactionHash];
|
||||
delete receipts[jsonO.id];
|
||||
delete commList[jsonO.id];
|
||||
}
|
||||
};
|
||||
|
||||
exports.serve = function(ipc, host, port, ws){
|
||||
let proxy = httpProxy.createProxyServer({
|
||||
target: {
|
||||
host: canonicalHost(host),
|
||||
port: port + constants.blockchain.servicePortOnProxy
|
||||
},
|
||||
ws: ws
|
||||
exports.serve = function(ipc, host, port, ws) {
|
||||
let proxy = httpProxy.createProxyServer({
|
||||
target: {
|
||||
host: canonicalHost(host),
|
||||
port: port + constants.blockchain.servicePortOnProxy
|
||||
},
|
||||
ws: ws
|
||||
});
|
||||
|
||||
proxy.on('error', function(e) {
|
||||
console.error(__("Error forwarding requests to blockchain/simulator"), e.message);
|
||||
});
|
||||
|
||||
proxy.on('proxyRes', (proxyRes) => {
|
||||
let resBody = [];
|
||||
proxyRes.on('data', (b) => resBody.push(b));
|
||||
proxyRes.on('end', function() {
|
||||
resBody = Buffer.concat(resBody).toString();
|
||||
if (resBody) {
|
||||
parseResponse(ipc, resBody);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
proxy.on('error', function (e) {
|
||||
console.error(__("Error forwarding requests to blockchain/simulator"), e.message);
|
||||
});
|
||||
|
||||
proxy.on('proxyRes', (proxyRes) => {
|
||||
let resBody = [];
|
||||
proxyRes.on('data', (b) => resBody.push(b));
|
||||
proxyRes.on('end', function () {
|
||||
resBody = Buffer.concat(resBody).toString();
|
||||
if(resBody){
|
||||
parseResponse(ipc, resBody);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let server = http.createServer((req, res) => {
|
||||
let reqBody = [];
|
||||
req.on('data', (b) => { reqBody.push(b); })
|
||||
.on('end', () => {
|
||||
reqBody = Buffer.concat(reqBody).toString();
|
||||
if(reqBody){
|
||||
parseRequest(reqBody);
|
||||
}
|
||||
});
|
||||
|
||||
if(!ws){
|
||||
proxy.web(req, res);
|
||||
let server = http.createServer((req, res) => {
|
||||
let reqBody = [];
|
||||
req.on('data', (b) => {
|
||||
reqBody.push(b);
|
||||
})
|
||||
.on('end', () => {
|
||||
reqBody = Buffer.concat(reqBody).toString();
|
||||
if (reqBody) {
|
||||
parseRequest(reqBody);
|
||||
}
|
||||
});
|
||||
|
||||
if (!ws) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
});
|
||||
|
||||
if (ws) {
|
||||
const WsParser = require('simples/lib/parsers/ws'); // npm install simples
|
||||
|
||||
server.on('upgrade', function(req, socket, head) {
|
||||
proxy.ws(req, socket, head);
|
||||
});
|
||||
|
||||
if(ws){
|
||||
const WsParser = require('simples/lib/parsers/ws'); // npm install simples
|
||||
proxy.on('open', (proxySocket) => {
|
||||
proxySocket.on('data', (data) => {
|
||||
parseResponse(ipc, data.toString().substr(data.indexOf("{")));
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function (req, socket, head) {
|
||||
proxy.ws(req, socket, head);
|
||||
});
|
||||
proxy.on('proxyReqWs', (proxyReq, req, socket) => {
|
||||
var parser = new WsParser(0, false);
|
||||
socket.pipe(parser);
|
||||
parser.on('frame', function(frame) {
|
||||
parseRequest(frame.data);
|
||||
});
|
||||
|
||||
proxy.on('open', (proxySocket) => {
|
||||
proxySocket.on('data', (data) => {
|
||||
parseResponse(ipc, data.toString().substr(data.indexOf("{")));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
proxy.on('proxyReqWs', (proxyReq, req, socket) => {
|
||||
var parser = new WsParser(0, false);
|
||||
socket.pipe(parser);
|
||||
parser.on('frame', function (frame) {
|
||||
parseRequest(frame.data);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
server.listen(port, defaultHost);
|
||||
return server;
|
||||
server.listen(port, defaultHost);
|
||||
return server;
|
||||
};
|
||||
|
|
|
@ -2,10 +2,12 @@ const utils = require('../../utils/utils.js');
|
|||
|
||||
class ConsoleListener {
|
||||
constructor(embark, options) {
|
||||
this.embark = embark;
|
||||
this.logger = embark.logger;
|
||||
this.ipc = options.ipc;
|
||||
this.events = embark.events;
|
||||
this.addressToContract = [];
|
||||
this.logs = [];
|
||||
this.contractsConfig = embark.config.contractsConfig;
|
||||
this.contractsDeployed = false;
|
||||
this.outputDone = false;
|
||||
|
@ -13,6 +15,7 @@ class ConsoleListener {
|
|||
this.events.on('outputDone', () => {
|
||||
this.outputDone = true;
|
||||
});
|
||||
this._registerAPI();
|
||||
|
||||
this.events.on("contractsDeployed", () => {
|
||||
this.contractsDeployed = true;
|
||||
|
@ -91,12 +94,25 @@ class ConsoleListener {
|
|||
gasUsed = utils.hexToNumber(gasUsed);
|
||||
blockNumber = utils.hexToNumber(blockNumber);
|
||||
|
||||
this.logs.push(Object.assign({}, request, {name, functionName, paramString}));
|
||||
|
||||
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
|
||||
} else {
|
||||
this.logger.info(JSON.stringify(request));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_registerAPI() {
|
||||
const apiRoute = '/embark-api/contracts/logs';
|
||||
this.embark.registerAPICall(
|
||||
'get',
|
||||
apiRoute,
|
||||
(req, res) => {
|
||||
res.send(JSON.stringify(this.logs));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConsoleListener;
|
||||
|
|
Loading…
Reference in New Issue