Skip to content

Commit

Permalink
feat: expose settings via node api (#254)
Browse files Browse the repository at this point in the history
* Added "Get settings" node endpoint for route: /settings

* Change settings "genesisAmount" from 10000000000000 to 5000000000000
* Change settings "incomeBase" from 50000000000 to 100000000000
* Change settings "incomeLimit" from 10000000000000 to 5000000000000
  • Loading branch information
JeremyPansier authored Nov 14, 2023
1 parent c2e3808 commit 2750101
Show file tree
Hide file tree
Showing 37 changed files with 687 additions and 330 deletions.
8 changes: 4 additions & 4 deletions config/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"blocksCountLimit": 1440,
"genesisAmountInParticles": 10000000000000,
"genesisAmount": 5000000000000,
"halfLifeInDays": 373.59,
"incomeBaseInParticles": 50000000000,
"incomeLimitInParticles": 10000000000000,
"incomeBase": 100000000000,
"incomeLimit": 5000000000000,
"maxOutboundsCount": 8,
"minimalTransactionFee": 1000,
"particlesPerToken": 100000000,
"smallestUnitsPerCoin": 100000000,
"synchronizationIntervalInSeconds": 10,
"validationIntervalInSeconds": 60,
"validationTimeoutInSeconds": 5,
Expand Down
68 changes: 48 additions & 20 deletions src/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,79 @@ package config

import (
"encoding/json"
"fmt"
"io"
"os"
"time"
)

type settingsDto struct {
BlocksCountLimit uint64
GenesisAmountInParticles uint64
GenesisAmount uint64
HalfLifeInDays float64
IncomeBaseInParticles uint64
IncomeLimitInParticles uint64
IncomeBase uint64
IncomeLimit uint64
MaxOutboundsCount int
MinimalTransactionFee uint64
ParticlesPerToken uint64
SmallestUnitsPerCoin uint64
SynchronizationIntervalInSeconds int
ValidationIntervalInSeconds int64
ValidationTimeoutInSeconds int64
VerificationsCountPerValidation int64
}

type Settings struct {
bytes []byte
blocksCountLimit uint64
genesisAmountInParticles uint64
genesisAmount uint64
halfLifeInNanoseconds float64
incomeBaseInParticles uint64
incomeLimitInParticles uint64
incomeBase uint64
incomeLimit uint64
maxOutboundsCount int
minimalTransactionFee uint64
particlesPerToken uint64
smallestUnitsPerCoin uint64
synchronizationTimer time.Duration
validationTimestamp int64
validationTimer time.Duration
validationTimeout time.Duration
verificationsCountPerValidation int64
}

func NewSettings(path string) (*Settings, error) {
jsonFile, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("unable to open file: %w", err)
}
var settings *Settings
bytes, err := io.ReadAll(jsonFile)
if err != nil {
return nil, fmt.Errorf("unable to read file: %w", err)
}
if err = jsonFile.Close(); err != nil {
return nil, fmt.Errorf("unable to close file: %w", err)
}
if err = json.Unmarshal(bytes, &settings); err != nil {
return nil, fmt.Errorf("unable to unmarshal: %w", err)
}
return settings, nil
}

func (settings *Settings) UnmarshalJSON(data []byte) error {
var dto *settingsDto
err := json.Unmarshal(data, &dto)
if err != nil {
return err
}
settings.bytes = data
settings.blocksCountLimit = dto.BlocksCountLimit
settings.genesisAmountInParticles = dto.GenesisAmountInParticles
settings.genesisAmount = dto.GenesisAmount
hoursByDay := 24.
settings.halfLifeInNanoseconds = dto.HalfLifeInDays * hoursByDay * float64(time.Hour.Nanoseconds())
settings.incomeBaseInParticles = dto.IncomeBaseInParticles
settings.incomeLimitInParticles = dto.IncomeLimitInParticles
settings.incomeBase = dto.IncomeBase
settings.incomeLimit = dto.IncomeLimit
settings.maxOutboundsCount = dto.MaxOutboundsCount
settings.minimalTransactionFee = dto.MinimalTransactionFee
settings.particlesPerToken = dto.ParticlesPerToken
settings.smallestUnitsPerCoin = dto.SmallestUnitsPerCoin
settings.synchronizationTimer = time.Duration(dto.SynchronizationIntervalInSeconds) * time.Second
settings.validationTimestamp = dto.ValidationIntervalInSeconds * time.Second.Nanoseconds()
settings.validationTimer = time.Duration(dto.ValidationIntervalInSeconds) * time.Second
Expand All @@ -59,24 +83,28 @@ func (settings *Settings) UnmarshalJSON(data []byte) error {
return nil
}

func (settings *Settings) Bytes() []byte {
return settings.bytes
}

func (settings *Settings) BlocksCountLimit() uint64 {
return settings.blocksCountLimit
}

func (settings *Settings) GenesisAmountInParticles() uint64 {
return settings.genesisAmountInParticles
func (settings *Settings) GenesisAmount() uint64 {
return settings.genesisAmount
}

func (settings *Settings) HalfLifeInNanoseconds() float64 {
return settings.halfLifeInNanoseconds
}

func (settings *Settings) IncomeBaseInParticles() uint64 {
return settings.incomeBaseInParticles
func (settings *Settings) IncomeBase() uint64 {
return settings.incomeBase
}

func (settings *Settings) IncomeLimitInParticles() uint64 {
return settings.incomeLimitInParticles
func (settings *Settings) IncomeLimit() uint64 {
return settings.incomeLimit
}

func (settings *Settings) MaxOutboundsCount() int {
Expand All @@ -87,8 +115,8 @@ func (settings *Settings) MinimalTransactionFee() uint64 {
return settings.minimalTransactionFee
}

func (settings *Settings) ParticlesPerToken() uint64 {
return settings.particlesPerToken
func (settings *Settings) SmallestUnitsPerCoin() uint64 {
return settings.smallestUnitsPerCoin
}

func (settings *Settings) SynchronizationTimer() time.Duration {
Expand Down
2 changes: 1 addition & 1 deletion src/log/console/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ func (logger *Logger) Error(msg string) {

func (logger *Logger) Fatal(msg string) {
if logger.level <= Fatal {
log.Println("FATAL:", msg)
log.Panicln("FATAL:", msg)
}
}
83 changes: 83 additions & 0 deletions src/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ Each request value or response value shall be marshaled to bytes or un-marshaled
* **response value:** *none*
</details>

### Node
<details>
<summary><b>Get settings</b></summary>

![/settings](https://img.shields.io/badge//settings-dimgray?style=flat-square)

*Description:* Get node settings.
* **request value:** [settings](#settings)
* **response value:** *none*
</details>

### Transactions pool
<details>
<summary><b>Add transaction</b></summary>
Expand Down Expand Up @@ -240,6 +251,78 @@ The value at the transaction timestamp
</tr>
</table>

#### Settings
<table>
<th>
Schema
</th>
<th>
Description
</th>
<th>
Example
</th>
<tr>
<td>

```
{
"blocksCountLimit": uint64
"genesisAmount": uint64
"halfLifeInDays": float64
"incomeBase": uint64
"incomeLimit": uint64
"maxOutboundsCount": int
"minimalTransactionFee": uint64
"smallestUnitsPerCoin": uint64
"synchronizationIntervalInSeconds": int
"validationIntervalInSeconds": int64
"validationTimeoutInSeconds": int64
"verificationsCountPerValidation": int64
}
```
</td>
<td>

```
The maximum blocks count returned by a blocks request
The genesis amount in smallest unit
The half-life in days
The income base in smallest unit
The income limit in smallest unit
The maximum node outbounds count
The minimal transaction fee in smallest unit
The number of smallest uints per coin
The synchronization interval in seconds
The validation interval in seconds
The validation timeout in seconds
The verifications count per validation
```
</td>
<td>

```
{
"blocksCountLimit": 1440,
"genesisAmount": 5000000000000,
"halfLifeInDays": 373.59,
"incomeBase": 100000000000,
"incomeLimit": 5000000000000,
"maxOutboundsCount": 8,
"minimalTransactionFee": 1000,
"smallestUnitsPerCoin": 100000000,
"synchronizationIntervalInSeconds": 10,
"validationIntervalInSeconds": 60,
"validationTimeoutInSeconds": 5,
"verificationsCountPerValidation": 6
}
```
</td>
</tr>
</table>

#### Transaction
<table>
<th>
Expand Down
13 changes: 6 additions & 7 deletions src/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,19 @@ func decodeAddress(mnemonic *string, derivationPath *string, password *string, p
}

func createHost(settingsPath *string, infuraKey *string, seedsPath *string, ip *string, port *int, address string, logger *console.Logger) *p2p.Host {
parser := file.NewJsonParser()
var settings *config.Settings
err := parser.Parse(*settingsPath, &settings)
settings, err := config.NewSettings(*settingsPath)
if err != nil {
logger.Fatal(fmt.Errorf("unable to parse settings: %w", err).Error())
}
registry := poh.NewRegistry(*infuraKey, logger)
watch := tick.NewWatch()
synchronizer := createSynchronizer(parser, *seedsPath, *ip, *port, settings, watch, logger)
synchronizer := createSynchronizer(*seedsPath, *ip, *port, settings, watch, logger)
blockchain := verification.NewBlockchain(registry, settings, synchronizer, logger)
transactionsPool := validation.NewTransactionsPool(blockchain, settings, synchronizer, address, logger)
synchronizationEngine := tick.NewEngine(synchronizer.Synchronize, watch, settings.SynchronizationTimer(), 1, 0)
validationEngine := tick.NewEngine(transactionsPool.Validate, watch, settings.ValidationTimer(), 1, 0)
verificationEngine := tick.NewEngine(blockchain.Update, watch, settings.ValidationTimer(), settings.VerificationsCountPerValidation(), 1)
handler := gp2p.NewHandler(blockchain, synchronizer, transactionsPool, watch, logger)
handler := gp2p.NewHandler(blockchain, settings.Bytes(), synchronizer, transactionsPool, watch, logger)
serverFactory := gp2p.NewServerFactory(handler, settings)
server, err := serverFactory.CreateServer(*port)
if err != nil {
Expand All @@ -83,8 +81,9 @@ func createHost(settingsPath *string, infuraKey *string, seedsPath *string, ip *
return p2p.NewHost(server, synchronizationEngine, validationEngine, verificationEngine, logger)
}

func createSynchronizer(parser *file.JsonParser, seedsPath string, hostIp string, port int, settings *config.Settings, watch *tick.Watch, logger *console.Logger) *p2p.Synchronizer {
func createSynchronizer(seedsPath string, hostIp string, port int, settings *config.Settings, watch *tick.Watch, logger *console.Logger) *p2p.Synchronizer {
var seedsStringTargets []string
parser := file.NewJsonParser()
err := parser.Parse(seedsPath, &seedsStringTargets)
if err != nil {
logger.Fatal(fmt.Errorf("unable to parse seeds: %w", err).Error())
Expand All @@ -100,6 +99,6 @@ func createSynchronizer(parser *file.JsonParser, seedsPath string, hostIp string
logger.Fatal(fmt.Errorf("failed to find the public IP: %w", err).Error())
}
}
clientFactory := gp2p.NewClientFactory(ipFinder, settings)
clientFactory := gp2p.NewClientFactory(ipFinder, settings.ValidationTimeout())
return p2p.NewSynchronizer(clientFactory, hostIp, strconv.Itoa(port), settings.MaxOutboundsCount(), scoresBySeedTarget, watch)
}
9 changes: 5 additions & 4 deletions src/node/network/neighbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package network

type Neighbor interface {
Target() string
GetBlocks(startingBlockHeight uint64) ([]byte, error)
GetFirstBlockTimestamp() (int64, error)
SendTargets(targets []string) (err error)
AddTransaction(transaction []byte) (err error)
GetBlocks(startingBlockHeight uint64) (blocks []byte, err error)
GetFirstBlockTimestamp() (firstBlockTimestamp int64, err error)
GetSettings() (settings []byte, err error)
SendTargets(targets []string) error
AddTransaction(transaction []byte) error
GetTransactions() (transactions []byte, err error)
GetUtxos(address string) (utxos []byte, err error)
}
4 changes: 2 additions & 2 deletions src/node/network/p2p/gp2p/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ type Client struct {
*gp2p.Client
}

func NewClient(ip string, port string, timeout time.Duration, logger log.Logger) (*Client, error) {
func NewClient(ip string, port string, connectionTimeout time.Duration, logger log.Logger) (*Client, error) {
tcp := gp2p.NewTCP(ip, port)
client, err := gp2p.NewClient(tcp)
if err != nil {
return nil, err
}
settings := gp2p.NewClientSettings()
settings.SetRetry(1, time.Nanosecond)
settings.SetConnTimeout(timeout)
settings.SetConnTimeout(connectionTimeout)
client.SetSettings(settings)
client.SetLogger(logger)
return &Client{client}, err
Expand Down
11 changes: 6 additions & 5 deletions src/node/network/p2p/gp2p/client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@ import (
"github.com/my-cloud/ruthenium/src/log/console"
"github.com/my-cloud/ruthenium/src/node/network"
"github.com/my-cloud/ruthenium/src/node/network/p2p"
"time"
)

type ClientFactory struct {
ipFinder network.IpFinder
settings p2p.Settings
ipFinder network.IpFinder
connectionTimeout time.Duration
}

func NewClientFactory(ipFinder network.IpFinder, settings p2p.Settings) *ClientFactory {
return &ClientFactory{ipFinder, settings}
func NewClientFactory(ipFinder network.IpFinder, connectionTimeout time.Duration) *ClientFactory {
return &ClientFactory{ipFinder, connectionTimeout}
}

func (factory *ClientFactory) CreateClient(ip string, port string) (p2p.Client, error) {
lookedUpIp, err := factory.ipFinder.LookupIP(ip)
if err != nil {
return nil, fmt.Errorf("failed to look up IP on addresse %s: %w", ip, err)
}
client, err := NewClient(lookedUpIp, port, factory.settings.ValidationTimeout(), console.NewLogger(console.Fatal))
client, err := NewClient(lookedUpIp, port, factory.connectionTimeout, console.NewLogger(console.Fatal))
if err != nil {
return nil, fmt.Errorf("failed to instantiate client for address %s: %w", ip, err)
}
Expand Down
Loading

0 comments on commit 2750101

Please sign in to comment.