Good morning. It is EOSeoul.
This article is about sharing our understanding of the Patroneos and our recommendations on 1) how Patroneos works, 2) Benchmark results, and 3) Operation and Implementation recommendations. We assume a reader has a sufficient knowledge and understanding of simple / advanced Patroneos settings.
All descriptions are based on the commits below;
6501e4429f43f4de78444a227149773b914e221bvalidateMaxTransactions added48422fa05b47373ad68013f4d77d290e7fc31aaeBlock.one released one of its own software, Patroneos, with the release of EOSIO Dawn 4.2. The name is derived from the Harry Potter novel. Patronus is the name of the spell to defeat a creature called Dementor. Patroneos borrowed the name from this spell.
Many people around the EOS have expressed concerns over a variety attempts to attacks on the EOS mainnet. Communities and Block.one have improved EOSIO software by projecting different attack scenarios. Patroneos is one of the final products of these efforts. It filters out attacks by the basic form of Denial of Service and passes only normal transactions to the EOS RPC API Endpoint.
The code consists of three main files. main.go,filter.go, and fail2ban-relay.go.
main.gomain.go implements the following functions.
Config definitionmain function
The program is implemented to operate in Filter mode (filter) or Relay mode (fail2ban-relay). Let's take a look at each.
filter.gofilter.go is a file that implements Filter mode.
TRANSACTION_FAILED error is generated and the connection is terminated./patroneos/fail2ban-relay message as a HTTP request to Patroneus, running in Filter mode, leaves a log and terminates the connection.validateJSON(): It verifies the validity of the JSON received as the body of the HTTP POST. If it fails, INVALID_JSON error occurs and it is filtered.validateMaxTransactions() : It verifies whether the number of transactions in a JSON array is less than the maximum value. When it is more than the maximun number, TOO_MANY_TRANSACTIONS error occurs and is filtered.validateTransactionSize(): It verifies whether the number of signatures in the transaction is less than the maximum value. When it is more than the maximum number, INVALID_NUMBER_SIGNATURES error occurs and is filtered.validateMaxSignatures(): It verifies whether the transaction is a blacklisted contract action. If it fails, BLACKLISTED_CONTRACT error is generated and filtered.validateContract(): It verifies whether the size of the transaction is less than the maximum value. If it fails, INVALID_TRANSACTION_SIZE error occurs and it is filtered.validateTransactionSize(), validateMaxSignatures() and validateContract() assume that the JSON of the HTTP Request Body is an Object. However, push_transactions of the HTTP Chain API uses a JSON Array and it is treated as PARSING_ERROR. We reported this on Patroneos Issue # 26. If the issue is resolved before this post becomes unchangeable, this port will be updated.fail2ban-relay.gofail2ban-relay.go is a file implemented in the Relay mode.
/ patroneos / fail2ban-relay in order for fail2ban to scan.Patroneos is written in Go, the programming language Google created in 2009. Go provides the Goroutine as an asynchronous mechanism. The routine is lightweight threads managed by the Go runtime. When you call a function with the keyword "go", the runtime executes the function concurrently in a time-division manner in the same memory address space.
Go program can be processed in parallel with a plurality of CPUs or cores. With the runtime.GOMAXPROCS() function, you can determine the number of logical cores it can use. Go has been changed to use all of the logical cores on machines since version 1.5. Therefore, the call functions are then processed in parallel on a multicore machine.
As of 1st June, 2018, it will typically install ‘Go 1.10.2’. The Ubuntu 18.04 LTS and macOS High Sierra 10.13.4 will install golang through apt and brew, respectively, and 1.10.2 will be installed. CentOS 7.5 will install the version 1.9.4.
Consequently using Goroutine with the recently released version of Go, the process can run automatically in parallel using the multicore machine.
ServMux &ListenAndServeUnder the Serve() function in http, after the connection is accepted, and the new connection is handled by goroutine in serve. It can be seen that the part receiving the HTTP request is processed in parallel using multicore.
ListenAndServe uses the default http.Server without a timeout. At the time of analysis, Patroneos uses Server without timeout setting. In case of when there is no appropriate timeout at the point where the HTTP requests are sent to the client or the Patroneos, the latter will wait indefinitely when no data is received after making an HTTP connection.
Therefore, we recommend the implementation of an appropriate architecture so that 1) Patroneos do not receive the client's request directly, and 2) Patroneos receive an HTTP request with a timeout.
The 5 validations are not called via "go" keyword(goroutine) and are processed in series. In fact, the validation is not processed in parallel at all. The validation logic so far is rather simple that it does not need to be processed in parallel.
As ServeMux.HandleFunc is executed after receiving all the HTTP body from the client, the validation logic does not have a timeout issue.
However, when using a relay, timeout may occur, but it seems that there is no big problem as described below.
ClientIn many documents, Go's HTTP Client has been confirmed it is safe to use concurrency with the goroutine.
HTTP Client has no timeout unless there is specific timeout settings. At the time of this analysis, Patroneos uses default HTTP Client to send Filter result to Relay Patroneos, and to replay Request to API Endpoint. In both cases, Timeout is not set.
If there is not an appropriate timeout in the API Endpoint that passes the verified request among the HTTP requests received by Patroneos, it will wait indefinitely until it receives a response.
Therefore, it is recommended that an API endpoint should set the appropriate timeout.
Let Patroneos in Relay mode as RP and Patronos in Filter mode as FP. If FP does not use RP, there are no issues. Let's assume that you let the FP to use the RP. If the RP is off or responds normally, there is no issue. As the logic of the RP is so simple, normal cases excluding the insufficient file descriptor or the delayed processing, it is very rare that the response of the RP is delayed. Moreover, when the ports used by the RP with the indefinite response TCP / HTTP server, that would be a big problem, but this case also is very rare. Therefore, when FP uses RP, the issue related to timeout is expected to be very rare.
The focus was on identifying the processing capacity of Patroneos itself. So we configured simple HTTP request and API endpoint. This benchmark can be understood as a laboratory benchmark.
Tests use two JSON of different size, two HTTP request concurrency in 100 or 1000. For understand what will happen when there is processing latency in API processing, tests use two latency setting, 0 ms or 100 ms.
Below is a test configuration for the benchmark. In the production environment, settings must be changed on the different situation accordingly.
-cpus 1 option.{
"listenPort": "8081",
"nodeosProtocol": "http",
"nodeosUrl": "127.0.0.1",
"nodeosPort": "8000",
"contractBlackList": {
"currency": true
},
"maxSignatures": 10,
"maxTransactionSize": 1000000,
"logEndpoints": ["http://127.0.0.1:8080"],
"filterEndpoints": [],
"logFileLocation": "./fail2ban.log"
}
{
"listenPort": "8080",
"nodeosProtocol": "http",
"nodeosUrl": "127.0.0.1",
"nodeosPort": "8000",
"contractBlackList": {
"currency": true
},
"maxSignatures": 10,
"maxTransactionSize": 1000000,
"logEndpoints": [],
"filterEndpoints": ["http://127.0.0.1:8081"],
"logFileLocation": "./fail2ban.log"
}
runtime.GOMAXPROCS(1)package main
import (
"fmt"
"log"
"net/http"
"time"
"runtime"
"io/ioutil"
)
func main() {
runtime.GOMAXPROCS(1)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if len(r.FormValue("case-two")) > 0 {
fmt.Println("case two")
} else {
time.Sleep(time.Millisecond * 100)
b, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(b)
//fmt.Println("case one end")
}
})
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}
{"account": "initb", "permission": "init", "authorization" active "}]," data ":" 000000000041934b000000008041934be803000000000000 "}
{ "Id": "37df4598d37bb8fdbc440e31caae07906ac90fd3fd2cd060f2ca13e59e78781e", "signatures": [ "SIG_K1_K8ojKDxMnWy5Q3zAVQPwJANbEE2h9kStmPX4BorEGGQKCJXUYK62UiEYxGyQbaynraMX5WvzEFYaQqAf5Mdwu2yBf36HG7"], "compression": "none", "packed_context_free_data": "", "context_free_data": [], "packed_trx": "5f3b125b17c726f418ba000000000100a6823403ea3055000000572d3ccdcd010000000000ea305500000000a8ed32322e0000000000ea305590d5cc5865570da420a107000000000004454f53000000000d4a756e676c652046617563657400", " 0, "max_cpu_usage_ms": 0, "delay_sec": 0, "ref_block_num": 50967, "ref_block_prefix": 3122197542, "max_net_usage_words" "eosio.token", "name": "transfer", "authorization": [{"actor": "eosio", "permission": " "memo": "Jungle Faucet"}, "hex_data": "active"}, "data": {"from": "eosio" 0000000000ea305590d5cc5865570da420a1070000000004454f53000000000d4a756e676c6520466175636574 "}]," transaction_extensions ": [] }}
Test # 1
Test # 2
Test # 3
Test # 4
Test # 5
Test # 6
Test # 7
Test # 8
nodeosUrl setting, if possible, to minimize DNS resolving overheadpush_transactions of JSON arrays and reported this bug on Patroneos Issue # 26. You need to check the response of this issue. Before resolving, you should implement URI route bypass or reroute for HTTP requests which use push_transactions.push_transactions.access-control-allow-origin with * in nodeos config.
Patroneos Implementation Recommendations (to Patroneos Committer & Block.one)
Server andClientconfiguration.
Server:ReadTimeout, ReadHeaderTimeout,WriteTimeoutClientTransport for HTTP ClientTransport: MaxIdleConnsPerHost, MaxIdleConns, IdleConnTimeout, ResponseHeaderTimeout, net.Dialer.Timeoutnet/http implementation recommendationsConclusion
push_transactions in the architecture.net/http docs and source codes
net/http implementation recommendations
Suggestions and questions are always welcome. Please do not hesitate to give a feedback to EOSeoul. Join the Telegram Group below to share the latest news from EOSeoul and technical discussions about EOS.
Thank you!
EOSeoul
Telegram (English) : http://t.me/eoseoul_en
Telegram (简体中文) : http://t.me/eoseoul_cn
Telegram (日本語) : http://t.me/eoseoul_jp
Telegram (General Talk, 한국어) : https://t.me/eoseoul
Telegram (Developer Talk, 한국어) : https://t.me/eoseoul_testnet
Steemit : https://steemit.com/@eoseoul
Github : https://github.com/eoseoul
Twitter : https://twitter.com/eoseoul_kor
Facebook : https://www.facebook.com/EOSeoul.kr
Wechat account: neoply
EOSeoul Documentations : https://github.com/eoseoul/docs