Merge pull request #154 from status-im/debugger_integration_squashed_cherry_picked

Debugger integration squashed cherry picked
This commit is contained in:
Iuri Matias 2018-10-24 13:11:37 -04:00 committed by GitHub
commit b301a0e82b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 26195 additions and 84 deletions

View File

@ -12194,14 +12194,6 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"requires": {
"is-typedarray": "^1.0.0"
}
},
"ua-parser-js": {
"version": "0.7.18",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
@ -12932,8 +12924,19 @@
"integrity": "sha512-wAnENuZx75T5ZSrT2De2LOaUuPf2yRjq1VfcbD7+Zd79F3DZZLBJcPyCNVQ1U0fAXt0wfgCKl7sVw5pffqR9Bw==",
"requires": {
"underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.36",
"websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
"web3-core-helpers": "1.0.0-beta.36"
},
"dependencies": {
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
"typedarray-to-buffer": "^3.1.2",
"yaeti": "^0.0.6"
}
}
}
},
"web3-shh": {
@ -13371,16 +13374,6 @@
"source-map": "~0.6.1"
}
},
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
"typedarray-to-buffer": "^3.1.2",
"yaeti": "^0.0.6"
}
},
"websocket-driver": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
@ -13611,11 +13604,6 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
},
"yaeti": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",

View File

@ -349,6 +349,67 @@ export const web3EstimateGas = {
failure: (error, payload) => action(WEB3_ESTIMAGE_GAS[FAILURE], {web3Error: error, contract: payload.contract})
};
export const START_DEBUG = createRequestTypes('START_DEBUG');
export const startDebug = {
request: (txHash) => action(START_DEBUG[REQUEST], {txHash}),
success: () => action(START_DEBUG[SUCCESS]),
failure: (error) => action(START_DEBUG[FAILURE], {error})
};
export const DEBUG_JUMP_BACK = createRequestTypes('DEBUG_JUMP_BACK');
export const debugJumpBack = {
request: () => action(DEBUG_JUMP_BACK[REQUEST], {}),
success: () => action(DEBUG_JUMP_BACK[SUCCESS]),
failure: (error) => action(DEBUG_JUMP_BACK[FAILURE], {error})
};
export const DEBUG_JUMP_FORWARD = createRequestTypes('DEBUG_JUMP_FORWARD');
export const debugJumpForward = {
request: () => action(DEBUG_JUMP_FORWARD[REQUEST], {}),
success: () => action(DEBUG_JUMP_FORWARD[SUCCESS]),
failure: (error) => action(DEBUG_JUMP_FORWARD[FAILURE], {error})
};
export const DEBUG_STEP_OVER_BACKWARD = createRequestTypes('DEBUG_STEP_OVER_BACKWARD');
export const debugStepOverBackward = {
request: () => action(DEBUG_STEP_OVER_BACKWARD[REQUEST], {}),
success: () => action(DEBUG_STEP_OVER_BACKWARD[SUCCESS]),
failure: (error) => action(DEBUG_STEP_OVER_BACKWARD[FAILURE], {error})
};
export const DEBUG_STEP_OVER_FORWARD = createRequestTypes('DEBUG_STEP_OVER_FORWARD');
export const debugStepOverForward = {
request: () => action(DEBUG_STEP_OVER_FORWARD[REQUEST], {}),
success: () => action(DEBUG_STEP_OVER_FORWARD[SUCCESS]),
failure: (error) => action(DEBUG_STEP_OVER_FORWARD[FAILURE], {error})
};
export const DEBUG_STEP_INTO_BACKWARD = createRequestTypes('DEBUG_STEP_INTO_BACKWARD');
export const debugStepIntoBackward = {
request: () => action(DEBUG_STEP_INTO_BACKWARD[REQUEST], {}),
success: () => action(DEBUG_STEP_INTO_BACKWARD[SUCCESS]),
failure: (error) => action(DEBUG_STEP_INTO_BACKWARD[FAILURE], {error})
};
export const DEBUG_STEP_INTO_FORWARD = createRequestTypes('DEBUG_STEP_INTO_FORWARD');
export const debugStepIntoForward = {
request: () => action(DEBUG_STEP_INTO_FORWARD[REQUEST], {}),
success: () => action(DEBUG_STEP_INTO_FORWARD[SUCCESS]),
failure: (error) => action(DEBUG_STEP_INTO_FORWARD[FAILURE], {error})
};
export const TOGGLE_BREAKPOINT = createRequestTypes('TOGGLE_BREAKPOINT');
export const toggleBreakpoint = {
request: (filename, lineNumber) => action(TOGGLE_BREAKPOINT[REQUEST], {filename, lineNumber}),
success: (data, payload) => action(TOGGLE_BREAKPOINT[SUCCESS], {payload}),
failure: (error) => action(TOGGLE_BREAKPOINT[FAILURE], {error})
};
export const DEBUGGER_INFO = createRequestTypes('DEBUGGER_INFO');
export const debuggerInfo = {
success: (data) => action(DEBUGGER_INFO[SUCCESS], {data})
};
// Web Socket
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';
@ -358,6 +419,7 @@ export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
export const STOP_BLOCK_HEADER = 'STOP_BLOCK_HEADER';
export const WATCH_GAS_ORACLE = 'WATCH_GAS_ORACLE';
export const STOP_GAS_ORACLE = 'STOP_GAS_ORACLE';
export const STOP_DEBUGGER = 'STOP_DEBUGGER';
export function listenToProcessLogs(processName) {
return {
@ -409,6 +471,12 @@ export function stopGasOracle(){
};
}
export function stopDebugger(){
return {
type: STOP_DEBUGGER
}
}
// Actions without Side Effect
export const UPDATE_BASE_ETHER = 'UPDATE_BASE_ETHER';
export function updateBaseEther(value) {
@ -418,18 +486,10 @@ export function updateBaseEther(value) {
};
}
export const TOGGLE_BREAKPOINT = 'TOGGLE_BREAKPOINT';
export function toggleBreakpoint(filename, lineNumber) {
return {
type: TOGGLE_BREAKPOINT,
payload: {filename, lineNumber}
};
}
export const UPDATE_DEPLOYMENT_PIPELINE = 'UPDATE_DEPLOYMENT_PIPELINE';
export function updateDeploymentPipeline(value) {
return {
type: UPDATE_DEPLOYMENT_PIPELINE,
payload: value
};
}
}

View File

@ -0,0 +1,95 @@
import PropTypes from "prop-types";
import React, {Component} from 'react';
import {
Row,
Col,
FormGroup,
Label,
Input,
Button,
Card,
CardBody,
CardHeader,
CardTitle,
CardFooter,
ListGroup,
ListGroupItem
} from "reactstrap";
import ReactJson from 'react-json-view';
class ContractDebugger extends Component {
constructor(props) {
super(props);
}
handleChange(e) {
this.setState({txHash: e.target.value});
}
debug(e) {
this.props.startDebug(this.state.txHash);
}
debugJumpBack(e) {
this.props.debugJumpBack()
}
debugJumpForward(e) {
this.props.debugJumpForward()
}
debugStepOverForward(e) {
this.props.debugStepOverForward()
}
debugStepOverBackward(e) {
this.props.debugStepOverBackward()
}
debugStepIntoForward(e) {
this.props.debugStepIntoForward()
}
debugStepIntoBackward(e) {
this.props.debugStepIntoBackward()
}
render() {
return (
<div>
<Row>
<Col>
<Input name="txHash" id="txHash" onChange={(e) => this.handleChange(e)}/>
<Button color="primary" onClick={(e) => this.debug(e)}>Debug Tx</Button>
</Col>
</Row>
<Row>
<Col>
<Button color="light" className="btn-square debugButton jumpBack" alt="jump to previous breakpoint" onClick={(e) => this.debugJumpBack(e)}></Button>
<Button color="light" className="btn-square debugButton jumpForward" alt="jump to revious breakpoint" onClick={(e) => this.debugJumpForward(e)}></Button>
<Button color="light" className="btn-square debugButton stepOverBack" alt="step back" onClick={(e) => this.debugStepOverBackward(e)}></Button>
<Button color="light" className="btn-square debugButton stepOverForward" alt="step over" onClick={(e) => this.debugStepOverForward(e)}></Button>
<Button color="light" className="btn-square debugButton stepIntoForward" alt="step into" onClick={(e) => this.debugStepIntoForward(e)}></Button>
<Button color="light" className="btn-square debugButton stepIntoBack" alt="step out" onClick={(e) => this.debugStepIntoBackward(e)}></Button>
</Col>
</Row>
<Row>
<Col>
<br /><strong>Scopes</strong>
<div>
<ReactJson src={{locals: this.props.debuggerInfo.locals, contract: this.props.debuggerInfo.globals}} theme="monokai" sortKeys={true} name={false} collapse={1} />
</div>
</Col>
</Row>
</div>
);
}
}
ContractDebugger.propTypes = {
contract: PropTypes.object.isRequired,
};
export default ContractDebugger;

View File

@ -6,6 +6,7 @@ import classnames from 'classnames';
import ContractDetail from '../components/ContractDetail';
import ContractLoggerContainer from '../containers/ContractLoggerContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
import ContractDebuggerContainer from '../containers/ContractDebuggerContainer';
class ContractLayout extends React.Component {
constructor(props) {
@ -55,6 +56,14 @@ class ContractLayout extends React.Component {
Logger
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: this.state.activeTab === '4' })}
onClick={() => { this.toggle('4'); }}
>
Debugger
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
@ -66,6 +75,9 @@ class ContractLayout extends React.Component {
<TabPane tabId="3">
<ContractLoggerContainer contract={this.props.contract} />
</TabPane>
<TabPane tabId="4">
<ContractDebuggerContainer contract={this.props.contract} />
</TabPane>
</TabContent>
</CardBody>
</Card>

View File

@ -97,8 +97,9 @@ class TextEditor extends React.Component {
}
));
//TODO remove me when debuggerLine comes from the debugger API
let debuggerLine = this.props.debuggerLine || 11;
let debuggerLine = this.props.debuggerLine;
console.dir("debuggerLine")
console.dir(debuggerLine)
newDecorations.push({
range: new monaco.Range(debuggerLine,1,debuggerLine,1),
options: {
@ -116,9 +117,8 @@ class TextEditor extends React.Component {
}
this.updateMarkers();
// TODO replace with const expectedDecorationsLength = this.props.debuggerLine ? this.props.breakpoints.length + 1 : this.props.breakpoints.length
const expectedDecorationsLength = this.props.breakpoints.length + 1;
if (expectedDecorationsLength !== this.state.decorations.length) {
const expectedDecorationsLength = this.props.debuggerLine ? this.props.breakpoints.length + 1 : this.props.breakpoints.length
if (expectedDecorationsLength !== this.state.decorations.length || this.props.debuggerLine !== prevProps.debuggerLine) {
this.updateDecorations();
}
this.updateLanguage();
@ -142,4 +142,4 @@ TextEditor.propTypes = {
debuggerLine: PropTypes.number
};
export default TextEditor;
export default TextEditor;

View File

@ -41,6 +41,9 @@ const TextEditorToolbar = (props) => (
Logger
</Button>
<span className="mx-2">|</span>
<Button size="sm" color="primary" onClick={() => props.openAsideTab('debugger')}>
Debugger
</Button>
</React.Fragment>
}
<Button size="sm" color="primary" onClick={() => props.openAsideTab('browser')}>

View File

@ -0,0 +1,54 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {contractLogs as contractLogsAction, listenToContractLogs, startDebug, debugJumpBack, debugJumpForward, debugStepOverForward, debugStepOverBackward, debugStepIntoForward, debugStepIntoBackward} from '../actions';
import ContractDebugger from '../components/ContractDebugger';
import DataWrapper from "../components/DataWrapper";
import {getContractLogsByContract, debuggerInfo} from "../reducers/selectors";
class ContractDebuggerContainer extends Component {
componentDidMount() {
// if (this.props.contractLogs.length === 0) {
// this.props.listenToContractLogs();
// this.props.fetchContractLogs(this.props.contract.className);
// }
}
render() {
return (
<DataWrapper shouldRender={this.props.contractLogs !== undefined } {...this.props} render={() => (
<ContractDebugger contract={this.props.contract} startDebug={this.props.startDebug} debugJumpBack={this.props.debugJumpBack} debugJumpForward={this.props.debugJumpForward} debugStepOverForward={this.props.debugStepOverForward} debugStepOverBackward={this.props.debugStepOverBackward} debugStepIntoForward={this.props.debugStepIntoForward} debugStepIntoBackward={this.props.debugStepIntoBackward} debuggerInfo={this.props.debuggerInfo}
/>
)} />
);
}
}
function mapStateToProps(state, props) {
return {
contractLogs: getContractLogsByContract(state, props.contract.className),
debuggerInfo: debuggerInfo(state)
};
}
ContractDebuggerContainer.propTypes = {
contractLogs: PropTypes.array,
fetchContractLogs: PropTypes.func,
listenToContractLogs: PropTypes.func,
match: PropTypes.object
};
export default connect(
mapStateToProps,
{
startDebug: startDebug.request,
debugJumpBack: debugJumpBack.request,
debugJumpForward: debugJumpForward.request,
debugStepOverForward: debugStepOverForward.request,
debugStepOverBackward: debugStepOverBackward.request,
debugStepIntoForward: debugStepIntoForward.request,
debugStepIntoBackward: debugStepIntoBackward.request
}
)(ContractDebuggerContainer);

View File

@ -9,6 +9,7 @@ import {getContractsByPath} from "../reducers/selectors";
import ContractDetail from '../components/ContractDetail';
import ContractLoggerContainer from '../containers/ContractLoggerContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
import ContractDebuggerContainer from '../containers/ContractDebuggerContainer';
class TextEditorAsideContainer extends Component {
componentDidMount() {
@ -19,6 +20,17 @@ class TextEditorAsideContainer extends Component {
switch(this.props.currentAsideTab) {
case 'browser':
return <Preview />
case 'debugger':
return this.props.contracts.map((contract, index) => {
return (
<Card>
<CardBody>
<CardTitle style={{"font-size": "2em"}}>{contract.className} - Details</CardTitle>
<ContractDebuggerContainer key={index} contract={contract} />
</CardBody>
</Card>
)
})
case 'detail':
return this.props.contracts.map((contract, index) => {
return (

View File

@ -5,18 +5,21 @@ import TextEditor from '../components/TextEditor';
import {
toggleBreakpoint,
} from '../actions';
import {getBreakpointsByFilename} from '../reducers/selectors';
import {getCurrentFile, getContractCompile, getContractDeploys, getBreakpointsByFilename, getDebuggerLine} from '../reducers/selectors';
const TextEditorContainer = (props) => (
<TextEditor file={props.currentFile}
breakpoints={props.breakpoints}
toggleBreakpoint={props.toggleBreakpoint}
debuggerLine={props.debuggerLine}
onFileContentChange={props.onFileContentChange} />
)
function mapStateToProps(state, props) {
const breakpoints = getBreakpointsByFilename(state, props.currentFile.name);
return {breakpoints};
const debuggerLine = getDebuggerLine(state);
return {breakpoints, debuggerLine};
}
TextEditorContainer.propTypes = {
@ -24,6 +27,7 @@ TextEditorContainer.propTypes = {
onFileContentChange: PropTypes.func,
toggleBreakpoints: PropTypes.func,
breakpoints: PropTypes.array,
toggleBreakpoint: PropTypes.object
};
export default connect(

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,360 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="224" height="216" id="svg2" version="1.1" inkscape:version="0.92.2pre0 (973e216, 2017-07-25)" sodipodi:docname="largeIcons.svg">
<metadata id="metadata606">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs604"/>
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1946" inkscape:window-height="1546" id="namedview602" showgrid="true" inkscape:zoom="4.3703705" inkscape:cx="92.781028" inkscape:cy="118.13064" inkscape:window-x="1047" inkscape:window-y="228" inkscape:window-maximized="0" inkscape:current-layer="svg2" inkscape:snap-grids="true" showguides="true" inkscape:guide-bbox="true">
<inkscape:grid type="xygrid" id="grid3583" empspacing="5" visible="true" enabled="true" snapvisiblegridlinesonly="true" spacingx="28" spacingy="24" originx="0" originy="0"/>
</sodipodi:namedview>
<g id="g4">
<g transform="translate(-322,-72)" id="g8">
<circle transform="translate(326,74)" cx="15" cy="17" r="3" id="circle10" sodipodi:cx="15" sodipodi:cy="17" sodipodi:rx="3" sodipodi:ry="3" style="fill:#009802"/>
<path d="m 329,77 h 18 v 18 h -18 z" id="path12" inkscape:connector-curvature="0" style="fill:none"/>
<path d="m 327.25,75.25 h 20 v 20 h -20 z" id="path14" inkscape:connector-curvature="0" style="fill:none"/>
<path d="m 325.12,73.125 h 20 v 20 h -20 z" id="path16" inkscape:connector-curvature="0" style="fill:none"/>
</g>
</g>
<g transform="translate(26,0)" id="g18">
<path transform="translate(-32,0)" d="M 57,12 53.5,8 H 39 v 8 h 14.5" id="path22" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2.003,24)" id="g24">
<path transform="translate(-224,-120)" d="m 240.77,127 h -1.534 v 4.233 h -4.233 v 1.534 h 4.233 V 137 h 1.534 v -4.233 h 4.233 v -1.534 H 240.77 V 127 z" id="path28" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(26,24)" id="g30">
<path transform="translate(-96,-145)" d="m 103,148 h 18 v 18 h -18 z" id="path34" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-96,-145)" d="m 115.42,154.7 -6.705,-6.705 -1.0575,1.0575 1.785,1.785 -3.8625,3.8625 c -0.4425,0.4425 -0.4425,1.155 0,1.59 l 4.125,4.125 c 0.2175,0.2175 0.51,0.33 0.795,0.33 0.285,0 0.5775,-0.1125 0.795,-0.33 l 4.125,-4.125 c 0.4425,-0.435 0.4425,-1.1475 0,-1.59 z m -8.5125,0.795 3.5925,-3.5925 3.5925,3.5925 h -7.185 z m 10.342,1.125 c 0,0 -1.5,1.6275 -1.5,2.625 0,0.825 0.675,1.5 1.5,1.5 0.825,0 1.5,-0.675 1.5,-1.5 0,-0.9975 -1.5,-2.625 -1.5,-2.625 z" id="path36" inkscape:connector-curvature="0"/>
<path transform="translate(-96,-145)" d="m 103,163 h 18 v 3 h -18 z" id="path38" inkscape:connector-curvature="0" style="fill-opacity:0.36000001"/>
</g>
<g transform="translate(0,48)" id="g40">
<path transform="translate(7,3)" d="M 0,0 H 18 V 18 H 0 z" id="path44" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(7,3)" d="m 13,5 h 1.4936 C 15.32558,5 16,5.67154 16,6.5064 v 7.9871 c 0,0.83198 -0.67154,1.5064 -1.5064,1.5064 H 6.5065 c -0.83198,0 -1.5064,-0.67154 -1.5064,-1.5064 v -1.4936 h 6.4936 c 0.8349,0 1.5064,-0.67446 1.5064,-1.5064 V 4.9999 z" id="path46" inkscape:connector-curvature="0" style="fill-opacity:0.36000001"/>
<path transform="translate(7,3)" d="M 3.5,2 C 2.669,2 2,2.669 2,3.5 v 8 C 2,12.331 2.669,13 3.5,13 h 8 c 0.831,0 1.5,-0.669 1.5,-1.5 v -8 C 13,2.669 12.331,2 11.5,2 h -8 z m 0,1.5 h 8 v 8 h -8 v -8 z" id="path48" inkscape:connector-curvature="0" style="fill:#212121"/>
</g>
<g transform="translate(26,48)" id="g50">
<path transform="translate(-96,-24)" d="m 107,33 h 8 v 6 h -8 z" stroke-miterlimit="4.2" id="path54" inkscape:connector-curvature="0" style="stroke:#000000;stroke-width:2;stroke-miterlimit:4.19999981"/>
<path transform="translate(-96,-24)" d="m 115,36 4,-4 v 8" id="path56" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,0)" id="g58">
<g id="g62" style="stroke:#000000">
<path transform="matrix(0.36,0,0,0.36,-2.5,7.46)" d="m 53,14 a 3,3 0 1 1 -6,0 3,3 0 1 1 6,0 z" id="path64" inkscape:connector-curvature="0" style="fill-rule:evenodd;stroke-width:2.77800012"/>
<path transform="translate(-128,-120)" d="m 143.48,129.5 2.5403,-2 h -1.5242 v -2 h -2 l -0.0161,2 h -1.5403 z" id="path66" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-120)" d="m 146.5,132.5 2,2.5 v -1.4998 l 2,-1e-4 v -2.0002 l -2,1e-4 V 130 z" id="path68" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-120)" d="m 143.5,135.5 -2.5,2 h 1.5 v 2 h 2 v -2 h 1.5 z" id="path70" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-120)" d="m 140.5,132.5 -2,-2.5 v 1.4999 h -2 v 2.0002 l 2,-3e-4 V 135 z" id="path72" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(56,24)" id="g74">
<g id="g78" style="fill-rule:evenodd">
<path transform="matrix(0.9018,0,0,0.9018,4.308,4.525)" d="M 0,0 H 18 V 18 H 0 z" id="path80" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="matrix(0.9018,0,0,0.9018,4.308,4.525)" d="M 4.174,8.343 2.76,9.757 7.003,13.999 15.488,5.514 14.074,4.1 7.003,11.171 z" id="path82" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(57,47)" id="g84">
<path transform="translate(-68,-143)" d="M 76.94,152 76,152.94 79.0533,156 76,159.06 l 0.94,0.94 4,-4 z" id="path88" inkscape:connector-curvature="0"/>
<path transform="translate(-68,-143)" d="M 80.94,152 80,152.94 83.0533,156 80,159.06 l 0.94,0.94 4,-4 z" id="path90" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2,72)" id="g92">
<path transform="translate(-64,0)" d="m 80.44,16.94 c -2.48,0 -4.5,-2.02 -4.5,-4.5 0,-0.88 0.26,-1.7 0.69,-2.39 l 6.2,6.2 c -0.69,0.44 -1.51,0.69 -2.39,0.69 m 4.5,-4.5 c 0,0.88 -0.26,1.7 -0.69,2.39 l -6.2,-6.2 c 0.69,-0.44 1.51,-0.69 2.39,-0.69 2.48,0 4.5,2.02 4.5,4.5 M 80.5,6 C 76.91,6 74,8.91 74,12.5 74,16.09 76.91,19 80.5,19 84.09,19 87,16.09 87,12.5 87,8.91 84.09,6 80.5,6" id="path96" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(28,72)" id="g98">
<g id="g102" style="fill-rule:evenodd">
<path transform="matrix(0.87153,0,0,0.87153,4.071,4.568)" d="M 0,0 H 18 V 18 H 0 z" id="path104" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="matrix(0.87153,0,0,0.87153,4.071,4.568)" d="M 12,3.5 A 1.505,1.505 0 0 0 10.494,2 H 4.506 C 3.676,2 3,2.674 3,3.506 v 7.988 C 3,12.326 3.671,12.997 4.5,13 V 3.5 H 12 z M 6,6.506 C 6,5.674 6.676,5 7.506,5 h 5.988 C 14.326,5 15,5.672 15,6.506 v 7.988 C 15,15.326 14.324,16 13.494,16 H 7.506 A 1.505,1.505 0 0 1 6,14.494 V 6.506 z M 7.5,6.5 h 6 v 8 h -6 v -8 z" id="path106" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(54,72)" id="g108">
<path transform="translate(0,-24)" d="M 25,36 21.36,32 H 19.57 L 14,40 h 7.36 L 25,36 z" id="path112" inkscape:connector-curvature="0"/>
<path transform="translate(0,-24)" d="m 7,32 v 8 H 9.05 L 14,32 H 7 z" id="path114" inkscape:connector-curvature="0"/>
<path transform="translate(0,-24)" d="M 9.67,44.55 8.08,43.23 18.84,27.45 20.43,28.77 9.67,44.55 z" id="path116" inkscape:connector-curvature="0"/>
<path transform="translate(0,-24)" d="M 0,24 H 24 V 48 H 0 z" id="path118" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(0,-24)" d="M 0,24 H 24 V 48 H 0 z m 0,0 H 24 V 48 H 0 z m 0,0 H 24 V 48 H 0 z m 0,0 H 24 V 48 H 0 z" id="path120" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(0,-24)" d="M 0,24 H 24 V 48 H 0 z" id="path122" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(0,-24)" d="M 0,24 H 24 V 48 H 0 z" id="path124" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(0,-24)" d="M 0,24 H 24 V 48 H 0 z" id="path126" inkscape:connector-curvature="0" style="fill:none"/>
</g>
<g transform="translate(82,0)" id="g128">
<path transform="translate(-128,0)" d="M 149,8.33 147.67,7 144,10.67 140.33,7 139,8.33 142.67,12 139,15.67 140.33,17 144,13.33 147.67,17 149,15.67 145.33,12 149,8.33 z" id="path132" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,24)" id="g134">
<path transform="translate(-32,-24)" d="M 53,37 H 43 v -5 h 10 v 5 z M 41,42 H 55 V 30 H 41 v 12 z" id="path138" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,48)" id="g140">
<path transform="translate(-224,-48)" d="m 238,64 h 7 v -8 h -7 z m 9,2 H 233 V 54 h 14 z" id="path144" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,72)" id="g146">
<path transform="translate(-256,-48)" d="m 274,64 h -7 v -8 h 7 v 8 z m -9,2 h 14 V 54 h -14 v 12 z" id="path150" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2,96)" id="g152">
<path transform="translate(-160,0)" d="m 169,16.089 v 2.9105 h 2.9105 L 180.1944,10.7156 177.2838,7.8051 169,16.089 z m 13.769,-7.9387 c 0.30785,-0.30785 0.30785,-0.79294 0,-1.1008 l -1.8191,-1.8191 c -0.30785,-0.30784 -0.79295,-0.30784 -1.1008,0 l -1.5206,1.5299 2.9105,2.9105 1.5299,-1.5206 z" id="path156" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(26,96)" id="g158">
<path transform="translate(-288,-120)" d="m 310.77,127.04 -1.816,-1.8164 c -0.30331,-0.30338 -0.79716,-0.30338 -1.1005,0 l -2.4304,2.4309 -1.4894,-1.4935 -1.1005,1.1007 1.1044,1.1046 -6.9373,6.9348 v 3.695 h 3.6942 l 6.9373,-6.9387 1.1005,1.1046 1.1005,-1.1007 -1.4932,-1.4935 2.4304,-2.4309 c 0.3072,-0.30338 0.3072,-0.79345 0,-1.0968 z m -10.721,10.4 -1.4932,-1.4935 6.2724,-6.2736 1.4932,1.4935 -6.2723,6.2736 z" id="path162" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,96)" id="g164">
<path transform="translate(-32,-48)" d="m 44,59 2,3 v 4 h 4 v -4 l 2,-3 z" id="path168" inkscape:connector-curvature="0" style="opacity:0.5;fill:#424242"/>
<path transform="translate(-32,-48)" d="m 46.5,65.23 c 0.32,0.13 0.84,0.24 1.47,0.24 0.59,0 1.14,-0.1 1.53,-0.26 v -3.93 l 4,-4.57 v -0.19 h -11 v 0.22 l 4,4.57 v 3.93 z M 47.97,67 C 46.81,66.91 45.82,66.71 45,66.01 V 61.89 L 41,57.32 V 55 h 14 v 2.35 l -4,4.57 v 4.13 c -0.92,0.67 -2.1,0.94 -3.03,0.95" id="path170" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82.001,96)" id="g172">
<path transform="matrix(0.75,0,0,0.75,-74.421,-143.43)" d="m 108.56,195.24 h 24 v 24 h -24 z" id="path176" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="matrix(0.75,0,0,0.75,-74.421,-143.43)" d="m 108.56,215.24 h 24 v 4 h -24 z" id="path178" inkscape:connector-curvature="0" style="fill-opacity:0.36000001"/>
<path transform="matrix(0.75,0,0,0.75,-74.421,-143.43)" d="m 119.56,198.24 -5.5,14 h 2.25 l 1.12,-3 h 6.25 l 1.12,3 h 2.25 l -5.49,-14 h -2 z m -1.38,9 2.38,-6.33 2.38,6.33 h -4.76 z" id="path180" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(106,1)" id="g182">
<path transform="matrix(0,-1,1,0,-112,-260)" d="m -278.5,126.5 h 14 v 12 h -14 z" id="path186" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="matrix(0,1,1,0,-112,-260)" d="m 272,132.5 -5,-3.25 v 6.5" id="path188" inkscape:connector-curvature="0"/>
<path transform="matrix(0,-1,1,0,-112,-260)" d="m -275,126 h 1 v 13 h -1 z" id="path190" inkscape:connector-curvature="0"/>
</g>
<path style="fill:none;stroke:#000000" inkscape:connector-curvature="0" id="path196" d="m 119.5,30.5 h 14 v 12 h -14 z"/>
<path inkscape:connector-curvature="0" id="path198" d="m 126,36.5 5,-3.25 v 6.5"/>
<path inkscape:connector-curvature="0" id="path200" d="m 123,30 h 1 v 13 h -1 z"/>
<g transform="translate(111,48)" id="g202">
<path transform="matrix(-1,0,0,1,-192,-120)" d="m -214.5,126.5 h 14 v 12 h -14 z" id="path206" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="translate(-192,-120)" d="m 208,132.5 -5,-3.25 v 6.5" id="path208" inkscape:connector-curvature="0"/>
<path transform="matrix(-1,0,0,1,-192,-120)" d="m -211,126 h 1 v 13 h -1 z" id="path210" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(106,73)" id="g212">
<path transform="matrix(0,1,1,0,-88,283)" d="m -278.5,102.5 h 14 v 12 h -14 z" id="path216" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="matrix(0,-1,1,0,-88,283)" d="m 272,108.5 -5,-3.25 v 6.5" id="path218" inkscape:connector-curvature="0"/>
<path transform="matrix(0,1,1,0,-88,283)" d="m -275,102 h 1 v 13 h -1 z" id="path220" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(110,96)" id="g222">
<path transform="translate(-224,0)" d="m 237,11 h -4 V 7 h 4 v 4 z" id="path226" inkscape:connector-curvature="0"/>
<path transform="translate(-224,0)" d="m 247,9 h -9 V 7 h 9 v 2 z" id="path228" inkscape:connector-curvature="0"/>
<path transform="translate(-224,0)" d="m 247,11 h -9 v -1 h 9 v 1 z" id="path230" inkscape:connector-curvature="0"/>
<path transform="translate(-224,0)" d="m 247,15 h -9 v -2 h 9 v 2 z" id="path232" inkscape:connector-curvature="0"/>
<path transform="translate(-224,0)" d="m 237,17 h -4 v -4 h 4 v 4 z" id="path234" inkscape:connector-curvature="0"/>
<path transform="translate(-224,0)" d="m 247,17 h -9 v -1 h 9 v 1 z" id="path236" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2,120)" id="g238">
<path transform="translate(0,-144)" d="m 23,157 h -2 v 2 h 2 z" id="path242" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 23,161 h -2 v 2 c 1,0 2,-1 2,-2 z" id="path244" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 23,153 h -2 v 2 h 2 z" id="path246" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 21,149 v 2 h 2 c 0,-1 -1,-2 -2,-2 z" id="path248" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 11,163 h 4 v -6 H 9 v 4 c 0,1.1 0.9,2 2,2 z" id="path250" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="M 11,153 H 9 v 2 h 2 z" id="path252" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 19,149 h -2 v 2 h 2 z" id="path254" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 19,161 h -2 v 2 h 2 z" id="path256" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 11,149 c -1,0 -2,1 -2,2 h 2 z" id="path258" inkscape:connector-curvature="0"/>
<path transform="translate(0,-144)" d="m 15,149 h -2 v 2 h 2 z" id="path260" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(26,120)" id="g262">
<path transform="translate(-290,-46)" d="m 317,69 v -5 l -5,5 h 5 z" id="path266" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,120)" id="g268">
<g id="g272" style="fill-rule:evenodd">
<path transform="matrix(1.4142,0,0,1.4142,-278.88,-36.772)" d="m 209.92,31.305 a 1.0607,1.0607 0 1 1 -2.1213,0 1.0607,1.0607 0 1 1 2.1213,0 z" id="path274" inkscape:connector-curvature="0"/>
<path transform="matrix(1.4142,0,0,1.4142,-278.88,-31.772)" d="m 209.92,31.305 a 1.0607,1.0607 0 1 1 -2.1213,0 1.0607,1.0607 0 1 1 2.1213,0 z" id="path276" inkscape:connector-curvature="0"/>
<path transform="matrix(1.4142,0,0,1.4142,-278.88,-26.772)" d="m 209.92,31.305 a 1.0607,1.0607 0 1 1 -2.1213,0 1.0607,1.0607 0 1 1 2.1213,0 z" id="path278" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(84,120)" id="g280">
<path transform="translate(-162,-144)" d="m 166,147 h 18 v 18 h -18 z" id="path284" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-162,-144)" d="m 181.52,154.53 c -0.51,-2.58 -2.7862,-4.5262 -5.5162,-4.5262 -2.1675,0 -4.0462,1.23 -4.9875,3.0262 -2.2538,0.24375 -4.0125,2.1525 -4.0125,4.4738 0,2.4862 2.0138,4.5 4.5,4.5 h 9.75 c 2.07,0 3.75,-1.68 3.75,-3.75 0,-1.98 -1.5412,-3.585 -3.4838,-3.7238 z m -0.26625,5.9738 h -9.75 c -1.6575,0 -3,-1.3425 -3,-3 0,-1.6575 1.3425,-3 3,-3 h 0.5325 c 0.49125,-1.7288 2.0775,-3 3.9675,-3 2.28,0 4.125,1.845 4.125,4.125 v 0.375 h 1.125 c 1.2412,0 2.25,1.0088 2.25,2.25 0,1.2412 -1.0088,2.25 -2.25,2.25 z" id="path286" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(112,120)" id="g288">
<path transform="translate(-226,-72)" d="m 235,76 c -0.55,0 -0.99,0.45 -0.99,1 L 234,91 c 0,0.55 0.44,1 1,1 h 10 c 0.55,0 1,-0.45 1,-1 V 81 l -5,-5 h -6 z m 6,5 v -4 l 4,4 h -4 z" id="path292" inkscape:connector-curvature="0"/>
<path transform="translate(-226,-72)" d="m 231,75 h 18 v 18 h -18 z" id="path294" inkscape:connector-curvature="0" style="fill:none"/>
</g>
<g transform="translate(140,0)" id="g296">
<path transform="translate(-162,-24)" d="m 169,29 h 18 v 18 h -18 z" id="path300" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-162,-24)" d="m 167.25,27.25 h 20 v 20 h -20 z" id="path302" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-162,-24)" d="m 165.12,25.125 h 20 v 20 h -20 z" id="path304" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-162,-24)" d="m 171,28 c -0.55,0 -1,0.45 -1,1 v 14 c 0,0.55 0.44,1 1,1 h 5.0938 c -0.0656,-0.32311 -0.0937,-0.65753 -0.0937,-1 0,-2.7614 2.2386,-5 5,-5 0.34247,0 0.67689,0.02816 1,0.09375 v -5.0938 l -5,-5 h -6 z m 6,1 4,4 h -4 v -4 z" id="path306" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(140,24)" id="g308">
<path transform="translate(-66,-120)" d="m 71,124 h 18 v 18 H 71 z" id="path312" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-66,-120)" d="m 82,129 -2,-2 h -5 c -0.55,0 -1,0.45 -1,1 v 10 c 0,0.55 0.45,1 1,1 h 12 c 0.55,0 1,-0.45 1,-1 v -8 c 0,-0.55 -0.45,-1 -1,-1 h -5 z" id="path314" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(140,48)" id="g316">
<path transform="translate(-258,-144)" d="m 278,150 h -10.5 c -0.8325,0 -1.5,0.675 -1.5,1.5 v 9 c 0,0.825 0.6675,1.5 1.5,1.5 H 278 c 0.825,0 1.5,-0.675 1.5,-1.5 v -9 c 0,-0.825 -0.6675,-1.5 -1.5,-1.5 z m 0,10.5 H 267.5 V 153 H 278 v 7.5 z" id="path320" inkscape:connector-curvature="0" style="fill:#010101"/>
<path transform="translate(-258,-144)" d="m 262.97,147.07 h 18 v 18 h -18 z" id="path322" inkscape:connector-curvature="0" style="fill:none"/>
</g>
<g transform="translate(140,72)" id="g324">
<path transform="translate(6,3.9)" d="M 9,1 H 3.9954 C 3.45567,1 3,1.45078 3,2.0068 v 11.986 c 0,0.5569 0.44565,1.0068 0.9954,1.0068 h 8.0092 C 12.54433,14.9996 13,14.54882 13,13.9928 V 4.9996 l -4,-4 z m 3,4 H 9 V 2 l 3,3 z M 6,7 11,9.5 6,12 V 7 z" id="path328" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(140,96)" id="g330">
<g id="g334" style="stroke:#000000">
<path transform="matrix(0.72907,0,0,0.72907,5.617,4.598)" d="M 7.854,6.963 6.877,7.279 6.857,8.617 6.889,8.705 7.398,9.699 6.188,10.578 5.398,9.787 5.326,9.73 4.047,9.336 3.443,10.166 4.213,11.26 4.291,11.313 5.287,11.817 4.824,13.241 3.72,13.065 3.628,13.061 2.362,13.493 v 1.027 l 1.266,0.434 0.092,-0.004 1.104,-0.178 0.463,1.424 -0.996,0.506 -0.078,0.051 -0.77,1.094 0.604,0.83 1.279,-0.393 0.072,-0.059 0.789,-0.791 1.211,0.879 -0.51,0.996 -0.031,0.086 0.02,1.338 0.977,0.316 0.803,-1.068 0.025,-0.09 0.172,-1.104 h 1.498 l 0.172,1.104 0.025,0.09 0.803,1.068 0.977,-0.316 0.02,-1.338 -0.031,-0.086 -0.51,-0.996 1.211,-0.879 0.789,0.791 0.072,0.059 1.279,0.393 0.604,-0.83 -0.771,-1.094 -0.076,-0.051 -0.996,-0.506 0.461,-1.424 1.105,0.178 0.092,0.004 1.266,-0.434 v -1.027 l -1.266,-0.432 -0.092,0.004 -1.105,0.176 -0.461,-1.424 0.996,-0.504 0.076,-0.053 0.771,-1.094 L 15.159,9.336 13.88,9.731 13.808,9.788 13.019,10.579 11.808,9.7 12.318,8.706 12.349,8.618 12.329,7.28 11.352,6.964 10.549,8.034 10.524,8.122 10.352,9.227 H 8.854 L 8.682,8.121 8.656,8.03 7.854,6.963 m 1.748,3.398 a 3.621,3.645 0 0 1 3.621,3.645 3.621,3.645 0 0 1 -3.621,3.646 3.621,3.645 0 0 1 -3.619,-3.646 3.621,3.645 0 0 1 3.619,-3.645 z" id="path336" inkscape:connector-curvature="0"/>
<path transform="matrix(0.72907,0,0,0.72907,5.227,3.617)" d="M 14.885,1.563 14.178,1.957 14.365,3.24 14.396,3.279 15.14,4.04 14.553,5.03 13.529,4.736 13.479,4.729 12.262,5.172 12.25,5.98 13.455,6.459 13.506,6.453 14.537,6.189 15.1,7.191 14.332,7.932 14.301,7.971 14.08,9.248 14.771,9.662 15.787,8.859 15.807,8.811 16.1,7.787 17.242,7.803 17.502,8.834 17.52,8.883 18.514,9.715 19.219,9.32 19.03,8.04 19,7.998 18.258,7.234 18.844,6.25 19.869,6.541 19.92,6.551 21.14,6.105 21.15,5.297 19.943,4.818 19.893,4.824 18.861,5.088 18.301,4.086 19.07,3.346 19.1,3.307 19.322,2.029 18.627,1.615 17.609,2.418 17.59,2.467 17.303,3.49 16.16,3.475 15.896,2.443 15.879,2.395 14.885,1.563 z m 1.814,2.138 a 1.938,1.938 0 0 1 1.938,1.938 1.938,1.938 0 0 1 -1.938,1.937 1.938,1.938 0 0 1 -1.937,-1.937 1.938,1.938 0 0 1 1.937,-1.938 z" id="path338" inkscape:connector-curvature="0" style="stroke-width:0.89899999"/>
</g>
</g>
<g transform="translate(138,120)" id="g340">
<g id="g344" style="fill:none">
<path transform="translate(8,4)" d="M 0,0 H 16 V 16 H 0 V 0 z" id="path346" inkscape:connector-curvature="0" style="opacity:0.5"/>
<path transform="translate(8,4)" d="M 6,14 H 3.5 C 2.5,14 2,13.5 2,12.5 v -9 C 2,2.5 2.5,2 3.5,2 h 9 C 14,2 14,3.4678 14,3.5 V 6 H 13 V 3 H 3 v 10 h 3 v 1 z m 9,-5 -3,2 3,3 -1,1 -3,-3 -2,3 -2,-8 8,2 z" id="path348" inkscape:connector-curvature="0" style="fill:#000000"/>
</g>
</g>
<g transform="translate(0,144)" id="g350">
<path transform="translate(-98,-120)" d="m 110.5,127.5 h -1 l 2,-2 2,2 h -1 c 0,0 -0.0345,4.6379 -0.0345,4.0345 l 4.0345,-0.034 v -1 l 2,2 -2,2 v -1 l -4.0345,-0.034 0.0345,4.0346 h 1 l -2,2 -2,-2 h 1 l 0.0345,-4.0346 -4.0345,0.034 v 1 l -2,-2 2,-2 v 1 l 4.0345,0.034 z" id="path354" inkscape:connector-curvature="0" style="stroke:#000000"/>
</g>
<g transform="translate(26,144)" id="g356">
<path transform="translate(-320,0)" d="m 333.6,15.2 h 1.6 V 8.8 h -1.6 v 6.4 z M 336,4 c -4.42,0 -8,3.58 -8,8 0,4.42 3.58,8 8,8 4.42,0 8,-3.58 8,-8 0,-4.42 -3.58,-8 -8,-8 z m 0,14.4 c -3.528,0 -6.4,-2.872 -6.4,-6.4 0,-3.528 2.872,-6.4 6.4,-6.4 3.528,0 6.4,2.872 6.4,6.4 0,3.528 -2.872,6.4 -6.4,6.4 z m 0.8,-3.2 h 1.6 V 8.8 h -1.6 v 6.4 z" id="path360" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,144)" id="g362">
<path transform="translate(-32,-72)" d="m 47,88 h -3 v -8 h 3 v 8 z" id="path366" inkscape:connector-curvature="0"/>
<path transform="translate(-32,-72)" d="m 53,88 h -3 v -8 h 3 v 8 z" id="path368" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,144)" id="g370">
<path transform="translate(-256,0)" d="m 275,15 h -2 V 9 h 2 z m -4,0 h -2 V 9 h 2 z m 4,-10 h -6 l -4,4 v 6 l 4,4 h 6 l 4,-4.12 V 9 z" id="path374" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(110,144)" id="g376">
<g font-weight="400" id="g380" style="font-weight:400;font-family:Sans">
<path transform="translate(-289,-96)" d="m 300,101 v 4.0001 1.0001 l 3.3434,0.53115 -0.0156,8.0468 -1.3278,0.42183 c 0.006,0.59278 0.43931,1.0114 1,1 h 8 c 0.57896,0.002 0.98177,-0.42708 1,-1 v -14 c -0.0102,-0.53477 -0.48177,-0.99739 -1,-1 h -10 c -0.53297,0.008 -0.99716,0.45677 -1,1 z m 1,0 h 10 v 14 h -8 l 0.71845,-0.42179 0.0937,-8.4218 -2.8122,-0.1561 v -0.99993 z" overflow="visible" style="text-indent:0;line-height:normal;text-transform:none;block-progression:tb;overflow:visible" id="path382" inkscape:connector-curvature="0"/>
<path transform="translate(-289,-96)" d="m 297,106 v 9 c 0.006,0.59278 0.43931,1.0114 1,1 h 5 c 0.57896,0.002 0.98177,-0.42708 1,-1 v -9 c -0.0102,-0.53477 -0.48177,-0.99739 -1,-1 h -5 c -0.53297,0.008 -0.99716,0.45677 -1,1 z m 1,1.0002 h 5 v 7 h -5 z" overflow="visible" style="text-indent:0;line-height:normal;text-transform:none;block-progression:tb;overflow:visible" id="path384" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(138,144)" id="g386">
<path transform="translate(-320,-48)" d="m 328,60 c 0,4.42 3.58,8 8,8 4.42,0 8,-3.58 8,-8 0,-4.42 -3.58,-8 -8,-8 -4.42,0 -8,3.58 -8,8 z m 8,6.4 c -3.528,0 -6.4,-2.872 -6.4,-6.4 0,-3.528 2.872,-6.4 6.4,-6.4 3.528,0 6.4,2.872 6.4,6.4 0,3.528 -2.872,6.4 -6.4,6.4 z M 334.5067,63.4286 339.0781,60 334.5067,56.5714 v 6.8571 z" id="path390" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2,168)" id="g392">
<path transform="translate(-96,-48)" d="m 108,60 10,-5 v 10" id="path396" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(26,168)" id="g398">
<path transform="translate(-64,-48)" d="M 86,60 76,55 v 10" id="path402" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,168)" id="g404">
<path transform="translate(-256,-24)" d="m 268.01,35.99 c 0.61,0.28 1.07,0.9 1.07,1.58 0.11,0.85 -0.05,1.72 0.12,2.57 0.27,0.54 1,0.28 1.43,0.55 0.49,0.24 0.48,1.01 -0.06,1.18 -0.56,0.22 -1.18,0.08 -1.74,-0.05 -0.71,-0.2 -1.41,-0.72 -1.5,-1.5 -0.18,-0.89 0.01,-1.8 -0.16,-2.68 -0.22,-0.64 -0.94,-0.9 -1.57,-0.93 -0.58,-0.1 -0.83,-0.94 -0.35,-1.3 0.51,-0.35 1.26,-0.14 1.69,-0.66 0.44,-0.48 0.29,-1.18 0.32,-1.78 0,-0.81 -0.02,-1.77 0.65,-2.34 0.66,-0.54 1.58,-0.71 2.41,-0.63 0.63,0 0.98,0.87 0.4,1.22 -0.44,0.37 -1.2,0.06 -1.51,0.65 -0.14,0.56 -0.05,1.15 -0.07,1.73 -0.01,0.75 -0.05,1.64 -0.72,2.13 -0.12,0.1 -0.26,0.19 -0.4,0.26" id="path408" inkscape:connector-curvature="0"/>
<path transform="translate(-256,-24)" d="m 276.98,35.99 c -0.67,-0.3 -1.08,-1.02 -1.08,-1.75 -0.07,-0.76 0.03,-1.52 -0.06,-2.28 -0.24,-0.58 -0.98,-0.4 -1.46,-0.59 -0.59,-0.24 -0.48,-1.18 0.14,-1.31 0.73,-0.15 1.52,-0.01 2.18,0.32 0.56,0.28 0.95,0.86 0.99,1.48 0.13,0.83 -0.03,1.68 0.13,2.5 0.2,0.68 0.94,0.83 1.54,0.9 0.56,0.07 0.86,0.8 0.46,1.21 -0.44,0.46 -1.2,0.2 -1.65,0.66 -0.51,0.46 -0.4,1.21 -0.4,1.83 -0.03,0.78 0.06,1.69 -0.52,2.3 -0.74,0.65 -1.8,0.86 -2.75,0.68 -0.52,-0.16 -0.69,-1.01 -0.15,-1.25 0.44,-0.23 1.02,-0.08 1.41,-0.45 0.26,-0.45 0.09,-0.98 0.14,-1.47 0.01,-0.76 -0.07,-1.63 0.43,-2.26 0.18,-0.21 0.42,-0.37 0.66,-0.51" id="path410" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,168)" id="g412">
<g id="g416">
<path d="M 0,0 H 24 V 24 H 0 z" id="path418" inkscape:connector-curvature="0" style="fill:none"/>
<path d="m 16,6 c -3.31,0 -6,2.69 -6,6 0,3.31 2.69,6 6,6 3.31,0 6,-2.69 6,-6 h -2 c 0,2.2091 -1.7909,4 -4,4 -2.2091,0 -4,-1.7909 -4,-4 0,-2.2091 1.7909,-4 4,-4 1.2756,0 2.3926,0.60127 3.125,1.5312 h 2.3438 C 20.52659,7.4496 18.4308,6 16,6 z" id="path420" inkscape:connector-curvature="0"/>
<path d="M 21.091,6.88 V 9.9718 H 17.9992 z" id="path422" inkscape:connector-curvature="0" style="stroke:#000000;stroke-width:1.06799996"/>
</g>
</g>
<g transform="translate(110,168)" id="g424">
<path transform="translate(8,4)" d="M 2.5858,13.891 C 0.9959,12.4291 0,10.3315 0,8.0002 c 0,-4.42 3.58,-8 8,-8 4.42,0 8,3.58 8,8 0,4.42 -3.58,8 -8,8 v -1.6 c 3.528,0 6.4,-2.872 6.4,-6.4 0,-3.528 -2.872,-6.4 -6.4,-6.4 -3.528,0 -6.4,2.872 -6.4,6.4 0,1.9357 0.86461,3.674 2.2278,4.8487 l 1.6925,-1.4201 0.015145,4.463 -4.3925,-0.7899 1.443,-1.2108 z M 6.5066,11.4287 11.078,8.0001 6.5066,4.5715 v 6.8571 z" id="path428" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(138,168)" id="g430">
<path transform="translate(0,-72)" d="M 23,84 15,79 V 89" id="path434" inkscape:connector-curvature="0"/>
<path transform="translate(0,-72)" d="M 13,89 H 10 V 79 h 3 v 10 z" id="path436" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,0)" id="g438">
<path transform="translate(-160,-120)" d="m 169.2,130.15 c 0.0551,-0.1128 0.15339,-0.22 0.2313,-0.3064 0.54584,-0.6074 1.3889,-1.1974 3.0053,-1.5702 v -2.1715 c -1.2931,0.2523 -2.3146,0.6686 -3.304,1.5597 -0.33248,0.3832 -0.50543,0.6247 -0.514,1.1665 -0.007,0.443 0.17732,0.8863 0.58141,1.3219 z m 6.6309,3.1292 c -1.6164,-0.024 -3.1091,-0.2558 -4.6228,-0.764 -2.6938,-0.9037 -3.0286,-2.1697 -3.1703,-2.5931 -0.0958,1.6897 0.028,2.9573 0.0346,3.0648 0.092,1.5004 1.2918,2.6742 2.1575,3.1151 1.7796,0.9067 3.6614,1.3756 5.601,1.4151 v 2.4842 l 3.251,-4.6483 -3.251,-4.6486 v 2.5748 z m -0.32327,-5.2959 c 1.6164,0.01 3.4529,0.3384 5.2454,1.1131 0.73528,0.3177 1.3275,0.7904 1.8082,1.3048 0.33152,-0.1523 0.80672,-0.7308 0.74126,-1.2923 -0.16163,-1.3879 -1.6264,-2.1912 -1.8801,-2.3045 -1.7704,-0.7908 -3.9751,-1.0356 -5.9147,-1.0589 v -1.7442 l -2.0132,2.8787 2.0132,2.8783 v -1.775 z m 8.4236,1.8658 c -0.0824,0.6478 -0.82676,2.0349 -3.8978,2.9138 v 4.5247 c 1.1314,-0.3677 2.465,-1.2956 3.1787,-2.1988 0.2911,-0.3677 0.46906,-0.875 0.57736,-1.242 0.33216,-1.1229 0.18604,-3.3136 0.14175,-3.9977 z" id="path442" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,24)" id="g444">
<path transform="translate(8,4)" d="m 10.624,2.47 c 1.9155,0.90417 3.2862,2.7533 3.4971,4.9467 h 0.87866 C 14.70102,3.8234 11.68426,1 7.99976,1 L 7.61315,1.0175 9.84495,3.24 10.62403,2.47 z M 6.9629,2.02083 c -0.34561,-0.34417 -0.90209,-0.34417 -1.2418,0 l -3.7255,3.71 c -0.34561,0.34417 -0.34561,0.89833 0,1.2367 l 7.041,7.0117 c 0.34561,0.34417 0.90209,0.34417 1.2418,0 l 3.7255,-3.71 c 0.34561,-0.34417 0.34561,-0.89833 0,-1.2367 l -7.041,-7.0117 z m 2.6946,11.34 -7.041,-7.0117 3.7255,-3.71 7.041,7.0117 -3.7255,3.71 z M 5.3755,13.53 C 3.46,12.63167 2.0893,10.7767 1.8784,8.5833 H 0.99974 C 1.29848,12.1766 4.31524,15 7.99974,15 L 8.38635,14.9825 6.15455,12.76 5.37547,13.53 z" id="path448" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,48)" id="g450">
<path transform="translate(-288,-72)" d="m 293.96,74.013 h 20 v 20 h -20 z" id="path454" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-288,-72)" d="m 297,77 h 18 v 18 h -18 z" id="path456" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-288,-72)" d="m 295.25,75.25 h 20 v 20 h -20 z" id="path458" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-288,-72)" d="m 293.12,73.125 h 20 v 20 h -20 z" id="path460" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-288,-72)" d="m 288,72 h 24 v 24 h -24 z" id="path462" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(-288,-72)" d="m 309.35,84.686 c 0.0288,-0.224 0.0504,-0.448 0.0504,-0.686 0,-0.238 -0.0216,-0.462 -0.0504,-0.686 l 1.5184,-1.155 c 0.13673,-0.105 0.17272,-0.294 0.0863,-0.448 l -1.4393,-2.422 c -0.0863,-0.154 -0.28065,-0.21 -0.43897,-0.154 l -1.7919,0.7 c -0.37422,-0.28 -0.77722,-0.511 -1.2162,-0.686 l -0.27341,-1.855 C 305.77334,77.126 305.62223,77 305.44232,77 h -2.8785 c -0.17992,0 -0.33104,0.126 -0.35263,0.294 l -0.27346,1.855 c -0.43898,0.175 -0.84198,0.413 -1.2162,0.686 l -1.7919,-0.7 c -0.16551,-0.063 -0.35262,0 -0.43898,0.154 l -1.4393,2.422 c -0.0935,0.154 -0.0504,0.343 0.0863,0.448 l 1.5184,1.155 c -0.0287,0.224 -0.0504,0.455 -0.0504,0.686 0,0.231 0.0216,0.462 0.0504,0.686 l -1.5184,1.155 c -0.13673,0.105 -0.17271,0.294 -0.0863,0.448 l 1.4393,2.422 c 0.0863,0.154 0.28067,0.21 0.43898,0.154 l 1.7919,-0.7 c 0.37421,0.28 0.77721,0.511 1.2162,0.686 l 0.27346,1.855 c 0.0216,0.168 0.17271,0.294 0.35263,0.294 h 2.8785 c 0.17991,0 0.33103,-0.126 0.35263,-0.294 l 0.27345,-1.855 c 0.43898,-0.175 0.84198,-0.413 1.2162,-0.686 l 1.7919,0.7 c 0.16552,0.063 0.35263,0 0.43898,-0.154 l 1.4393,-2.422 c 0.0863,-0.154 0.0504,-0.343 -0.0863,-0.448 l -1.5184,-1.155 z m -5.3469,1.764 c -1.3889,0 -2.5187,-1.099 -2.5187,-2.45 0,-1.351 1.1298,-2.45 2.5187,-2.45 1.3889,0 2.5187,1.099 2.5187,2.45 0,1.351 -1.1298,2.45 -2.5187,2.45 z" id="path464" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(162,73)" id="g466">
<path transform="matrix(0,-1,1,0,-136,251)" d="m 232.5,150.5 h 14 v 12 h -14 z" id="path470" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="matrix(0,-1,1,0,-136,251)" d="m 244,156.5 -5,-3.25 v 6.5" id="path472" inkscape:connector-curvature="0"/>
<path transform="matrix(0,-1,1,0,-136,251)" d="m 236,150 h 1 v 13 h -1 z" id="path474" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,96)" id="g476">
<path transform="translate(-160,-72)" d="m 168.5,78.5 h 14 v 12 h -14 z" id="path480" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="translate(-160,-72)" d="m 180,84.5 -5,-3.25 v 6.5" id="path482" inkscape:connector-curvature="0"/>
<path transform="translate(-160,-72)" d="m 172,78 h 1 v 13 h -1 z" id="path484" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,120)" id="g486">
<path transform="matrix(-1,0,0,1,-192,-96)" d="m -214.5,102.5 h 14 v 12 h -14 z" id="path490" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="translate(-192,-96)" d="m 203,108.5 5,-3.25 v 6.5" id="path492" inkscape:connector-curvature="0"/>
<path transform="matrix(-1,0,0,1,-192,-96)" d="m -211,102 h 1 v 13 h -1 z" id="path494" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(162,145)" id="g496">
<path transform="matrix(0,1,1,0,-88,219)" d="m -214.5,102.5 h 14 v 12 h -14 z" id="path500" inkscape:connector-curvature="0" style="fill:none;stroke:#000000"/>
<path transform="matrix(0,-1,1,0,-88,219)" d="m 203,108.5 5,-3.25 v 6.5" id="path502" inkscape:connector-curvature="0"/>
<path transform="matrix(0,1,1,0,-88,219)" d="m -211,102 h 1 v 13 h -1 z" id="path504" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(166,168)" id="g506">
<path transform="translate(-288,0)" d="m 298,12 c 0,3.31 2.69,6 6,6 3.31,0 6,-2.69 6,-6 0,-3.31 -2.69,-6 -6,-6 -3.31,0 -6,2.69 -6,6" id="path510" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(-2,192)" id="g512">
<path transform="translate(-64,-72)" d="m 78.5,89 c 0,1.1 0.9,2 2,2 1.1,0 2,-0.9 2,-2 0,-1.1 -0.9,-2 -2,-2 -1.1,0 -2,0.9 -2,2" id="path516" inkscape:connector-curvature="0"/>
<path transform="translate(-64,-72)" d="m 79,78 v 4 h -3 l 4.5,4 4.5,-4 h -3 v -4 h -3 z" id="path518" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(26,192)" id="g520">
<path transform="translate(-96,-72)" d="m 110.5,89 c 0,1.1 0.9,2 2,2 1.1,0 2,-0.9 2,-2 0,-1.1 -0.9,-2 -2,-2 -1.1,0 -2,0.9 -2,2" id="path524" inkscape:connector-curvature="0"/>
<path transform="translate(-96,-72)" d="m 112.5,78 -4.5,4 h 3 v 4 h 3 v -4 h 3 l -4.5,-4 z" id="path526" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(54,192)" id="g528">
<path transform="translate(-128,-72)" d="m 142,86.5 c 0,1.1 0.9,2 2,2 1.1,0 2,-0.9 2,-2 0,-1.1 -0.9,-2 -2,-2 -1.1,0 -2,0.9 -2,2" id="path532" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-72)" d="m 137.25,87.03 c 2.55,-8.43 11.4,-8.73 13.94,0" id="path534" inkscape:connector-curvature="0" style="fill:none;stroke:#000000;stroke-width:2.5"/>
<path transform="translate(-128,-72)" d="m 151.68,89 -4.54,-2.76 6.68,-2.1" id="path536" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(82,192)" id="g538">
<path transform="translate(-288,-24)" d="m 295,36 c 0,4.97 4.03,9 9,9 4.97,0 9,-4.03 9,-9 0,-4.97 -4.03,-9 -9,-9 -4.97,0 -9,4.03 -9,9" id="path542" inkscape:connector-curvature="0" style="fill:url(#sprite60_a)"/>
<path transform="translate(-288,-24)" d="m 298,36 c 0,3.31 2.69,6 6,6 3.31,0 6,-2.69 6,-6 0,-3.31 -2.69,-6 -6,-6 -3.31,0 -6,2.69 -6,6" id="path544" inkscape:connector-curvature="0"/>
<defs id="defs546">
<radialGradient id="sprite60_b" cx="0" cy="0" r="1" gradientTransform="matrix(18,0,0,-18,680,341)" gradientUnits="userSpaceOnUse">
<stop offset="0" id="stop549"/>
<stop stop-opacity="0" offset="1" id="stop551"/>
</radialGradient>
<radialGradient id="sprite60_a" cx="0" cy="0" r="1" gradientTransform="matrix(9,0,0,9,304,36)" gradientUnits="userSpaceOnUse" xlink:href="#sprite60_b"/>
</defs>
</g>
<g transform="translate(110,192)" id="g554">
<g id="g558">
<path transform="translate(7,3)" d="M 0,0 H 18 V 18 H 0 z" id="path560" inkscape:connector-curvature="0" style="fill:none"/>
<path transform="translate(7,3)" d="M 15.25,8 H 16 V 6.125 5 H 5 V 6.125 8 h 0.75 c 0,-0.82843 0.67157,-1.5 1.5,-1.5 h 2.5 v 7.25 c 0,0.82843 -0.67157,1.5 -1.5,1.5 V 16 h 1.875 0.75 1.875 v -0.75 c -0.82843,0 -1.5,-0.67157 -1.5,-1.5 V 6.5 h 2.5 c 0.82843,0 1.5,0.67157 1.5,1.5 z" id="path562" inkscape:connector-curvature="0" style="fill-opacity:0.36000001"/>
<path transform="translate(7,3)" d="M 12.25,5 H 13 V 3.125 2 H 2 V 3.125 5 h 0.75 c 0,-0.82843 0.67157,-1.5 1.5,-1.5 h 2.5 v 7.25 c 0,0.82843 -0.67157,1.5 -1.5,1.5 V 13 h 1.875 0.75 1.875 v -0.75 c -0.82843,0 -1.5,-0.67157 -1.5,-1.5 V 3.5 h 2.5 c 0.82843,0 1.5,0.67157 1.5,1.5 z" id="path564" inkscape:connector-curvature="0"/>
</g>
</g>
<g transform="translate(138,192)" id="g566">
<path transform="translate(-128,-24)" d="m 139.5,33 h 9 L 147,43 h -6" id="path570" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-24)" d="m 147.5,30 h -2 v -1 h -3 v 1 h -2 c -0.55,0 -1,0.48 -1,1 v 1 h 1 7 1 v -1 c 0,-0.52 -0.45,-1 -1,-1" id="path572" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(168,192)" id="g574">
<path transform="translate(0,-48)" d="M 10,57 H 8 v 9 h 11 v -2 h -9 z" id="path578" inkscape:connector-curvature="0"/>
<path transform="translate(0,-48)" d="m 13,56 h 7 v 5 h -7 z m -2,-2 v 9 h 11 v -9 z" id="path580" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(194,0)" id="g582">
<path transform="translate(-96,0)" d="m 112,6.6909 c -3.6364,0 -6.7418,2.2618 -8,5.4545 1.2582,3.1927 4.3636,5.4545 8,5.4545 3.6364,0 6.7418,-2.2618 8,-5.4545 -1.2582,-3.1927 -4.3636,-5.4545 -8,-5.4545 z m 0,9.0909 c -2.0073,0 -3.6364,-1.6291 -3.6364,-3.6364 0,-2.0073 1.6291,-3.6364 3.6364,-3.6364 2.0073,0 3.6364,1.6291 3.6364,3.6364 0,2.0073 -1.6291,3.6364 -3.6364,3.6364 z m 0,-5.8182 c -1.2073,0 -2.1818,0.97455 -2.1818,2.1818 0,1.2073 0.97455,2.1818 2.1818,2.1818 1.2073,0 2.1818,-0.97455 2.1818,-2.1818 0,-1.2073 -0.97455,-2.1818 -2.1818,-2.1818 z" id="path586" inkscape:connector-curvature="0"/>
</g>
<g transform="translate(194,24)" id="g588">
<path transform="translate(-128,-48)" d="m 153,57 h -11 v -2 h 11 v 2 z" id="path592" inkscape:connector-curvature="0" style="opacity:0.2"/>
<path transform="translate(-128,-48)" d="m 142,57 h -6 v -2 h 6 v 2 z" id="path594" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-48)" d="m 145,60 h -6 v -2 h 6 v 2 z" id="path596" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-48)" d="m 150,63 h -6 v -2 h 6 v 2 z" id="path598" inkscape:connector-curvature="0"/>
<path transform="translate(-128,-48)" d="m 152,66 h -6 v -2 h 6 v 2 z" id="path600" inkscape:connector-curvature="0"/>
</g>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="12.355932" y="231.55931" id="text4355"><tspan sodipodi:role="line" id="tspan4357" x="12.355932" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">a</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="40.355934" y="231.55931" id="text4355-3"><tspan sodipodi:role="line" id="tspan4357-6" x="40.355934" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">b</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="68.355934" y="231.55931" id="text4355-7"><tspan sodipodi:role="line" id="tspan4357-5" x="68.355934" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">c</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="96.355934" y="231.55931" id="text4355-35"><tspan sodipodi:role="line" id="tspan4357-62" x="96.355934" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">d</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="124.35593" y="231.55931" id="text4355-9"><tspan sodipodi:role="line" id="tspan4357-1" x="124.35593" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">e</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="152.35593" y="231.55931" id="text4355-2"><tspan sodipodi:role="line" id="tspan4357-7" x="152.35593" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">f</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="180.35593" y="231.55931" id="text4355-0"><tspan sodipodi:role="line" id="tspan4357-9" x="180.35593" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">g</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="208.35593" y="231.55931" id="text4355-36"><tspan sodipodi:role="line" id="tspan4357-0" x="208.35593" y="231.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">h</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="207.55931" id="text4355-6"><tspan sodipodi:role="line" id="tspan4357-2" x="-15.644068" y="207.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">1</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="183.55931" id="text4355-61"><tspan sodipodi:role="line" id="tspan4357-8" x="-15.644068" y="183.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">2</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="159.55931" id="text4355-79"><tspan sodipodi:role="line" id="tspan4357-20" x="-15.644068" y="159.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">3</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="135.55931" id="text4355-23"><tspan sodipodi:role="line" id="tspan4357-75" x="-15.644068" y="135.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">4</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="111.55931" id="text4355-92"><tspan sodipodi:role="line" id="tspan4357-28" x="-15.644068" y="111.55931" style="font-size:12px;line-height:1.25;font-family:sans-serif">5</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="87.559311" id="text4355-97"><tspan sodipodi:role="line" id="tspan4357-3" x="-15.644068" y="87.559311" style="font-size:12px;line-height:1.25;font-family:sans-serif">6</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="63.559311" id="text4355-612"><tspan sodipodi:role="line" id="tspan4357-93" x="-15.644068" y="63.559311" style="font-size:12px;line-height:1.25;font-family:sans-serif">7</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="39.559311" id="text4355-1"><tspan sodipodi:role="line" id="tspan4357-94" x="-15.644068" y="39.559311" style="font-size:12px;line-height:1.25;font-family:sans-serif">8</tspan></text>
<text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#b3b3b3;fill-opacity:1;stroke:none" x="-15.644068" y="15.559311" id="text4355-78"><tspan sodipodi:role="line" id="tspan4357-4" x="-15.644068" y="15.559311" style="font-size:12px;line-height:1.25;font-family:sans-serif">9</tspan></text>
<g id="Page-1-6" style="fill:none;stroke:none" transform="translate(200,50)">
<g id="undo">
<rect height="20" width="20" y="0" x="0" id="bounds" style="fill:#ffffff;fill-opacity:0"/>
<path id="shape" d="M 4.3431457,9.3431458 C 5.790861,7.8954305 7.790861,7 10,7 c 3.555275,0 6.568879,2.3191676 7.610506,5.527197 L 16.155367,13 15.658589,13 C 14.834916,10.669615 12.612438,9 10,9 8.3431458,9 6.8431457,9.6715729 5.7573593,10.757359 L 8,13 2,13 2,7 l 2.3431457,2.3431458 0,0 z" inkscape:connector-curvature="0" style="fill:#000000"/>
</g>
</g>
<path style="fill:#000000" sodipodi:nodetypes="ccccccccccccc" inkscape:connector-curvature="0" d="m 215,82 -3,0 0,-4 -4,0 0,4 -3,0 5,6 z m -10,7 0,1 10,0 0,-1 z" id="path4295"/>
<path style="fill:none" inkscape:connector-curvature="0" d="m 196,72 h 24 v 24 h -24 z" id="path4297"/>
<path style="fill:#000000" d="m 210,102 -5,6 3,0 0,4 4,0 0,-4 3,0 z m -5,11 0,1 10,0 0,-1 z" id="path4295-3" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccccc"/>
<rect style="fill:#000000;" id="square-icon" width="8" height="8" x="206" y="176"/>
<g style="fill:#000000" id="g4456" transform="matrix(0.78548728,0,0,0.78548728,200.6822,123.63151)">
<path id="path4442" d="M 15.5,14 H 14.71 L 14.43,13.73 C 15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3 5.91,3 3,5.91 3,9.5 3,13.09 5.91,16 9.5,16 c 1.61,0 3.09,-0.59 4.23,-1.57 L 14,14.71 v 0.79 l 5,4.99 1.49,-1.49 z m -6,0 C 7.01,14 5,11.99 5,9.5 5,7.01 7.01,5 9.5,5 11.99,5 14,7.01 14,9.5 14,11.99 11.99,14 9.5,14 Z" inkscape:connector-curvature="0"/>
<path id="path4444" d="M 0,0 H 24 V 24 H 0 Z" inkscape:connector-curvature="0" style="fill:none"/>
</g>
<path inkscape:connector-curvature="0" id="path524-5" d="m 211.71941,203.4233 c 0,1.1 0.9,2 2,2 1.1,0 2,-0.9 2,-2 0,-1.1 -0.9,-2 -2,-2 -1.1,0 -2,0.9 -2,2"/>
<g transform="translate(190.16772,189.5042)" id="g520-3">
<path d="m 20.83228,13.9958 -4,-4.5 v 3 h -4 v 3 h 4 v 3 z" id="path526-7" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccc"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -36,3 +36,40 @@
.contractFunction {
margin-bottom: 0px;
}
.debugButton {
width: 28px;
height: 24px;
}
.jumpBack {
background-position: 0px 48px;
background-image: url(images/icons.png);
}
.jumpForward {
background-position: -28px 48px;
background-image: url(images/icons.png);
}
.stepOverBack {
transform: scaleX(-1);
background-position: -56px 24px;
background-image: url(images/icons.png);
}
.stepOverForward {
background-position: -56px 24px;
background-image: url(images/icons.png);
}
.stepIntoForward {
background-position: 0px 24px;
background-image: url(images/icons.png);
}
.stepIntoBack {
background-position: -28px 24px;
background-image: url(images/icons.png);
}

View File

@ -1,6 +1,6 @@
import {combineReducers} from 'redux';
import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE,
FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH,
FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH, DEBUGGER_INFO,
SIGN_MESSAGE, VERIFY_MESSAGE, TOGGLE_BREAKPOINT,
UPDATE_DEPLOYMENT_PIPELINE, WEB3_CONNECT, WEB3_DEPLOY, WEB3_ESTIMAGE_GAS} from "../actions";
import {EMBARK_PROCESS_NAME, DARK_THEME, DEPLOYMENT_PIPELINES, DEFAULT_HOST} from '../constants';
@ -313,7 +313,7 @@ function messageVerification(state = DEFAULT_MESSAGE_VERIFICATION_STATE, action)
}
function breakpoints(state = {}, action) {
if (action.type === TOGGLE_BREAKPOINT) {
if (action.type === TOGGLE_BREAKPOINT[SUCCESS]) {
const {filename, lineNumber} = action.payload;
let lineNumbers = state[filename] || [];
if (lineNumbers.includes(lineNumber)){
@ -347,6 +347,13 @@ function web3(state = {deployments: {}, gasEstimates: {}}, action) {
return state
}
function debuggerInfo(state={}, action) {
if (action.type === DEBUGGER_INFO[SUCCESS]) {
return action.data;
}
return state;
}
const rootReducer = combineReducers({
entities,
loading,
@ -355,13 +362,15 @@ const rootReducer = combineReducers({
errorEntities,
credentials,
baseEther,
theme,
searchResult,
messageSignature,
messageVerification,
breakpoints,
deploymentPipeline,
web3
web3,
searchResult,
debuggerInfo,
theme
});
export default rootReducer;

View File

@ -224,3 +224,13 @@ export function getWeb3GasEstimates(state) {
export function getWeb3Deployments(state) {
return state.web3.deployments;
}
export function debuggerInfo(state) {
return state.debuggerInfo;
}
export function getDebuggerLine(state) {
if (!state.debuggerInfo.sources) return 10;
return state.debuggerInfo.sources.lineColumnPos[0].start.line + 1;
}

View File

@ -69,6 +69,14 @@ export const fetchFile = doRequest.bind(null, actions.file, api.fetchFile);
export const postFile = doRequest.bind(null, actions.saveFile, api.postFile);
export const deleteFile = doRequest.bind(null, actions.removeFile, api.deleteFile);
export const fetchEthGas = doRequest.bind(null, actions.gasOracle, api.getEthGasAPI);
export const startDebug = doRequest.bind(null, actions.startDebug, api.startDebug);
export const debugJumpBack = doRequest.bind(null, actions.debugJumpBack, api.debugJumpBack);
export const debugJumpForward = doRequest.bind(null, actions.debugJumpForward, api.debugJumpForward);
export const debugStepOverForward = doRequest.bind(null, actions.debugStepOverForward, api.debugStepOverForward);
export const debugStepOverBackward = doRequest.bind(null, actions.debugStepOverBackward, api.debugStepOverBackward);
export const debugStepIntoForward = doRequest.bind(null, actions.debugStepIntoForward, api.debugStepIntoForward);
export const debugStepIntoBackward = doRequest.bind(null, actions.debugStepIntoBackward, api.debugStepIntoBackward);
export const toggleBreakpoint = doRequest.bind(null, actions.toggleBreakpoint, api.toggleBreakpoint);
export const authenticate = doRequest.bind(null, actions.authenticate, api.authenticate);
export const fetchCurrentFile = doRequest.bind(null, actions.currentFile, storage.fetchCurrentFile);
@ -225,6 +233,38 @@ export function *watchFetchEthGas() {
yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
}
export function *watchStartDebug() {
yield takeEvery(actions.START_DEBUG[actions.REQUEST], startDebug);
}
export function *watchDebugJumpBack() {
yield takeEvery(actions.DEBUG_JUMP_BACK[actions.REQUEST], debugJumpBack);
}
export function *watchDebugJumpForward() {
yield takeEvery(actions.DEBUG_JUMP_FORWARD[actions.REQUEST], debugJumpForward);
}
export function *watchDebugStepOverForward() {
yield takeEvery(actions.DEBUG_STEP_OVER_FORWARD[actions.REQUEST], debugStepOverForward);
}
export function *watchDebugStepOverBackward() {
yield takeEvery(actions.DEBUG_STEP_OVER_BACKWARD[actions.REQUEST], debugStepOverBackward);
}
export function *watchDebugStepIntoForward() {
yield takeEvery(actions.DEBUG_STEP_INTO_FORWARD[actions.REQUEST], debugStepIntoForward);
}
export function *watchDebugStepIntoBackward() {
yield takeEvery(actions.DEBUG_STEP_INTO_BACKWARD[actions.REQUEST], debugStepIntoBackward);
}
export function *watchToggleBreakpoint() {
yield takeEvery(actions.TOGGLE_BREAKPOINT[actions.REQUEST], toggleBreakpoint);
}
export function *watchAuthenticate() {
yield takeEvery(actions.AUTHENTICATE[actions.REQUEST], authenticate);
}
@ -385,6 +425,28 @@ export function *watchListenGasOracle() {
yield takeEvery(actions.WATCH_GAS_ORACLE, listenGasOracle);
}
export function *listenDebugger() {
const credentials = yield select(getCredentials);
const socket = api.listenToDebugger(credentials);
const channel = yield call(createChannel, socket);
while (true) {
const { cancel, debuggerInfo } = yield race({
debuggerInfo: take(channel),
cancel: take(actions.STOP_DEBUGGER)
});
if (cancel) {
channel.close();
return;
}
yield put(actions.debuggerInfo.success(debuggerInfo));
}
}
export function *watchListenDebugger() {
yield takeEvery(actions.START_DEBUG[actions.SUCCESS], listenDebugger);
}
export function *listenToMessages(action) {
const credentials = yield select(getCredentials);
const socket = api.listenToChannel(credentials, action.messageChannels[0]);
@ -436,6 +498,14 @@ export default function *root() {
fork(watchPostFileSuccess),
fork(watchFetchCredentials),
fork(watchFetchEthGas),
fork(watchStartDebug),
fork(watchDebugJumpBack),
fork(watchDebugJumpForward),
fork(watchDebugStepOverForward),
fork(watchDebugStepOverBackward),
fork(watchDebugStepIntoForward),
fork(watchDebugStepIntoBackward),
fork(watchToggleBreakpoint),
fork(watchAuthenticate),
fork(watchAuthenticateSuccess),
fork(watchLogout),
@ -447,6 +517,7 @@ export default function *root() {
fork(watchVerifyMessage),
fork(watchWeb3EstimateGas),
fork(watchWeb3Deploy),
fork(watchUpdateDeploymentPipeline)
fork(watchUpdateDeploymentPipeline),
fork(watchListenDebugger)
]);
}

View File

@ -185,6 +185,42 @@ export function verifyMessage(payload) {
return post('/messages/verify', ...arguments);
}
export function startDebug(payload) {
return post('/debugger/start', {params: payload, credentials: payload.credentials});
}
export function debugJumpBack(payload) {
return post('/debugger/jumpBack', {params: payload, credentials: payload.credentials});
}
export function debugJumpForward(payload) {
return post('/debugger/jumpForward', {params: payload, credentials: payload.credentials});
}
export function debugStepOverForward(payload) {
return post('/debugger/stepOverForward', {params: payload, credentials: payload.credentials});
}
export function debugStepOverBackward(payload) {
return post('/debugger/stepOverBackward', {params: payload, credentials: payload.credentials});
}
export function debugStepIntoForward(payload) {
return post('/debugger/stepIntoForward', {params: payload, credentials: payload.credentials});
}
export function debugStepIntoBackward(payload) {
return post('/debugger/stepIntoBackward', {params: payload, credentials: payload.credentials});
}
export function toggleBreakpoint(payload) {
return post('/debugger/breakpoint', {params: payload, credentials: payload.credentials});
}
export function listenToDebugger(credentials) {
return new WebSocket(`ws://${credentials.host}/embark-api/debugger`, [credentials.token]);
}
export function listenToChannel(credentials, channel) {
return new WebSocket(`ws://${credentials.host}/embark-api/communication/listenTo/${channel}`, [credentials.token]);
}

View File

@ -217,6 +217,7 @@ class Engine {
this.registerModule('console_listener', {ipc: self.ipc});
this.registerModule('deployment', {plugins: this.plugins, onlyCompile: options.onlyCompile});
this.registerModule('transactionTracker');
this.registerModule('debugger');
this.events.on('file-event', function ({fileType, path}) {
clearTimeout(self.fileTimeout);

View File

@ -33,6 +33,10 @@ class BlockchainConnector {
cb(self);
});
self.events.setCommandHandler("blockchain:getTransaction", (txHash, cb) => {
self.getTransactionByHash(txHash, cb);
});
embark.registerActionForEvent("contracts:deploy:afterAll", this.subscribeToContractEvents.bind(this));
if (!this.web3) {
@ -580,7 +584,7 @@ class BlockchainConnector {
this.web3.eth.getBlock(blockNumber, true, cb);
}
getTransaction(hash, cb) {
getTransactionByHash(hash, cb) {
this.web3.eth.getTransaction(hash, cb);
}
@ -595,6 +599,14 @@ class BlockchainConnector {
return this.web3.eth.net.getId();
}
//TODO: fix me, why is this gasPrice??
getTransaction(hash, cb) {
const self = this;
this.onReady(() => {
self.web3.eth.getGasPrice(cb);
});
}
ContractObject(params) {
return new this.web3.eth.Contract(params.abi, params.address);
}

View File

@ -47,7 +47,7 @@ class Console {
}
processEmbarkCmd (cmd) {
if (cmd === 'help' || cmd === __('help')) {
if (cmd === 'help' || cmd === __('help') || cmd === '01189998819991197253') {
let helpText = [
__('Welcome to Embark') + ' ' + this.version,
'',

View File

@ -101,6 +101,7 @@ class ConsoleListener {
this.events.emit('contracts:log', this.logs[this.logs.length - 1]);
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
this.events.emit('blockchain:tx', { name: name, functionName: functionName, paramString: paramString, transactionHash: transactionHash, gasUsed: gasUsed, blockNumber: blockNumber, status: status });
});
}

View File

@ -36,6 +36,10 @@ class ContractsManager {
cb(self.getContract(contractName));
});
self.events.setCommandHandler("contracts:contract:byTxHash", (txHash, cb) => {
self.getContractByTxHash(txHash, cb)
});
self.events.setCommandHandler("contracts:build", (configOnly, cb) => {
self.deployOnlyOnConfig = configOnly; // temporary, should refactor
self.build((err) => {
@ -490,6 +494,19 @@ class ContractsManager {
return this.contracts[className];
}
getContractByTxHash(txHash, cb) {
this.events.request("blockchain:getTransaction", txHash, (err, tx) => {
if (err) return cb(err);
for (let contractName in this.contracts) {
let contract = this.contracts[contractName];
if (tx.to === contract.deployedAddress) {
return cb(null, contract);
}
}
cb("no known contract found for txHash: " + txHash);
});
}
sortContracts(contractList) {
let converted_dependencies = [], i;

View File

@ -0,0 +1,78 @@
var RemixDebug = require('remix-debug-debugtest');
var CmdLine = RemixDebug.CmdLine;
const async = require('async');
class DebuggerManager {
constructor(nodeUrl) {
this.nodeUrl = nodeUrl
this.outputJson = {}
this.inputJson = {}
}
setInputJson(inputJson) {
this.inputJson = inputJson
}
setOutputJson(outputJson) {
this.outputJson = outputJson
}
createDebuggerSession(txHash, filename, cb) {
return this.debug(txHash, filename, cb)
}
debug(txHash, filename, cb) {
console.dir("debugging tx " + txHash)
var cmd_line = new CmdLine()
this.cmd_line = cmd_line
cmd_line.connect("http", this.nodeUrl)
cmd_line.loadCompilationData(this.inputJson, this.outputJson)
cmd_line.initDebugger(() => {
this.isDebugging = true
cmd_line.startDebug(txHash, filename, () => {
if (cb) {
cmd_line.triggerSourceUpdate()
cb()
}
})
})
return cmd_line
}
getLastLine(txHash, filename, outputCb) {
const self = this;
let cmd_line = new CmdLine()
async.waterfall([
function initDebugger(next) {
cmd_line = new CmdLine()
cmd_line.connect("http", self.nodeUrl)
cmd_line.loadCompilationData(self.inputJson, self.outputJson)
cmd_line.initDebugger(() => {
// self.isDebugging = true
next()
})
},
function startDebug(next) {
cmd_line.startDebug(txHash, filename, () => {
cmd_line.events.on("source", () => {
outputCb(cmd_line.getSource())
})
let total_size = cmd_line.getTraceLength()
cmd_line.jumpTo(total_size - 1)
cmd_line.unload()
next()
})
}
], () => {
})
}
}
module.exports = DebuggerManager;

View File

@ -0,0 +1,212 @@
var RemixDebug = require('remix-debug-debugtest');
var CmdLine = RemixDebug.CmdLine;
var DebuggerManager = require('./debugger_manager.js');
class TransactionDebugger {
constructor(embark, _options) {
const self = this
this.embark = embark
this.debugger_manager = new DebuggerManager("http://localhost:8545")
embark.events.on('contracts:compile:solc', this.debugger_manager.setInputJson.bind(this.debugger_manager))
embark.events.on('contracts:compiled:solc', this.debugger_manager.setOutputJson.bind(this.debugger_manager))
this.tx_tracker = {}
this.last_tx = ""
this.isDebugging = false
this.listenToEvents()
this.listenToCommands()
this.listentoAPI()
}
listenToEvents() {
const self = this
this.embark.events.on('blockchain:tx', (tx) => {
this.embark.events.request("contracts:contract", tx.name, (contract) => {
self.tx_tracker[tx.transactionHash] = {tx: tx, contract: contract}
self.last_tx = tx.transactionHash
if (tx.status !== '0x0') return
self.embark.logger.info("Transaction failed");
self.debugger_manager.getLastLine(tx.transactionHash, contract.filename, (lines) => {
lines.forEach((line) => {
self.embark.logger.error(line)
})
})
})
})
}
listentoAPI() {
this.debuggerData = {}
this.apiDebugger = false
this.embark.registerAPICall('post', '/embark-api/debugger/start', (req, res) => {
let txHash = req.body.params.txHash
this.embark.events.request("contracts:contract:byTxHash", txHash, (err, contract) => {
if (err) {
this.embark.logger.error(err);
return res.send({error: err})
}
let filename = contract.filename
this.debuggerData = {}
this.apiDebugger = this.debugger_manager.createDebuggerSession(txHash, filename, () => {
})
res.send({ok :true})
})
});
this.embark.registerAPICall('post', '/embark-api/debugger/JumpBack', (req, res) => {
this.apiDebugger.stepJumpNextBreakpoint()
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/JumpForward', (req, res) => {
this.apiDebugger.stepJumpPreviousBreakpoint()
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepOverForward', (req, res) => {
this.apiDebugger.stepOverForward(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepOverBackward', (req, res) => {
this.apiDebugger.stepOverBack(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepIntoForward', (req, res) => {
this.apiDebugger.stepIntoForward(true)
res.send({ok :true})
})
this.embark.registerAPICall('post', '/embark-api/debugger/StepIntoBackward', (req, res) => {
this.apiDebugger.stepIntoBack(true)
res.send({ok :true})
});
this.embark.registerAPICall('post', '/embark-api/debugger/breakpoint', (req, res) => {
console.dir("new breakpoint")
res.send({ok :true})
});
this.embark.registerAPICall('ws', '/embark-api/debugger', (ws, _req) => {
if (!this.apiDebugger) return
this.apiDebugger.events.on("source", (lineColumnPos, rawLocation) => {
this.debuggerData.sources = {lineColumnPos, rawLocation}
ws.send(JSON.stringify(this.debuggerData), () => {})
})
this.apiDebugger.events.on("locals", (data) => {
this.debuggerData.locals = this.simplifyDebuggerVars(data)
ws.send(JSON.stringify(this.debuggerData), () => {})
})
this.apiDebugger.events.on("globals", (data) => {
this.debuggerData.globals = this.simplifyDebuggerVars(data)
ws.send(JSON.stringify(this.debuggerData), () => {})
})
});
}
simplifyDebuggerVars(data) {
let new_data = {};
for (let key in data) {
let field = data[key];
new_data[`${key} (${field.type})`] = field.value
}
return new_data
}
listenToCommands() {
const self = this
this.cmdDebugger = false
this.embark.registerConsoleCommand((cmd, _options) => {
let cmdName = cmd.split(" ")[0]
let txHash = cmd.split(" ")[1]
return {
match: () => cmdName === 'debug',
process: (cb) => {
if (txHash) {
this.embark.events.request("contracts:contract:byTxHash", txHash, (err, contract) => {
if (err) {
this.embark.logger.error(err);
return;
}
let filename = contract.filename
self.cmdDebugger = self.debugger_manager.createDebuggerSession(txHash, filename, () => {
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
})
});
return
}
let filename = self.tx_tracker[self.last_tx].contract.filename
self.cmdDebugger = self.debugger_manager.createDebuggerSession(self.last_tx, filename, () => {
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'next' || cmd === 'n'),
process: (cb) => {
if (!self.cmdDebugger.currentStep()) {
console.dir("end of execution reached")
return self.cmdDebugger.unload()
}
self.cmdDebugger.stepOverForward(true)
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'previous' || cmd === 'p'),
process: (cb) => {
if (!self.cmdDebugger.currentStep()) {
console.dir("end of execution reached")
return self.cmdDebugger.unload()
}
self.cmdDebugger.stepOverBack(true)
self.cmdDebugger.getSource().forEach((line) => {
console.dir(line)
})
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'var local' || cmd === 'v l' || cmd === 'vl'),
process: (cb) => {
self.cmdDebugger.displayLocals()
}
};
})
this.embark.registerConsoleCommand((cmd, _options) => {
return {
match: () => (cmd === 'var global' || cmd === 'v g' || cmd === 'vg'),
process: (cb) => {
self.cmdDebugger.displayGlobals()
}
};
})
}
}
module.exports = TransactionDebugger

View File

@ -88,7 +88,7 @@ class Solidity {
},
outputSelection: {
'*': {
'': ['ast'],
'': ['ast', 'legacyAST'], // legacyAST is needed by the debugger, for now
'*': [
'abi',
'devdoc',

23439
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -115,6 +115,7 @@
"swarm-api": "0.1.2",
"tar": "3.2.1",
"toposort": "1.0.7",
"remix-debug-debugtest": "latest",
"underscore": "1.9.1",
"url-loader": "1.1.1",
"uuid": "3.3.2",

BIN
test_apps/.DS_Store vendored Normal file

Binary file not shown.

5
test_apps/embark_demo/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.embark/
node_modules/
dist/
config/production/password
config/livenet/password

View File

@ -0,0 +1,87 @@
import EmbarkJS from 'Embark/EmbarkJS';
import SimpleStorage from 'Embark/contracts/SimpleStorage';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
class Blockchain extends React.Component {
constructor(props) {
super(props);
this.state = {
valueSet: 10,
valueGet: "",
logs: []
}
}
handleChange(e) {
this.setState({ valueSet: e.target.value });
}
checkEnter(e, func) {
if (e.key !== 'Enter') {
return;
}
e.preventDefault();
func.apply(this, [e]);
}
setValue(e) {
e.preventDefault();
var value = parseInt(this.state.valueSet, 10);
SimpleStorage.methods.set(value).send();
this._addToLog("SimpleStorage.methods.set(value).send()");
}
getValue(e) {
e.preventDefault();
SimpleStorage.methods.get().call().then(_value => this.setState({ valueGet: _value }));
this._addToLog("SimpleStorage.methods.get(console.log)");
}
_addToLog(txt) {
this.state.logs.push(txt);
this.setState({ logs: this.state.logs });
}
render() {
return (<React.Fragment>
<h3> 1. Set the value in the blockchain</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.setValue)}>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.valueSet}
onChange={(e) => this.handleChange(e)}/>
<Button bsStyle="primary" onClick={(e) => this.setValue(e)}>Set Value</Button>
<HelpBlock>Once you set the value, the transaction will need to be mined and then the value will be updated
on the blockchain.</HelpBlock>
</FormGroup>
</Form>
<h3> 2. Get the current value</h3>
<Form inline>
<FormGroup>
<HelpBlock>current value is <span className="value">{this.state.valueGet}</span></HelpBlock>
<Button bsStyle="primary" onClick={(e) => this.getValue(e)}>Get Value</Button>
<HelpBlock>Click the button to get the current value. The initial value is 100.</HelpBlock>
</FormGroup>
</Form>
<h3> 3. Contract Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default Blockchain;

View File

@ -0,0 +1,180 @@
/*global web3*/
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import { Alert, Form, FormGroup, FormControl, Button } from 'react-bootstrap';
window.EmbarkJS = EmbarkJS;
class ENS extends React.Component {
constructor(props) {
super(props);
this.state = {
valueResolve: 'eth',
responseResolve: null,
isResolveError: false,
valueLookup: '',
responseLookup: null,
isLookupError: false,
valueRegister: '',
addressRegister: '',
responseRegister: null,
isRegisterError: false,
embarkLogs: []
};
}
componentDidMount() {
EmbarkJS.onReady(() => {
if (!web3.eth.defaultAccount) {
this.setState({
globalError: 'There is currently no default account. If Metamask is active, please sign in or deactivate it.'
});
}
this.setState({
addressRegister: web3.eth.defaultAccount,
valueLookup: web3.eth.defaultAccount
})
});
}
handleChange(stateName, e) {
this.setState({ [stateName]: e.target.value });
}
checkEnter(e, func) {
if (e.key !== 'Enter') {
return;
}
e.preventDefault();
func.apply(this, [e]);
}
registerSubDomain(e) {
e.preventDefault();
const self = this;
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.registerSubDomain('${this.state.valueRegister}', '${this.state.addressRegister}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.registerSubDomain(this.state.valueRegister, this.state.addressRegister, (err, transaction) => {
const message = err ? err : `Successfully registered "${this.state.valueRegister}" with ${transaction.gasUsed} gas`;
self.setState({
responseRegister: message,
isRegisterError: !!err
});
});
}
resolveName(e) {
e.preventDefault();
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.resolve('${this.state.valueResolve}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.resolve(this.state.valueResolve, (err, result) => {
if (err) {
return this.setState({
responseResolve: err.message || err,
isResolveError: true
});
}
this.setState({
responseResolve: result,
isResolveError: false
});
});
}
lookupAddress(e) {
e.preventDefault();
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.resolve('${this.state.valueLookup}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.lookup(this.state.valueLookup, (err, result) => {
if (err) {
return this.setState({
responseLookup: err.message || err,
isLookupError: true
});
}
this.setState({
responseLookup: result,
isLookupError: false
});
});
}
render() {
return (<React.Fragment>
{this.state.globalError && <Alert bsStyle="danger">{this.state.globalError}</Alert>}
<h3>Resolve a name</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.resolveName)}>
<FormGroup>
{this.state.responseResolve &&
<Alert className="alert-result" bsStyle={this.state.isResolveError ? 'danger' : 'success'}>
Resolved address: <span className="value">{this.state.responseResolve}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueResolve}
onChange={(e) => this.handleChange('valueResolve', e)}/>
<Button bsStyle="primary" onClick={(e) => this.resolveName(e)}>Resolve name</Button>
</FormGroup>
</Form>
<h3>Lookup an address</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.lookupAddress)}>
<FormGroup>
{this.state.responseLookup &&
<Alert className="alert-result" bsStyle={this.state.isLookupError ? 'danger' : 'success'}>
Looked up domain: <span className="value">{this.state.responseLookup}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueLookup}
onChange={(e) => this.handleChange('valueLookup', e)}/>
<Button bsStyle="primary" onClick={(e) => this.lookupAddress(e)}>Lookup address</Button>
</FormGroup>
</Form>
<h3>Register subdomain</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.registerSubDomain)}>
<FormGroup>
{this.state.responseRegister &&
<Alert className="alert-result" bsStyle={this.state.isRegisterError ? 'danger' : 'success'}>
<span className="value">{this.state.responseRegister}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueRegister}
onChange={(e) => this.handleChange('valueRegister', e)}/>
<FormControl
type="text"
defaultValue={this.state.addressRegister}
onChange={(e) => this.handleChange('addressRegister', e)}/>
<Button bsStyle="primary" onClick={(e) => this.registerSubDomain(e)}>Register subdomain</Button>
</FormGroup>
</Form>
<h3>Embark Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.embarkLogs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default ENS;

View File

@ -0,0 +1,272 @@
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import {Alert, Form, FormGroup, FormControl, HelpBlock, Button} from 'react-bootstrap';
class Storage extends React.Component {
constructor(props) {
super(props);
this.state = {
textToSave: 'hello world!',
generatedHash: '',
loadText: '',
storedText: '',
fileToUpload: null,
fileHash: '',
imageToDownload: '',
url: '',
logs: [],
storageError: '',
valueRegister: '',
valueResolver: '',
};
}
handleChange(e, name) {
this.state[name] = e.target.value;
this.setState(this.state);
}
checkEnter(e, func) {
if (e.key !== 'Enter') {
return;
}
e.preventDefault();
func.apply(this, [e]);
}
handleFileUpload(e) {
this.setState({fileToUpload: [e.target]});
}
addToLog(txt) {
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
setText(e) {
e.preventDefault();
EmbarkJS.Storage.saveText(this.state.textToSave)
.then((hash) => {
this.setState({
generatedHash: hash,
loadText: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.saveText('" + this.state.textToSave + "').then(function(hash) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage saveText Error => " + err.message);
}
});
}
loadHash(e) {
e.preventDefault();
EmbarkJS.Storage.get(this.state.loadText)
.then((content) => {
this.setState({storedText: content, storageError: ''});
this.addToLog("EmbarkJS.Storage.get('" + this.state.loadText + "').then(function(content) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage get Error => " + err.message);
}
});
}
uploadFile(e) {
e.preventDefault();
EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
.then((hash) => {
this.setState({
fileHash: hash,
imageToDownload: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.uploadFile(this.state.fileToUpload).then(function(hash) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage uploadFile Error => " + err.message);
}
});
}
loadFile(e) {
let _url = EmbarkJS.Storage.getUrl(this.state.imageToDownload);
this.setState({url: _url});
this.addToLog("EmbarkJS.Storage.getUrl('" + this.state.imageToDownload + "')");
}
ipnsRegister(e) {
e.preventDefault();
this.setState({ registering: true, responseRegister: false });
this.addToLog("EmbarkJS.Storage.register(this.state.ipfsHash).then(function(hash) { })");
EmbarkJS.Storage.register(this.state.valueRegister, (err, name) => {
let responseRegister;
let isRegisterError = false;
if (err) {
isRegisterError = true;
responseRegister = "Name Register Error: " + (err.message || err)
} else {
responseRegister = name;
}
this.setState({
registering: false,
responseRegister,
isRegisterError
});
});
}
ipnsResolve(e) {
e.preventDefault();
this.setState({ resolving: true, responseResolver: false });
this.addToLog("EmbarkJS.Storage.resolve(this.state.ipnsName, function(err, path) { })");
EmbarkJS.Storage.resolve(this.state.valueResolver, (err, path) => {
let responseResolver;
let isResolverError = false;
if (err) {
isResolverError = true;
responseResolver = "Name Resolve Error: " + (err.message || err)
} else {
responseResolver = path;
}
this.setState({
resolving: false,
responseResolver,
isResolverError
});
});
}
isIpfs(){
return EmbarkJS.Storage.currentProviderName === 'ipfs';
}
render() {
return <React.Fragment>
{
!this.props.enabled ?
<React.Fragment>
<Alert bsStyle="warning">The node you are using does not support IPFS. Please ensure <a
href="https://github.com/ipfs/js-ipfs-api#cors" target="_blank">CORS</a> is setup for the IPFS
node.</Alert>
</React.Fragment> : ''
}
{
this.state.storageError !== '' ?
<Alert bsStyle="danger">{this.state.storageError}</Alert>
: ''
}
<h3>Save text to storage</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.setText)}>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.textToSave}
onChange={e => this.handleChange(e, 'textToSave')}/>
<Button bsStyle="primary" onClick={(e) => this.setText(e)}>Save Text</Button>
<HelpBlock>generated Hash: <span className="textHash">{this.state.generatedHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Load text from storage given an hash</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.loadHash)}>
<FormGroup>
<FormControl
type="text"
value={this.state.loadText}
onChange={e => this.handleChange(e, 'loadText')}/>
<Button bsStyle="primary" onClick={(e) => this.loadHash(e)}>Load</Button>
<HelpBlock>result: <span className="textHash">{this.state.storedText}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Upload file to storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="file"
onChange={(e) => this.handleFileUpload(e)}/>
<Button bsStyle="primary" onClick={(e) => this.uploadFile(e)}>Upload</Button>
<HelpBlock>generated hash: <span className="fileHash">{this.state.fileHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Get file or image from storage</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.loadFile)}>
<FormGroup>
<FormControl
type="text"
value={this.state.imageToDownload}
onChange={e => this.handleChange(e, 'imageToDownload')}/>
<Button bsStyle="primary" onClick={(e) => this.loadFile(e)}>Download</Button>
<HelpBlock>file available at: <span><a href={this.state.url}
target="_blank">{this.state.url}</a></span></HelpBlock>
<HelpBlock><img src={this.state.url}/></HelpBlock>
</FormGroup>
</Form>
{!this.isIpfs() && <Alert bsStyle="warning">The 2 functions below are only available with IPFS</Alert>}
<h3>Register to IPNS</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.ipnsRegister)}>
<FormGroup>
<FormControl
type="text"
value={this.state.valueRegister}
onChange={e => this.handleChange(e, 'valueRegister')}/>
<Button bsStyle="primary" onClick={(e) => this.ipnsRegister(e)}>
{this.state.registering ? 'Registering...' : 'Register' }
</Button>
<HelpBlock>It will take around 1 minute</HelpBlock>
{this.state.responseRegister &&
<Alert className="alert-result" bsStyle={this.state.isRegisterError ? 'danger' : 'success'}>
<span className="value">{this.state.responseRegister}</span>
</Alert>}
</FormGroup>
</Form>
<h3>Resolve name</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.ipnsResolve)}>
<FormGroup>
<FormControl
type="text"
value={this.state.valueResolver}
onChange={e => this.handleChange(e, 'valueResolver')}/>
<Button bsStyle="primary" onClick={(e) => this.ipnsResolve(e)}>
{this.state.resolving ? 'Resolving...' : 'Resolve' }
</Button>
<HelpBlock>It will take around 1 minute</HelpBlock>
{this.state.responseResolver &&
<Alert className="alert-result" bsStyle={this.state.isResolverError ? 'danger' : 'success'}>
<span className="value">{this.state.responseResolver}</span>
</Alert>}
</FormGroup>
</Form>
<p>Javascript calls being made: </p>
<div className="logs">
<p>EmbarkJS.Storage.setProvider('ipfs',{'{'}server: 'localhost', port: '5001'{'}'})</p>
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>;
}
}
export default Storage;

View File

@ -0,0 +1,126 @@
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import {Alert, Form, FormGroup, FormControl, Button} from 'react-bootstrap';
class Whisper extends React.Component {
constructor (props) {
super(props);
this.state = {
listenTo: '',
channel: '',
message: '',
subscribedChannels: [],
messageList: [],
logs: []
};
}
handleChange (e, name) {
this.state[name] = e.target.value;
this.setState(this.state);
}
checkEnter(e, func) {
if (e.key !== 'Enter') {
return;
}
e.preventDefault();
func.apply(this, [e]);
}
sendMessage (e) {
e.preventDefault();
EmbarkJS.Messages.sendMessage({topic: this.state.channel, data: this.state.message});
this.addToLog("EmbarkJS.Messages.sendMessage({topic: '" + this.state.channel + "', data: '" + this.state.message + "'})");
}
listenToChannel (e) {
e.preventDefault();
const subscribedChannels = this.state.subscribedChannels;
subscribedChannels.push(<span>Subscribed to <b>{this.state.listenTo}</b>. Now try sending a message</span>);
this.setState({
subscribedChannels
});
EmbarkJS.Messages.listenTo({topic: [this.state.listenTo]}, (error, message) => {
const messageList = this.state.messageList;
if (error) {
messageList.push(<span className="alert-danger">Error: {error}</span>);
} else {
messageList.push(<span>Channel: <b>{message.topic}</b> | Message: <b>{message.data}</b></span>);
}
this.setState({
messageList
});
});
this.addToLog("EmbarkJS.Messages.listenTo({topic: ['" + this.state.listenTo + "']}).then(function(message) {})");
}
addToLog (txt) {
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
render () {
return (
<React.Fragment>
{
!this.props.enabled ?
<React.Fragment>
<Alert bsStyle="warning">The node you are using does not support Whisper</Alert>
<Alert bsStyle="warning">The node uses an unsupported version of Whisper</Alert>
</React.Fragment> : ''
}
<h3>Listen To channel</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.listenToChannel)}>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.listenTo}
placeholder="channel"
onChange={e => this.handleChange(e, 'listenTo')}/>
<Button bsStyle="primary" onClick={(e) => this.listenToChannel(e)}>Start Listening</Button>
<div id="subscribeList">
{this.state.subscribedChannels.map((item, i) => <p key={i}>{item}</p>)}
</div>
<p>messages received:</p>
<div id="messagesList">
{this.state.messageList.map((item, i) => <p key={i}>{item}</p>)}
</div>
</FormGroup>
</Form>
<h3>Send Message</h3>
<Form inline onKeyDown={(e) => this.checkEnter(e, this.sendMessage)}>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.channel}
placeholder="channel"
onChange={e => this.handleChange(e, 'channel')}/>
<FormControl
type="text"
defaultValue={this.state.message}
placeholder="message"
onChange={e => this.handleChange(e, 'message')}/>
<Button bsStyle="primary" onClick={(e) => this.sendMessage(e)}>Send Message</Button>
</FormGroup>
</Form>
<p>Javascript calls being made: </p>
<div className="logs">
<p>EmbarkJS.Messages.setProvider('whisper')</p>
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default Whisper;

View File

@ -0,0 +1,57 @@
div {
margin: 15px;
}
.logs {
background-color: black;
font-size: 14px;
color: white;
font-weight: bold;
padding: 10px;
border-radius: 8px;
}
.tab-content {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
margin: 0px;
}
.nav-tabs {
margin-bottom: 0;
}
.status-offline {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: red;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.status-online {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: mediumseagreen;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
input.form-control {
margin-right: 5px;
}
.alert-result {
margin-left: 0;
}

View File

@ -0,0 +1,94 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {Tabs, Tab} from 'react-bootstrap';
import EmbarkJS from 'Embark/EmbarkJS';
import Blockchain from './components/blockchain';
import Whisper from './components/whisper';
import Storage from './components/storage';
import ENS from './components/ens';
import './dapp.css';
class App extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
error: null,
activeKey: 1,
whisperEnabled: false,
storageEnabled: false,
blockchainEnabled: false,
ensEnabled: false
};
}
componentDidMount() {
EmbarkJS.onReady((err) => {
this.setState({blockchainEnabled: true});
if (err) {
// If err is not null then it means something went wrong connecting to ethereum
// you can use this to ask the user to enable metamask for e.g
return this.setState({error: err.message || err});
}
EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => {
if (err) {
return console.log(err);
}
this.setState({whisperEnabled: true});
});
EmbarkJS.Storage.isAvailable().then((result) => {
this.setState({storageEnabled: result});
}).catch(() => {
this.setState({storageEnabled: false});
});
});
}
_renderStatus(title, available) {
let className = available ? 'pull-right status-online' : 'pull-right status-offline';
return <React.Fragment>
{title}
<span className={className}></span>
</React.Fragment>;
}
handleSelect(key) {
this.setState({ activeKey: key });
}
render() {
const ensEnabled = EmbarkJS.Names.currentNameSystems && EmbarkJS.Names.isAvailable();
if (this.state.error) {
return (<div>
<div>Something went wrong connecting to ethereum. Please make sure you have a node running or are using metamask to connect to the ethereum network:</div>
<div>{this.state.error}</div>
</div>);
}
return (<div>
<h3>Embark - Usage Example</h3>
<Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example">
<Tab eventKey={1} title={this._renderStatus('Blockchain', this.state.blockchainEnabled)}>
<Blockchain/>
</Tab>
<Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}>
<Storage enabled={this.state.storageEnabled}/>
</Tab>
<Tab eventKey={3} title={this._renderStatus('P2P communication (Whisper)', this.state.whisperEnabled)}>
<Whisper enabled={this.state.whisperEnabled}/>
</Tab>
<Tab eventKey={4} title={this._renderStatus('Naming (ENS)', ensEnabled)}>
<ENS enabled={ensEnabled}/>
</Tab>
</Tabs>
</div>);
}
}
ReactDOM.render(<App></App>, document.getElementById('app'));

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Embark - SimpleStorage Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body class="container">
<div id="app">
</div>
<script src="js/dapp.js"></script>
</body>
</html>

View File

@ -0,0 +1,22 @@
{
"0x4c88925b6fabc1d138606800ff7f4fb5d90b1977624f38f99576056449a6a500": {
"contracts": {
"0x0aea975ceeaff5c320a0559708c5c6f94999421a0be61587cdece9b4542cc110": {
"name": "SimpleStorage",
"address": "0x74A18cBcd27e4652cD3FE852C5362432056E6480"
},
"0x3043b04ad856d169c8f0b0509c0bc63192dc7edd92d6933c58708298a0e381be": {
"name": "ENSRegistry",
"address": "0x88D7fD23Fc1c96FF89BeAd9aEb24Ccca96432c63"
},
"0x60daddeb23aae81f0807e071c25012fc78e521a3e829d2b1fcda1b8bc40d3815": {
"name": "Resolver",
"address": "0xcF1FF793d4510E1E2838642BC4B118AB14C5B1ac"
},
"0x842276d67a5bf54ee66032015bbeb8f58ca3ce6cabf05cf7ec19b0b282c6dbc0": {
"name": "FIFSRegistrar",
"address": "0x8fcDa55C6F48A9c591eA5227837dA30B9560Ed33"
}
}
}
}

View File

@ -0,0 +1,99 @@
module.exports = {
// applies to all environments
default: {
enabled: true,
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
rpcCorsDomain: "auto", // Comma separated list of domains from which to accept cross origin requests (browser enforced)
// When set to "auto", Embark will automatically set the cors to the address of the webserver
wsRPC: true, // Enable the WS-RPC server
wsOrigins: "auto", // Origins from which to accept websockets requests
// When set to "auto", Embark will automatically set the cors to the address of the webserver
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
wsPort: 8546 // WS-RPC server listening port (default: 8546)
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run` and `embark blockchain`
development: {
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
proxy: true, // Proxy is used to present meaningful information about transactions
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet
simulatorBlocktime: 0, // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
account: {
// numAccounts: 3, // When specified, creates accounts for use in the dapp. This option only works in the development environment, and can be used as a quick start option that bypasses the need for MetaMask in development. These accounts are unlocked and funded with the below settings.
// password: "config/development/password", // Password for the created accounts (as specified in the `numAccounts` setting). If `mineWhenNeeded` is enabled (and isDev is not), this password is used to create a development account controlled by the node.
// balance: "5 ether" // Balance to be given to the created accounts (as specified in the `numAccounts` setting)
}
},
// merges with the settings in default
// used with "embark run privatenet" and/or "embark blockchain privatenet"
privatenet: {
networkType: "custom",
networkId: "1337",
isDev: false,
datadir: ".embark/privatenet/datadir",
// -- mineWhenNeeded --
// This options is only valid when isDev is false.
// Enabling this option uses our custom script to mine only when needed.
// Embark creates a development account for you (using `geth account new`) and funds the account. This account can be used for
// development (and even imported in to MetaMask). To enable correct usage, a password for this account must be specified
// in the `account > password` setting below.
// NOTE: once `mineWhenNeeded` is enabled, you must run an `embark reset` on your dApp before running
// `embark blockchain` or `embark run` for the first time.
mineWhenNeeded: true,
// -- genesisBlock --
// This option is only valid when mineWhenNeeded is true (which is only valid if isDev is false).
// When enabled, geth uses POW to mine transactions as it would normally, instead of using POA as it does in --dev mode.
// On the first `embark blockchain or embark run` after this option is enabled, geth will create a new chain with a
// genesis block, which can be configured using the `genesisBlock` configuration option below.
genesisBlock: "config/privatenet/genesis.json", // Genesis block to initiate on first creation of a development node
nodiscover: true,
maxpeers: 0,
proxy: true,
account: {
// "address": "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account
},
targetGasLimit: 8000000,
wsHost: "localhost",
wsPort: 8546,
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
simulatorBlocktime: 0
},
// merges with the settings in default
// used with "embark run testnet" and/or "embark blockchain testnet"
testnet: {
networkType: "testnet",
syncMode: "light",
account: {
password: "config/testnet/password"
}
},
// merges with the settings in default
// used with "embark run livenet" and/or "embark blockchain livenet"
livenet: {
networkType: "livenet",
syncMode: "light",
rpcCorsDomain: "http://localhost:8000",
wsOrigins: "http://localhost:8000",
account: {
password: "config/livenet/password"
}
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
//custom_name: {
//}
};

View File

@ -0,0 +1,46 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
available_providers: ["whisper"], // Array of available providers
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
connection: {
host: "localhost", // Host of the blockchain node
port: 8546, // Port of the blockchain node
type: "ws" // Type of connection (ws or rpc)
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name"
//custom_name: {
//}
// Use this section when you need a specific symmetric or private keys in whisper
/*
,keys: {
symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
}
*/
};

View File

@ -0,0 +1,72 @@
module.exports = {
// default applies to all environments
default: {
// Blockchain node to deploy the contracts
deployment: {
host: "localhost", // Host of the blockchain node
port: 8545, // Port of the blockchain node
type: "rpc" // Type of connection (ws or rpc),
// Accounts to use instead of the default account to populate your wallet
/*,accounts: [
{
privateKey: "your_private_key",
balance: "5 ether" // You can set the balance of the account in the dev environment
// Balances are in Wei, but you can specify the unit with its name
},
{
privateKeyFile: "path/to/file", // Either a keystore or a list of keys, separated by , or ;
password: "passwordForTheKeystore" // Needed to decrypt the keystore file
},
{
mnemonic: "12 word mnemonic",
addressIndex: "0", // Optionnal. The index to start getting the address
numAddresses: "1", // Optionnal. The number of addresses to get
hdpath: "m/44'/60'/0'/0/" // Optionnal. HD derivation path
}
]*/
},
// order of connections the dapp should connect to
dappConnection: [
"$WEB3", // uses pre existing web3 object if available (e.g in Mist)
"ws://localhost:8546",
"http://localhost:8545"
],
gas: "auto",
contracts: {
SimpleStorage: {
fromIndex: 0,
args: [100]
}
}
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
dappConnection: [
"ws://localhost:8546",
"http://localhost:8545",
"$WEB3" // uses pre existing web3 object if available (e.g in Mist)
]
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
//custom_name: {
//}
};

View File

@ -0,0 +1,39 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
available_providers: ["ens"],
provider: "ens"
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
register: {
rootDomain: "eth",
subdomains: {
'embark': '0x1a2f3b98e434c02363f3dac3174af93c1d690914'
}
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name" or "embark blockchain custom_name"
//custom_name: {
//}
};

View File

@ -0,0 +1,18 @@
{
"config": {
"homesteadBlock": 0,
"byzantiumBlock": 0,
"daoForkSupport": true
},
"nonce": "0x0000000000000042",
"difficulty": "0x0",
"alloc": {
"0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"}
},
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x7a1200"
}

View File

@ -0,0 +1 @@
dev_password

View File

@ -0,0 +1,59 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
ipfs_bin: "ipfs",
provider: "ipfs",
available_providers: ["ipfs"],
upload: {
host: "localhost",
port: 5001
},
dappConnection: [
{
provider:"ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
]
// Configuration to start Swarm in the same terminal as `embark run`
/*,account: {
address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
},
swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm)*/
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
enabled: true,
provider: "ipfs",
upload: {
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name"
//custom_name: {
//}
};

View File

@ -0,0 +1 @@
test_password

View File

@ -0,0 +1,6 @@
module.exports = {
enabled: true,
host: "localhost",
openBrowser: true,
port: 8000
};

View File

@ -0,0 +1,22 @@
pragma solidity ^0.4.25;
contract SimpleStorage {
uint public storedData;
address owner;
constructor(uint initialValue) public {
storedData = initialValue;
owner = msg.sender;
}
function set(uint x) public {
storedData = x;
require(msg.sender != owner);
storedData = x + 2;
}
function get() public view returns (uint retVal) {
return storedData;
}
}

View File

@ -0,0 +1,23 @@
{
"contracts": ["contracts/**"],
"app": {
"js/dapp.js": ["app/dapp.js"],
"index.html": "app/index.html",
"images/": ["app/images/**"]
},
"buildDir": "dist/",
"config": "config/",
"versions": {
"web3": "1.0.0-beta",
"solc": "0.4.25",
"ipfs-api": "17.2.4"
},
"plugins": {
},
"options": {
"solc": {
"optimize": true,
"optimize-runs": 200
}
}
}

278
test_apps/embark_demo/package-lock.json generated Normal file
View File

@ -0,0 +1,278 @@
{
"name": "app_name",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
},
"dependencies": {
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"dom-helpers": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz",
"integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg=="
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"keycode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
"integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
},
"prop-types": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"prop-types-extra": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
"requires": {
"react-is": "^16.3.2",
"warning": "^3.0.0"
}
},
"react": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
"integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
"requires": {
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-bootstrap": {
"version": "0.32.1",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.1.tgz",
"integrity": "sha512-RbfzKUbsukWsToWqGHfCCyMFq9QQI0TznutdyxyJw6dih2NvIne25Mrssg8LZsprqtPpyQi8bN0L0Fx3fUsL8Q==",
"requires": {
"babel-runtime": "^6.11.6",
"classnames": "^2.2.5",
"dom-helpers": "^3.2.0",
"invariant": "^2.2.1",
"keycode": "^2.1.2",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-overlays": "^0.8.0",
"react-prop-types": "^0.4.0",
"react-transition-group": "^2.0.0",
"uncontrollable": "^4.1.0",
"warning": "^3.0.0"
}
},
"react-dom": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.2.tgz",
"integrity": "sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw==",
"requires": {
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-is": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.2.tgz",
"integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-overlays": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz",
"integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==",
"requires": {
"classnames": "^2.2.5",
"dom-helpers": "^3.2.1",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-transition-group": "^2.2.0",
"warning": "^3.0.0"
}
},
"react-prop-types": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz",
"integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=",
"requires": {
"warning": "^3.0.0"
}
},
"react-transition-group": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz",
"integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==",
"requires": {
"dom-helpers": "^3.3.1",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"ua-parser-js": {
"version": "0.7.18",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
"integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
},
"uncontrollable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz",
"integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=",
"requires": {
"invariant": "^2.1.0"
}
},
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
},
"whatwg-fetch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
}
}
}

View File

@ -0,0 +1,17 @@
{
"name": "app_name",
"version": "0.0.1",
"description": "",
"main": "Gruntfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"homepage": "",
"dependencies": {
"react": "^16.3.2",
"react-bootstrap": "0.32.1",
"react-dom": "^16.3.2"
}
}

View File

@ -0,0 +1,41 @@
/*global contract, config, it, assert*/
const SimpleStorage = require('Embark/contracts/SimpleStorage');
let accounts;
// For documentation please see https://embark.status.im/docs/contracts_testing.html
config({
//deployment: {
// accounts: [
// // you can configure custom accounts with a custom balance
// // see https://embark.status.im/docs/contracts_testing.html#Configuring-accounts
// ]
//},
contracts: {
"SimpleStorage": {
args: [100]
}
}
}, (_err, web3_accounts) => {
accounts = web3_accounts
});
contract("SimpleStorage", function () {
this.timeout(0);
it("should set constructor value", async function () {
let result = await SimpleStorage.methods.storedData().call();
assert.strictEqual(parseInt(result, 10), 100);
});
it("set storage value", async function () {
await SimpleStorage.methods.set(150).send();
let result = await SimpleStorage.methods.get().call();
assert.strictEqual(parseInt(result, 10), 150);
});
it("should have account with balance", async function() {
let balance = await web3.eth.getBalance(accounts[0]);
assert.ok(parseInt(balance, 10) > 0);
});
});

View File

@ -1,46 +1,22 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.25;
import "ownable.sol";
library Assert {
event TestEvent(bool passed, string message);
function triggerEvent(bool passed, string message) internal {
emit TestEvent(passed, message);
}
}
contract SimpleStorage is Ownable {
contract SimpleStorage {
uint public storedData;
address public registar;
event EventOnSet2(bool passed, string message);
function() public payable { }
address owner;
constructor(uint initialValue) public {
storedData = initialValue;
owner = msg.sender;
}
function set(uint x) public {
storedData = x;
Assert.triggerEvent(true, "hi");
}
function set2(uint x) public onlyOwner {
storedData = x;
emit EventOnSet2(true, "hi");
require(msg.sender == 0x0);
storedData = x + 2;
}
function get() public view returns (uint retVal) {
return storedData;
}
function getS() public pure returns (string d) {
return "hello";
}
function setRegistar(address x) public {
registar = x;
}
}

View File

@ -0,0 +1,34 @@
pragma solidity ^0.4.25;
import "ownable.sol";
contract SimpleStorageTest is Ownable {
uint public storedData;
address owner;
constructor(uint initialValue) public {
storedData = initialValue;
owner = msg.sender;
}
function set(uint x) public onlyOwner {
storedData = x;
require(msg.sender != owner);
storedData = x + 2;
}
function test(uint x) public {
uint value = 1;
assembly {
let a := 1
let b := 2
revert(0, 0)
}
value = 2;
}
function get() public view returns (uint retVal) {
return storedData;
}
}

View File

@ -4,7 +4,10 @@ import React, { Component } from 'react';
import EmbarkJS from 'Embark/EmbarkJS';
import SimpleStorage from 'Embark/contracts/SimpleStorage';
import Test from 'Embark/contracts/Test';
import Assert from 'Embark/contracts/Assert';
//import Assert from 'Embark/contracts/Assert';
import SimpleStorageTest from 'Embark/contracts/SimpleStorageTest';
window.SimpleStorageTest = SimpleStorageTest
import ReactDOM from 'react-dom';
@ -23,7 +26,7 @@ import { Navbar, Jumbotron, Button } from 'react-bootstrap';
window.EmbarkJS = EmbarkJS;
window.SimpleStorage = SimpleStorage;
window.Test = Test;
window.Assert = Assert;
//window.Assert = Assert;
window.React = React;

View File

@ -3,7 +3,13 @@ module.exports = {
deployment: {
host: "localhost",
port: 8546,
type: "ws"
type: "ws",
accounts: [
{
mnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
balance: "5 ether"
}
]
},
dappConnection: [
"ws://localhost:8546",
@ -17,6 +23,9 @@ module.exports = {
Ownable: {
deploy: false
},
SimpleStorageTest: {
args: [100]
},
SimpleStorage: {
fromIndex: 0,
args: [100],

View File

@ -15,7 +15,7 @@
"buildDir": "dist/",
"config": "config/",
"versions": {
"solc": "0.4.24",
"solc": "0.4.25",
"web3": "1.0.0-beta",
"ipfs-api": "17.2.7"
},

BIN
test_apps/test_app/test.file Executable file

Binary file not shown.