mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Merge branch 'master' into swarm-rather-stable
Conflicts were in:
swarm/api/http/test_server.go
swarm/storage/ldbstore_test.go
from 26b50e3ebe
This commit is contained in:
commit
432349a93a
15 changed files with 577 additions and 95 deletions
File diff suppressed because one or more lines are too long
273
cmd/clef/bindata.go
Normal file
273
cmd/clef/bindata.go
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -18,6 +18,9 @@
|
||||||
// arbitrary data.
|
// arbitrary data.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
//go:generate go-bindata -o bindata.go resources/4byte.json
|
||||||
|
//go:generate gofmt -s -w bindata.go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -57,14 +60,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const legalWarning = `
|
const legalWarning = `
|
||||||
WARNING!
|
WARNING!
|
||||||
|
|
||||||
Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there
|
Clef is an account management tool. It may, like any software, contain bugs.
|
||||||
are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software
|
|
||||||
unless you agree to take full responsibility for doing so, and know what you are doing.
|
|
||||||
|
|
||||||
TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE!
|
Please take care to
|
||||||
|
- backup your keystore files,
|
||||||
|
- verify that the keystore(s) can be opened with your password.
|
||||||
|
|
||||||
|
Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. See the GNU General Public License for more details.
|
||||||
`
|
`
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -101,11 +107,6 @@ var (
|
||||||
Name: "signersecret",
|
Name: "signersecret",
|
||||||
Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash",
|
Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash",
|
||||||
}
|
}
|
||||||
dBFlag = cli.StringFlag{
|
|
||||||
Name: "4bytedb",
|
|
||||||
Usage: "File containing 4byte-identifiers",
|
|
||||||
Value: "./4byte.json",
|
|
||||||
}
|
|
||||||
customDBFlag = cli.StringFlag{
|
customDBFlag = cli.StringFlag{
|
||||||
Name: "4bytedb-custom",
|
Name: "4bytedb-custom",
|
||||||
Usage: "File used for writing new 4byte-identifiers submitted via API",
|
Usage: "File used for writing new 4byte-identifiers submitted via API",
|
||||||
|
|
@ -142,7 +143,7 @@ var (
|
||||||
configdirFlag,
|
configdirFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
Description: `
|
||||||
The init command generates a master seed which Clef can use to store credentials and data needed for
|
The init command generates a master seed which Clef can use to store credentials and data needed for
|
||||||
the rule-engine to work.`,
|
the rule-engine to work.`,
|
||||||
}
|
}
|
||||||
attestCommand = cli.Command{
|
attestCommand = cli.Command{
|
||||||
|
|
@ -156,10 +157,10 @@ the rule-engine to work.`,
|
||||||
signerSecretFlag,
|
signerSecretFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
Description: `
|
||||||
The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
|
The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
|
||||||
incoming requests.
|
incoming requests.
|
||||||
|
|
||||||
Whenever you make an edit to the rule file, you need to use attestation to tell
|
Whenever you make an edit to the rule file, you need to use attestation to tell
|
||||||
Clef that the file is 'safe' to execute.`,
|
Clef that the file is 'safe' to execute.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +175,7 @@ Clef that the file is 'safe' to execute.`,
|
||||||
signerSecretFlag,
|
signerSecretFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
Description: `
|
||||||
The setpw command stores a password for a given address (keyfile). If you enter a blank passphrase, it will
|
The setpw command stores a password for a given address (keyfile). If you enter a blank passphrase, it will
|
||||||
remove any stored credential for that address (keyfile)
|
remove any stored credential for that address (keyfile)
|
||||||
`}
|
`}
|
||||||
gendocCommand = cli.Command{
|
gendocCommand = cli.Command{
|
||||||
|
|
@ -203,7 +204,6 @@ func init() {
|
||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
rpcPortFlag,
|
rpcPortFlag,
|
||||||
signerSecretFlag,
|
signerSecretFlag,
|
||||||
dBFlag,
|
|
||||||
customDBFlag,
|
customDBFlag,
|
||||||
auditLogFlag,
|
auditLogFlag,
|
||||||
ruleFlag,
|
ruleFlag,
|
||||||
|
|
@ -270,12 +270,12 @@ func initializeSecrets(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
fmt.Printf("A master seed has been generated into %s\n", location)
|
fmt.Printf("A master seed has been generated into %s\n", location)
|
||||||
fmt.Printf(`
|
fmt.Printf(`
|
||||||
This is required to be able to store credentials, such as :
|
This is required to be able to store credentials, such as :
|
||||||
* Passwords for keystores (used by rule engine)
|
* Passwords for keystores (used by rule engine)
|
||||||
* Storage for javascript rules
|
* Storage for javascript rules
|
||||||
* Hash of rule-file
|
* Hash of rule-file
|
||||||
|
|
||||||
You should treat that file with utmost secrecy, and make a backup of it.
|
You should treat that file with utmost secrecy, and make a backup of it.
|
||||||
NOTE: This file does not contain your accounts. Those need to be backed up separately!
|
NOTE: This file does not contain your accounts. Those need to be backed up separately!
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
|
@ -362,13 +362,17 @@ func signer(c *cli.Context) error {
|
||||||
log.Info("Using CLI as UI-channel")
|
log.Info("Using CLI as UI-channel")
|
||||||
ui = core.NewCommandlineUI()
|
ui = core.NewCommandlineUI()
|
||||||
}
|
}
|
||||||
fourByteDb := c.GlobalString(dBFlag.Name)
|
// 4bytedb data
|
||||||
fourByteLocal := c.GlobalString(customDBFlag.Name)
|
fourByteLocal := c.GlobalString(customDBFlag.Name)
|
||||||
db, err := core.NewAbiDBFromFiles(fourByteDb, fourByteLocal)
|
data, err := Asset("resources/4byte.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf(err.Error())
|
utils.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
log.Info("Loaded 4byte db", "signatures", db.Size(), "file", fourByteDb, "local", fourByteLocal)
|
db, err := core.NewAbiDBFromFiles(data, fourByteLocal)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
log.Info("Loaded 4byte db", "signatures", db.Size(), "local", fourByteLocal)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api core.ExternalAPI
|
api core.ExternalAPI
|
||||||
|
|
|
||||||
1
cmd/clef/resources/4byte.json
Normal file
1
cmd/clef/resources/4byte.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -52,11 +52,12 @@ func TestACT(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
initCluster(t)
|
cluster := newTestCluster(t, clusterSize)
|
||||||
|
defer cluster.Shutdown()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
f func(t *testing.T)
|
f func(t *testing.T, cluster *testCluster)
|
||||||
}{
|
}{
|
||||||
{"Password", testPassword},
|
{"Password", testPassword},
|
||||||
{"PK", testPK},
|
{"PK", testPK},
|
||||||
|
|
@ -65,7 +66,9 @@ func TestACT(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, tc.f)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.f(t, cluster)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +77,7 @@ func TestACT(t *testing.T) {
|
||||||
// The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded
|
// The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded
|
||||||
// is then fetched through 2nd node. since the tested code is not key-aware - we can just
|
// is then fetched through 2nd node. since the tested code is not key-aware - we can just
|
||||||
// fetch from the 2nd node using HTTP BasicAuth
|
// fetch from the 2nd node using HTTP BasicAuth
|
||||||
func testPassword(t *testing.T) {
|
func testPassword(t *testing.T, cluster *testCluster) {
|
||||||
dataFilename := testutil.TempFileWithContent(t, data)
|
dataFilename := testutil.TempFileWithContent(t, data)
|
||||||
defer os.RemoveAll(dataFilename)
|
defer os.RemoveAll(dataFilename)
|
||||||
|
|
||||||
|
|
@ -226,7 +229,7 @@ func testPassword(t *testing.T) {
|
||||||
// The parties participating - node (publisher), uploads to second node (which is also the grantee) then disappears.
|
// The parties participating - node (publisher), uploads to second node (which is also the grantee) then disappears.
|
||||||
// Content which was uploaded is then fetched through the grantee's http proxy. Since the tested code is private-key aware,
|
// Content which was uploaded is then fetched through the grantee's http proxy. Since the tested code is private-key aware,
|
||||||
// the test will fail if the proxy's given private key is not granted on the ACT.
|
// the test will fail if the proxy's given private key is not granted on the ACT.
|
||||||
func testPK(t *testing.T) {
|
func testPK(t *testing.T, cluster *testCluster) {
|
||||||
dataFilename := testutil.TempFileWithContent(t, data)
|
dataFilename := testutil.TempFileWithContent(t, data)
|
||||||
defer os.RemoveAll(dataFilename)
|
defer os.RemoveAll(dataFilename)
|
||||||
|
|
||||||
|
|
@ -359,13 +362,13 @@ func testPK(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// testACTWithoutBogus tests the creation of the ACT manifest end-to-end, without any bogus entries (i.e. default scenario = 3 nodes 1 unauthorized)
|
// testACTWithoutBogus tests the creation of the ACT manifest end-to-end, without any bogus entries (i.e. default scenario = 3 nodes 1 unauthorized)
|
||||||
func testACTWithoutBogus(t *testing.T) {
|
func testACTWithoutBogus(t *testing.T, cluster *testCluster) {
|
||||||
testACT(t, 0)
|
testACT(t, cluster, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testACTWithBogus tests the creation of the ACT manifest end-to-end, with 100 bogus entries (i.e. 100 EC keys + default scenario = 3 nodes 1 unauthorized = 103 keys in the ACT manifest)
|
// testACTWithBogus tests the creation of the ACT manifest end-to-end, with 100 bogus entries (i.e. 100 EC keys + default scenario = 3 nodes 1 unauthorized = 103 keys in the ACT manifest)
|
||||||
func testACTWithBogus(t *testing.T) {
|
func testACTWithBogus(t *testing.T, cluster *testCluster) {
|
||||||
testACT(t, 100)
|
testACT(t, cluster, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
|
// testACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
|
||||||
|
|
@ -373,7 +376,7 @@ func testACTWithBogus(t *testing.T) {
|
||||||
// set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
|
// set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
|
||||||
// the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
|
// the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
|
||||||
// the publisher uploads through one of the nodes then disappears.
|
// the publisher uploads through one of the nodes then disappears.
|
||||||
func testACT(t *testing.T, bogusEntries int) {
|
func testACT(t *testing.T, cluster *testCluster, bogusEntries int) {
|
||||||
var uploadThroughNode = cluster.Nodes[0]
|
var uploadThroughNode = cluster.Nodes[0]
|
||||||
client := swarmapi.NewClient(uploadThroughNode.URL)
|
client := swarmapi.NewClient(uploadThroughNode.URL)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,15 +59,6 @@ func init() {
|
||||||
|
|
||||||
const clusterSize = 3
|
const clusterSize = 3
|
||||||
|
|
||||||
var clusteronce sync.Once
|
|
||||||
var cluster *testCluster
|
|
||||||
|
|
||||||
func initCluster(t *testing.T) {
|
|
||||||
clusteronce.Do(func() {
|
|
||||||
cluster = newTestCluster(t, clusterSize)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverFunc(api *api.API) swarmhttp.TestServer {
|
func serverFunc(api *api.API) swarmhttp.TestServer {
|
||||||
return swarmhttp.NewServer(api, "")
|
return swarmhttp.NewServer(api, "")
|
||||||
}
|
}
|
||||||
|
|
@ -165,10 +156,8 @@ outer:
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testCluster) Shutdown() {
|
func (c *testCluster) Shutdown() {
|
||||||
for _, node := range c.Nodes {
|
c.Stop()
|
||||||
node.Shutdown()
|
c.Cleanup()
|
||||||
}
|
|
||||||
os.RemoveAll(c.TmpDir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testCluster) Stop() {
|
func (c *testCluster) Stop() {
|
||||||
|
|
@ -179,16 +168,35 @@ func (c *testCluster) Stop() {
|
||||||
|
|
||||||
func (c *testCluster) StartNewNodes(t *testing.T, size int) {
|
func (c *testCluster) StartNewNodes(t *testing.T, size int) {
|
||||||
c.Nodes = make([]*testNode, 0, size)
|
c.Nodes = make([]*testNode, 0, size)
|
||||||
|
|
||||||
|
errors := make(chan error, size)
|
||||||
|
nodes := make(chan *testNode, size)
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
|
go func(nodeIndex int) {
|
||||||
if err := os.Mkdir(dir, 0700); err != nil {
|
dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", nodeIndex))
|
||||||
t.Fatal(err)
|
if err := os.Mkdir(dir, 0700); err != nil {
|
||||||
|
errors <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node := newTestNode(t, dir)
|
||||||
|
node.Name = fmt.Sprintf("swarm%02d", nodeIndex)
|
||||||
|
nodes <- node
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
select {
|
||||||
|
case node := <-nodes:
|
||||||
|
c.Nodes = append(c.Nodes, node)
|
||||||
|
case err := <-errors:
|
||||||
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node := newTestNode(t, dir)
|
if t.Failed() {
|
||||||
node.Name = fmt.Sprintf("swarm%02d", i)
|
c.Shutdown()
|
||||||
|
t.FailNow()
|
||||||
c.Nodes = append(c.Nodes, node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,12 @@ func TestSwarmUp(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
initCluster(t)
|
cluster := newTestCluster(t, clusterSize)
|
||||||
|
defer cluster.Shutdown()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
f func(t *testing.T)
|
f func(t *testing.T, cluster *testCluster)
|
||||||
}{
|
}{
|
||||||
{"NoEncryption", testNoEncryption},
|
{"NoEncryption", testNoEncryption},
|
||||||
{"Encrypted", testEncrypted},
|
{"Encrypted", testEncrypted},
|
||||||
|
|
@ -60,31 +61,33 @@ func TestSwarmUp(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, tc.f)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.f(t, cluster)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testNoEncryption tests that running 'swarm up' makes the resulting file
|
// testNoEncryption tests that running 'swarm up' makes the resulting file
|
||||||
// available from all nodes via the HTTP API
|
// available from all nodes via the HTTP API
|
||||||
func testNoEncryption(t *testing.T) {
|
func testNoEncryption(t *testing.T, cluster *testCluster) {
|
||||||
testDefault(false, t)
|
testDefault(t, cluster, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testEncrypted tests that running 'swarm up --encrypted' makes the resulting file
|
// testEncrypted tests that running 'swarm up --encrypted' makes the resulting file
|
||||||
// available from all nodes via the HTTP API
|
// available from all nodes via the HTTP API
|
||||||
func testEncrypted(t *testing.T) {
|
func testEncrypted(t *testing.T, cluster *testCluster) {
|
||||||
testDefault(true, t)
|
testDefault(t, cluster, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRecursiveNoEncryption(t *testing.T) {
|
func testRecursiveNoEncryption(t *testing.T, cluster *testCluster) {
|
||||||
testRecursive(false, t)
|
testRecursive(t, cluster, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRecursiveEncrypted(t *testing.T) {
|
func testRecursiveEncrypted(t *testing.T, cluster *testCluster) {
|
||||||
testRecursive(true, t)
|
testRecursive(t, cluster, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDefault(toEncrypt bool, t *testing.T) {
|
func testDefault(t *testing.T, cluster *testCluster, toEncrypt bool) {
|
||||||
tmpFileName := testutil.TempFileWithContent(t, data)
|
tmpFileName := testutil.TempFileWithContent(t, data)
|
||||||
defer os.Remove(tmpFileName)
|
defer os.Remove(tmpFileName)
|
||||||
|
|
||||||
|
|
@ -189,7 +192,7 @@ func testDefault(toEncrypt bool, t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRecursive(toEncrypt bool, t *testing.T) {
|
func testRecursive(t *testing.T, cluster *testCluster, toEncrypt bool) {
|
||||||
tmpUploadDir, err := ioutil.TempDir("", "swarm-test")
|
tmpUploadDir, err := ioutil.TempDir("", "swarm-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -279,14 +282,14 @@ func testRecursive(toEncrypt bool, t *testing.T) {
|
||||||
|
|
||||||
// testDefaultPathAll tests swarm recursive upload with relative and absolute
|
// testDefaultPathAll tests swarm recursive upload with relative and absolute
|
||||||
// default paths and with encryption.
|
// default paths and with encryption.
|
||||||
func testDefaultPathAll(t *testing.T) {
|
func testDefaultPathAll(t *testing.T, cluster *testCluster) {
|
||||||
testDefaultPath(false, false, t)
|
testDefaultPath(t, cluster, false, false)
|
||||||
testDefaultPath(false, true, t)
|
testDefaultPath(t, cluster, false, true)
|
||||||
testDefaultPath(true, false, t)
|
testDefaultPath(t, cluster, true, false)
|
||||||
testDefaultPath(true, true, t)
|
testDefaultPath(t, cluster, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDefaultPath(toEncrypt bool, absDefaultPath bool, t *testing.T) {
|
func testDefaultPath(t *testing.T, cluster *testCluster, toEncrypt bool, absDefaultPath bool) {
|
||||||
tmp, err := ioutil.TempDir("", "swarm-defaultpath-test")
|
tmp, err := ioutil.TempDir("", "swarm-defaultpath-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type exp struct {
|
type exp struct {
|
||||||
|
|
@ -42,6 +43,7 @@ func Exp(r metrics.Registry) {
|
||||||
// http.HandleFunc("/debug/vars", e.expHandler)
|
// http.HandleFunc("/debug/vars", e.expHandler)
|
||||||
// haven't found an elegant way, so just use a different endpoint
|
// haven't found an elegant way, so just use a different endpoint
|
||||||
http.Handle("/debug/metrics", h)
|
http.Handle("/debug/metrics", h)
|
||||||
|
http.Handle("/debug/metrics/prometheus", prometheus.Handler(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpHandler will return an expvar powered metrics handler.
|
// ExpHandler will return an expvar powered metrics handler.
|
||||||
|
|
|
||||||
115
metrics/prometheus/collector.go
Normal file
115
metrics/prometheus/collector.go
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
typeGaugeTpl = "# TYPE %s gauge\n"
|
||||||
|
typeCounterTpl = "# TYPE %s counter\n"
|
||||||
|
typeSummaryTpl = "# TYPE %s summary\n"
|
||||||
|
keyValueTpl = "%s %v\n\n"
|
||||||
|
keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// collector is a collection of byte buffers that aggregate Prometheus reports
|
||||||
|
// for different metric types.
|
||||||
|
type collector struct {
|
||||||
|
buff *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCollector createa a new Prometheus metric aggregator.
|
||||||
|
func newCollector() *collector {
|
||||||
|
return &collector{
|
||||||
|
buff: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addCounter(name string, m metrics.Counter) {
|
||||||
|
c.writeGaugeCounter(name, m.Count())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addGauge(name string, m metrics.Gauge) {
|
||||||
|
c.writeGaugeCounter(name, m.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64) {
|
||||||
|
c.writeGaugeCounter(name, m.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addHistogram(name string, m metrics.Histogram) {
|
||||||
|
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
||||||
|
ps := m.Percentiles(pv)
|
||||||
|
c.writeSummaryCounter(name, m.Count())
|
||||||
|
for i := range pv {
|
||||||
|
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addMeter(name string, m metrics.Meter) {
|
||||||
|
c.writeGaugeCounter(name, m.Count())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addTimer(name string, m metrics.Timer) {
|
||||||
|
pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
|
||||||
|
ps := m.Percentiles(pv)
|
||||||
|
c.writeSummaryCounter(name, m.Count())
|
||||||
|
for i := range pv {
|
||||||
|
c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) {
|
||||||
|
if len(m.Values()) <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ps := m.Percentiles([]float64{50, 95, 99})
|
||||||
|
val := m.Values()
|
||||||
|
c.writeSummaryCounter(name, len(val))
|
||||||
|
c.writeSummaryPercentile(name, "0.50", ps[0])
|
||||||
|
c.writeSummaryPercentile(name, "0.95", ps[1])
|
||||||
|
c.writeSummaryPercentile(name, "0.99", ps[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) writeGaugeCounter(name string, value interface{}) {
|
||||||
|
name = mutateKey(name)
|
||||||
|
c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name))
|
||||||
|
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) writeSummaryCounter(name string, value interface{}) {
|
||||||
|
name = mutateKey(name + "_count")
|
||||||
|
c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name))
|
||||||
|
c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) writeSummaryPercentile(name, p string, value interface{}) {
|
||||||
|
name = mutateKey(name)
|
||||||
|
c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, name))
|
||||||
|
c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func mutateKey(key string) string {
|
||||||
|
return strings.Replace(key, "/", "_", -1)
|
||||||
|
}
|
||||||
68
metrics/prometheus/prometheus.go
Normal file
68
metrics/prometheus/prometheus.go
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package prometheus exposes go-metrics into a Prometheus format.
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler returns an HTTP handler which dump metrics in Prometheus format.
|
||||||
|
func Handler(reg metrics.Registry) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Gather and pre-sort the metrics to avoid random listings
|
||||||
|
var names []string
|
||||||
|
reg.Each(func(name string, i interface{}) {
|
||||||
|
names = append(names, name)
|
||||||
|
})
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
// Aggregate all the metris into a Prometheus collector
|
||||||
|
c := newCollector()
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
i := reg.Get(name)
|
||||||
|
|
||||||
|
switch m := i.(type) {
|
||||||
|
case metrics.Counter:
|
||||||
|
c.addCounter(name, m.Snapshot())
|
||||||
|
case metrics.Gauge:
|
||||||
|
c.addGauge(name, m.Snapshot())
|
||||||
|
case metrics.GaugeFloat64:
|
||||||
|
c.addGaugeFloat64(name, m.Snapshot())
|
||||||
|
case metrics.Histogram:
|
||||||
|
c.addHistogram(name, m.Snapshot())
|
||||||
|
case metrics.Meter:
|
||||||
|
c.addMeter(name, m.Snapshot())
|
||||||
|
case metrics.Timer:
|
||||||
|
c.addTimer(name, m.Snapshot())
|
||||||
|
case metrics.ResettingTimer:
|
||||||
|
c.addResettingTimer(name, m.Snapshot())
|
||||||
|
default:
|
||||||
|
log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
|
w.Header().Add("Content-Length", fmt.Sprint(c.buff.Len()))
|
||||||
|
w.Write(c.buff.Bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -183,17 +184,13 @@ func NewAbiDBFromFile(path string) (*AbiDb, error) {
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAbiDBFromFiles loads both the standard signature database and a custom database. The latter will be used
|
// NewAbiDBFromFiles loads both the standard signature database (resource file)and a custom database.
|
||||||
// to write new values into if they are submitted via the API
|
// The latter will be used to write new values into if they are submitted via the API
|
||||||
func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) {
|
func NewAbiDBFromFiles(raw []byte, custom string) (*AbiDb, error) {
|
||||||
|
|
||||||
db := &AbiDb{make(map[string]string), make(map[string]string), custom}
|
db := &AbiDb{make(map[string]string), make(map[string]string), custom}
|
||||||
db.customdbPath = custom
|
db.customdbPath = custom
|
||||||
|
|
||||||
raw, err := ioutil.ReadFile(standard)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(raw, &db.db); err != nil {
|
if err := json.Unmarshal(raw, &db.db); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +204,6 @@ func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +213,7 @@ func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) {
|
||||||
if len(id) < 4 {
|
if len(id) < 4 {
|
||||||
return "", fmt.Errorf("Expected 4-byte id, got %d", len(id))
|
return "", fmt.Errorf("Expected 4-byte id, got %d", len(id))
|
||||||
}
|
}
|
||||||
sig := common.ToHex(id[:4])
|
sig := hex.EncodeToString(id[:4])
|
||||||
if key, exists := db.db[sig]; exists {
|
if key, exists := db.db[sig]; exists {
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
@ -226,6 +222,7 @@ func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) {
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("Signature %v not found", sig)
|
return "", fmt.Errorf("Signature %v not found", sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *AbiDb) Size() int {
|
func (db *AbiDb) Size() int {
|
||||||
return len(db.db)
|
return len(db.db)
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +252,6 @@ func (db *AbiDb) AddSignature(selector string, data []byte) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sig := common.ToHex(data[:4])
|
sig := hex.EncodeToString(data[:4])
|
||||||
return db.saveCustomAbi(selector, sig)
|
return db.saveCustomAbi(selector, sig)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ func TestCustomABI(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
filename := fmt.Sprintf("%s/4byte_custom.json", d)
|
filename := fmt.Sprintf("%s/4byte_custom.json", d)
|
||||||
abidb, err := NewAbiDBFromFiles("../../cmd/clef/4byte.json", filename)
|
abidb, err := NewAbiDBFromFiles([]byte(""), filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ type Validator struct {
|
||||||
func NewValidator(db *AbiDb) *Validator {
|
func NewValidator(db *AbiDb) *Validator {
|
||||||
return &Validator{db}
|
return &Validator{db}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelector(selector string, data []byte) (*decodedCallData, error) {
|
func testSelector(selector string, data []byte) (*decodedCallData, error) {
|
||||||
if selector == "" {
|
if selector == "" {
|
||||||
return nil, fmt.Errorf("selector not found")
|
return nil, fmt.Errorf("selector not found")
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
||||||
|
|
||||||
|
|
@ -43,7 +45,13 @@ func serverFunc(api *api.API) swarmhttp.TestServer {
|
||||||
func TestClientUploadDownloadRaw(t *testing.T) {
|
func TestClientUploadDownloadRaw(t *testing.T) {
|
||||||
testClientUploadDownloadRaw(false, t)
|
testClientUploadDownloadRaw(false, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientUploadDownloadRawEncrypted(t *testing.T) {
|
func TestClientUploadDownloadRawEncrypted(t *testing.T) {
|
||||||
|
if testutil.RaceEnabled {
|
||||||
|
t.Skip("flaky with -race on Travis")
|
||||||
|
// See: https://github.com/ethersphere/go-ethereum/issues/1254
|
||||||
|
}
|
||||||
|
|
||||||
testClientUploadDownloadRaw(true, t)
|
testClientUploadDownloadRaw(true, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,41 +34,41 @@ type TestServer interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer {
|
func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer {
|
||||||
dir, err := ioutil.TempDir("", "swarm-storage-test")
|
swarmDir, err := ioutil.TempDir("", "swarm-storage-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
localStore, err := localstore.New(dir, make([]byte, 32), nil)
|
localStore, err := localstore.New(swarmDir, make([]byte, 32), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.RemoveAll(dir)
|
os.RemoveAll(swarmDir)
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStore := storage.NewFileStore(localStore, storage.NewFileStoreParams())
|
fileStore := storage.NewFileStore(localStore, storage.NewFileStoreParams())
|
||||||
|
|
||||||
// Swarm feeds test setup
|
// Swarm feeds test setup
|
||||||
feedsDir, err := ioutil.TempDir("", "swarm-feeds-test")
|
feedsDir, err := ioutil.TempDir("", "swarm-feeds-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rhparams := &feed.HandlerParams{}
|
feeds, err := feed.NewTestHandler(feedsDir, &feed.HandlerParams{})
|
||||||
rh, err := feed.NewTestHandler(feedsDir, rhparams)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a := api.NewAPI(fileStore, resolver, rh.Handler, nil)
|
swarmApi := api.NewAPI(fileStore, resolver, feeds.Handler, nil)
|
||||||
srv := httptest.NewServer(serverFunc(a))
|
apiServer := httptest.NewServer(serverFunc(swarmApi))
|
||||||
|
|
||||||
tss := &TestSwarmServer{
|
tss := &TestSwarmServer{
|
||||||
Server: srv,
|
Server: apiServer,
|
||||||
FileStore: fileStore,
|
FileStore: fileStore,
|
||||||
dir: dir,
|
dir: swarmDir,
|
||||||
Hasher: storage.MakeHashFunc(storage.DefaultHash)(),
|
Hasher: storage.MakeHashFunc(storage.DefaultHash)(),
|
||||||
cleanup: func() {
|
cleanup: func() {
|
||||||
srv.Close()
|
apiServer.Close()
|
||||||
rh.Close()
|
fileStore.Close()
|
||||||
os.RemoveAll(dir)
|
feeds.Close()
|
||||||
|
os.RemoveAll(swarmDir)
|
||||||
os.RemoveAll(feedsDir)
|
os.RemoveAll(feedsDir)
|
||||||
},
|
},
|
||||||
CurrentTime: 42,
|
CurrentTime: 42,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue