Skip to content

Commit 5741cc0

Browse files
authored
Integration test for feeds (smartcontractkit#72)
* FM programmatic setup working * Reverting fm-demo, adding on another branch * Revert to working version + sync with master * Multiple feeds * using new runlog * Merged master * Fix for bounds + adapter generic name * Updated types, importing single file, querying status * Showing actual result * cookiefile in gitignore * Updated go scripts + parsing from specs folder * Multiple adapters * Copied go scripts until they are public * Updated CL version
1 parent ef7f415 commit 5741cc0

File tree

151 files changed

+22259
-15
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+22259
-15
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
pallet-chainlink**/Cargo.lock
44
**/node_modules
55
**/yarn.lock
6-
external_initiator.env
6+
external_initiator.env
7+
cookiefile

fm-demo/README.md

+12-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ accounts. In a real environment, each of these accounts would run their own Chai
88

99
> This is in active development. Some Docker images may be outdated and may require to be built from source.
1010
11+
To programmatically run the demo, see on the bottom of the document.
12+
1113
## Setup
1214

1315
1. Start up the substrate chain
@@ -108,7 +110,7 @@ EI_IC_SECRET=[SECRET]
108110
"requestData": {
109111
"data": { "from": "DOT", "to": "USD" }
110112
},
111-
"feeds": [{ "url": "http://coingecko-adapter:8080" }],
113+
"feeds": [{ "url": "http://adapter1:8080" }],
112114
"threshold": 0.5,
113115
"absoluteThreshold": 0,
114116
"precision": 8,
@@ -143,7 +145,7 @@ EI_IC_SECRET=[SECRET]
143145
"requestData": {
144146
"data": { "from": "DOT", "to": "USD" }
145147
},
146-
"feeds": [{ "url": "http://coingecko-adapter:8080" }],
148+
"feeds": [{ "url": "http://adapter1:8080" }],
147149
"threshold": 0.5,
148150
"absoluteThreshold": 0,
149151
"precision": 8,
@@ -162,3 +164,11 @@ EI_IC_SECRET=[SECRET]
162164
]
163165
}
164166
```
167+
168+
# Programmatically setup feeds
169+
170+
```bash
171+
./scripts/run-all.sh
172+
```
173+
174+
On first run, `chain` image is being built. Might take several minutes while getting "API-WS: disconnected from ws://localhost:9944: 1006:: connection failed". You can check the progress with `docker attach chain`

fm-demo/checkAnswer.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const {ApiPromise, Keyring, WsProvider} = require('@polkadot/api');
2+
const {cryptoWaitReady} = require('@polkadot/util-crypto');
3+
const feedConfigs = require('./feeds.json');
4+
const types = require('../substrate-node-example/types.json');
5+
6+
async function main() {
7+
await cryptoWaitReady();
8+
9+
// Connect to the local chain
10+
const wsProvider = new WsProvider('ws://localhost:9944');
11+
const api = await ApiPromise.create({
12+
provider: wsProvider,
13+
types
14+
});
15+
for (feedId = 0; feedId < feedConfigs.length; feedId++) {
16+
console.log(`Feed ID: ${feedId}`)
17+
const feed = (await api.query.chainlinkFeed.feeds(feedId)).unwrap()
18+
19+
const latestRoundNumber = feed['latest_round'].words[0]
20+
21+
const latestRound = (await api.query.chainlinkFeed.rounds(0, latestRoundNumber)).unwrap()
22+
23+
const latestAnswer = latestRound.answer.unwrap().toNumber()
24+
console.log("Latest answer on chain: ")
25+
console.log(latestAnswer)
26+
}
27+
28+
}
29+
30+
main().catch(console.error).then(() => process.exit());

fm-demo/docker-compose.yml

+23-4
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ services:
2121
- CARGO_HOME=/var/www/node-template/.cargo
2222
volumes:
2323
- ../substrate-node-example/:/var/www/node-template
24+
- ../pallet-chainlink/:/var/www/pallet-chainlink
2425
- ../pallet-chainlink-feed/:/var/www/pallet-chainlink-feed
2526
- "chain:/root/.local"
2627
command: bash -c "cargo build --release && ./target/release/node-template --dev --ws-external"
2728
networks:
2829
- fm-substrate
2930

3031
chainlink:
31-
image: smartcontract/chainlink:0.10.3
32+
image: smartcontract/chainlink:0.10.7
3233
container_name: chainlink-node
3334
restart: on-failure
3435
ports:
@@ -111,15 +112,33 @@ services:
111112
# Account ID: 0x06f0d58c43477508c0e5d5901342acf93a0208088816ff303996564a1d8c1c54
112113
# SS58 Address: 5CDogos4Dy2tSCvShBHkeFeMscwx9Wi2vFRijjTRRFau3vkJ
113114

114-
coingecko-adapter:
115-
image: public.ecr.aws/chainlink-staging/adapters/coingecko-adapter:develop-latest
116-
container_name: coingecko-adapter
115+
adapter1:
116+
image: public.ecr.aws/chainlink-staging/adapters/coinpaprika-adapter:develop-latest
117+
container_name: adapter1
117118
restart: on-failure
118119
ports:
119120
- "8083:8080"
120121
networks:
121122
- fm-substrate
122123

124+
adapter2:
125+
image: public.ecr.aws/chainlink-staging/adapters/coingecko-adapter:develop-latest
126+
container_name: adapter2
127+
restart: on-failure
128+
ports:
129+
- "8084:8080"
130+
networks:
131+
- fm-substrate
132+
133+
adapter3:
134+
image: public.ecr.aws/chainlink-staging/adapters/amberdata-adapter:develop-latest
135+
container_name: adapter3
136+
restart: on-failure
137+
ports:
138+
- "8085:8080"
139+
networks:
140+
- fm-substrate
141+
123142
volumes:
124143
pg:
125144
cl:

fm-demo/feedSetup.js

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
const {ApiPromise, Keyring, WsProvider} = require('@polkadot/api');
2+
const {cryptoWaitReady} = require('@polkadot/util-crypto');
3+
const feedConfigs = require('./feeds.json');
4+
const types = require('../substrate-node-example/types.json');
5+
6+
7+
async function fundAccountIfNeeded(api, senderAccount, receiverAddress) {
8+
return new Promise(async (resolve) => {
9+
const balance = await api.query.system.account(receiverAddress);
10+
console.log(`Free balance of ${receiverAddress} is: ${balance.data.free}`);
11+
if (parseInt(balance.data.free) === 0) {
12+
await api.tx.balances.transfer(receiverAddress, 123456666000).signAndSend(senderAccount, async ({status}) => {
13+
if (status.isFinalized) {
14+
console.log(`Account ${receiverAddress} funded`);
15+
resolve();
16+
}
17+
});
18+
} else {
19+
resolve();
20+
}
21+
});
22+
}
23+
24+
async function registerFeedCreatorIfNeeded(api, aliceAccount, operatorAccount) {
25+
console.log(`Registering feed creator ${operatorAccount.address}`);
26+
return new Promise(async (resolve) => {
27+
const feedCreator = await api.query.chainlinkFeed.feedCreators(operatorAccount.address);
28+
if (feedCreator.isNone) {
29+
await api.tx.chainlinkFeed.setFeedCreator(operatorAccount.address).signAndSend(aliceAccount, async ({status}) => {
30+
if (status.isFinalized) {
31+
console.log('Feed creator registered');
32+
resolve();
33+
}
34+
});
35+
} else {
36+
console.log('Feed creator already registered');
37+
resolve();
38+
}
39+
});
40+
}
41+
async function createFeed(api, sender) {
42+
console.log(`Creating feed with config: ${JSON.stringify(feedConfig, null, 4)}`);
43+
return new Promise(async (resolve) => {
44+
await api.tx.chainlinkFeed.createFeed(feedConfig.payment, feedConfig.timeout, feedConfig.submissionValueBounds, feedConfig.minSubmissions, feedConfig.decimals, feedConfig.description, feedConfig.restartDelay, feedConfig.oracles,feedConfig.pruningWindow,feedConfig.maxDebt).signAndSend(sender, ({ status, events }) => {
45+
if (status.isInBlock || status.isFinalized) {
46+
events
47+
// find/filter for failed events
48+
.filter(({ event }) =>
49+
api.events.system.ExtrinsicFailed.is(event)
50+
)
51+
// we know that data for system.ExtrinsicFailed is
52+
// (DispatchError, DispatchInfo)
53+
.forEach(({ event: { data: [error, info] } }) => {
54+
if (error.isModule) {
55+
// for module errors, we have the section indexed, lookup
56+
const decoded = api.registry.findMetaError(error.asModule);
57+
const { documentation, method, section } = decoded;
58+
59+
console.log(`${section}.${method}: ${documentation.join(' ')}`);
60+
} else {
61+
// Other, CannotLookup, BadOrigin, no extra info
62+
console.log(error.toString());
63+
}
64+
});
65+
}
66+
if (status.isFinalized) {
67+
resolve()
68+
}
69+
});
70+
});
71+
}
72+
73+
74+
async function main() {
75+
await cryptoWaitReady();
76+
77+
// Connect to the local chain
78+
const wsProvider = new WsProvider('ws://localhost:9944');
79+
const api = await ApiPromise.create({
80+
provider: wsProvider,
81+
types
82+
});
83+
84+
// Add an account, straight from mnemonic
85+
const keyring = new Keyring({type: 'sr25519'});
86+
87+
for (feedConfig of feedConfigs) {
88+
const operatorAccount = keyring.addFromUri(feedConfig.operatorSeedPhrase);
89+
const oracleAddress1 = feedConfig.oracles[0]
90+
const oracleAddress2 = feedConfig.oracles[1]
91+
92+
console.log(`Using operator with address ${operatorAccount.address}`);
93+
94+
const aliceAccount = keyring.addFromUri('//Alice');
95+
96+
97+
await fundAccountIfNeeded(api, aliceAccount, operatorAccount.address);
98+
99+
await fundAccountIfNeeded(api, aliceAccount, oracleAddress1);
100+
101+
await fundAccountIfNeeded(api, aliceAccount, oracleAddress2);
102+
103+
await registerFeedCreatorIfNeeded(api, aliceAccount, operatorAccount);
104+
105+
await createFeed(api, operatorAccount);
106+
107+
// const feed = await api.query.chainlinkFeed.feeds(0)
108+
// console.log(feed)
109+
}
110+
111+
}
112+
113+
main().catch(console.error).then(() => process.exit());

fm-demo/feeds.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
{
3+
"operatorSeedPhrase": "fruit start corn kingdom leg public thumb scrub negative jazz pig critic volcano voice suspect",
4+
"payment" : 0,
5+
"timeout" : 600,
6+
"submissionValueBounds": ["0", "99999999999999999999999999999999"],
7+
"minSubmissions" : 1,
8+
"decimals" : 8,
9+
"description" : "Test",
10+
"restartDelay": 0,
11+
"oracles": ["0x7c522c8273973e7bcf4a5dbfcc745dba4a3ab08c1e410167d7b1bdf9cb924f6c", "0x06f0d58c43477508c0e5d5901342acf93a0208088816ff303996564a1d8c1c54"],
12+
"pruningWindow" : 56,
13+
"maxDebt" : 1
14+
}
15+
]
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: golangci-lint
2+
on:
3+
push:
4+
tags:
5+
- v*
6+
branches:
7+
- master
8+
- main
9+
pull_request:
10+
jobs:
11+
golangci:
12+
name: lint
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
- name: golangci-lint
17+
uses: golangci/golangci-lint-action@v2
18+
with:
19+
args: --exclude-use-default=false --skip-dirs=contracts/ethereum
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: test
2+
on:
3+
push:
4+
tags:
5+
- v*
6+
branches:
7+
- master
8+
- main
9+
pull_request:
10+
jobs:
11+
test:
12+
strategy:
13+
matrix:
14+
go-version: [1.16.x]
15+
os: [ubuntu-latest]
16+
runs-on: ${{ matrix.os }}
17+
services:
18+
hardhat:
19+
image: smartcontract/hardhat-network
20+
ports:
21+
- 8545:8545
22+
options: >-
23+
--health-cmd "curl -X POST --data '{"jsonrpc":"2.0","method":"net_listening","params":[],"id":31337}' http://localhost:8545"
24+
--health-interval 10s
25+
--health-timeout 5s
26+
--health-retries 5
27+
steps:
28+
- name: Install Go
29+
uses: actions/setup-go@v2
30+
with:
31+
go-version: ${{ matrix.go-version }}
32+
- name: Checkout code
33+
uses: actions/checkout@v2
34+
- name: Test
35+
run: go test ./... -v -covermode=count -coverprofile=coverage.out
36+
- name: Convert coverage to lcov
37+
uses: jandelgado/[email protected]
38+
- name: Report code coverage
39+
uses: romeovs/[email protected]
40+
with:
41+
github-token: ${{ secrets.GITHUB_TOKEN }}
42+
lcov-file: ./coverage.lcov
43+
- name: Publish Unit Test Results
44+
uses: mikepenz/action-junit-report@v2
45+
if: always()
46+
with:
47+
report_paths: '**/junit.xml'
48+
github_token: ${{ secrets.GITHUB_TOKEN }}

fm-demo/go-scripts/.gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.DS_Store
2+
3+
# Hardhat config that might sneak through
4+
node_modules/
5+
hardhat.config.js
6+
package-lock.json
7+
package.json
8+
artifacts/
9+
10+
# Binaries for programs and plugins
11+
*.exe
12+
*.exe~
13+
*.dll
14+
*.so
15+
*.dylib
16+
17+
# Test binary, built with `go test -c`
18+
*.test
19+
20+
# Output of the go coverage tool, specifically when used with LiteIDE
21+
*.out
22+
23+
# Dependency directories (remove the comment below to include it)
24+
# vendor/
25+
26+
# JUnit Test Reports
27+
junit.xml

fm-demo/go-scripts/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 SmartContract
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)