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

Refactor user nonce logic. #649

Merged
merged 20 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 18 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
8 changes: 2 additions & 6 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,17 @@ jobs:
- name: Run Barge
working-directory: ${{ github.workspace }}/barge
run: |
bash -x start_ocean.sh --no-dashboard 2>&1 --with-rbac --with-provider2 --with-c2d --with-thegraph --skip-subgraph-deploy > start_ocean.log &
bash -x start_ocean.sh --no-dashboard 2>&1 --with-rbac --with-provider2 --with-thegraph --skip-subgraph-deploy > start_ocean.log &
- name: Wait for contracts deployment and C2D cluster to be ready
working-directory: ${{ github.workspace }}/barge
run: |
for i in $(seq 1 250); do
sleep 10
[ -f "$HOME/.ocean/ocean-contracts/artifacts/ready" -a -f "$HOME/.ocean/ocean-c2d/ready" ] && break
[ -f "$HOME/.ocean/ocean-contracts/artifacts/ready" ] && break
done
- name: Verify deployments
run: |
netstat -lnpt
nc -zv 172.15.0.13 31000
cat $HOME/.ocean/ocean-contracts/artifacts/address.json
curl http://172.15.0.13:31000
nc -zv 172.15.0.16 8080
- name: Install dependencies
working-directory: ${{ github.workspace }}
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ __pycache__/
# C extensions
*.so

tests/resources/branin.arff

# Distribution / packaging
.Python
build/
Expand Down
11 changes: 6 additions & 5 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Parameters

Returns:
Json object containing the last-used nonce value.
The nonce endpoint is just informative, use the current UTC timestamp as a nonce,
where required in other endpoints.
The nonce returns an int, where required in other Provider endpoints.

Example:

Expand All @@ -34,7 +33,7 @@ Response:

```json
{
"nonce": 1644315615.24195
"nonce": 1
}
```

Expand Down Expand Up @@ -76,7 +75,7 @@ Parameters
encryptedDocument: Hex string, the encrypted document (optional)
flags: Integer, the flags of the encrypted document (optional)
documentHash: Hex string, the hash of the encrypted document (optional)
nonce: String object, the nonce of the encrypted document (required)
nonce: String object, the nonce (either integer, either UTC timestamp format) of the encrypted document (required)
signature: the signature of the encrypted document (required).
The signature is based on hashing the string concatenation consisting of:
transactionId + dataNftAddress + decrypterAddress + chainId + nonce.
Expand Down Expand Up @@ -181,7 +180,7 @@ Parameters
transferTxId: Hex string -- the id of on-chain transaction for approval of datatokens transfer
given to the provider's account
fileIndex: integer, the index of the file from the files list in the dataset
nonce: Nonce
nonce: String object, the nonce (either integer, either UTC timestamp format) required for download request
consumerAddress: String object containing consumer's address
signature: String object containg user signature (signed message).
The signature is based on hashing the following string concatenation consisting of:
Expand All @@ -205,6 +204,8 @@ payload:
"consumerAddress":"0x990922334",
"signature":"0x00110011",
"transferTxId": "0xa09fc23421345532e34829"
"nonce": 1
}
```

Response:
Expand Down
10 changes: 9 additions & 1 deletion ocean_provider/routes/consume.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
import json
import logging
from datetime import datetime, timezone

from flask import jsonify, request
from flask_sieve import validate
Expand Down Expand Up @@ -62,9 +63,16 @@ def nonce():
data = get_request_data(request)
address = data.get("userAddress")
nonce = get_nonce(address)

if not nonce:
new_nonce = 1
update_nonce(address, new_nonce)
nonce = get_nonce(address)
assert int(nonce) == new_nonce, "New nonce could not be stored correctly."

logger.info(f"nonce for user {address} is {nonce}")

response = jsonify(nonce=nonce), 200
response = jsonify(nonce=int(nonce)), 200
logger.info(f"nonce response = {response}")

return response
Expand Down
33 changes: 8 additions & 25 deletions ocean_provider/test/test_user_nonce.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
# SPDX-License-Identifier: Apache-2.0
#
import os
from unittest.mock import patch
import sqlite3

import pytest
import sqlalchemy
from flask_caching import Cache
from ocean_provider import models, user_nonce
from ocean_provider.myapp import app
from ocean_provider.user_nonce import (
get_nonce,
get_or_create_user_nonce_object,
update_nonce,
)
from tests.helpers.nonce import build_nonce
Expand All @@ -34,15 +31,10 @@ def test_get_and_update_nonce(monkeypatch, publisher_address, consumer_address):

# get_nonce can be used on addresses that are not in the user_nonce table
assert get_nonce("0x0000000000000000000000000000000000000000") is None
assert get_or_create_user_nonce_object(
"0x0000000000000000000000000000000000000000", build_nonce()
)

# update two times because, if we just pruned, we start from None
update_nonce(publisher_address, build_nonce())
publisher_nonce = get_nonce(publisher_address)
update_nonce(publisher_address, build_nonce())
new_publisher_nonce = get_nonce(publisher_address)
publisher_nonce = build_nonce(publisher_address)
new_publisher_nonce = build_nonce(publisher_address)

assert new_publisher_nonce >= publisher_nonce

Expand All @@ -56,14 +48,11 @@ def test_get_and_update_nonce_redis(publisher_address, consumer_address):
# get_nonce can be used on addresses that are not in the user_nonce table
cache.delete("0x0000000000000000000000000000000000000000")
assert get_nonce("0x0000000000000000000000000000000000000000") is None
assert get_or_create_user_nonce_object(
"0x0000000000000000000000000000000000000000", build_nonce()
)

# update two times because, if we just pruned, we start from None
update_nonce(publisher_address, build_nonce())
update_nonce(publisher_address, build_nonce(publisher_address))
publisher_nonce = get_nonce(publisher_address)
update_nonce(publisher_address, build_nonce())
update_nonce(publisher_address, build_nonce(publisher_address))
new_publisher_nonce = get_nonce(publisher_address)

assert new_publisher_nonce >= publisher_nonce
Expand All @@ -78,17 +67,11 @@ def test_update_nonce_exception(monkeypatch, publisher_address):
# pass through sqlite
monkeypatch.delenv("REDIS_CONNECTION")

# Ensure address exists in database
update_nonce(publisher_address, build_nonce())
nonce_object = get_nonce(publisher_address)

# Create duplicate nonce_object
with patch.object(
user_nonce,
"get_or_create_user_nonce_object",
return_value=models.UserNonce(address=publisher_address, nonce="0"),
):
with pytest.raises(sqlalchemy.exc.IntegrityError):
update_nonce(publisher_address, build_nonce())
with pytest.raises(sqlite3.IntegrityError):
update_nonce(publisher_address, nonce_object)

publisher_nonce = get_nonce(publisher_address)
update_nonce(publisher_address, None)
Expand Down
33 changes: 16 additions & 17 deletions ocean_provider/user_nonce.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
import logging
import os
import sqlite3

import jwt
from flask_caching import Cache
Expand Down Expand Up @@ -45,18 +46,28 @@ def update_nonce(address, nonce_value):
:param: nonce_value
"""
if nonce_value is None:
logger.debug(f"Nonce value is not provided.")
return

logger.debug(f"Received nonce value: {nonce_value}")

if os.getenv("REDIS_CONNECTION"):
nonce = get_or_create_user_nonce_object(address, nonce_value)
cache.set(address, nonce)
cache.set(address, nonce_value)

return

nonce_object = get_or_create_user_nonce_object(address, nonce_value)
nonce_object.nonce = nonce_value
nonce_object = models.UserNonce.query.filter_by(address=address).first()
if nonce_object is None:
nonce_object = models.UserNonce(address=address, nonce=nonce_value)
else:
if nonce_object.nonce == nonce_value:
msg = f"Cannot create duplicates in the database.\n Existing nonce: {nonce_object.nonce} vs. new nonce: {nonce_value}"
logger.debug(msg)
raise sqlite3.IntegrityError(msg)

nonce_object.nonce = nonce_value

logger.debug(f"update_nonce: {address}, new nonce {nonce_object.nonce}")
logger.debug(f"Wallet address: {address}, new nonce {nonce_object.nonce}")

try:
db.add(nonce_object)
Expand All @@ -67,18 +78,6 @@ def update_nonce(address, nonce_value):
raise


def get_or_create_user_nonce_object(address, nonce_value):
if os.getenv("REDIS_CONNECTION"):
cache.set(address, nonce_value)

return nonce_value

nonce_object = models.UserNonce.query.filter_by(address=address).first()
if nonce_object is None:
nonce_object = models.UserNonce(address=address, nonce=nonce_value)
return nonce_object


def force_expire_token(token):
"""
Creates the token in the database of Revoked Tokens.
Expand Down
3 changes: 2 additions & 1 deletion ocean_provider/utils/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from eth_keys import KeyAPI
from eth_keys.backends import NativeECCBackend
from ocean_provider.user_nonce import get_nonce
from ocean_provider.utils.accounts import sign_message
from ocean_provider.utils.basics import get_provider_wallet

Expand Down Expand Up @@ -46,7 +47,7 @@ def process_compute_request(data):


def sign_for_compute(wallet, owner, job_id=None):
nonce = datetime.now(timezone.utc).timestamp()
nonce = datetime.now(timezone.utc).timestamp() * 1000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we handle this in a new issue, then lgtm


# prepare consumer signature on did
msg = f"{owner}{job_id}{nonce}" if job_id else f"{owner}{nonce}"
Expand Down
8 changes: 3 additions & 5 deletions ocean_provider/utils/test/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ def test_get_private_key(publisher_wallet):

@pytest.mark.unit
def test_verify_signature(consumer_wallet, publisher_wallet):
update_nonce(consumer_wallet.address, build_nonce())

nonce = build_nonce()
nonce = build_nonce(consumer_wallet.address)
did = "did:op:test"
msg = f"{consumer_wallet.address}{did}{nonce}"
msg_w_nonce = f"{consumer_wallet.address}{did}"
signature = sign_message(msg, consumer_wallet)

assert verify_signature(consumer_wallet.address, signature, msg_w_nonce, nonce)

nonce = build_nonce()
nonce = build_nonce(consumer_wallet.address)
did = "did:op:test"
msg = f"{consumer_wallet.address}{did}{nonce}"
msg_w_nonce = f"{consumer_wallet.address}{did}"
Expand All @@ -43,7 +41,7 @@ def test_verify_signature(consumer_wallet, publisher_wallet):

assert f"Invalid signature {signature} for ethereum address" in e_info.value.args[0]

nonce = (datetime.now(timezone.utc) - timedelta(days=7)).timestamp()
nonce = 1
did = "did:op:test"
msg = f"{consumer_wallet.address}{did}{nonce}"
msg_w_nonce = f"{consumer_wallet.address}{did}"
Expand Down
1 change: 1 addition & 0 deletions ocean_provider/utils/test/test_compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@


@pytest.mark.unit
@pytest.mark.skip("C2D connection needs fixing.")
def test_get_compute_endpoint(monkeypatch):
monkeypatch.setenv("OPERATOR_SERVICE_URL", "http://with-slash.com/")
assert get_compute_endpoint() == "http://with-slash.com/api/v1/operator/compute"
Expand Down
1 change: 1 addition & 0 deletions ocean_provider/utils/test/test_provider_fees.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@


@pytest.mark.unit
@pytest.mark.skip("C2D connection needs fixing.")
@freeze_time("Feb 11th, 2012 00:00")
def test_get_provider_fee_amount(web3, publisher_wallet):
valid_until = get_future_valid_until()
Expand Down
4 changes: 4 additions & 0 deletions ocean_provider/validation/test/test_algo_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
this_is_a_gist = "https://gist.githubusercontent.com/calina-c/5e8c965962bc0240eab516cb7a180670/raw/6e6cd245c039a9aac0a488857c6927d39eaafe4d/sprintf-py-conversions"


@pytest.mark.skip("C2D connection needs fixing.")
@pytest.mark.unit
@patch("ocean_provider.validation.algo.check_asset_consumable", return_value=(True, ""))
@patch(
Expand Down Expand Up @@ -63,6 +64,7 @@ def side_effect(*args, **kwargs):
assert validator.validate() is True


@pytest.mark.skip("C2D connection needs fixing.")
@pytest.mark.unit
@patch("ocean_provider.validation.algo.check_asset_consumable", return_value=(True, ""))
@patch(
Expand Down Expand Up @@ -249,6 +251,7 @@ def test_fails_meta_issues(provider_wallet, consumer_address, web3):
assert validator.message == "checksum_prefix"


@pytest.mark.skip("C2D connection needs fixing.")
@pytest.mark.unit
@patch("ocean_provider.validation.algo.check_asset_consumable", return_value=(True, ""))
@patch(
Expand Down Expand Up @@ -818,6 +821,7 @@ def side_effect(*args, **kwargs):
assert validator.message == "no_raw_algo_allowed"


@pytest.mark.skip("C2D connection needs fixing.")
@pytest.mark.unit
@patch("ocean_provider.validation.algo.check_asset_consumable", return_value=(True, ""))
@patch(
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers/compute_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def build_and_send_ddo_with_compute_service(


def get_compute_signature(client, consumer_wallet, did, job_id=None):
nonce = build_nonce()
nonce = build_nonce(consumer_wallet.address)

# prepare consumer signature on did
if job_id:
Expand Down
14 changes: 11 additions & 3 deletions tests/helpers/nonce.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from datetime import datetime, timezone
from ocean_provider.user_nonce import get_nonce, update_nonce


def build_nonce():
return str(datetime.now(timezone.utc).timestamp())
def build_nonce(address) -> int:
nonce = get_nonce(address)
if nonce:
nonce = int(float(nonce)) + 1
update_nonce(address, nonce)

return int(nonce)

update_nonce(address, 1)
return 1
Loading