Skip to content

Commit 466db28

Browse files
tkyonezupetermetz
authored andcommitted
feat(validator): add a draft of Iroha Validator
Signed-off-by: Takeshi Yonezu <[email protected]>
1 parent 90e4dd1 commit 466db28

17 files changed

+661
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Socket.io-typed Validator for Iroha
2+
3+
## Features
4+
5+
This validator codes provides the following features:
6+
- execSyncFunction (* under construction)
7+
- sendSignedTransaction (* under construction)
8+
- monitoring blocks
9+
10+
## How to test this validator
11+
12+
### Requirements:
13+
14+
- OS: Linux (Ubuntu or CentOS)
15+
- node.js: v12
16+
- python: v3.8
17+
- Available port number: 5060
18+
19+
### How to execute test client (in the current status, only the monitor feature can be tested)
20+
21+
1. Launch [iroha-testnet](https://github.com/hyperledger/cactus/tree/main/tools/docker/iroha-testnet) docker and execute its wallet script
22+
```
23+
$ cd cactus/tools/docker/iroha-testnet/
24+
$ docker-compose up -d
25+
$ cd example/iroha-wallet
26+
$ bash setup-iroha-wallet.sh
27+
```
28+
29+
1. Launch validator server on the first console
30+
```
31+
$ cd cactus/packages-python/cactus_validator_socketio_iroha/validator-python
32+
$ pip3 install websocket eventlet flask requests flask-socketio==4.3.2 pyyaml pyjwt cryptography iroha schedule
33+
$ python3 -m main
34+
```
35+
- If there is the following message on your first console, the validator is successfully launched.
36+
37+
```
38+
socket port: 5060
39+
Server initialized for eventlet.
40+
```
41+
42+
1. Execute test script on the second console
43+
44+
```
45+
$ cd cactus/packages-python/cactus_validator_socketio_iroha/testcli
46+
$ npm install
47+
$ node testsock.js
48+
```
49+
50+
- If there is the following message on your second console, the block monitoring request is successfully executed.
51+
52+
```
53+
connect
54+
81680a4dc06a4685b8219b22fd002023
55+
polling
56+
call nop!
57+
##exec requestStartMonitor()
58+
```
59+
60+
- After this request is executed, the messages about monitoring blocks (`##get_block block num is : n`) will appear on your first console.
61+
62+
```
63+
81680a4dc06a4685b8219b22fd002023: Sending packet OPEN data {'sid': '81680a4dc06a4685b8219b22fd002023', 'upgrades': ['websocket'], 'pingTimeout': 60000, 'pingInterval': 25000}
64+
on connect (sessionid: 81680a4dc06a4685b8219b22fd002023)
65+
##called getValidatorInstance()
66+
##IrohaConnector.__init__
67+
81680a4dc06a4685b8219b22fd002023: Sending packet MESSAGE data 0
68+
81680a4dc06a4685b8219b22fd002023: Received request to upgrade to websocket
69+
81680a4dc06a4685b8219b22fd002023: Received packet MESSAGE data 2["test-event"]
70+
received event "test-event" from 81680a4dc06a4685b8219b22fd002023 [/]
71+
##IrohaConnector.cb()
72+
81680a4dc06a4685b8219b22fd002023: Upgrade to websocket successful
73+
81680a4dc06a4685b8219b22fd002023: Received packet MESSAGE data 2["nop"]
74+
received event "nop" from 81680a4dc06a4685b8219b22fd002023 [/]
75+
received nop
76+
##IrohaConnector.nop()
77+
81680a4dc06a4685b8219b22fd002023: Received packet MESSAGE data 2["startMonitor"]
78+
received event "startMonitor" from 81680a4dc06a4685b8219b22fd002023 [/]
79+
on startMonitor
80+
##called monitoring_routine()
81+
##get_block block num is : 1
82+
##get_block block num is : 2
83+
##get_block block num is : 3
84+
...
85+
##get_block block num is : 12
86+
```
87+
88+
- After 180 seconds on the second console, run requestStopMonitor and the test script will stop running.
89+
90+
```
91+
##exec requestStopMonitor()
92+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
port: 8000
2+
logging_dir: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
proto: ""
2+
url: ""
3+
publickey: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
sign_key: ""
2+
auth_credential: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
port: 5060
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBdTCCARoCCQC/F+Mh551QzDAKBggqhkjOPQQDAjBCMQswCQYDVQQGEwJKUDEQ
3+
MA4GA1UECAwHZXNqbXMxMjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg
4+
THRkMB4XDTE4MDYyNzA3MjIzNVoXDTI4MDYyNDA3MjIzNVowQjELMAkGA1UEBhMC
5+
SlAxEDAOBgNVBAgMB2Vzam1zMTIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
6+
UHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDPpSD2w0zrqJKraGD1b
7+
5Jq2sDuacThSUqi7fvz8oyrWtuKDjZ15zIaSOtak6XRxFh9V9Gokdg5GNbW/pTZc
8+
TuowCgYIKoZIzj0EAwIDSQAwRgIhAKH6ERsyd5bpEMIkY4clPqguwDWoTLk2VKq6
9+
ONEhUqotAiEA4yJxGmZpFdRScG2gDUIF2VDeX+XfHdJI2J41hyW9/zI=
10+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "testcli",
3+
"version": "0.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"json-bigint": "^1.0.0",
7+
"jsonwebtoken": "^8.5.1",
8+
"socket.io-client": "^2.4.0"
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const fs = require("fs");
2+
const jwt = require("jsonwebtoken");
3+
const JSONbig = require('json-bigint');
4+
5+
// NOTE: Run pip install socket.io-client.
6+
const io = require('socket.io-client');
7+
const url = "http://localhost:5060";
8+
const socket = io(url, {
9+
//transports: [ 'websocket', 'polling']
10+
});
11+
12+
const crtPath = "3PfTJw8g.crt";
13+
const paramAlgorithm = "ES256";
14+
15+
socket.on('connect', () => {
16+
console.log('connect');
17+
console.log(socket.id);
18+
const transport = socket.io.engine.transport.name;
19+
console.log(transport);
20+
//socket.emit('mymessage', 'hoge');
21+
});
22+
23+
socket.on('mymessage', () => {
24+
console.log('received mymessage');
25+
});
26+
27+
socket.on("response", (result) => {
28+
console.log(`#[recv]response, res: ${result}`);
29+
responseFlag = true;
30+
decodeFunc(result.resObj.data);
31+
});
32+
33+
socket.on("eventReceived", (result) => {
34+
console.log(`#[recv]eventReceived, res: ${result}`);
35+
responseFlag = true;
36+
console.log(`#[recv]eventReceived, res_status: ${result.status}`)
37+
decodeFunc(result.blockData);
38+
});
39+
40+
const verify = (signature) => new Promise(resolve => {
41+
const publicKey = fs.readFileSync(crtPath);
42+
43+
const option = {
44+
algorithms: paramAlgorithm,
45+
};
46+
47+
jwt.verify(signature, publicKey, option, (err, decoded) => {
48+
return new Promise((resolve, reject) => {
49+
if (err) {
50+
// Authentication NG
51+
console.log(`Authentication NG : error = ${err}`);
52+
reject(err);
53+
} else {
54+
// Authentication OK
55+
console.log(`Authentication OK`);
56+
console.log(`decoded : ${JSON.stringify(decoded)}`);
57+
58+
resolve(decoded);
59+
}
60+
});
61+
});
62+
});
63+
64+
const decodeFunc = async (signsignature) => {
65+
try {
66+
console.log("call verify()");
67+
console.log(`##signsignature: ${signsignature}`);
68+
const decoded = await verify(signsignature);
69+
console.log(`##decoded: ${decoded}`);
70+
71+
} catch (err) {
72+
console.log(`err: ${err}`);
73+
}
74+
};
75+
76+
const requestStartMonitor = () => {
77+
console.log('##exec requestStartMonitor()');
78+
socket.emit('startMonitor');
79+
80+
setTimeout(requestStopMonitor,180000);
81+
}
82+
83+
const requestStopMonitor = () => {
84+
console.log('##exec requestStopMonitor()');
85+
socket.emit('stopMonitor');
86+
87+
setTimeout(function(){
88+
// end communication
89+
socket.disconnect();
90+
process.exit(0);
91+
},5000);
92+
}
93+
94+
setTimeout(requestStartMonitor, 2000);
95+
96+
socket.emit('test-event');
97+
98+
setTimeout(() => {
99+
console.log('call nop!');
100+
socket.emit('nop');
101+
}, 1000);
102+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from typing import Type
2+
from iroha import Iroha, IrohaCrypto, IrohaGrpc
3+
import schedule
4+
5+
net = IrohaGrpc('localhost:50051')
6+
7+
iroha = Iroha('admin@test')
8+
admin_priv_key = 'f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70' #Private Key of user decided at previous line
9+
10+
temp_blocks = []
11+
latestNumOfBlocks = 0
12+
isMonitoring = False
13+
14+
def init():
15+
global temp_blocks
16+
global latestNumOfBlocks
17+
global isMonitoring
18+
temp_blocks = []
19+
latestNumOfBlocks = 0
20+
isMonitoring = True
21+
get_diff_blocks()
22+
clear_temp_blocks()
23+
24+
def get_block(blockNum):
25+
# create Query
26+
get_block_query = iroha.query(
27+
'GetBlock',
28+
height = blockNum
29+
)
30+
# sign Query
31+
IrohaCrypto.sign_query(get_block_query, admin_priv_key)
32+
# send Query
33+
response = net.send_query(get_block_query)
34+
return response
35+
36+
def get_diff_blocks():
37+
print("called get_diff_blocks")
38+
global temp_blocks
39+
global latestNumOfBlocks
40+
41+
print(f"latestNumOfBlocks before execute: {latestNumOfBlocks}")
42+
43+
height = latestNumOfBlocks
44+
is_latest = False
45+
46+
# get blocks from latestNumOfBlocks + 1
47+
while(not is_latest):
48+
height += 1
49+
response = get_block(height)
50+
51+
if(response.error_response.error_code == 0):
52+
temp_blocks.append(response)
53+
elif(response.error_response.error_code == 3):
54+
print(response.error_response.message)
55+
latestNumOfBlocks = height - 1
56+
is_latest = True
57+
58+
print(f"latestNumOfBlocks after execute: {latestNumOfBlocks}")
59+
60+
def clear_temp_blocks():
61+
print("called clear_temp_blocks")
62+
global temp_blocks
63+
temp_blocks = []
64+
65+
def monitoring_routine():
66+
get_diff_blocks()
67+
print("temp_blocks")
68+
print(temp_blocks)
69+
# send blocks to verifier
70+
# TODO implement
71+
#after sending, clear temp
72+
clear_temp_blocks()
73+
74+
75+
# start monitoring
76+
# when starting monitoring, call get_diff_blocks and clear_temp_blocks once as for get latest number of blocks
77+
init()
78+
79+
schedule.every(1).minutes.do(monitoring_routine)
80+
81+
while isMonitoring:
82+
schedule.run_pending()
83+
84+
print("finish")
85+
86+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__
2+
*.log
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN EC PARAMETERS-----
2+
BggqhkjOPQMBBw==
3+
-----END EC PARAMETERS-----
4+
-----BEGIN EC PRIVATE KEY-----
5+
MHcCAQEEICIlCfK3zMTFzUgdaj01LAHjJmHlbg6Xql9+i70iPz5EoAoGCCqGSM49
6+
AwEHoUQDQgAEM+lIPbDTOuokqtoYPVvkmrawO5pxOFJSqLt+/PyjKta24oONnXnM
7+
hpI61qTpdHEWH1X0aiR2DkY1tb+lNlxO6g==
8+
-----END EC PRIVATE KEY-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from validator_socketio_module.SocketIoValidator import SocketIoValidator
2+
3+
if __name__ == '__main__':
4+
validator = SocketIoValidator()
5+
validator.run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from abc import ABCMeta, abstractmethod
2+
3+
class AbstractConnector:
4+
@abstractmethod
5+
def __init__(self):
6+
pass
7+
8+
@abstractmethod
9+
def getValidatorInformation(self, validatorURL):
10+
"""Get the validator information including version, name, ID, and other information"""
11+
pass
12+
13+
@abstractmethod
14+
def sendSignedTransaction(self, signedTransaction):
15+
"""Request a verifier to execute a ledger operation"""
16+
pass
17+
18+
@abstractmethod
19+
def getBalance(self, address):
20+
"""Get balance of an account for native token on a leder"""
21+
pass
22+
23+
@abstractmethod
24+
def execSyncFunction(self, address, funcName, args):
25+
"""Execute a synchronous function held by a smart contract"""
26+
pass
27+
28+
@abstractmethod
29+
def startMonitor(self, clientId, cb):
30+
"""Request a validator to start monitoring ledger"""
31+
pass
32+
33+
@abstractmethod
34+
def stopMonitor(self, clientId):
35+
"""Request a validator to stop monitoring ledger"""
36+
pass
37+
38+
@abstractmethod
39+
def cb(self, callbackData):
40+
"""Callback function to call when receiving data from Ledger"""
41+
pass
42+
43+
@abstractmethod
44+
def nop(self):
45+
"""Nop function for testing"""
46+
pass

0 commit comments

Comments
 (0)