arkeonetwork / arkeo

Arkeo - Free Market Blockchain Data Infrastructure
Other
21 stars 20 forks source link

Implement "hashcash" option for data provider requests #164

Open cbarraford opened 1 year ago

cbarraford commented 1 year ago

Data providers should have the ability to enable "hashcash" or PoW for requests. This enables them to protect themselves from DoS attacks. Each request type (free, subscription, pay-as-you-go, etc) can configure its own hashing configuration independently of the other.

Implementation Details

To find a valid nonce, a user needs to generate a sha256 hash of a string. The string is formatted as such.

<contract_id>:<user_pubkey>:<provider_pubkey>:<seed>:<nonce>

Seed

The "seed" is generated by the data provider as a "random" hash. To save on disk/memory, this hash is generated by using sha256 with the following string

<contract_id>:<user_pubkey>:<salt>:<query_nonce>

The salt can be randomly generated at startup (does NOT have to persist). The query nonce is tracking the number of queries this specific user requesting has made. It should increment on each request.

Difficulty Calculation

A single data provider can configure the level of difficulty they will use and the rate at which it increases. This rate of increase is typically aligned via number of queries made in the last "hash period, which is a length of time. Query counts are tracked on a per-contract basis, while free tier uses a contract id of "-1". The data provider sets two additional configs that set A) the number of queries that are made to increase the difficulty, and B) the amount of increase.

'''
cm = max hash (config hash)
cq = queries per step (config int)
ci = difficulty incrementer (config int)
q = count of queries in the last hash period
'''

def hash_to_int(hash_str):
    return int(hash_str, 16)

def compare_hashes(hash1, hash2):
    int_hash1 = hash_to_int(hash1)
    int_hash2 = hash_to_int(hash2)
    return int_hash1 <= int_hash2

def get_difficulty(q):
    return hash_to_int(cm) // (q / cq * ci)

def valid_hash(user_hash):
    max = hash_to_int(cm)
    difficulty = get_difficulty(q)
    return difficulty <= hash_to_int(user_hash)
mperklin commented 1 year ago

With the existing complexity of ECDSA signatures and signature-verifications being used by BOTH requester AND responder (for every single request), I do not like the idea of adding proof of work on top of it.

Laptops and desktops won't notice much difference, but lower-powered devices like Raspberry Pis and mobile devices will.

I'm fine for having this as "an option" for projects to consider when adding one-or-more gates to their pay-for-everyone contracts as long as the project can choose to disable it in favour of other gates that do not affect their mobile users as much.

I think the token-balance gate would be almost as effective at deterring sybil attacks without the additional computational overhead.

cbarraford commented 1 year ago

btw, mobile devices are almost just as powerful as laptop these days (believe it or not)

0xean commented 1 year ago

per conversation on 4/6 we will delay this until post launch