Skip to content

Instantly share code, notes, and snippets.

View zsfelfoldi's full-sized avatar

Felföldi Zsolt zsfelfoldi

View GitHub Profile
INFO [06-03|01:17:56.023] Imported new block headers count=1 elapsed=3.429ms number=10189345 hash="3560fb…8bbae5"
INFO [06-03|01:17:57.551] Looking for peers peercount=2 tried=8 static=0
INFO [06-03|01:18:06.389] Imported new block headers count=1 elapsed=10.590ms number=10189346 hash="0fc3d7…39d77d"
INFO [06-03|01:18:07.553] Looking for peers peercount=2 tried=11 static=0
INFO [06-03|01:18:17.556] Looking for peers peercount=2 tried=12 static=0
INFO [06-03|01:18:18.846] Imported new block headers count=1 elapsed=8.864ms number=10189347 hash="004ea0…1aa5e7"
INFO [06-03|01:18:27.559] Looking for peers peercount=2 tried=8 static=0
INFO [06-03|01:18:45.564] Imported new block headers count=1 elapsed=9.115ms number=10189348 hash="30b581…41ad96"
INFO [06-03|01:19:00.867] Imported new block headers count=1 elapsed=9.135ms number=101893

Proposed NodeStateMachine specs

Persistence rules

  • a subset of the node state flags and fields can be persisted in a database
    • a flag is persistent if NodeStateFlag.persistent is set
    • a field is persistent if encode and decode are not nil
    • persistent flags and fields are created with a separate constructor
  • stored states are loaded during startup
  • in-memory states that have been changed since loading the stored version or newly created ones with no stored version are considered "dirty" until they are saved (again)

General utilities

Weighted random selector

WeightedRandomSelect is a passive, not thread-safe data structure that holds a set of items with assigned selection weights and can quickly select one of them randomly. Weights are specified by a callback function but the last known weights are also stored in the data structure. If the weight increases compared to the last returned value then it should be explicitly updated. If it decreases then update happens automatically.

Node state machine

NodeStateMachine connects different system components operating on subsets of network nodes. It can be considered an enhanced version of les.clientPeerSet / les.serverPeerSet which it could probably replace over time. Node states are represented by uint64 bit vectors with each bit assigned to a state flag. Each state flag has a string name and the mapping is created automatically. It is possible to subscribe to subsets of state flags and receive a callback if one of the nodes has a relevant state flag c

A quick explanation of the expiredValue structure

Goal

The lesPay token sale model uses service tokens that continuously "expire" under certain conditions, meaning their value is reduced exponentially over time. Expiration happens when some amount of free connection is possible and therefore all existing tokens are spendable. Expiration can be interpreted as a kind of "spendability" fee that incentivizes clients to indeed spend their tokens in a limited time frame. This can be realized in two ways:

  • with "inflationary" units; if all prices rise exponentially then value of existing tokens is reduced inversely
  • with "stable tokens"; if the nominal value of each token is stable then the token balances should be reduced continuously

The second interpretation fits with the model better because service tokens (which are basically a promise from the server) are meant to have a more or less predictable purchasing power. With stable token nominations the client can expect the prices to not change qui

package main
import (
"fmt"
"math"
"sort"
)
const (
maxLevel = 10
  • createDeposit(amount map[common.Address]uint64) depositID
    • sends a transaction to create a new deposit with the given target allowances
  • subscribeDepositEvents(ch chan struct {id depositID, state int})
    • sends an event to the given channel every time a deposit goes into pending, active or expired state
  • estimatedExpiry(id depositID) time.Duration
    • returns the estimated remaining time while cheques can still be written using this deposit
  • allowancesLeft(id depositID) map[common.Address]uint64
  • lastCheque(id depositID, to common.Address) (totalSpent, allowanceLeft uint64)
  • writeCheque(id depositID, to common.Address, amount uint64) (totalSpent, allowanceLeft uint64, proofOfPayment []byte)
    • creates a cheque if the deposit is in active state and there is enough allowance left (otherwise proofOfPayment should be nil)

Simple probabilistic payment channel for LES payments

It is possible to do simple probabilistic one-to-many payments with one condition: the set of potential recipients and their assigned probabilities should be decided when creating the payment contract. This is actually a very good fit for light client incentivization. A client can collect many potential recipients (anyone who provided free service) but cannot really know how a particular server will actually react to receiving payments and whether it is worth paying for over the long term. Starting with very small payments is preferable. A one-to-one channel can limit the amount redeemable at the receiver side but the sender still has to pay transaction costs to build a payment route to every new potential receiver. Therefore it has to expect a high amount of free service from every server before risking the first payment. This could make the bootstrapping process for clients slow and/or expensive. One the other hand the proposed method could build a p

Payment module

LES in-protocol payments can be sent using multiple payment technologies or even multiple currencies. These are realized as plug-in modules. The payment sender module generates a "proof of payment" that the receiver module can interpret and verify. These proofs can be transmitted through an LES connection or LESTALK UDP messages.

Note: the design allows using multiple currencies on both the client and server side. Each payment module uses one specific currency but it can handle value conversions on both ends. In the first implementation we won't allow this though, the first payment module should only handle ETH. This makes value calculations a lot easier for requestPayment and paymentCost. Expected transaction costs have to be considered but that can be very simple too (hardcoded gas cost, user specified gas price).

Payment receiver module

  • func receivePayment(from enode.ID, proofOfPayment, oldMeta []byte) (value uint64, newMeta []byte, err error)
title sort_key
les Namespace
C

The les API allows you to manage LES server settings, including client parameters and payment settings for prioritized clients. It also provides functions to query checkpoint information in both server and client mode.

  • TOC {:toc}

ServerInfo() map[string]interface{}: query server info

  • minimumCapacity: minimum allowed capacity for any client
  • freeClientCapacity: capacity assigned to free (unknown, non-priority) clients
  • totalCapacity: total allowed capacity of simultaneously connected clients (may change dynamically during operation)
  • totalConnectedCapacity: total capacity of currently connected clients
  • priorityConnectedCapacity: total capacity of currently connected priority clients
  • totalPriorityCapacity: total capacity of all priority clients (may exceed totalCapacity in which case priority clients might be rejected too)

SetClientParams(ids []string, tags []string, params map[string]interface{}) error: set certain parameter fields for each client whose id is in the ids list or has all tags in the tags list

  • userTags: user tags assigned to the client