# Cryptographic Addition Chain Generation in Go

### Related tags

##### Overview

Cryptographic Addition Chain Generation in Go

`addchain` generates short addition chains for exponents of cryptographic interest with results rivaling the best hand-optimized chains. Intended as a building block in elliptic curve or other cryptographic code generators.

• Suite of algorithms from academic research: continued fractions, dictionary-based and Bos-Coster heuristics
• Custom run-length techniques exploit structure of cryptographic exponents with excellent results on Solinas primes
• Generic optimization methods eliminate redundant operations
• Simple domain-specific language for addition chain computations
• Command-line interface or library

## Background

An addition chain for a target integer n is a sequence of numbers starting at 1 and ending at n such that every term is a sum of two numbers appearing earlier in the sequence. For example, an addition chain for 29 is

``````1, 2, 4, 8, 9, 17, 25, 29
``````

Addition chains arise in the optimization of exponentiation algorithms with fixed exponents. For example, the addition chain above corresponds to the following sequence of multiplications to compute `x29`

``` x2 = x1 * x1
x4 = x2 * x2
x8 = x4 * x4
x9 = x1 * x8
x17 = x8 * x9
x25 = x8 * x17
x29 = x4 * x25
```

An exponentiation algorithm for a fixed exponent n reduces to finding a minimal length addition chain for n. This is especially relevent in cryptography where exponentiation by huge fixed exponents forms a performance-critical component of finite-field arithmetic. In particular, constant-time inversion modulo a prime p is performed by computing `xp-2 (mod p)`, thanks to Fermat's Little Theorem. Square root also reduces to exponentiation for some prime moduli. Finding short addition chains for these exponents is one important part of high-performance finite field implementations required for elliptic curve cryptography or RSA.

Minimal addition chain search is famously hard. No practical optimal algorithm is known, especially for cryptographic exponents of size 256-bits and up. Given its importance for the performance of cryptographic implementations, implementers devote significant effort to hand-tune addition chains. The goal of the `addchain` project is to match or exceed the best hand-optimized addition chains using entirely automated approaches, building on extensive academic research and applying new tweaks that exploit the unique nature of cryptographic exponents.

## Results

The following table shows the results of the `addchain` library on popular cryptographic exponents. For each one we also show the length of the best known hand-optimized addition chain, and the delta from the library result.

Name This Library Best Known Delta
Curve25519 Field Inversion 266 265 +1
NIST P-256 Field Inversion 266 266 +0
NIST P-384 Field Inversion 397 396 +1
secp256k1 (Bitcoin) Field Inversion 269 269 +0
Curve25519 Scalar Inversion 283 284 -1
NIST P-256 Scalar Inversion 294 292 +2
NIST P-384 Scalar Inversion 434 433 +1
secp256k1 (Bitcoin) Scalar Inversion 293 290 +3

See full results listing for more detail and results for less common exponents.

These results demonstrate that `addchain` is competitive with hand-optimized chains, often with equivalent or better performance. Even when `addchain` is slightly sub-optimal, it can still be considered valuable since it fully automates a laborious manual process. As such, `addchain` can be trusted to produce high quality results in an automated code generation tool.

## Usage

### Command-line Interface

Install a pre-compiled release binary:

``````curl -sSfL https://git.io/addchain | sh -s -- -b /usr/local/bin
``````

Alternatively build from source:

``````go get -u github.com/mmcloughlin/addchain/cmd/addchain
``````

Search for a curve25519 field inversion addition chain with:

`addchain search '2^255 - 19 - 2'`

Output:

``````addchain: expr: "2^255 - 19 - 2"
_10       = 2*1
_11       = 1 + _10
_1100     = _11 << 2
_1111     = _11 + _1100
_11110000 = _1111 << 4
_11111111 = _1111 + _11110000
x10       = _11111111 << 2 + _11
x20       = x10 << 10 + x10
x30       = x20 << 10 + x10
x60       = x30 << 30 + x30
x120      = x60 << 60 + x60
x240      = x120 << 120 + x120
x250      = x240 << 10 + x10
return      (x250 << 2 + 1) << 3 + _11
``````

### Library

Install:

``````go get -u github.com/mmcloughlin/addchain
``````

Algorithms all conform to the `alg.ChainAlgorithm` or `alg.SequenceAlgorithm` interfaces and can be used directly. However the most user-friendly method uses the `alg/ensemble` package to instantiate a sensible default set of algorithms and the `alg/exec` helper to execute them in parallel. The following code uses this method to find an addition chain for curve25519 field inversion:

```func Example() {
// Target number: 2²⁵⁵ - 21.
n := new(big.Int)
n.SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb", 16)

// Default ensemble of algorithms.
algorithms := ensemble.Ensemble()

// Use parallel executor.
ex := exec.NewParallel()
results := ex.Execute(n, algorithms)

// Output best result.
best := 0
for i, r := range results {
if r.Err != nil {
log.Fatal(r.Err)
}
if len(results[i].Program) < len(results[best].Program) {
best = i
}
}
r := results[best]
fmt.Printf("best: %d\n", len(r.Program))
fmt.Printf("algorithm: %s\n", r.Algorithm)

// Output:
// best: 266
// algorithm: opt(runs(continued_fractions(dichotomic)))
}```

## Algorithms

This section summarizes the algorithms implemented by `addchain` along with references to primary literature. See the bibliography for the complete references list.

### Binary

The `alg/binary` package implements the addition chain equivalent of the basic square-and-multiply exponentiation method. It is included for completeness, but is almost always outperformed by more advanced algorithms below.

### Continued Fractions

The `alg/contfrac` package implements the continued fractions methods for addition sequence search introduced by Bergeron-Berstel-Brlek-Duboc in 1989 and later extended. This approach utilizes a decomposition of an addition chain akin to continued fractions, namely

``````(1,..., k,..., n) = (1,...,n mod k,..., k) ⊗ (1,..., n/k) ⊕ (n mod k).
``````

for certain special operators ⊗ and ⊕. This decomposition lends itself to a recursive algorithm for efficient addition sequence search, with results dependent on the strategy for choosing the auxillary integer k. The `alg/contfrac` package provides a laundry list of strategies from the literature: binary, co-binary, dichotomic, dyadic, fermat, square-root and total.

### Bos-Coster Heuristics

Bos and Coster described an iterative algorithm for efficient addition sequence generation in which at each step a heuristic proposes new numbers for the sequence in such a way that the maximum number always decreases. The original Bos-Coster paper defined four heuristics: Approximation, Divison, Halving and Lucas. Package `alg/heuristic` implements a variation on these heuristics:

• Approximation: looks for two elements a, b in the current sequence with sum close to the largest element.
• Halving: applies when the target is at least twice as big as the next largest, and if so it will propose adding a sequence of doublings.
• Delta Largest: proposes adding the delta between the largest two entries in the current sequence.

Divison and Lucas are not implemented due to disparities in the literature about their precise definition and poor results from early experiments. Furthermore, this library does not apply weights to the heuristics as suggested in the paper, rather it simply uses the first that applies. However both of these remain possible avenues for improvement.

### Dictionary

Dictionary methods decompose the binary representation of a target integer n into a set of dictionary terms, such that n may be written as a sum

```n = ∑ 2ei di
```

for exponents e and elements d from a dictionary D. Given such a decomposition we can construct an addition chain for n by

1. Find a short addition sequence containing every element of the dictionary D. Continued fractions and Bos-Coster heuristics can be used here.
2. Build n from the dictionary terms according to the sum decomposition.

The efficiency of this approach boils down to the decomposition method. The `alg/dict` package provides:

• Fixed Window: binary representation of n is broken into fixed k-bit windows
• Sliding Window: break n into k-bit windows, skipping zeros where possible
• Run Length: decompose n into runs of 1s up to a maximal length
• Hybrid: mix of sliding window and run length methods

### Runs

The runs algorithm is a custom variant of the dictionary approach that decomposes a target into runs of ones. It leverages the observation that building a dictionary consisting of runs of 1s of lengths `l1, l2, ..., lk` can itself be reduced to:

1. Find an addition sequence containing the run lengths `li`. As with dictionary approaches we can use Bos-Coster heuristics and continued fractions here. However here we have the advantage that the `li` are typically very small, meaning that a wider range of algorithms can be brought to bear.
2. Use the addition sequence for the run lengths `li` to build an addition sequence for the runs themselves `r(li)` where `r(e) = 2e-1`. See `dict.RunsChain`.

This approach has proved highly effective against cryptographic exponents which frequently exhibit binary structure, such as those derived from Solinas primes.

I have not seen this method discussed in the literature. Please help me find references to prior art if you know any.

### Optimization

Close inspection of addition chains produced by other algorithms revealed cases of redundant computation. This motivated a final optimization pass over addition chains to remove unecessary steps. The `alg/opt` package implements the following optimization:

1. Determine all possible ways each element can be computed from those prior.
2. Count how many times each element is used where it is the only possible way of computing that entry.
3. Prune elements that are always used in computations that have an alternative.

These micro-optimizations were vital in closing the gap between `addchain`'s automated approaches and hand-optimized chains. This technique is reminiscent of basic passes in optimizing compilers, raising the question of whether other compiler optimizations could apply to addition chains?

I have not seen this method discussed in the literature. Please help me find references to prior art if you know any.

## Citing

If you use `addchain` in your research a citation would be appreciated. Citing a specific release is preferred, since they are archived on Zenodo and assigned a DOI. Please use the following BibTeX to cite the most recent 0.2.0 release.

```@misc{addchain,
author       = {Michael B. McLoughlin},
year         = 2021,
month        = apr,
version      = {0.2.0},
doi          = {10.5281/zenodo.4662389},
url          = {https://doi.org/10.5281/zenodo.4662389},
}```

If you need to cite a currently unreleased version please consider filing an issue to request a new release, or to discuss an appropriate format for the citation.

## Thanks

Thank you to Tom Dean, Riad Wahby, Brian Smith and str4d for advice and encouragement. Thanks also to Damian Gryski and Martin Glancy for review.

## Contributing

Contributions to `addchain` are welcome:

`addchain` is available under the BSD 3-Clause License.

• #### alg: extremely slow for 3000+ bit exponents

`addchain search '2^3072 - 1103717'` takes an extremely long time, and in fact appears to get stuck.

See https://gophers.slack.com/archives/C6WDZJ70S/p1614191115020200 for more context.

perf
opened by mmcloughlin 10
• #### all: generate citable release

Build releases of `addchain` with DOIs from Zenodo.

https://github.com/goreleaser/goreleaser https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/referencing-and-citing-content

opened by mmcloughlin 4
• #### all: look for unused code

Since this was extracted from a larger project, it's likely some of the library code isn't needed. Perhaps use `staticcheck`'s `unused` check to look for anything that could be removed.

Related #6

cleanup
opened by mmcloughlin 3
• #### results: NIST P-256 Field Inversion is out of date

Playing around, I ran NIST P-256 Field Inversion. The results page (and readme) says the best known cost is 266:

But a local run of addchain says 264. :)

``````addchain: expr: "2^256-2^224+2^192+2^96-1"
_10       = 2*1
_11       = 1 + _10
_1100     = _11 << 2
_1111     = _11 + _1100
_11110000 = _1111 << 4
_11111111 = _1111 + _11110000
x16       = _11111111 << 8 + _11111111
x32       = x16 << 16 + x16
i230      = ((x32 << 32 + 1) << 128 + x32) << 32
return      (x32 + i230) << 32 + x32
``````

I wonder how many other "best knowns" you've now bested. :)

opened by josharian 2
• #### cmd/addchain: search options to modify costs

Doubling (squaring) is typically cheaper than addition (multiplication). This PR adds options to the search subcommand to score addition chains based on a weighted sum.

opened by mmcloughlin 2
• #### alg: include weighted costs in the optimization function

addchain optimizes for the minimum of M+S. However, this assumes the cost of M and S are equal.

It's common the rate S/M varies depending on the field.

``````| Example | S=M | S=0.8M | S=0.6 M |
| 3M + 2S | 5.0 |  5.6   |  4.2    |
| 4M + 1S | 5.0 |  4.8   |  4.6    |
``````

The feature request is allow the user to specify the cost of operations.

duplicate
opened by armfazh 2
• #### code generation support

People use this package for code generation support #93. The current output is intended to be as concise as possible, similar to the descriptions in The Most Efficient Known Addition Chains for Field Element & Scalar Inversion for the Most Popular & Most Unpopular Elliptic Curves. However this format can be a little annoying to convert into code. See for example the `decoding_AddChains.py` script.

The `ec3` project has code to output a Go program for a given addition chain:

https://github.com/mmcloughlin/ec3/blob/3948e750fa5e745b6f160c22d5b8fab3dc6436e7/gen/fp/api.go#L219-L282

Providing access to this logic would be helpful. Specifically, temporary variable allocation and the sequence of `add`, `double` and `shift` operations.

enhancement
opened by mmcloughlin 2
• #### addchain/acc: prettify ast building and printing

The chain we find for the curve25519 scalar is converted to the following `acc` script.

``````_10 = 2*1
_11 = 1 + _10
_100 = 1 + _11
_1000 = 2*_100
_1010 = _10 + _1000
_1011 = 1 + _1010
_10000 = 2*_1000
_10110 = 2*_1011
_100000 = _1010 + _10110
_100110 = _10000 + _10110
_1000000 = 2*_100000
_1010000 = _10000 + _1000000
_1010011 = _11 + _1010000
_1100011 = _10000 + _1010011
_1100111 = _100 + _1100011
_1101011 = _100 + _1100111
_10010011 = _1000000 + _1010011
_10010111 = _100 + _10010011
_10111101 = _100110 + _10010111
_11010011 = _10110 + _10111101
_11100111 = _1010000 + _10010111
_11101011 = _100 + _11100111
_11110101 = _1010 + _11101011
((((((((((((((_1011 + _11110101) << 126 + _1010011) << 9 + _10 + _11110101) << 7 + _1100111) << 9 + _11110101) << 11 + _10111101) << 8 + _11100111) << 9 + _1101011) << 6 + _1011) << 14 + _10010011) << 10 + _1100011) << 9 + _10010111) << 10 + _11110101) << 8 + _11010011) << 8 + _11101011
``````

The last expression is ugly. Change the generation so this looks more intelligible.

opened by mmcloughlin 2
• #### acc/pass: handle missing allocations

The variable allocator currently raises an assertion error if it encounters a write to an operand without an allocation. Allocation is done like liveness analysis: the instructions are processed in reverse and variables are allocated as they are read, freed when they're written to. Therefore this assertion would trigger if the operand is never read after it's written.

Therefore, this would only happen for a redundant chain which computes a value it never needs. Clearly this isn't ideal, but it does seem to happen for some algorithms. It seems best that the allocator doesn't crash in these cases.

This PR updates the allocation logic to handle this case. In practice if this happens the variable would be immediately allocated and freed.

opened by mmcloughlin 1
• #### meta: extract from internal

We intend to expose package metadata through the code generation interface #94. If so, it seems wrong to publically expose an internal package. Instead of duplicating data we'll just make it public instead.

opened by mmcloughlin 1
• #### script: use go install for goreleaser

The existing install script appears to be broken on Mac:

``````m1:addchain mbm\$ ./script/bootstrap
...
+ curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh
+ sh -s -- -b /Users/mbm/Development/gopath/bin v0.177.0
goreleaser/goreleaser crit uname_arch_check 'arm64' got converted to 'all' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib
``````

This install method was also recently removed from the goreleaser documentation:

https://github.com/goreleaser/goreleaser/commit/7ddcfb08464583ef0432b2170b0184f5a57ea1a8

opened by mmcloughlin 1
• #### alg: prune chains with unused intermediate values

When testing changes to the allocator related to #129 I was running the allocator on chains randomly generated by `alg/rand.SolverGenerator`. This test triggered an assertion in the allocator:

Allocation is done like liveness analysis: the instructions are processed in reverse and variables are allocated as they are read, freed when they're written to. Therefore this assertion would trigger if the operand is never read after it's written.

It appears this is actually what's happening. See the example:

Example
``````    exec_test.go:21: pre:
[1] ← 2 * [0]
[2] ← 2 * [1]
[3] ← [0] + [2]
[4] ← [0] + [3] 		exec_test.go:29: assertion failure: output operand 4 missing allocation
[5] ← [2] + [3]
[6] ← [2] + [5]
[7] ← [1] + [6]
[8] ← [2] + [7]
[9] ← [1] + [8]
[10] ← [1] + [9]
[11] ← [2] + [10]
[12] ← [1] + [11]
[13] ← [1] + [12]
[14] ← [8] + [12]
[16] ← [14] ≪ 2
[17] ← [3] + [16]
[25] ← [17] ≪ 8
[26] ← [5] + [25]
[35] ← [26] ≪ 9
[36] ← [13] + [35]
[40] ← [36] ≪ 4
[41] ← [7] + [40]
[47] ← [41] ≪ 6
[48] ← [9] + [47]
[53] ← [48] ≪ 5
[54] ← [13] + [53]
[62] ← [54] ≪ 8
[63] ← [9] + [62]
[67] ← [63] ≪ 4
[68] ← [6] + [67]
[74] ← [68] ≪ 6
[75] ← [13] + [74]
[80] ← [75] ≪ 5
[81] ← [8] + [80]
[86] ← [81] ≪ 5
[87] ← [11] + [86]
[95] ← [87] ≪ 8
[96] ← [10] + [95]
[102] ← [96] ≪ 6
[103] ← [10] + [102]
[107] ← [103] ≪ 4
[108] ← [6] + [107]
[116] ← [108] ≪ 8
[117] ← [5] + [116]
[121] ← [117] ≪ 4
[122] ← [3] + [121]
[129] ← [122] ≪ 7
[130] ← [13] + [129]
[138] ← [130] ≪ 8
[139] ← [3] + [138]
[146] ← [139] ≪ 7
[147] ← [8] + [146]
[153] ← [147] ≪ 6
[154] ← [9] + [153]
[161] ← [154] ≪ 7
[162] ← [12] + [161]
[169] ← [162] ≪ 7
[170] ← [12] + [169]
[181] ← [170] ≪ 11
[182] ← [11] + [181]
[183] ← 2 * [182]
[184] ← [0] + [183]
[191] ← [184] ≪ 7
[192] ← [0] + [191]
``````

It seems there's either a bug somewhere, perhaps in the conversion from `addchain.Program` to `ir.Program`, or this is a trivial optimization pass that could be added to `alg/opt`.

opened by mmcloughlin 1
• #### cmd/addchain,internal/gen: allow input parameters

Initial code generation support #94 #127 has landed but has some limitations, the most notable of which is that variable names are hard-coded:

At a minimum these should be configurable.

It may also make sense to support additional input parameters that are passed on to the template. For example, in the code generation case you might want things like the function name or package name to be configurable so that one template could be shared.

enhancement
opened by mmcloughlin 0
• #### cost models

Currently, addition chains are chosen with basic cost models: typically just length, but also #120 allowed weightings for add/double.

It could be good to support more complex cost functions. For example #54 suggested number of live variables, or estimate of pipeline stalls.

At the moment this issue is just a placeholder to record the idea and invite discussion.

idea low
opened by mmcloughlin 0
• #### alg: implement Itoh-Tsujii algorithm

This algorithm is by far the most used for small addition chains. It is formulated for binary fields, but the core idea can be adapted to work in prime fields,

https://doi.org/10.1016/0890-5401(88)90024-7

-- didn't check if this algorithm is already covered

idea
opened by armfazh 1
• #### code: better variable names

it is noisy to get variables named in binary `_100` or `_101`. Better to follow a standard rule, such as `i4` or any other counter in decimal or hex. or at least, allow the user to specify the format desired.

opened by armfazh 2
• #### alg/dict: SlidingWindow: max zeroes and shortening

As discussed in #56. I finally got around to submitting these improvements.

This change adds new variations of SlidingWindow and modifies Hybrid to use an arbitrary Decomposer after runs are removed. SlidingWindowRTL and SlidingWindowShortRTL construct the windows from least to most significant bit (right-to-left) instead of the default left-to-right approach. SlidingWindowShort and SlidingWindowShortRTL incorporate a "shortening" heuristic that sometimes cuts windows short. The Z parameter restricts the maximum number of zeroes that may appear in a window. If a window is maximum length, contains at least one zero, and the bit following the window is a one, then the window is shortened in order to yield all trailing ones to the next window.

This new behavior was inspired by the windowing technique used for the upper half of smooth isogeny primes in [isogenychains]. This update also adds p512-2 from [isogenychains] to the result set.

Improvements with the new Ensemble:

• p256_scalar improved from +2 to -1
• p384_scalar improved from +1 to +0
• isop512_field (new) is -3
• p2519_field improved from 263 to 261

Notably, the isop512_field results are better than [isogenychains] when using their weighting metric (square = 0.8 * multiply).

opened by Nik-U 1
##### Releases(v0.4.0)
###### The goal of Binance Smart Chain is to bring programmability and interoperability to Binance Chain

Binance Smart Chain The goal of Binance Smart Chain is to bring programmability

0 Dec 31, 2021
###### XT Smart Chain, a chain based on the go-ethereum fork

XT Smart Chain XT Smart Chain (XSC) is a decentralized, high-efficiency and ener

4 May 17, 2022
###### Berylbit PoW chain using Ethash, EPI-Burn and geth. The chain will be using bot congestion flashbot bundles through nodes

Berylbit PoW chain using Ethash, EPI-Burn and geth. The chain will be using bot congestion flashbot bundles through nodes. Soon, We will work towards

8 Mar 22, 2022
###### Go-chain - EVM-compatible chain secured by the Lachesis consensus algorithm

ICICB galaxy EVM-compatible chain secured by the Lachesis consensus algorithm. B

4 Jun 8, 2022
###### Go implementation of BLAKE2 (b) cryptographic hash function (optimized for 64-bit platforms).

Go implementation of BLAKE2b collision-resistant cryptographic hash function created by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, an

89 May 1, 2022
###### Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

1.5k Jun 30, 2022
###### whirlpool cryptographic hashing library

whirlpool.go A whirlpool hashing library for go Build status Setup \$ go get github.com/jzelinskie/whirlpool Example package main import ( "fmt" "

21 Jan 18, 2022
###### Pure Go GOST cryptographic functions library.

Pure Go GOST cryptographic functions library. GOST is GOvernment STandard of Russian Federation (and Soviet Union). GOST 28147-89 (RFC 5830) block cip

3 Apr 5, 2022
###### goKryptor is a small and portable cryptographic tool for encrypting and decrypting files.

goKryptor goKryptor is a small and portable cryptographic tool for encrypting and decrypting files. This tool supports XOR and AES-CTR (Advanced Encry

0 Dec 6, 2021
###### An out-of-the-box cryptographic message communication.

An out-of-the-box cryptographic message communication.

0 Feb 8, 2022
###### Huobi Eco Chain client based on the go-ethereum fork

The Huobi Open Platform is a unified infrastructure platform based on the technical, traffic and ecological resources of the Huobi Group, and will be gradually open to the blockchain industry.

231 Jun 10, 2022
###### Moeing chain is an EVM&Web3 compatible sidechain for Bitcoin Cash

Full node client of smartBCH This repository contains the code of the full node client of smartBCH, an EVM&Web3 compatible sidechain for Bitcoin Cash.

111 Jun 19, 2022
###### Yet another Binance Smart Chain client based on TrustFi Network

TrustFi Smart Chain The goal of TrustFi Smart Chain is to bring programmability and interoperability to Binance Chain. In order to embrace the existin

19 Mar 27, 2021
###### A Binance Chain vanity address generator written in golang.

VaniBNB A Binance Chain vanity address generator written in golang. For example address ending with 0xkat Raw https://github.com/makevoid/vanieth http

6 Apr 21, 2022
###### Frontier Chain is a blockchain application built using Cosmos SDK and Tendermint.

Frontier Chain Frontier Chain is a blockchain application built using Cosmos SDK and Tendermint. Setup Initialize the blockchain with one validator no

12 Jun 15, 2022
###### Dump BitClout chain data into MongoDB

mongodb-dumper mongodb-dumper runs a full BitClout node and dumps the chain data into a MongoDB database Build Running the following commands will cre

16 May 17, 2022
###### A phoenix Chain client based on the go-ethereum fork,the new PoA consensus engine is based on the VRF algorithm.

Phoenix Official Golang implementation of the Phoenix protocol. !!!The current version is for testing and developing purposes only!!! Building the sou

14 Apr 28, 2022