Skip to content

Commit 272ed84

Browse files
authored
Merge pull request #17 from breakpointer/5_simple_message_bus
Basic test setup and simple message bus contract
2 parents f8b551b + ea0247a commit 272ed84

File tree

10 files changed

+1636
-184
lines changed

10 files changed

+1636
-184
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
"private": true,
44
"scripts": {
55
"test": "yarn lint && yarn test:packages",
6-
"test:packages": "lerna run test --concurrency 1",
6+
"test:packages": "lerna run test --concurrency 1 --stream",
77
"lint": "yarn eslint . && yarn solium -d .",
88
"prettier": "prettier \"**/*.{js,json,css,md}\" --write"
99
},
1010
"devDependencies": {
11+
"@0x/sol-coverage": "^3.0.4",
12+
"@0x/sol-trace": "^2.0.4",
13+
"dotenv": "^6.2.0",
1114
"eslint": "^5.16.0",
1215
"eslint-config-web3studio": "^1.2.0",
1316
"husky": "^2.2.0",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
3+
cleanup_ganache() {
4+
# Kill the ganache instance that we started (if we started one and if it's still running).
5+
if [ -n "$ganache_pid" ] && ps -p $ganache_pid > /dev/null; then
6+
echo "Stopping Ganache... [pid:${ganache_pid}]"
7+
kill $ganache_pid
8+
echo "Ganache stopped!"
9+
fi
10+
}
11+
12+
ganache_port=7545
13+
14+
ganache_running() {
15+
nc -z localhost "$ganache_port" &> /dev/null
16+
}
17+
18+
start_ganache() {
19+
echo "Starting Ganache..."
20+
ganache-cli --port "$ganache_port" -d -i 42 > /dev/null &
21+
ganache_pid=$!
22+
echo "Ganache started!"
23+
while ! ganache_running; do
24+
sleep 0.1
25+
done
26+
}
27+
28+
trap cleanup_ganache EXIT
29+
30+
if ganache_running; then
31+
echo "Using existing ganache instance on port ${ganache_port}"
32+
else
33+
start_ganache
34+
fi
35+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
. sidejam-setup-ganache
6+
7+
truffle test --network test && \
8+
istanbul report text html --include coverage/coverage.json && \
9+
istanbul check-coverage --statements 100 --branches 100 --functions 100 --lines 100 --include coverage/coverage.json
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
pragma solidity ^0.5.0;
2+
3+
contract MessageReceiver {
4+
5+
// Each receiver manages messages for only one topic
6+
string public topicName;
7+
// Message key to message mapping for look-up
8+
mapping(bytes32 => Message) public messages;
9+
// Message key stored in an array for ordering/indexing
10+
bytes32[] public messageList;
11+
12+
// Event to notify listening/interested parties
13+
event MessageReceived(string topicName, address msgFrom, bytes32 msgKey);
14+
15+
struct Message {
16+
address sender;
17+
string message;
18+
}
19+
20+
/**
21+
* @notice Create a new MessageReceiver for a specific topic
22+
*
23+
* @param topic string The topic name for the messages
24+
*/
25+
constructor (string memory topic) public {
26+
topicName = topic;
27+
}
28+
29+
/**
30+
* @notice Send a new message to the "bus". Emits a MessageReceived event.
31+
*
32+
* @param key bytes32 The key for your message, should be unique
33+
* @param newMessage string The message contents
34+
*/
35+
function sendMessage(bytes32 key, string memory newMessage) public {
36+
messageList.push(key);
37+
messages[key].sender = msg.sender;
38+
messages[key].message = newMessage;
39+
40+
emit MessageReceived(topicName, msg.sender, key);
41+
}
42+
43+
/**
44+
* @notice Gets the count of the messages sent to this topic
45+
*
46+
* @return uint256 Count of messages
47+
*/
48+
function getMessageCount() public view returns(uint256) {
49+
return messageList.length;
50+
}
51+
52+
/**
53+
* @notice Gets a message given a key
54+
*
55+
* @return address, string Returns the sender address and message contents
56+
*/
57+
function getMessage(bytes32 key) public view returns (address, string memory) {
58+
return (messages[key].sender, messages[key].message);
59+
}
60+
}

packages/message-bus/package.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
"url": "git+https://github.com/ConsenSys/web3studio-sidejam.git"
1111
},
1212
"scripts": {
13-
"build": "yarn buidl",
14-
"buidl": "truffle compile",
13+
"test": "sidejam-truffle-test",
14+
"build": "truffle compile",
1515
"migrate": "truffle migrate --reset",
1616
"truffle": "truffle"
1717
},
1818
"bugs": {
1919
"url": "https://github.com/ConsenSys/web3studio-sidejam/issues"
2020
},
21-
"dependencies": {},
22-
"devDependencies": {}
21+
"bin": {
22+
"sidejam-truffle-test": "./bin/sidejam-truffle-test",
23+
"sidejam-setup-ganache": "./bin/sidejam-setup-ganache"
24+
},
25+
"dependencies": {
26+
"istanbul": "^0.4.5",
27+
"jest-matchers": "^20.0.3"
28+
},
29+
"devDependencies": {
30+
"ganache-cli": "^6.4.3"
31+
}
2332
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const expect = require('jest-matchers');
2+
const MessageReceiver = artifacts.require('MessageReceiver');
3+
4+
contract('MessageReceiver', accounts => {
5+
const senderAccount = accounts[0];
6+
const messageContent = 'The Dude abides';
7+
const msgKey = web3.utils.randomHex(32);
8+
let msgReceiver;
9+
10+
it('accepts a topic name in the constructor', async () => {
11+
msgReceiver = await MessageReceiver.new('MyTopic');
12+
expect(await msgReceiver.topicName()).toEqual('MyTopic');
13+
});
14+
15+
describe('sending a new message', async () => {
16+
let sendResult;
17+
18+
beforeEach(async () => {
19+
msgReceiver = await MessageReceiver.new('MyTopic');
20+
sendResult = await msgReceiver.sendMessage(msgKey, messageContent, {
21+
from: senderAccount
22+
});
23+
});
24+
25+
it('emits a message received event', async () => {
26+
expect(sendResult.logs[0]['event']).toEqual('MessageReceived');
27+
});
28+
29+
it('can retrieve the message and who sent it', async () => {
30+
const message = await msgReceiver.getMessage(msgKey);
31+
expect(message[0]).toEqual(senderAccount);
32+
expect(message[1]).toEqual(messageContent);
33+
});
34+
35+
it('increments the message count', async () => {
36+
const initialCount = await msgReceiver.getMessageCount();
37+
await msgReceiver.sendMessage(
38+
web3.utils.randomHex(32),
39+
'This is what happens Larry!'
40+
);
41+
const newCount = await msgReceiver.getMessageCount();
42+
expect(newCount - initialCount).toEqual(1);
43+
});
44+
});
45+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const expect = require('jest-matchers');
2+
3+
global.expect = expect;
4+
5+
after(async () => {
6+
// `coverageSubProvider` defined in ./truffle-config
7+
await global.coverageSubProvider.writeCoverageAsync();
8+
});
Lines changed: 87 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,97 @@
1+
const ProviderEngine = require('web3-provider-engine');
2+
const WebsocketProvider = require('web3-provider-engine/subproviders/websocket.js');
3+
const {
4+
TruffleArtifactAdapter,
5+
CoverageSubprovider
6+
} = require('@0x/sol-coverage');
7+
const { RevertTraceSubprovider } = require('@0x/sol-trace');
8+
9+
require('dotenv').config();
10+
11+
const solcVersion = '0.5.4';
12+
const defaultFromAddress = '0x627306090abab3a6e1400e9345bc60c78a8bef57';
13+
const isVerbose = true;
114
/**
2-
* Use this file to configure your truffle project. It's seeded with some
3-
* common settings for different networks and features like migrations,
4-
* compilation and testing. Uncomment the ones you need or modify
5-
* them to suit your project as necessary.
6-
*
7-
* More information about configuration can be found at:
8-
*
9-
* truffleframework.com/docs/advanced/configuration
10-
*
11-
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12-
* to sign your transactions before they're sent to a remote public node. Infura accounts
13-
* are available for free at: infura.io/register.
14-
*
15-
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16-
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
17-
* phrase from a file you've .gitignored so it doesn't accidentally become public.
15+
* Create's a test provider using 0x tracing and coverage
1816
*
17+
* @param {string} projectRoot - path of project using this config
18+
* @returns {ProviderEngine} A web3 provider
19+
* @private
1920
*/
21+
const createTestProvider = projectRoot => {
22+
const testProvider = new ProviderEngine();
23+
const artifactAdapter = new TruffleArtifactAdapter(
24+
`${projectRoot}`,
25+
solcVersion
26+
);
27+
const coverageSubProvider = new CoverageSubprovider(
28+
artifactAdapter,
29+
defaultFromAddress,
30+
{
31+
isVerbose: isVerbose,
32+
ignoreFilesGlobs: ['**/Migrations.sol']
33+
}
34+
);
35+
36+
global.coverageSubProvider = coverageSubProvider;
2037

21-
// const HDWalletProvider = require('truffle-hdwallet-provider');
22-
// const infuraKey = "fj4jll3k.....";
23-
//
24-
// const fs = require('fs');
25-
// const mnemonic = fs.readFileSync(".secret").toString().trim();
38+
testProvider.addProvider(coverageSubProvider);
2639

27-
module.exports = {
28-
/**
29-
* Networks define how you connect to your ethereum client and let you set the
30-
* defaults web3 uses to send transactions. If you don't specify one truffle
31-
* will spin up a development blockchain for you on port 9545 when you
32-
* run `develop` or `test`. You can ask a truffle command to use a specific
33-
* network from the command line, e.g
34-
*
35-
* $ truffle test --network <network-name>
36-
*/
40+
testProvider.addProvider(
41+
new RevertTraceSubprovider(artifactAdapter, defaultFromAddress, isVerbose)
42+
);
3743

38-
networks: {
39-
// Useful for testing. The `development` name is special - truffle uses it by default
40-
// if it's defined here and no other network is specified at the command line.
41-
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
42-
// tab if you use this network and you must also set the `host`, `port` and `network_id`
43-
// options below to some value.
44-
//
45-
// development: {
46-
// host: "127.0.0.1", // Localhost (default: none)
47-
// port: 8545, // Standard Ethereum port (default: none)
48-
// network_id: "*", // Any network (default: none)
49-
// },
50-
// Another network with more advanced options...
51-
// advanced: {
52-
// port: 8777, // Custom port
53-
// network_id: 1342, // Custom network
54-
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
55-
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
56-
// from: <address>, // Account to send txs from (default: accounts[0])
57-
// websockets: true // Enable EventEmitter interface for web3 (default: false)
58-
// },
59-
// Useful for deploying to a public network.
60-
// NB: It's important to wrap the provider as a function.
61-
// ropsten: {
62-
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
63-
// network_id: 3, // Ropsten's id
64-
// gas: 5500000, // Ropsten has a lower block limit than mainnet
65-
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
66-
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
67-
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
68-
// },
69-
// Useful for private networks
70-
// private: {
71-
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
72-
// network_id: 2111, // This network is yours, in the cloud.
73-
// production: true // Treats this network as if it was a public net. (default: false)
74-
// }
75-
},
44+
testProvider.send = testProvider.sendAsync.bind(testProvider);
7645

77-
// Set default mocha options here, use special reporters etc.
78-
mocha: {
79-
// timeout: 100000
80-
},
46+
return testProvider;
47+
};
48+
49+
/**
50+
* A config generation helper function
51+
*
52+
* @param {string} projectRoot - path to the project using this config
53+
* @returns {Object} - Config object for truffle
54+
*/
55+
const configHelper = projectRoot => {
56+
const testProvider = createTestProvider(projectRoot);
57+
let testProviderStarted = false;
58+
59+
return {
60+
networks: {
61+
development: {
62+
host: '127.0.0.1',
63+
port: 7545,
64+
network_id: '*' // Match any network id
65+
},
66+
test: {
67+
provider: () => {
68+
if (!testProviderStarted) {
69+
// Within this function to not start the provider until it's needed
70+
testProvider.addProvider(
71+
new WebsocketProvider({ rpcUrl: 'http://localhost:7545' })
72+
);
8173

82-
// Configure your compilers
83-
compilers: {
84-
solc: {
85-
// version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version)
86-
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
87-
// settings: { // See the solidity docs for advice about optimization and evmVersion
88-
// optimizer: {
89-
// enabled: false,
90-
// runs: 200
91-
// },
92-
// evmVersion: "byzantium"
93-
// }
74+
testProvider.start(err => {
75+
testProviderStarted = true;
76+
77+
if (err !== undefined) {
78+
// eslint-disable-next-line no-console
79+
console.log('Failed to start provider', err);
80+
process.exit(1);
81+
}
82+
});
83+
}
84+
return testProvider;
85+
},
86+
network_id: '*'
87+
}
88+
},
89+
compilers: {
90+
solc: {
91+
version: solcVersion
92+
}
9493
}
95-
}
94+
};
9695
};
96+
97+
module.exports = configHelper(__dirname);

packages/stratego-four/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"reselect": "^4.0.0",
3535
"rimble-ui": "^0.8.0",
3636
"styled-components": "^4.2.0",
37+
"truffle": "^5.0.17",
3738
"web3-provider-engine": "^15.0.0"
3839
},
3940
"devDependencies": {

0 commit comments

Comments
 (0)