Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add learner nodes and hostname support #1

Merged
merged 6 commits into from
Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,6 @@ dmypy.json

# Pyre type checker
.pyre/

# VSCode
.vscode/
101 changes: 54 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,59 @@
# web3quorum ⛓

_A web3py extension that supports [Quorum](https://www.goquorum.com/) APIs: [RAFT](https://docs.goquorum.com/en/latest/Consensus/raft/) and [Istanbul](https://docs.goquorum.com/en/latest/Consensus/ibft/istanbul-rpc-api/)._
_A web3py extension that supports [Quorum](https://www.goquorum.com/) APIs: [Raft](https://docs.goquorum.com/en/latest/Consensus/raft/) and [Istanbul](https://docs.goquorum.com/en/latest/Consensus/ibft/istanbul-rpc-api/)._

## Quick start

- pip install web3quorum

- Examples of usage

```python
from web3 import HTTPProvider
from web3quorum import Web3Quorum

w3 = Web3Quorum(HTTPProvider('http://foo.bar'))

# retrieve an info about cluster
>>> w3.raft.cluster
({'ip': '31.247.117.573',
'nodeId': 'db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c',
'p2pPort': 30303,
'raftId': 2,
'raftPort': 50400},
{'ip': '54.84.81.417',
'nodeId': 'c9391a893f9d3e44d820419259f2e441f209c0e4e1957ca3df35473f20f529c3c94fecb1e1c37e3bb0f15b74defd0db3c72a1b83d5f2d65d34dd7d255efdba0c',
'p2pPort': 30303,
'raftId': 1,
'raftPort': 50400})

# add new raft peer
>>> w3.raft.add_peer('enode://db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c@1.1.1.1:30303?discport=0&raftport=50400')
2

# delete raft peer
>>> w3.raft.remove_peer(2)

```
- Resolve enode or hostname to raft Id
```
from web3quorum import enode_to_raft_id

>>> enode_to_raft_id(api, 'enode://db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c@1.1.1.1:30303')
2
```

- Use web3Quorum mocking object in your tests
from web3quorum import Web3QuorumMock
```
>>> w3 = Web3QuorumMock()
>>> w3.raft.role
verifier
```
`pip install web3quorum`

## Usage examples

``` python
from web3 import HTTPProvider
from web3quorum import Web3Quorum

w3 = Web3Quorum(HTTPProvider('http://foo.bar'))

# retrieve cluster info
>>> w3.raft.cluster
({'hostname': '31.247.117.573',
'nodeId': 'db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c',
'p2pPort': 30303,
'raftId': 2,
'raftPort': 50400},
{'hostname': '54.84.81.417',
'nodeId': 'c9391a893f9d3e44d820419259f2e441f209c0e4e1957ca3df35473f20f529c3c94fecb1e1c37e3bb0f15b74defd0db3c72a1b83d5f2d65d34dd7d255efdba0c',
'p2pPort': 30303,
'raftId': 1,
'raftPort': 50400})

# add new learner peer
>>> w3.raft.add_learner('enode://db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c@1.1.1.1:30303?discport=0&raftport=50400')
2

# promote learner peer to verifier
>>> w3.raft.promote_to_peer(2)
True

# delete verifier peer
>>> w3.raft.remove_peer(2)
```

### Resolve enode or hostname to raft Id

``` python
from web3quorum import enode_to_raft_id

>>> enode_to_raft_id(api, 'enode://db207d418bbbfcb0639f303259f62bf1da3c95aceb051f32cac8361a1c08276f4296f8e3f57833caac9c2aaa28a90158bf6eaf3f8ad602ac27a3e0ed6c8c765c@1.1.1.1:30303')
2
```

### Use mock object in tests

``` python
from web3quorum import Web3QuorumMock

>>> w3 = Web3QuorumMock()
>>> w3.raft.role
verifier
```
334 changes: 272 additions & 62 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[tool.poetry]
name = "web3quorum"
version = "1.0.2"
version = "1.1.0"
description = "A library to interact with Quorum API"
authors = ["Alex Khaerov <i@hayorov.ru>"]
authors = ["Chainstack <dev@chainstack.com>"]
license = "MIT"

[tool.poetry.dependencies]
python = "^3.7"
web3 = "^5.3"
web3 = "^5.4"

[tool.poetry.dev-dependencies]

Expand Down
14 changes: 11 additions & 3 deletions web3quorum/quorum.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ def cluster(self):
def role(self) -> str:
return self.web3.manager.request_blocking("raft_role", [])

def add_learner(self, enode_url: str) -> int:
return self.web3.manager.request_blocking("raft_addLearner", [enode_url])

def promote_to_peer(self, raft_id: bool) -> bool:
return self.web3.manager.request_blocking("raft_promoteToPeer", [raft_id])

def add_peer(self, enode_url: str) -> int:
return self.web3.manager.request_blocking("raft_addPeer", [enode_url])

Expand Down Expand Up @@ -58,7 +64,7 @@ def get_snapshot_at_hash(self, hash: str):
class Web3Quorum(Web3):

def __init__(self, *args, **kwargs):
# add Raft and ibft apis
# add Raft and IBFT APIs
kwargs['modules'] = kwargs.get('modules', {})
kwargs['modules'].update({'raft': (Raft,), 'istanbul': (Istanbul,)})
super().__init__(*args, **kwargs)
Expand All @@ -67,17 +73,19 @@ def __init__(self, *args, **kwargs):
self.middleware_onion.inject(geth_poa_middleware, layer=0)


attrs = {'raft.cluster': [{'ip': '1.2.3.4',
attrs = {'raft.cluster': [{'hostname': '1.2.3.4',
'nodeId': 'foo',
'p2pPort': 30303,
'raftId': 2,
'raftPort': 50400},
{'ip': '9.9.9.9',
{'hostname': '9.9.9.9',
'nodeId': 'bar',
'p2pPort': 30303,
'raftId': 1,
'raftPort': 50400},
],
'raft.add_learner.return_value': 1,
'raft.promote_to_peer.return_value': True,
'raft.add_peer.return_value': 1,
'raft.remove_peer.return_value': None,
'raft.role': 'verifier',
Expand Down
4 changes: 2 additions & 2 deletions web3quorum/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def test_enode_to_raft_id(self):

assert raft_id == 2

def test_ip_to_raft_id(self):
raft_id = utils.ip_to_raft_id(web3mock, '1.2.3.4')
def test_hostname_to_raft_id(self):
raft_id = utils.hostname_to_raft_id(web3mock, '1.2.3.4')

assert raft_id == 2

Expand Down
6 changes: 3 additions & 3 deletions web3quorum/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ def enode_to_raft_id(w3, enode: str) -> int:
return int(node.get('raftId'))


def ip_to_raft_id(w3, ip: str) -> int:
"""Resolves IP into raft Id."""
def hostname_to_raft_id(w3, hostname: str) -> int:
"""Resolves hostname into raft Id."""
cluster = w3.raft.cluster
for node in cluster:
if str(node.get('ip')) == ip:
if str(node.get('hostname')) == hostname:
return int(node.get('raftId'))