From 311d3ecbdc19a7eca756184100866bc89fe755a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 25 Sep 2023 22:16:31 +0100 Subject: [PATCH 01/12] all: make use of changes in Go 1.21 --- README.md | 2 +- api/wallet.go | 4 +-- apiclient/election.go | 4 +-- data/datamocktest.go | 3 +- data/downloader/downloader.go | 3 +- data/ipfs/init.go | 44 ++++++++++++--------------- data/ipfs/ipfs.go | 4 ++- db/prefixeddb/prefixeddb.go | 2 +- vochain/indexer/indexer.go | 4 +-- vochain/state/processblockregistry.go | 4 +-- 10 files changed, 31 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index fda2e9757..b3a271a73 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ For running vocdoni-node in gateway mode, 8 GiB of ram memory is recommended (4 #### Compile and run -Compile from source in a golang environment (Go>1.20 required): +Compile from source in a golang environment (Go>1.21 required): ```bash git clone https://github.com/vocdoni/vocdoni-node.git diff --git a/api/wallet.go b/api/wallet.go index dc3123de3..b0214b095 100644 --- a/api/wallet.go +++ b/api/wallet.go @@ -392,9 +392,7 @@ func (a *API) walletElectionHandler(msg *apirest.APIdata, ctx *httprouter.HTTPCo maxChoiceValue := 0 for _, question := range description.Questions { - if len(question.Choices) > maxChoiceValue { - maxChoiceValue = len(question.Choices) - } + maxChoiceValue = max(maxChoiceValue, len(question.Choices)) metaQuestion := Question{ Choices: []ChoiceMetadata{}, Description: question.Description, diff --git a/apiclient/election.go b/apiclient/election.go index 47393f1bb..1c27347c1 100644 --- a/apiclient/election.go +++ b/apiclient/election.go @@ -153,9 +153,7 @@ func (c *HTTPclient) NewElection(description *api.ElectionDescription) (types.He maxChoiceValue := 0 for _, question := range description.Questions { - if len(question.Choices) > maxChoiceValue { - maxChoiceValue = len(question.Choices) - } + maxChoiceValue = max(maxChoiceValue, len(question.Choices)) metaQuestion := api.Question{ Choices: []api.ChoiceMetadata{}, Description: question.Description, diff --git a/data/datamocktest.go b/data/datamocktest.go index e14f3646a..1bfe180ef 100644 --- a/data/datamocktest.go +++ b/data/datamocktest.go @@ -2,12 +2,11 @@ package data import ( "context" + "maps" "os" "sync" "time" - "golang.org/x/exp/maps" - "go.vocdoni.io/dvote/data/ipfs" "go.vocdoni.io/dvote/metrics" "go.vocdoni.io/dvote/test/testcommon/testutil" diff --git a/data/downloader/downloader.go b/data/downloader/downloader.go index da17de126..17521f547 100644 --- a/data/downloader/downloader.go +++ b/data/downloader/downloader.go @@ -3,14 +3,13 @@ package downloader import ( "context" "fmt" + "maps" "os" "strings" "sync" "sync/atomic" "time" - "golang.org/x/exp/maps" - "go.vocdoni.io/dvote/data" "go.vocdoni.io/dvote/log" ) diff --git a/data/ipfs/init.go b/data/ipfs/init.go index a2633681d..6fc2fabcf 100644 --- a/data/ipfs/init.go +++ b/data/ipfs/init.go @@ -27,15 +27,10 @@ import ( "go.vocdoni.io/dvote/log" ) -var ( - pluginOnce sync.Once - ConfigRoot string -) +var ConfigRoot string -const ( - // ChunkerTypeSize is the chunker type used by IPFS to calculate to build the DAG. - ChunkerTypeSize = "size-262144" -) +// ChunkerTypeSize is the chunker type used by IPFS to calculate to build the DAG. +const ChunkerTypeSize = "size-262144" func init() { // Initialize the DAG builder with offline exchange and the correct CID format @@ -68,7 +63,9 @@ func initRepository() error { return err } - installDatabasePlugins() + if err := installDatabasePlugins(); err != nil { + return err + } _, err = doInit(io.Discard, ConfigRoot, 2048) return err } @@ -169,22 +166,19 @@ func cmdCtx(node *ipfscore.IpfsNode, repoPath string) commands.Context { } } -func installDatabasePlugins() { - pluginOnce.Do(func() { - loader, err := loader.NewPluginLoader("") - if err != nil { - log.Fatal(err) - } - err = loader.Initialize() - if err != nil { - log.Fatal(err) - } - err = loader.Inject() - if err != nil { - log.Fatal(err) - } - }) -} +var installDatabasePlugins = sync.OnceValue(func() error { + loader, err := loader.NewPluginLoader("") + if err != nil { + return err + } + if err := loader.Initialize(); err != nil { + return err + } + if err := loader.Inject(); err != nil { + return err + } + return nil +}) func doInit(out io.Writer, repoRoot string, nBitsForKeypair int) (*config.Config, error) { log.Infow("initializing new IPFS repository", "root", repoRoot) diff --git a/data/ipfs/ipfs.go b/data/ipfs/ipfs.go index dc1afc95e..6960a4446 100644 --- a/data/ipfs/ipfs.go +++ b/data/ipfs/ipfs.go @@ -61,7 +61,9 @@ func (i *Handler) Init(d *types.DataStore) error { i.LogLevel = "ERROR" } ipfslog.SetLogLevel("*", i.LogLevel) - installDatabasePlugins() + if err := installDatabasePlugins(); err != nil { + return err + } ConfigRoot = d.Datadir os.Setenv("IPFS_FD_MAX", "4096") diff --git a/db/prefixeddb/prefixeddb.go b/db/prefixeddb/prefixeddb.go index c5998c9ef..ccc0d41ee 100644 --- a/db/prefixeddb/prefixeddb.go +++ b/db/prefixeddb/prefixeddb.go @@ -2,9 +2,9 @@ package prefixeddb import ( "bytes" + "slices" "go.vocdoni.io/dvote/db" - "golang.org/x/exp/slices" ) // PrefixedDatabase wraps a db.Database prefixing all keys with `prefix`. diff --git a/vochain/indexer/indexer.go b/vochain/indexer/indexer.go index a2378007e..a139ec400 100644 --- a/vochain/indexer/indexer.go +++ b/vochain/indexer/indexer.go @@ -325,7 +325,7 @@ func (idx *Indexer) Commit(height uint32) error { } log.Debugw("updated process", "processID", hex.EncodeToString(pid)) } - maps.Clear(idx.blockUpdateProcs) + clear(idx.blockUpdateProcs) // Add votes collected by onVote (live results) newVotes := 0 @@ -437,7 +437,7 @@ func (idx *Indexer) Rollback() { } idx.blockTx = nil } - maps.Clear(idx.blockUpdateProcs) + clear(idx.blockUpdateProcs) } // OnProcess indexer stores the processID diff --git a/vochain/state/processblockregistry.go b/vochain/state/processblockregistry.go index 7c5314ad2..9a81829bc 100644 --- a/vochain/state/processblockregistry.go +++ b/vochain/state/processblockregistry.go @@ -56,9 +56,7 @@ func (pbr *ProcessBlockRegistry) MaxEndBlock(fromBlock uint32) (uint32, error) { if err != nil { return false } - if endBlock := p.StartBlock + p.BlockCount; endBlock > maxEndBlock { - maxEndBlock = endBlock - } + maxEndBlock = max(maxEndBlock, p.StartBlock+p.BlockCount) return true }); err != nil { return 0, err From 4c147bb050f68da3e81569da6ed5894e8d39aa63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 25 Sep 2023 22:25:07 +0100 Subject: [PATCH 02/12] bump a few minor deps Also note that kubo's first v0.23 RC is out, so we can move from master. --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index d61cae52b..37cb9f221 100644 --- a/go.mod +++ b/go.mod @@ -39,8 +39,8 @@ require ( github.com/ipfs/go-ipfs-keystore v0.1.0 github.com/ipfs/go-ipld-format v0.5.0 github.com/ipfs/go-log/v2 v2.5.1 - github.com/ipfs/kubo v0.22.1-0.20230922134002-00e26e9396c3 - github.com/klauspost/compress v1.16.7 + github.com/ipfs/kubo v0.23.0-rc1 + github.com/klauspost/compress v1.17.0 github.com/libp2p/go-libp2p v0.31.0 github.com/libp2p/go-libp2p-pubsub v0.9.3 github.com/libp2p/go-reuseport v0.4.0 @@ -60,7 +60,7 @@ require ( go.mongodb.org/mongo-driver v1.12.1 go.vocdoni.io/proto v1.14.6-0.20230802094125-e07a41fda290 golang.org/x/crypto v0.13.0 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.15.0 google.golang.org/protobuf v1.31.0 ) @@ -157,7 +157,7 @@ require ( github.com/ipfs/go-ds-leveldb v0.5.0 // indirect github.com/ipfs/go-ds-measure v0.2.0 // indirect github.com/ipfs/go-fs-lock v0.0.7 // indirect - github.com/ipfs/go-graphsync v0.14.4 // indirect + github.com/ipfs/go-graphsync v0.15.1 // indirect github.com/ipfs/go-ipfs-cmds v0.10.0 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect @@ -173,7 +173,7 @@ require ( github.com/ipfs/go-merkledag v0.11.0 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect - github.com/ipfs/go-unixfsnode v1.7.1 // indirect + github.com/ipfs/go-unixfsnode v1.8.1 // indirect github.com/ipfs/go-verifcid v0.0.2 // indirect github.com/ipld/go-car v0.5.0 // indirect github.com/ipld/go-car/v2 v2.10.2-0.20230622090957-499d0c909d33 // indirect @@ -308,7 +308,7 @@ require ( golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index e61279151..8b7da2a15 100644 --- a/go.sum +++ b/go.sum @@ -749,8 +749,8 @@ github.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjAp github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE= github.com/ipfs/go-fs-lock v0.0.7 h1:6BR3dajORFrFTkb5EpCUFIAypsoxpGpDSVUdFwzgL9U= github.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEcvYrHhhc= -github.com/ipfs/go-graphsync v0.14.4 h1:ysazATpwsIjYtYEZH5CdD/HRaonCJd4pASUtnzESewk= -github.com/ipfs/go-graphsync v0.14.4/go.mod h1:yT0AfjFgicOoWdAlUJ96tQ5AkuGI4r1taIQX/aHbBQo= +github.com/ipfs/go-graphsync v0.15.1 h1:7v4VfRQ/8pKzPuE0wHeMaWhKu8D/RlezIrzvGWIBtHQ= +github.com/ipfs/go-graphsync v0.15.1/go.mod h1:eUIYS0OKkdBbG4vHhfGkY3lZ7h1G5Dlwd+HxTCe18vA= github.com/ipfs/go-ipfs-blockstore v1.3.0 h1:m2EXaWgwTzAfsmt5UdJ7Is6l4gJcaM/A12XwJyvYvMM= github.com/ipfs/go-ipfs-blockstore v1.3.0/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= @@ -813,12 +813,12 @@ github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVzte github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= github.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= -github.com/ipfs/go-unixfsnode v1.7.1 h1:RRxO2b6CSr5UQ/kxnGzaChTjp5LWTdf3Y4n8ANZgB/s= -github.com/ipfs/go-unixfsnode v1.7.1/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= +github.com/ipfs/go-unixfsnode v1.8.1 h1:nEWQl2XL+Zoyh6u0OMzNI8mUeCKLyRgg65WDbTm/oNU= +github.com/ipfs/go-unixfsnode v1.8.1/go.mod h1:HxRu9HYHOjK6HUqFBAi++7DVoWAHn0o4v/nZ/VA+0g8= github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/kubo v0.22.1-0.20230922134002-00e26e9396c3 h1:57xs2+D+TexC50uvttG0o9CQqZPBatj0q4Q5uJX5zSw= -github.com/ipfs/kubo v0.22.1-0.20230922134002-00e26e9396c3/go.mod h1:mvCFDAqqTErJ1rApyrviRIJwfHYPIBS2+EdZYXT3tSs= +github.com/ipfs/kubo v0.23.0-rc1 h1:zs/tIEnZY9d3nOoa4xpI+Wwc46dum/556kHkgco3Vqs= +github.com/ipfs/kubo v0.23.0-rc1/go.mod h1:Z2kk6sTjXmRX7eZGVjBG6RDP2Cy/QPwA40XJt2PR0j0= github.com/ipld/go-car v0.5.0 h1:kcCEa3CvYMs0iE5BzD5sV7O2EwMiCIp3uF8tA6APQT8= github.com/ipld/go-car v0.5.0/go.mod h1:ppiN5GWpjOZU9PgpAZ9HbZd9ZgSpwPMr48fGRJOWmvE= github.com/ipld/go-car/v2 v2.10.2-0.20230622090957-499d0c909d33 h1:0OZwzSYWIuiKEOXd/2vm5cMcEmmGLFn+1h6lHELCm3s= @@ -900,8 +900,8 @@ github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1698,8 +1698,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2029,8 +2029,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 61d26f59b0afbbc012faa8ecd9adae0953c55f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Thu, 28 Sep 2023 16:17:13 +0100 Subject: [PATCH 03/12] bump staticcheck version for Go 1.21 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0beafc038..65120d15a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: # staticcheck provides a github action, use it (https://staticcheck.io/docs/running-staticcheck/ci/github-actions/) # or use golangci-lint (github action) with staticcheck as enabled linter run: | - curl -L https://github.com/dominikh/go-tools/releases/download/2023.1.2/staticcheck_linux_amd64.tar.gz | tar -xzf - + curl -L https://github.com/dominikh/go-tools/releases/download/2023.1.6/staticcheck_linux_amd64.tar.gz | tar -xzf - - name: Run staticcheck run: | ./staticcheck/staticcheck ./... 2> staticcheck/stderr From e48b7068895c526722b5edcbad3ae836e6d3c4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Jos=C3=A9=20D=C3=A1vila?= Date: Mon, 2 Oct 2023 07:58:27 -0400 Subject: [PATCH 04/12] en2endtest: extend annoneletion adding a test with TempSIKs set to true to vote with half of the accounts that not registered, and the remaining half with registered accounts. (#1117) --- apiclient/account.go | 2 +- cmd/end2endtest/helpers.go | 127 +++++++++++++++++++--------- cmd/end2endtest/zkweighted.go | 83 +++++++++++++++++- dockerfiles/testsuite/start_test.sh | 1 + 4 files changed, 171 insertions(+), 42 deletions(-) diff --git a/apiclient/account.go b/apiclient/account.go index 08784d251..0a42aeee5 100644 --- a/apiclient/account.go +++ b/apiclient/account.go @@ -322,7 +322,7 @@ func (c *HTTPclient) DelSIK() (types.HexBytes, error) { } // RegisterSIKForVote function performs the free RegisterSIKTx to the vochain -// helping to non registered accounts to vote in a on going election, but only +// helping to non registered accounts to vote in an ongoing election, but only // if the account is in the election census. The function returns the hash of // the sent transaction, and requires the election ID. The census proof and the // secret are optional. If no proof is provided, it will be generated. diff --git a/cmd/end2endtest/helpers.go b/cmd/end2endtest/helpers.go index 507e9ff51..0dd7d954f 100644 --- a/cmd/end2endtest/helpers.go +++ b/cmd/end2endtest/helpers.go @@ -290,39 +290,7 @@ func (t *e2eElection) setupCensus(censusType string, nAcct int, createAccounts b // Register the accounts in the vochain if is required if censusType == vapi.CensusTypeZKWeighted && createAccounts { - errorChan := make(chan error) // Create a buffered channel to prevent deadlock - wg := &sync.WaitGroup{} - - for i, acc := range voterAccounts { - if i%10 == 0 { - // Print some information about progress on large censuses - log.Infof("creating %d anonymous census accounts...", len(voterAccounts)) - } - - wg.Add(1) - go func(i int, acc *ethereum.SignKeys) { - defer wg.Done() - pKey := acc.PrivateKey() - if _, _, err := t.createAccount(pKey.String()); err != nil && - !strings.Contains(err.Error(), "createAccountTx: account already exists") { - errorChan <- err - } - log.Infow("anonymous census account created", "index", i, "address", acc.AddressString()) - }(i, acc) // Pass the acc variable as a parameter to avoid data race - } - - go func() { // avoid blocking the main goroutine - wg.Wait() - close(errorChan) // close the error channel after all goroutines have finished - }() - - for err := range errorChan { // receive errors from the errorChan until it's closed. - if err != nil { - return nil, nil, err - } - } - // Set the first account as the default account - if err := t.api.SetAccount(t.config.accountPrivKeys[0]); err != nil { + if err := t.registerAnonAccts(voterAccounts); err != nil { return nil, nil, err } } @@ -331,17 +299,15 @@ func (t *e2eElection) setupCensus(censusType string, nAcct int, createAccounts b } func (t *e2eElection) setupElection(ed *vapi.ElectionDescription, nvAccts int) error { - var voterAccounts []*ethereum.SignKeys - if err := t.setupAccount(); err != nil { return err } - censusID, voterAccts, err := t.setupCensus(ed.Census.Type, nvAccts, true) + censusID, voterAccts, err := t.setupCensus(ed.Census.Type, nvAccts, !ed.TempSIKs) if err != nil { return err } - voterAccounts = voterAccts + ed.Census.RootHash, ed.Census.URL, err = t.publishCheckCensus(censusID, uint64(nvAccts)) if err != nil { return err @@ -371,7 +337,53 @@ func (t *e2eElection) setupElection(ed *vapi.ElectionDescription, nvAccts int) e return err } t.election = election - if err := t.generateProofs(nil, voterAccounts); err != nil { + + if ed.TempSIKs { + errorChan := make(chan error) + wg := &sync.WaitGroup{} + halfAccts := len(voterAccts) / 2 + + // register SIK for 1/2 accounts and left the rest to vote with account registered + for i, acc := range voterAccts[:halfAccts] { + wg.Add(1) + go func(i int, acc *ethereum.SignKeys) { + defer wg.Done() + pKey := acc.PrivateKey() + accountApi := t.api.Clone(pKey.String()) + hash, err := accountApi.RegisterSIKForVote(electionID, nil, nil) + if err != nil { + log.Errorf("could not register SIK for vote, address: %s, %v", acc.AddressString(), err) + errorChan <- err + } + log.Infow("sik registered for anonymous census uncreated account", "index", i, "address", acc.AddressString()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) + defer cancel() + + if _, err := accountApi.WaitUntilTxIsMined(ctx, hash); err != nil { + log.Errorf("gave up waiting for tx %x to be mined: %s", hash, err) + errorChan <- err + } + }(i, acc) + } + + go func() { // avoid blocking the main goroutine + wg.Wait() + close(errorChan) // close the error channel after all goroutines have finished + }() + + for err := range errorChan { // receive errors from the errorChan until it's closed. + if err != nil { + return err + } + } + + // register the remaining accounts + if err := t.registerAnonAccts(voterAccts[halfAccts:]); err != nil { + return err + } + + } + if err := t.generateProofs(nil, voterAccts); err != nil { return err } @@ -655,3 +667,42 @@ func (t *e2eElection) endElectionAndFetchResults() (*vapi.ElectionResults, error } return results, nil } + +func (t *e2eElection) registerAnonAccts(voterAccounts []*ethereum.SignKeys) error { + errorChan := make(chan error) + wg := &sync.WaitGroup{} + + for i, acc := range voterAccounts { + if i%10 == 0 { + // Print some information about progress on large censuses + log.Infof("creating %d anonymous census accounts...", len(voterAccounts)) + } + + wg.Add(1) + go func(i int, acc *ethereum.SignKeys) { + defer wg.Done() + pKey := acc.PrivateKey() + if _, _, err := t.createAccount(pKey.String()); err != nil && + !strings.Contains(err.Error(), "createAccountTx: account already exists") { + errorChan <- err + } + log.Infow("anonymous census account created", "index", i, "address", acc.AddressString()) + }(i, acc) // Pass the acc variable as a parameter to avoid data race + } + + go func() { // avoid blocking the main goroutine + wg.Wait() + close(errorChan) // close the error channel after all goroutines have finished + }() + + for err := range errorChan { // receive errors from the errorChan until it's closed. + if err != nil { + return err + } + } + // Set the first account as the default account + if err := t.api.SetAccount(t.config.accountPrivKeys[0]); err != nil { + return err + } + return nil +} diff --git a/cmd/end2endtest/zkweighted.go b/cmd/end2endtest/zkweighted.go index c02071a1f..8ad95bdb6 100644 --- a/cmd/end2endtest/zkweighted.go +++ b/cmd/end2endtest/zkweighted.go @@ -17,13 +17,18 @@ func init() { description: "Performs a complete test of anonymous election, from creating a census to voting and validating votes", example: os.Args[0] + " --operation=anonelection --votes=1000", } + ops["anonelectionTempSIKs"] = operation{ + test: &E2EAnonElectionTempSIKs{}, + description: "Performs a complete test of anonymous election with TempSIKs flag to vote with half of the accounts that are not registered, and the remaining half with registered accounts", + example: os.Args[0] + " --operation=anonelectionTempSIKS --votes=1000", + } } var _ VochainTest = (*E2EAnonElection)(nil) +var _ VochainTest = (*E2EAnonElectionTempSIKs)(nil) -type E2EAnonElection struct { - e2eElection -} +type E2EAnonElection struct{ e2eElection } +type E2EAnonElectionTempSIKs struct{ e2eElection } func (t *E2EAnonElection) Setup(api *apiclient.HTTPclient, c *config) error { t.api = api @@ -95,3 +100,75 @@ func (t *E2EAnonElection) Run() error { return nil } + +func (t *E2EAnonElectionTempSIKs) Setup(api *apiclient.HTTPclient, c *config) error { + t.api = api + t.config = c + + ed := newTestElectionDescription(2) + ed.ElectionType = vapi.ElectionType{ + Autostart: true, + Interruptible: true, + Anonymous: true, + } + ed.VoteType = vapi.VoteType{MaxVoteOverwrites: 1} + ed.Census = vapi.CensusTypeDescription{Type: vapi.CensusTypeZKWeighted} + ed.TempSIKs = true + + if err := t.setupElection(ed, t.config.nvotes); err != nil { + return err + } + log.Debugf("election details: %+v", *t.election) + return nil +} + +func (*E2EAnonElectionTempSIKs) Teardown() error { + // nothing to do here + return nil +} + +func (t *E2EAnonElectionTempSIKs) Run() error { + var vcount int + + startTime := time.Now() + votes := []*apiclient.VoteData{} + + log.Infow("enqueuing votes", "n", t.config.nvotes, "election", t.election.ElectionID) + t.voters.Range(func(key, value any) bool { + if acctp, ok := value.(acctProof); ok { + votes = append(votes, &apiclient.VoteData{ + ElectionID: t.election.ElectionID, + ProofMkTree: acctp.proof, + ProofSIKTree: acctp.proofSIK, + Choices: []int{vcount % 2}, + VoterAccount: acctp.account, + VoteWeight: big.NewInt(defaultWeight / 2), + }) + vcount += 1 + } + return true + }) + + errs := t.sendVotes(votes) + if len(errs) > 0 { + return fmt.Errorf("error in sendVotes %+v", errs) + } + + log.Infow("votes submitted successfully", + "n", t.config.nvotes, "time", time.Since(startTime), + "vps", int(float64(t.config.nvotes)/time.Since(startTime).Seconds())) + + if err := t.verifyVoteCount(t.config.nvotes); err != nil { + return err + } + + elres, err := t.endElectionAndFetchResults() + if err != nil { + return err + } + + log.Infof("election %s status is RESULTS", t.election.ElectionID.String()) + log.Infof("election results: %v", elres.Results) + + return nil +} diff --git a/dockerfiles/testsuite/start_test.sh b/dockerfiles/testsuite/start_test.sh index 6755dea48..8662b2cd2 100755 --- a/dockerfiles/testsuite/start_test.sh +++ b/dockerfiles/testsuite/start_test.sh @@ -112,6 +112,7 @@ e2etest_encryptedelection() { e2etest_anonelection() { e2etest anonelection + e2etest anonelectionTempSIKs } e2etest_hysteresis() { From a52728162aecbdb5da5e8e3aa89ade403acf88e6 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 2 Oct 2023 15:50:34 +0200 Subject: [PATCH 05/12] odh: fix log.Infow string message --- vochain/offchaindatahandler/offchaindatahandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vochain/offchaindatahandler/offchaindatahandler.go b/vochain/offchaindatahandler/offchaindatahandler.go index 00317b02e..efc37d5ba 100644 --- a/vochain/offchaindatahandler/offchaindatahandler.go +++ b/vochain/offchaindatahandler/offchaindatahandler.go @@ -76,7 +76,7 @@ func (d *OffChainDataHandler) Commit(_ uint32) error { // AddToQueue() writes to a channel that might be full, so we don't want to block the main thread. go d.enqueueOffchainCensus(item.censusRoot, item.uri) case itemTypeElectionMetadata, itemTypeAccountMetadata: - log.Infow("importing data", "type", "election metadata", "uri", item.uri) + log.Infow("importing metadata", "type", item.itemType, "uri", item.uri) go d.enqueueMetadata(item.uri) default: log.Errorf("unknown item %d", item.itemType) From 1a28680d675371373eda8115ff92994f4b5a194a Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 2 Oct 2023 17:02:12 +0200 Subject: [PATCH 06/12] vocone: refactor Vocone struct as an extension of service.VocdoniService this way, the whole init phase is much similar to what `node` does, avoiding bugs and making it easier to maintain in the future when features are added this refactor was originated by a bug introduced when OffchainDataDownloader feature was merged on `node` but not fully properly in `voconed`, so `voconed` was not doing vs.DataDownloader.Start() that `node` does during init the best solution was to make voconed call the same init function that node calls srv.OffChainDataHandler() --- service/service.go | 2 + vocone/vocone.go | 149 ++++++++++++++++++------------------------ vocone/vocone_test.go | 2 +- 3 files changed, 67 insertions(+), 86 deletions(-) diff --git a/service/service.go b/service/service.go index ac76df9fc..8fbcb108e 100644 --- a/service/service.go +++ b/service/service.go @@ -10,6 +10,7 @@ import ( "go.vocdoni.io/dvote/metrics" "go.vocdoni.io/dvote/vochain" "go.vocdoni.io/dvote/vochain/indexer" + "go.vocdoni.io/dvote/vochain/keykeeper" "go.vocdoni.io/dvote/vochain/offchaindatahandler" "go.vocdoni.io/dvote/vochain/vochaininfo" ) @@ -27,4 +28,5 @@ type VocdoniService struct { Stats *vochaininfo.VochainInfo Storage data.Storage Signer *ethereum.SignKeys + KeyKeeper *keykeeper.KeyKeeper } diff --git a/vocone/vocone.go b/vocone/vocone.go index 568e0444f..864fd91d1 100644 --- a/vocone/vocone.go +++ b/vocone/vocone.go @@ -18,11 +18,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/common" "go.vocdoni.io/dvote/api" - "go.vocdoni.io/dvote/api/censusdb" "go.vocdoni.io/dvote/config" "go.vocdoni.io/dvote/crypto/ethereum" - "go.vocdoni.io/dvote/data" - "go.vocdoni.io/dvote/data/downloader" "go.vocdoni.io/dvote/db" "go.vocdoni.io/dvote/db/metadb" "go.vocdoni.io/dvote/httprouter" @@ -32,7 +29,6 @@ import ( "go.vocdoni.io/dvote/vochain/genesis" "go.vocdoni.io/dvote/vochain/indexer" "go.vocdoni.io/dvote/vochain/keykeeper" - "go.vocdoni.io/dvote/vochain/offchaindatahandler" "go.vocdoni.io/dvote/vochain/state" "go.vocdoni.io/dvote/vochain/vochaininfo" "go.vocdoni.io/proto/build/go/models" @@ -48,16 +44,11 @@ const ( // Vocone is an implementation of the Vocdoni protocol run by a single (atomic) node. type Vocone struct { - sc *indexer.Indexer - kk *keykeeper.KeyKeeper + service.VocdoniService + mempool chan []byte // a buffered channel acts like a FIFO with a fixed size blockStore db.Database - dataDir string height atomic.Int64 - appInfo *vochaininfo.VochainInfo - app *vochain.BaseApplication - storage data.Storage - censusdb *censusdb.CensusDB lastBlockTime time.Time blockTimeTarget time.Duration txsPerBlock int @@ -69,17 +60,19 @@ type Vocone struct { // NewVocone returns a ready Vocone instance. func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool) (*Vocone, error) { - vc := &Vocone{} var err error - vc.dataDir = dataDir - vc.app, err = vochain.NewBaseApplication(db.TypePebble, dataDir) + + vc := &Vocone{} + vc.Config = &config.VochainCfg{} + vc.Config.DataDir = dataDir + vc.App, err = vochain.NewBaseApplication(db.TypePebble, dataDir) if err != nil { return nil, err } vc.mempool = make(chan []byte, mempoolSize) vc.blockTimeTarget = DefaultBlockTimeTarget vc.txsPerBlock = DefaultTxsPerBlock - version, err := vc.app.State.LastHeight() + version, err := vc.App.State.LastHeight() if err != nil { return nil, err } @@ -90,12 +83,12 @@ func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool) } vc.setDefaultMethods() - vc.app.State.SetHeight(uint32(vc.height.Load())) + vc.App.State.SetHeight(uint32(vc.height.Load())) // Create indexer - if vc.sc, err = indexer.NewIndexer( + if vc.Indexer, err = indexer.NewIndexer( filepath.Join(dataDir, "indexer"), - vc.app, + vc.App, true, ); err != nil { return nil, err @@ -107,34 +100,20 @@ func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool) } // Create vochain metrics collector - vc.appInfo = vochaininfo.NewVochainInfo(vc.app) - go vc.appInfo.Start(10) + vc.Stats = vochaininfo.NewVochainInfo(vc.App) + go vc.Stats.Start(10) - // Create the IPFS storage layer (we use the Vocdoni general service) - srv := service.VocdoniService{} + // Create the IPFS storage layer if !disableIPFS { - if vc.storage, err = srv.IPFS(&config.IPFSCfg{ + vc.Storage, err = vc.IPFS(&config.IPFSCfg{ ConfigPath: filepath.Join(dataDir, "ipfs"), - }); err != nil { + }) + if err != nil { return nil, err } - } - // Create the census database for storing census data - cdb, err := metadb.New(db.TypePebble, filepath.Join(dataDir, "census")) - if err != nil { - return nil, err - } - vc.censusdb = censusdb.NewCensusDB(cdb) - - // Create the data downloader and offchain data handler - if !disableIPFS { - offchaindatahandler.NewOffChainDataHandler( - vc.app, - downloader.NewDownloader(vc.storage), - vc.censusdb, - false, - ) + // Create the data downloader and offchain data handler + vc.OffChainDataHandler() } return vc, err @@ -146,16 +125,16 @@ func (vc *Vocone) EnableAPI(host string, port int, URLpath string) (*api.API, er if err := httpRouter.Init(host, port); err != nil { return nil, err } - uAPI, err := api.NewAPI(&httpRouter, URLpath, vc.dataDir, db.TypePebble) + uAPI, err := api.NewAPI(&httpRouter, URLpath, vc.Config.DataDir, db.TypePebble) if err != nil { return nil, err } uAPI.Attach( - vc.app, - vc.appInfo, - vc.sc, - vc.storage, - vc.censusdb, + vc.App, + vc.Stats, + vc.Indexer, + vc.Storage, + vc.CensusDB, ) return uAPI, uAPI.EnableHandlers( api.ElectionHandler, @@ -171,8 +150,8 @@ func (vc *Vocone) EnableAPI(host string, port int, URLpath string) (*api.API, er // Start starts the Vocone node. This function is blocking. func (vc *Vocone) Start() { vc.lastBlockTime = time.Now() - go vochainPrintInfo(10, vc.appInfo) - if vc.app.Height() == 0 { + go vochainPrintInfo(10, vc.Stats) + if vc.App.Height() == 0 { log.Infof("initializing new blockchain") genesisAppData, err := json.Marshal(&genesis.GenesisAppState{ MaxElectionSize: 1000000, @@ -182,8 +161,8 @@ func (vc *Vocone) Start() { if err != nil { panic(err) } - if _, err = vc.app.InitChain(context.Background(), &abcitypes.RequestInitChain{ - ChainId: vc.app.ChainID(), + if _, err = vc.App.InitChain(context.Background(), &abcitypes.RequestInitChain{ + ChainId: vc.App.ChainID(), AppStateBytes: genesisAppData, Time: time.Now(), }); err != nil { @@ -196,7 +175,7 @@ func (vc *Vocone) Start() { startTime := time.Now() height := vc.height.Load() // Create and execute block - resp, err := vc.app.FinalizeBlock( + resp, err := vc.App.FinalizeBlock( context.Background(), &abcitypes.RequestFinalizeBlock{ Txs: vc.prepareBlock(), @@ -208,7 +187,7 @@ func (vc *Vocone) Start() { log.Error(err, "finalize block error") } // Commit block to persistent state - _, err = vc.app.Commit(context.Background(), &abcitypes.RequestCommit{}) + _, err = vc.App.Commit(context.Background(), &abcitypes.RequestCommit{}) if err != nil { log.Error(err, "commit error") } @@ -243,10 +222,10 @@ func (vc *Vocone) SetBlockSize(txsCount int) { func (vc *Vocone) CreateAccount(key common.Address, acc *state.Account) error { vc.vcMtx.Lock() defer vc.vcMtx.Unlock() - if err := vc.app.State.SetAccount(key, acc); err != nil { + if err := vc.App.State.SetAccount(key, acc); err != nil { return err } - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } return nil @@ -256,33 +235,33 @@ func (vc *Vocone) CreateAccount(key common.Address, acc *state.Account) error { func (vc *Vocone) SetTreasurer(treasurer common.Address) error { vc.vcMtx.Lock() defer vc.vcMtx.Unlock() - if err := vc.app.State.SetTreasurer(treasurer, 0); err != nil { + if err := vc.App.State.SetTreasurer(treasurer, 0); err != nil { return err } - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } return nil } -// SetKeyKeeper adds a keykeper to the application. +// SetKeyKeeper adds a keykeeper to the application. func (vc *Vocone) SetKeyKeeper(key *ethereum.SignKeys) error { // Create key keeper // we need to add a validator, so the keykeeper functions are allowed vc.vcMtx.Lock() defer vc.vcMtx.Unlock() // remove existing validators before we add the new one (to avoid keyindex collision) - validators, err := vc.app.State.Validators(true) + validators, err := vc.App.State.Validators(true) if err != nil { return err } for _, v := range validators { - if err := vc.app.State.RemoveValidator(v.Address); err != nil { + if err := vc.App.State.RemoveValidator(v.Address); err != nil { log.Warnf("could not remove validator %x", v.Address) } } // add the new validator - if err := vc.app.State.AddValidator(&models.Validator{ + if err := vc.App.State.AddValidator(&models.Validator{ Address: key.Address().Bytes(), Power: 100, Name: "vocone-solo-validator", @@ -291,12 +270,12 @@ func (vc *Vocone) SetKeyKeeper(key *ethereum.SignKeys) error { return err } log.Infow("adding validator", "address", key.Address().Hex(), "keyIndex", 1) - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } - vc.kk, err = keykeeper.NewKeyKeeper( - filepath.Join(vc.dataDir, "keykeeper"), - vc.app, + vc.KeyKeeper, err = keykeeper.NewKeyKeeper( + filepath.Join(vc.Config.DataDir, "keykeeper"), + vc.App, key, 1) return err @@ -306,13 +285,13 @@ func (vc *Vocone) SetKeyKeeper(key *ethereum.SignKeys) error { func (vc *Vocone) MintTokens(to common.Address, amount uint64) error { vc.vcMtx.Lock() defer vc.vcMtx.Unlock() - if err := vc.app.State.InitChainMintBalance(to, amount); err != nil { + if err := vc.App.State.InitChainMintBalance(to, amount); err != nil { return err } - if err := vc.app.State.IncrementTreasurerNonce(); err != nil { + if err := vc.App.State.IncrementTreasurerNonce(); err != nil { return err } - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } return nil @@ -322,13 +301,13 @@ func (vc *Vocone) MintTokens(to common.Address, amount uint64) error { func (vc *Vocone) SetTxCost(txType models.TxType, cost uint64) error { vc.vcMtx.Lock() defer vc.vcMtx.Unlock() - if err := vc.app.State.SetTxBaseCost(txType, cost); err != nil { + if err := vc.App.State.SetTxBaseCost(txType, cost); err != nil { return err } - if err := vc.app.State.IncrementTreasurerNonce(); err != nil { + if err := vc.App.State.IncrementTreasurerNonce(); err != nil { return err } - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } return nil @@ -343,7 +322,7 @@ func (vc *Vocone) SetBulkTxCosts(txCost uint64, force bool) error { defer vc.vcMtx.Unlock() for k := range state.TxTypeCostToStateKey { if !force { - _, err := vc.app.State.TxBaseCost(k, true) + _, err := vc.App.State.TxBaseCost(k, true) if err == nil || errors.Is(err, state.ErrTxCostNotFound) { continue } @@ -351,11 +330,11 @@ func (vc *Vocone) SetBulkTxCosts(txCost uint64, force bool) error { return err } log.Infow("setting tx base cost", "txtype", models.TxType_name[int32(k)], "cost", txCost) - if err := vc.app.State.SetTxBaseCost(k, txCost); err != nil { + if err := vc.App.State.SetTxBaseCost(k, txCost); err != nil { return err } } - if _, err := vc.app.State.Save(); err != nil { + if _, err := vc.App.State.Save(); err != nil { return err } return nil @@ -365,7 +344,7 @@ func (vc *Vocone) SetBulkTxCosts(txCost uint64, force bool) error { func (vc *Vocone) SetElectionPrice() error { vc.vcMtx.Lock() defer vc.vcMtx.Unlock() - if err := vc.app.State.SetElectionPriceCalc(); err != nil { + if err := vc.App.State.SetElectionPriceCalc(); err != nil { return err } return nil @@ -373,18 +352,18 @@ func (vc *Vocone) SetElectionPrice() error { func (vc *Vocone) setDefaultMethods() { // first set the default methods, then override some of them - vc.app.SetDefaultMethods() - vc.app.SetFnIsSynchronizing(func() bool { return false }) - vc.app.SetFnSendTx(vc.addTx) - vc.app.SetFnGetTx(vc.getTx) - vc.app.SetFnGetBlockByHeight(vc.getBlock) - vc.app.SetFnGetTxHash(vc.getTxWithHash) - vc.app.SetFnMempoolSize(vc.mempoolSize) - vc.app.SetFnMempoolPrune(nil) + vc.App.SetDefaultMethods() + vc.App.SetFnIsSynchronizing(func() bool { return false }) + vc.App.SetFnSendTx(vc.addTx) + vc.App.SetFnGetTx(vc.getTx) + vc.App.SetFnGetBlockByHeight(vc.getBlock) + vc.App.SetFnGetTxHash(vc.getTxWithHash) + vc.App.SetFnMempoolSize(vc.mempoolSize) + vc.App.SetFnMempoolPrune(nil) } func (vc *Vocone) addTx(tx []byte) (*tmcoretypes.ResultBroadcastTx, error) { - resp, err := vc.app.CheckTx(context.Background(), &abcitypes.RequestCheckTx{Tx: tx}) + resp, err := vc.App.CheckTx(context.Background(), &abcitypes.RequestCheckTx{Tx: tx}) if err != nil { return nil, err } @@ -421,7 +400,7 @@ txLoop: break txLoop } // ensure all txs are still valid valid - resp, err := vc.app.CheckTx(context.Background(), &abcitypes.RequestCheckTx{Tx: tx}) + resp, err := vc.App.CheckTx(context.Background(), &abcitypes.RequestCheckTx{Tx: tx}) if err != nil { log.Errorw(err, "error on check tx") continue @@ -524,7 +503,7 @@ func vochainPrintInfo(sleepSecs int64, vi *vochaininfo.VochainInfo) { // SetChainID sets the chainID for the vocone instance func (vc *Vocone) SetChainID(chainID string) { - vc.app.SetChainID(chainID) + vc.App.SetChainID(chainID) } func defaultTxCosts() genesis.TransactionCosts { diff --git a/vocone/vocone_test.go b/vocone/vocone_test.go index 628c31c0c..ac5196382 100644 --- a/vocone/vocone_test.go +++ b/vocone/vocone_test.go @@ -31,7 +31,7 @@ func TestVocone(t *testing.T) { err = account.Generate() qt.Assert(t, err, qt.IsNil) - vc, err := NewVocone(dir, &keymng, true) + vc, err := NewVocone(dir, &keymng, false) qt.Assert(t, err, qt.IsNil) vc.SetBlockTimeTarget(time.Millisecond * 500) From 7acab6ee21f2211f4c4b211563400fd4d185c04c Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 2 Oct 2023 18:07:41 +0200 Subject: [PATCH 07/12] node: cosmetic refactor, KeyKeeper is now a field of VocdoniService --- cmd/node/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/node/main.go b/cmd/node/main.go index 109b7d867..7d54089d6 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -387,7 +387,6 @@ func main() { } var err error - var vochainKeykeeper *keykeeper.KeyKeeper srv := service.VocdoniService{Config: globalCfg.Vochain} if globalCfg.Mode == types.ModeGateway { @@ -503,7 +502,7 @@ func main() { } else { // start keykeeper service (if key index specified) if validator.KeyIndex > 0 { - vochainKeykeeper, err = keykeeper.NewKeyKeeper( + srv.KeyKeeper, err = keykeeper.NewKeyKeeper( path.Join(globalCfg.Vochain.DataDir, "keykeeper"), srv.App, &signer, @@ -511,7 +510,7 @@ func main() { if err != nil { log.Fatal(err) } - go vochainKeykeeper.RevealUnpublished() + go srv.KeyKeeper.RevealUnpublished() } else { log.Warnw("validator keyIndex disabled") } From 0a35fad82ede7eace5e23a62de4da7ca6419e92b Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Tue, 3 Oct 2023 14:07:15 +0200 Subject: [PATCH 08/12] voconed: add flags ipfsConnectKey, ipfsConnectPeers --- cmd/voconed/voconed.go | 15 ++++++++++++++- vocone/vocone.go | 6 ++++-- vocone/vocone_test.go | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cmd/voconed/voconed.go b/cmd/voconed/voconed.go index e39b0c21c..f70e20115 100644 --- a/cmd/voconed/voconed.go +++ b/cmd/voconed/voconed.go @@ -31,6 +31,8 @@ type VoconeConfig struct { disableIpfs bool fundedAccounts []string enableFaucetWithAmount uint64 + ipfsConnectKey string + ipfsConnectPeers []string } func main() { @@ -56,6 +58,11 @@ func main() { flag.Uint64Var(&config.txCosts, "txCosts", vocone.DefaultTxCosts, "transaction costs for all types") flag.Uint64Var(&config.enableFaucetWithAmount, "enableFaucet", 0, "enable faucet API service for the given amount") flag.BoolVar(&config.disableIpfs, "disableIpfs", false, "disable built-in IPFS node") + flag.StringVarP(&config.ipfsConnectKey, "ipfsConnectKey", "i", "", + "enable IPFS group synchronization using the given secret key") + flag.StringSliceVar(&config.ipfsConnectPeers, "ipfsConnectPeers", []string{}, + "use custom ipfsconnect peers/bootnodes for accessing the DHT (comma-separated)") + flag.StringSliceVar(&config.fundedAccounts, "fundedAccounts", []string{}, "list of pre-funded accounts (address:balance,address:balance,...)") flag.CommandLine.SortFlags = false @@ -127,6 +134,12 @@ func main() { } *setTxCosts = pviper.GetBool("setTxCosts") + pviper.BindPFlag("ipfsConnectKey", flag.Lookup("ipfsConnectKey")) + config.ipfsConnectKey = pviper.GetString("ipfsConnectKey") + + pviper.BindPFlag("ipfsConnectPeers", flag.Lookup("ipfsConnectPeers")) + config.ipfsConnectPeers = pviper.GetStringSlice("ipfsConnectPeers") + _, err = os.Stat(filepath.Join(config.dir, "voconed.yml")) if err != nil { if os.IsNotExist(err) { @@ -170,7 +183,7 @@ func main() { log.Fatal(err) } - vc, err := vocone.NewVocone(config.dir, &mngKey, config.disableIpfs) + vc, err := vocone.NewVocone(config.dir, &mngKey, config.disableIpfs, config.ipfsConnectKey, config.ipfsConnectPeers) if err != nil { log.Fatal(err) } diff --git a/vocone/vocone.go b/vocone/vocone.go index 864fd91d1..c23ee1ca0 100644 --- a/vocone/vocone.go +++ b/vocone/vocone.go @@ -59,7 +59,7 @@ type Vocone struct { } // NewVocone returns a ready Vocone instance. -func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool) (*Vocone, error) { +func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool, connectKey string, connectPeers []string) (*Vocone, error) { var err error vc := &Vocone{} @@ -106,7 +106,9 @@ func NewVocone(dataDir string, keymanager *ethereum.SignKeys, disableIPFS bool) // Create the IPFS storage layer if !disableIPFS { vc.Storage, err = vc.IPFS(&config.IPFSCfg{ - ConfigPath: filepath.Join(dataDir, "ipfs"), + ConfigPath: filepath.Join(dataDir, "ipfs"), + ConnectKey: connectKey, + ConnectPeers: connectPeers, }) if err != nil { return nil, err diff --git a/vocone/vocone_test.go b/vocone/vocone_test.go index ac5196382..326ff91ea 100644 --- a/vocone/vocone_test.go +++ b/vocone/vocone_test.go @@ -31,7 +31,7 @@ func TestVocone(t *testing.T) { err = account.Generate() qt.Assert(t, err, qt.IsNil) - vc, err := NewVocone(dir, &keymng, false) + vc, err := NewVocone(dir, &keymng, false, "", nil) qt.Assert(t, err, qt.IsNil) vc.SetBlockTimeTarget(time.Millisecond * 500) From 7c854c5fe950bee228a657ab49a20b1cc2ce47e6 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 2 Oct 2023 13:57:25 +0200 Subject: [PATCH 09/12] zkCircuits: fix BaseDir handling until now, voconed didn't put the files inside VOCONED_DIR now: * `go test ./...` uses ~/.cache/vocdoni/zkCircuits/ * `node` uses ~/.vocdoni/zkCircuits/ * `voconed` uses ~/.voconed/zkCircuits/ and both `node` and `voconed` use `/app/run/zkCircuits` when inside docker --- cmd/node/main.go | 2 +- cmd/voconed/voconed.go | 5 +++++ crypto/zk/circuit/circuit.go | 20 ++++++++------------ dockerfiles/vocone/.env | 1 + 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/cmd/node/main.go b/cmd/node/main.go index 7d54089d6..3cd678d11 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -348,7 +348,7 @@ func main() { // Overwrite the default path to download the zksnarks circuits artifacts // using the global datadir as parent folder. - circuit.BaseDir = filepath.Join(globalCfg.DataDir, circuit.BaseDir) + circuit.BaseDir = filepath.Join(globalCfg.DataDir, "zkCircuits") // Ensure we can have at least 8k open files. This is necessary, since // many components like IPFS and Tendermint require keeping many active diff --git a/cmd/voconed/voconed.go b/cmd/voconed/voconed.go index f70e20115..33c478462 100644 --- a/cmd/voconed/voconed.go +++ b/cmd/voconed/voconed.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/viper" "go.vocdoni.io/dvote/api/faucet" "go.vocdoni.io/dvote/crypto/ethereum" + "go.vocdoni.io/dvote/crypto/zk/circuit" "go.vocdoni.io/dvote/internal" "go.vocdoni.io/dvote/log" "go.vocdoni.io/dvote/vochain/state" @@ -178,6 +179,10 @@ func main() { log.Infof("using data directory at %s", config.dir) + // Overwrite the default path to download the zksnarks circuits artifacts + // using the global datadir as parent folder. + circuit.BaseDir = filepath.Join(config.dir, "zkCircuits") + mngKey := ethereum.SignKeys{} if err := mngKey.AddHexKey(config.keymanager); err != nil { log.Fatal(err) diff --git a/crypto/zk/circuit/circuit.go b/crypto/zk/circuit/circuit.go index b2c732411..ad38448b7 100644 --- a/crypto/zk/circuit/circuit.go +++ b/crypto/zk/circuit/circuit.go @@ -19,21 +19,17 @@ var downloadCircuitsTimeout = time.Minute * 5 // BaseDir is where the artifact cache is expected to be found. // If the artifacts are not found there, they will be downloaded and stored. -// If unset ("") it will default to ~/.cache/vocdoni/zkCircuits/ +// +// Defaults to ~/.cache/vocdoni/zkCircuits/ // // In any case, the LocalDir path associated with the circuit config will be appended at the end -var BaseDir = "" - -func init() { - // if base dir is unset, default to ~/.cache/vocdoni/ - if BaseDir == "" { - home, err := os.UserHomeDir() - if err != nil { - panic(err) - } - BaseDir = home + "/.cache/vocdoni/zkCircuits" +var BaseDir = func() string { + home, err := os.UserHomeDir() + if err != nil { + panic(err) } -} + return filepath.Join(home, ".cache", "vocdoni", "zkCircuits") +}() // ZkCircuit struct wraps the circuit configuration and contains the file // content of the circuit artifacts (provingKey, verificationKey and wasm) diff --git a/dockerfiles/vocone/.env b/dockerfiles/vocone/.env index d47766ad2..a9a68f933 100644 --- a/dockerfiles/vocone/.env +++ b/dockerfiles/vocone/.env @@ -1,5 +1,6 @@ IMAGE_TAG=main # VOCONED_KEYMANAGER=571063b70545d01b8c130bb411b5f0220e06b8db236bb12e77ae478f7078b77b +VOCONED_DIR=/app/run VOCONED_PORT=9090 VOCONED_URLPATH=/v2 VOCONED_BLOCKPERIOD=5 From 35a1ad425753ee28564f6ddd12912aa6cb7f6218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Jos=C3=A9=20D=C3=A1vila?= Date: Mon, 9 Oct 2023 07:23:00 -0400 Subject: [PATCH 10/12] remove extra newline not needed, fix method comments, fix typos, remove redundant type conversion (#1104) remove extra newline not needed, fix method comments, fix typos, remove redundant type conversion vochain/genesis: update types to remove package name, it will be redundant calling genesis.GenesisAppState, cmd/cli: fix exported function with the unexported return type util/net: fix unhandled error adding comments deepsource issues: removed method receiver 'd' that is not used replace len(s.Topic) == 0 with s.Topic == "" --- api/censuses.go | 2 +- api/elections.go | 2 +- api/faucet/faucet.go | 2 +- api/wallet.go | 2 +- cmd/cli/main.go | 26 ++++++------ cmd/cli/vocdonicli.go | 26 ++++++------ cmd/node/main.go | 2 +- config/config.go | 2 +- data/downloader/downloader.go | 2 +- data/ipfs/init.go | 6 ++- httprouter/httprouter.go | 1 - log/log.go | 2 +- statedb/statedb.go | 4 +- subpub/subpub.go | 4 +- types/consts.go | 40 ++++++++----------- util/net.go | 4 +- vochain/app.go | 6 +-- vochain/apputils.go | 4 +- vochain/genesis/genesis.go | 23 +++++------ vochain/genesis/types.go | 37 +++++++++-------- vochain/indexer/vote.go | 2 +- vochain/ist/ist.go | 2 +- vochain/keykeeper/keykeeper.go | 2 +- .../offchaindatahandler.go | 2 +- vochain/transaction/vote_tx.go | 2 +- vochain/vochaininfo/vochaininfo.go | 12 +++--- vocone/vocone.go | 2 +- 27 files changed, 110 insertions(+), 111 deletions(-) diff --git a/api/censuses.go b/api/censuses.go index cf3682fbf..30f2ed4a1 100644 --- a/api/censuses.go +++ b/api/censuses.go @@ -273,7 +273,7 @@ func (a *API) censusAddHandler(msg *apirest.APIdata, ctx *httprouter.HTTPContext // censusTypeHandler // // @Summary Get type of census -// @Description Get the type of a census +// @Description Get the census type // @Tags Censuses // @Accept json // @Produce json diff --git a/api/elections.go b/api/elections.go index ede86c8d6..032fa3037 100644 --- a/api/elections.go +++ b/api/elections.go @@ -147,7 +147,7 @@ func (a *API) electionFullListHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCo } list = append(list, a.electionSummary(e)) } - // wrap list in a struct to consistently return list in a object, return empty + // wrap list in a struct to consistently return list in an object, return empty // object if the list does not contains any result data, err := json.Marshal(struct { Elections []ElectionSummary `json:"elections"` diff --git a/api/faucet/faucet.go b/api/faucet/faucet.go index 7d72fd27e..92d3f2e5e 100644 --- a/api/faucet/faucet.go +++ b/api/faucet/faucet.go @@ -17,7 +17,7 @@ const ( FaucetHandler = "faucet" ) -// FaucetAPI is an httprouter/apirest handler for the faucet. +// FaucetAPI is a httprouter/apirest handler for the faucet. // It generates a signed package that can be used to request tokens from the faucet. type FaucetAPI struct { signingKey *ethereum.SignKeys diff --git a/api/wallet.go b/api/wallet.go index b0214b095..509e07908 100644 --- a/api/wallet.go +++ b/api/wallet.go @@ -132,7 +132,7 @@ func (a *API) walletSignAndSendTx(stx *models.SignedTx, wallet *ethereum.SignKey // walletAddHandler // // @Summary Add account -// @Description Add a new account to the local store. It return a token used to manage this account on the future. +// @Description Add a new account to the local store. It returns a token used to manage this account on the future. // @Tags Wallet // @Accept json // @Produce json diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 8dcf673e3..0499b210e 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -158,11 +158,11 @@ func main() { } -func accountIsSet(c *vocdoniCLI) bool { +func accountIsSet(c *VocdoniCLI) bool { return c.currentAccount >= 0 } -func accountHandler(c *vocdoniCLI) error { +func accountHandler(c *VocdoniCLI) error { accountAddNewStr := "-> import an account (from hexadecimal private key)" accountGenerateStr := "-> generate a new account" p := ui.Select{ @@ -193,7 +193,7 @@ func accountHandler(c *vocdoniCLI) error { return nil } -func accountSet(c *vocdoniCLI) error { +func accountSet(c *VocdoniCLI) error { p := ui.Prompt{ Label: "Account private key", } @@ -212,7 +212,7 @@ func accountSet(c *vocdoniCLI) error { return c.setAPIaccount(key, memo) } -func accountGen(c *vocdoniCLI) error { +func accountGen(c *VocdoniCLI) error { p := ui.Prompt{ Label: "Account memo note", } @@ -225,7 +225,7 @@ func accountGen(c *vocdoniCLI) error { return c.setAPIaccount(key, memo) } -func accountInfo(c *vocdoniCLI) error { +func accountInfo(c *VocdoniCLI) error { acc, err := c.api.Account("") if err != nil { return err @@ -243,7 +243,7 @@ func accountInfo(c *vocdoniCLI) error { if acc.Metadata != nil { accMetadata, err := json.MarshalIndent(acc.Metadata, "", " ") if err != nil { - log.Debug("account metadta cannot be unmarshal") + log.Debug("account metadata cannot be unmarshal") } else { fmt.Printf("%s:\n%s\n", keysPrint.Sprintf(" ➥ metadata"), valuesPrint.Sprintf("%s", accMetadata)) } @@ -252,7 +252,7 @@ func accountInfo(c *vocdoniCLI) error { return nil } -func networkInfo(cli *vocdoniCLI) error { +func networkInfo(cli *VocdoniCLI) error { info, err := cli.api.ChainInfo() if err != nil { return err @@ -265,7 +265,7 @@ func networkInfo(cli *vocdoniCLI) error { return nil } -func bootStrapAccount(cli *vocdoniCLI) error { +func bootStrapAccount(cli *VocdoniCLI) error { var faucetPkg *models.FaucetPackage p := ui.Prompt{ Label: "Do you have a faucet package? [y,n]", @@ -322,7 +322,7 @@ func bootStrapAccount(cli *vocdoniCLI) error { return nil } -func transfer(cli *vocdoniCLI) error { +func transfer(cli *VocdoniCLI) error { s := ui.Select{ Label: "Select a destination account", Items: append(cli.listAccounts(), "to external account"), @@ -397,10 +397,10 @@ func transfer(cli *vocdoniCLI) error { return nil } -func hostHandler(cli *vocdoniCLI) error { +func hostHandler(cli *VocdoniCLI) error { validateFunc := func(url string) error { log.Debugf("performing ping test to %s", url) - _, err := http.NewRequest("GET", url+"/ping", nil) + _, err := http.NewRequest("GET", url+"/ping", http.NoBody) return err } p := ui.Prompt{ @@ -431,7 +431,7 @@ func hostHandler(cli *vocdoniCLI) error { return cli.setAuthToken(token) } -func accountSetMetadata(cli *vocdoniCLI) error { +func accountSetMetadata(cli *VocdoniCLI) error { currentAccount, err := cli.api.Account("") if err != nil { return err @@ -515,7 +515,7 @@ func accountSetMetadata(cli *vocdoniCLI) error { } -func electionHandler(cli *vocdoniCLI) error { +func electionHandler(cli *VocdoniCLI) error { infoPrint.Printf("preparing the election template...\n") description := api.ElectionDescription{ Title: map[string]string{"default": "election title"}, diff --git a/cmd/cli/vocdonicli.go b/cmd/cli/vocdonicli.go index 9141d5b70..5bd5c287b 100644 --- a/cmd/cli/vocdonicli.go +++ b/cmd/cli/vocdonicli.go @@ -54,7 +54,7 @@ type Account struct { PublicKey types.HexBytes `json:"pubKey"` } -type vocdoniCLI struct { +type VocdoniCLI struct { filepath string config *Config api *apiclient.HTTPclient @@ -63,7 +63,7 @@ type vocdoniCLI struct { currentAccount int } -func NewVocdoniCLI(configFile, host string) (*vocdoniCLI, error) { +func NewVocdoniCLI(configFile, host string) (*VocdoniCLI, error) { cfg := Config{} if err := cfg.Load(configFile); err != nil { return nil, err @@ -101,7 +101,7 @@ func NewVocdoniCLI(configFile, host string) (*vocdoniCLI, error) { return nil, err } } - return &vocdoniCLI{ + return &VocdoniCLI{ filepath: configFile, config: &cfg, api: api, @@ -110,7 +110,7 @@ func NewVocdoniCLI(configFile, host string) (*vocdoniCLI, error) { }, nil } -func (v *vocdoniCLI) setHost(host string) error { +func (v *VocdoniCLI) setHost(host string) error { u, err := url.Parse(host) if err != nil { return err @@ -127,7 +127,7 @@ func (v *vocdoniCLI) setHost(host string) error { return v.save() } -func (v *vocdoniCLI) setAuthToken(token string) error { +func (v *VocdoniCLI) setAuthToken(token string) error { t, err := uuid.Parse(token) if err != nil { return err @@ -137,7 +137,7 @@ func (v *vocdoniCLI) setAuthToken(token string) error { return v.save() } -func (v *vocdoniCLI) useAccount(index int) error { +func (v *VocdoniCLI) useAccount(index int) error { if index >= len(v.config.Accounts) { return fmt.Errorf("account %d does not exist", index) } @@ -149,21 +149,21 @@ func (v *vocdoniCLI) useAccount(index int) error { return v.api.SetAccount(v.config.Accounts[index].PrivKey.String()) } -func (v *vocdoniCLI) getAccount(index int) (*Account, error) { +func (v *VocdoniCLI) getAccount(index int) (*Account, error) { if index >= len(v.config.Accounts) { return nil, fmt.Errorf("account %d does not exist", index) } return &v.config.Accounts[index], nil } -func (v *vocdoniCLI) getCurrentAccount() *Account { +func (v *VocdoniCLI) getCurrentAccount() *Account { if v.currentAccount < 0 { return nil } return &v.config.Accounts[v.currentAccount] } -func (v *vocdoniCLI) setAPIaccount(key, memo string) error { +func (v *VocdoniCLI) setAPIaccount(key, memo string) error { if err := v.api.SetAccount(key); err != nil { return err } @@ -198,7 +198,7 @@ func (v *vocdoniCLI) setAPIaccount(key, memo string) error { } // listAccounts list the memo notes of all stored accounts -func (v *vocdoniCLI) listAccounts() []string { +func (v *VocdoniCLI) listAccounts() []string { accounts := []string{} for _, a := range v.config.Accounts { accounts = append(accounts, a.Memo) @@ -206,12 +206,12 @@ func (v *vocdoniCLI) listAccounts() []string { return accounts } -func (v *vocdoniCLI) transactionMined(txHash types.HexBytes) bool { +func (v *VocdoniCLI) transactionMined(txHash types.HexBytes) bool { _, err := v.api.TransactionReference(txHash) return err == nil } -func (v *vocdoniCLI) waitForTransaction(txHash types.HexBytes) bool { +func (v *VocdoniCLI) waitForTransaction(txHash types.HexBytes) bool { startTime := time.Now() for time.Now().Before(startTime.Add(transactionConfirmationThreshold)) { if v.transactionMined(txHash) { @@ -222,6 +222,6 @@ func (v *vocdoniCLI) waitForTransaction(txHash types.HexBytes) bool { return false } -func (v *vocdoniCLI) save() error { +func (v *VocdoniCLI) save() error { return v.config.Save(v.filepath) } diff --git a/cmd/node/main.go b/cmd/node/main.go index 3cd678d11..6ee6267b5 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -438,7 +438,7 @@ func main() { // set IsSeedNode to true if seed mode configured globalCfg.Vochain.IsSeedNode = types.ModeSeed == globalCfg.Mode // do we need indexer? - globalCfg.Vochain.Indexer.Enabled = (globalCfg.Mode == types.ModeGateway) + globalCfg.Vochain.Indexer.Enabled = globalCfg.Mode == types.ModeGateway // offchainDataDownloader is only needed for gateways globalCfg.Vochain.OffChainDataDownloader = globalCfg.Vochain.OffChainDataDownloader && globalCfg.Mode == types.ModeGateway diff --git a/config/config.go b/config/config.go index 5d2c5ad54..51307e15f 100644 --- a/config/config.go +++ b/config/config.go @@ -8,7 +8,7 @@ import ( type Config struct { // Vochain config options Vochain *VochainCfg - // Ipfs ipfs config options + // Ipfs config options Ipfs *IPFSCfg // Metrics config options Metrics *MetricsCfg diff --git a/data/downloader/downloader.go b/data/downloader/downloader.go index 17521f547..e90ec1b50 100644 --- a/data/downloader/downloader.go +++ b/data/downloader/downloader.go @@ -21,7 +21,7 @@ const ( // ImportRetrieveTimeout the maximum duration the import queue will wait // for retrieving a remote file. ImportRetrieveTimeout = 5 * time.Minute - // ImportQueueTimeout is the maximum duration the import queue will wait + // ImportPinTimeout is the maximum duration the import queue will wait // for pinning a remote file. ImportPinTimeout = 3 * time.Minute // MaxFileSize is the maximum size of a file that can be imported. diff --git a/data/ipfs/init.go b/data/ipfs/init.go index 6fc2fabcf..6be4be748 100644 --- a/data/ipfs/init.go +++ b/data/ipfs/init.go @@ -13,7 +13,7 @@ import ( ihelper "github.com/ipfs/boxo/ipld/unixfs/importer/helpers" ipfscid "github.com/ipfs/go-cid" "github.com/ipfs/kubo/commands" - config "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/config" ipfscore "github.com/ipfs/kubo/core" ipfsapi "github.com/ipfs/kubo/core/coreapi" "github.com/ipfs/kubo/plugin/loader" @@ -236,7 +236,9 @@ func checkWritable(dir string) error { } return fmt.Errorf("unexpected error while checking writeablility of repo root: %s", err) } - fi.Close() + if err := fi.Close(); err != nil { + return err + } return os.Remove(testfile) } diff --git a/httprouter/httprouter.go b/httprouter/httprouter.go index 693f340bb..62514a1f6 100644 --- a/httprouter/httprouter.go +++ b/httprouter/httprouter.go @@ -158,7 +158,6 @@ func (r *HTTProuter) Init(host string, port int) error { } r.address = ln.Addr() return nil - } // EnablePrometheusMetrics enables go-chi prometheus metrics under specified ID. diff --git a/log/log.go b/log/log.go index 0bdf13b42..552673917 100644 --- a/log/log.go +++ b/log/log.go @@ -49,7 +49,7 @@ var logTestTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") type testHook struct{} -// To ensure that the log output in the test is deterministic. +// Run ensure that the log output in the test is deterministic. func (*testHook) Run(e *zerolog.Event, _ zerolog.Level, _ string) { e.Stringer("time", logTestTime) } diff --git a/statedb/statedb.go b/statedb/statedb.go index e9f5dfa15..8907ca087 100644 --- a/statedb/statedb.go +++ b/statedb/statedb.go @@ -360,7 +360,7 @@ func (*readOnlyWriteTx) Set(_ []byte, _ []byte) error { return ErrReadOnly } -// Set implements db.WriteTx.Delete but returns error always. +// Delete implements db.WriteTx.Delete but returns error always. func (*readOnlyWriteTx) Delete(_ []byte) error { return ErrReadOnly } @@ -373,7 +373,7 @@ func (*readOnlyWriteTx) Apply(_ db.WriteTx) error { // Commit implements db.WriteTx.Commit but returns nil always. func (*readOnlyWriteTx) Commit() error { return nil } -// Commit implements db.WriteTx.Discard as a no-op. +// Discard implements db.WriteTx.Discard as a no-op. func (*readOnlyWriteTx) Discard() {} // TreeView returns the mainTree opened at root as a TreeView for read-only. diff --git a/subpub/subpub.go b/subpub/subpub.go index 25efb6cc4..069cc6c5b 100644 --- a/subpub/subpub.go +++ b/subpub/subpub.go @@ -27,7 +27,7 @@ const ( // We use go-bare for export/import the trie. In order to support // big census (up to 8 Million entries) we need to increase the maximums. bareMaxArrayLength uint64 = 1024 * 1014 * 8 // 8 Million entries - bareMaxUnmarshalBytes uint64 = bareMaxArrayLength * 32 // Assuming 32 bytes per entry + bareMaxUnmarshalBytes = bareMaxArrayLength * 32 // Assuming 32 bytes per entry ) // SubPub is a simplified PubSub protocol using libp2p. @@ -90,7 +90,7 @@ func NewSubPub(groupKey [32]byte, node *core.IpfsNode) *SubPub { // Start connects the SubPub networking stack // and begins passing incoming messages to the receiver chan func (s *SubPub) Start(ctx context.Context, receiver chan *Message) { - if len(s.Topic) == 0 { + if s.Topic == "" { log.Fatal("no group key provided") } ipfslog.SetLogLevel("*", "ERROR") diff --git a/types/consts.go b/types/consts.go index 5e8231a37..0a83dfdf5 100644 --- a/types/consts.go +++ b/types/consts.go @@ -30,7 +30,7 @@ const ( // EthereumAddressSize is the size of an ethereum address EthereumAddressSize = 20 - // EntityIDsizeV2 legacy: in the past we used hash(addr) + // EntityIDsize V2 legacy: in the past we used hash(addr) // this is a temporal work around to support both EntityIDsize = 20 // KeyIndexSeparator is the default char used to split keys @@ -41,52 +41,46 @@ const ( // ENS Domains - // ENTITY RESOLVER - // EntityResolverDomain is the default entity resolver ENS domain + // EntityResolverDomain default entity resolver ENS domain EntityResolverDomain = "entities.voc.eth" // EntityResolverStageDomain is the default entity resolver ENS domain EntityResolverStageDomain = "entities.stg.voc.eth" // EntityResolverDevelopmentDomain is the default entity resolver ENS domain EntityResolverDevelopmentDomain = "entities.dev.voc.eth" - // PROCESSES - // ProcessesDomain + // ProcessesDomain default process domain ProcessesDomain = "processes.voc.eth" - // ProcessesStageDomain + // ProcessesStageDomain stage process domain ProcessesStageDomain = "processes.stg.voc.eth" - // ProcessesDevelopmentDomain + // ProcessesDevelopmentDomain dev process domain ProcessesDevelopmentDomain = "processes.dev.voc.eth" - // NAMESPACES - // NamespacesDomain + // NamespacesDomain default namespace domain NamespacesDomain = "namespaces.voc.eth" - // NamespacesStageDomain + // NamespacesStageDomain stage namespace domain NamespacesStageDomain = "namespaces.stg.voc.eth" - // NamespacesDevelopmentDomain + // NamespacesDevelopmentDomain dev namespace domain NamespacesDevelopmentDomain = "namespaces.dev.voc.eth" - // ERC20 PROOFS - // ERC20ProofsDomain + // ERC20ProofsDomain default domain for erc20 proofs ERC20ProofsDomain = "erc20.proofs.voc.eth" - // ERC20ProofsStageDomain + // ERC20ProofsStageDomain domain for erc20 proofs stage ERC20ProofsStageDomain = "erc20.proofs.stg.voc.eth" - // ERC20ProofsDevelopmentDomain + // ERC20ProofsDevelopmentDomain domain for erc20 proofs dev ERC20ProofsDevelopmentDomain = "erc20.proofs.dev.voc.eth" - // GENESIS - // GenesisDomain + // GenesisDomain default genesis domain GenesisDomain = "genesis.voc.eth" - // GenesisStageDomain + // GenesisStageDomain stage genesis domain GenesisStageDomain = "genesis.stg.voc.eth" - // GenesisDevelopmentDomain + // GenesisDevelopmentDomain dev genesis domain GenesisDevelopmentDomain = "genesis.dev.voc.eth" - // RESULTS - // ResultsDomain + // ResultsDomain default results domain ResultsDomain = "results.voc.eth" - // ResultsStageDomain + // ResultsStageDomain stage results domain ResultsStageDomain = "results.stg.voc.eth" - // ResultsDevelopmentDomain + // ResultsDevelopmentDomain dev results domain ResultsDevelopmentDomain = "results.dev.voc.eth" // EntityMetaKey is the key of an ENS text record for the entity metadata diff --git a/util/net.go b/util/net.go index 16d00d628..b22c4099f 100644 --- a/util/net.go +++ b/util/net.go @@ -22,6 +22,8 @@ func PublicIP(ipversion uint) (net.IP, error) { config := externalip.DefaultConsensusConfig().WithTimeout(publicIPTimeout) consensus := externalip.DefaultConsensus(config, nil) - consensus.UseIPProtocol(ipversion) + if err := consensus.UseIPProtocol(ipversion); err != nil { + return nil, err + } return consensus.ExternalIP() } diff --git a/vochain/app.go b/vochain/app.go index 6121778eb..e86f4a7dc 100644 --- a/vochain/app.go +++ b/vochain/app.go @@ -160,7 +160,7 @@ func (app *BaseApplication) InitChain(_ context.Context, req *abcitypes.RequestInitChain) (*abcitypes.ResponseInitChain, error) { // setting the app initial state with validators, height = 0 and empty apphash // unmarshal app state from genesis - var genesisAppState genesis.GenesisAppState + var genesisAppState genesis.AppState err := json.Unmarshal(req.AppStateBytes, &genesisAppState) if err != nil { return nil, fmt.Errorf("cannot unmarshal app state bytes: %w", err) @@ -583,7 +583,7 @@ func (app *BaseApplication) CircuitConfigurationTag() string { return app.circuitConfigTag } -// IsSynchronizing informes if the blockchain is synchronizing or not. +// IsSynchronizing informs if the blockchain is synchronizing or not. func (app *BaseApplication) isSynchronizingTendermint() bool { if app.Node == nil { return true @@ -591,7 +591,7 @@ func (app *BaseApplication) isSynchronizingTendermint() bool { return app.Node.ConsensusReactor().WaitSync() } -// IsSynchronizing informes if the blockchain is synchronizing or not. +// IsSynchronizing informs if the blockchain is synchronizing or not. // The value is updated every new block. func (app *BaseApplication) IsSynchronizing() bool { return app.isSynchronizing.Load() diff --git a/vochain/apputils.go b/vochain/apputils.go index db6a4e05f..8d59bef8c 100644 --- a/vochain/apputils.go +++ b/vochain/apputils.go @@ -174,10 +174,10 @@ func NewTemplateGenesisFile(dir string, validators int) (*tmtypes.GenesisDoc, er } // Build genesis app state and create genesis file - appState := genesis.GenesisAppState{ + appState := genesis.AppState{ Validators: appStateValidators, Treasurer: types.HexBytes(treasurer.Address().Bytes()), - Accounts: []genesis.GenesisAccount{ + Accounts: []genesis.Account{ { Address: faucet.Address().Bytes(), Balance: 100000, diff --git a/vochain/genesis/genesis.go b/vochain/genesis/genesis.go index c0c018e81..ea96f3603 100644 --- a/vochain/genesis/genesis.go +++ b/vochain/genesis/genesis.go @@ -7,8 +7,7 @@ import ( ) // Genesis is a map containing the default Genesis details -var Genesis = map[string]VochainGenesis{ - +var Genesis = map[string]Vochain{ // Development network "dev": { AutoUpdateGenesis: true, @@ -39,7 +38,7 @@ var Genesis = map[string]VochainGenesis{ }, } -var devGenesis = GenesisDoc{ +var devGenesis = Doc{ GenesisTime: time.Date(2023, time.September, 21, 1, 0, 0, 0, time.UTC), ChainID: "vocdoni-dev-20", ConsensusParams: &ConsensusParams{ @@ -58,7 +57,7 @@ var devGenesis = GenesisDoc{ AppVersion: 0, }, }, - AppState: GenesisAppState{ + AppState: AppState{ MaxElectionSize: 100000, NetworkCapacity: 20000, Validators: []AppStateValidators{ @@ -91,7 +90,7 @@ var devGenesis = GenesisDoc{ KeyIndex: 4, }, }, - Accounts: []GenesisAccount{ + Accounts: []Account{ { // faucet Address: types.HexStringToHexBytes("0xC7C6E17059801b6962cc144a374eCc3ba1b8A9e0"), Balance: 100000000, @@ -116,7 +115,7 @@ var devGenesis = GenesisDoc{ }, } -var stageGenesis = GenesisDoc{ +var stageGenesis = Doc{ GenesisTime: time.Date(2023, time.September, 21, 1, 0, 0, 0, time.UTC), ChainID: "vocdoni-stage-8", ConsensusParams: &ConsensusParams{ @@ -135,7 +134,7 @@ var stageGenesis = GenesisDoc{ AppVersion: 0, }, }, - AppState: GenesisAppState{ + AppState: AppState{ MaxElectionSize: 50000, NetworkCapacity: 2000, Validators: []AppStateValidators{ @@ -210,7 +209,7 @@ var stageGenesis = GenesisDoc{ KeyIndex: 4, }, }, - Accounts: []GenesisAccount{ + Accounts: []Account{ { // faucet Address: types.HexStringToHexBytes("0xC7C6E17059801b6962cc144a374eCc3ba1b8A9e0"), Balance: 1000000000, @@ -235,7 +234,7 @@ var stageGenesis = GenesisDoc{ }, } -var apexGenesis = GenesisDoc{ +var apexGenesis = Doc{ GenesisTime: time.Date(2023, time.May, 24, 9, 0, 0, 0, time.UTC), ChainID: "vocdoni-apex-v1", ConsensusParams: &ConsensusParams{ @@ -254,7 +253,7 @@ var apexGenesis = GenesisDoc{ AppVersion: 0, }, }, - AppState: GenesisAppState{ + AppState: AppState{ MaxElectionSize: 1000000, NetworkCapacity: 5000, Validators: []AppStateValidators{ @@ -311,7 +310,7 @@ var apexGenesis = GenesisDoc{ Address: types.HexStringToHexBytes("4945fd40d29870a931561b26a30a529081ded677"), PubKey: types.HexStringToHexBytes("038276c348971ef9d8b11abaf0cdce50e6cb89bd0f87df14301ef02d46db09db6d"), Power: 10, - Name: "vocdoni-validtaor7", + Name: "vocdoni-validator7", KeyIndex: 0, }, { // 8 @@ -322,7 +321,7 @@ var apexGenesis = GenesisDoc{ KeyIndex: 4, }, }, - Accounts: []GenesisAccount{ + Accounts: []Account{ { // faucet Address: types.HexStringToHexBytes("863a75f41025f0c8878d3a100c8c16576fe8fe4f"), Balance: 10000000, diff --git a/vochain/genesis/types.go b/vochain/genesis/types.go index e840e6425..a4b3da187 100644 --- a/vochain/genesis/types.go +++ b/vochain/genesis/types.go @@ -11,12 +11,12 @@ import ( "go.vocdoni.io/dvote/types" ) -// VochainGenesis is a struct containing the genesis details. -type VochainGenesis struct { +// Vochain is a struct containing the genesis details. +type Vochain struct { AutoUpdateGenesis bool SeedNodes []string CircuitsConfigTag string - Genesis *GenesisDoc + Genesis *Doc } // The genesis app state types are copied from @@ -24,18 +24,18 @@ type VochainGenesis struct { // lightweight and not have it import heavy indirect dependencies like grpc or // crypto/*. -// GenesisDoc defines the initial conditions for a Vocdoni blockchain. +// Doc defines the initial conditions for a Vocdoni blockchain. // It is mostly a wrapper around the Tendermint GenesisDoc. -type GenesisDoc struct { +type Doc struct { GenesisTime time.Time `json:"genesis_time"` ChainID string `json:"chain_id"` ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"` AppHash types.HexBytes `json:"app_hash"` - AppState GenesisAppState `json:"app_state,omitempty"` + AppState AppState `json:"app_state,omitempty"` } -// TendermintDoc returns the Tendermint GenesisDoc from the Vocdoni GenesisDoc. -func (g *GenesisDoc) TendermintDoc() tmtypes.GenesisDoc { +// TendermintDoc returns the Tendermint GenesisDoc from the Vocdoni genesis.Doc. +func (g *Doc) TendermintDoc() tmtypes.GenesisDoc { appState, err := json.Marshal(g.AppState) if err != nil { // must never happen @@ -56,8 +56,8 @@ func (g *GenesisDoc) TendermintDoc() tmtypes.GenesisDoc { } } -// Marshal returns the JSON encoding of the GenesisDoc. -func (g *GenesisDoc) Marshal() []byte { +// Marshal returns the JSON encoding of the genesis.Doc. +func (g *Doc) Marshal() []byte { data, err := json.Marshal(g) if err != nil { panic(err) @@ -65,8 +65,8 @@ func (g *GenesisDoc) Marshal() []byte { return data } -// Hash returns the hash of the GenesisDoc. -func (g *GenesisDoc) Hash() []byte { +// Hash returns the hash of the genesis.Doc. +func (g *Doc) Hash() []byte { data, err := json.Marshal(g) if err != nil { panic(err) @@ -90,32 +90,35 @@ type BlockParams struct { MaxGas StringifiedInt64 `json:"max_gas"` } +// EvidenceParams define limits on max evidence age and max duration type EvidenceParams struct { MaxAgeNumBlocks StringifiedInt64 `json:"max_age_num_blocks"` // only accept new evidence more recent than this MaxAgeDuration StringifiedInt64 `json:"max_age_duration"` } +// ValidatorParams define the validator key type ValidatorParams struct { PubKeyTypes []string `json:"pub_key_types"` } +// VersionParams define the version app information type VersionParams struct { AppVersion StringifiedInt64 `json:"app_version"` } // ________________________ GENESIS APP STATE ________________________ -// GenesisAccount represents an account in the genesis app state -type GenesisAccount struct { +// Account represents an account in the genesis app state +type Account struct { Address types.HexBytes `json:"address"` Balance uint64 `json:"balance"` } -// GenesisAppState is the main application state in the genesis file. -type GenesisAppState struct { +// AppState is the main application state in the genesis file. +type AppState struct { Validators []AppStateValidators `json:"validators"` - Accounts []GenesisAccount `json:"accounts"` + Accounts []Account `json:"accounts"` Treasurer types.HexBytes `json:"treasurer"` TxCost TransactionCosts `json:"tx_cost"` MaxElectionSize uint64 `json:"max_election_size"` diff --git a/vochain/indexer/vote.go b/vochain/indexer/vote.go index ac6287cbb..087df7e19 100644 --- a/vochain/indexer/vote.go +++ b/vochain/indexer/vote.go @@ -96,7 +96,7 @@ func (idx *Indexer) GetEnvelopes(processId []byte, max, from int, } -// CountVotes returns the total number of envelopes. +// CountTotalVotes returns the total number of envelopes. func (idx *Indexer) CountTotalVotes() (uint64, error) { height, err := idx.readOnlyQuery.CountVotes(context.TODO()) return uint64(height), err diff --git a/vochain/ist/ist.go b/vochain/ist/ist.go index 43e14f1e4..03be3d217 100644 --- a/vochain/ist/ist.go +++ b/vochain/ist/ist.go @@ -192,7 +192,7 @@ func (c *Controller) Commit(height uint32) error { for id, action := range actions { switch action.ID { case ActionCommitResults: - // if the election is setted up with tempSIKs as true, purge the election + // if the election is set up with tempSIKs as true, purge the election // related SIKs process, err := c.state.Process(action.ElectionID, false) if err != nil { diff --git a/vochain/keykeeper/keykeeper.go b/vochain/keykeeper/keykeeper.go index 4ca7ae0b7..37492a638 100644 --- a/vochain/keykeeper/keykeeper.go +++ b/vochain/keykeeper/keykeeper.go @@ -106,7 +106,7 @@ func NewKeyKeeper(dbPath string, v *vochain.BaseApplication, } // RevealUnpublished is a rescue function for revealing keys that should be already revealed. -// It should be callend once the Vochain is synchronized in order to have the correct height. +// It should be called once the Vochain is synchronized in order to have the correct height. func (k *KeyKeeper) RevealUnpublished() { // wait for vochain sync? height, err := k.vochain.State.LastHeight() diff --git a/vochain/offchaindatahandler/offchaindatahandler.go b/vochain/offchaindatahandler/offchaindatahandler.go index efc37d5ba..aebf24359 100644 --- a/vochain/offchaindatahandler/offchaindatahandler.go +++ b/vochain/offchaindatahandler/offchaindatahandler.go @@ -136,7 +136,7 @@ func (d *OffChainDataHandler) OnCensusUpdate(pid, censusRoot []byte, censusURI s } // OnProcessesStart is triggered when a process starts. Does nothing. -func (d *OffChainDataHandler) OnProcessesStart(_ [][]byte) { +func (*OffChainDataHandler) OnProcessesStart(_ [][]byte) { } // OnSetAccount is triggered when a new account is created or modified. If metadata info is present, it is enqueued. diff --git a/vochain/transaction/vote_tx.go b/vochain/transaction/vote_tx.go index 11fa5c421..b355843bf 100644 --- a/vochain/transaction/vote_tx.go +++ b/vochain/transaction/vote_tx.go @@ -76,7 +76,7 @@ func (t *TransactionHandler) VoteTxCheck(vtx *vochaintx.Tx, forCommit bool) (*vs // // We use CacheGetCopy because we will modify the vote to set // the Height. If we don't work with a copy we are racing with - // concurrent reads to the votes in the cache which happen in + // concurrent reads to the votes in the cache which happen // in State.CachePurge run via a goroutine in // started in BaseApplication.BeginBlock. // Warning: vote cache might change during the execution of this function. diff --git a/vochain/vochaininfo/vochaininfo.go b/vochain/vochaininfo/vochaininfo.go index 99b32600a..c2fc1c70e 100644 --- a/vochain/vochaininfo/vochaininfo.go +++ b/vochain/vochaininfo/vochaininfo.go @@ -101,7 +101,7 @@ func (vi *VochainInfo) EstimateBlockHeight(target time.Time) (uint64, error) { // Multiply by 1000 because t is represented in seconds, not ms. // Dividing t first can floor the integer, leading to divide-by-zero currentHeight := uint64(vi.Height()) - blockDiff := (uint64(absDiff*1000) / t) + blockDiff := uint64(absDiff*1000) / t if inPast { if blockDiff > currentHeight { return 0, fmt.Errorf("target time %v is before origin", target) @@ -244,11 +244,11 @@ func (vi *VochainInfo) Start(sleepSecs int64) { n60++ n360++ n1440++ - h1 += (height - pheight) - h10 += (height - pheight) - h60 += (height - pheight) - h360 += (height - pheight) - h1440 += (height - pheight) + h1 += height - pheight + h10 += height - pheight + h60 += height - pheight + h360 += height - pheight + h1440 += height - pheight if sleepSecs*n1 >= 60 && h1 > 0 { a1 = int32((n1 * sleepSecs * 1000) / h1) diff --git a/vocone/vocone.go b/vocone/vocone.go index c23ee1ca0..04234ae55 100644 --- a/vocone/vocone.go +++ b/vocone/vocone.go @@ -155,7 +155,7 @@ func (vc *Vocone) Start() { go vochainPrintInfo(10, vc.Stats) if vc.App.Height() == 0 { log.Infof("initializing new blockchain") - genesisAppData, err := json.Marshal(&genesis.GenesisAppState{ + genesisAppData, err := json.Marshal(&genesis.AppState{ MaxElectionSize: 1000000, NetworkCapacity: uint64(vc.txsPerBlock), TxCost: defaultTxCosts(), From 972f967100c2369d9d06ee6622f8d8d9cd02fee4 Mon Sep 17 00:00:00 2001 From: Pau Date: Mon, 9 Oct 2023 22:55:58 +0200 Subject: [PATCH 11/12] integrate a new signature schema for vochain transactions (#1124) integrate a new signature schema for vochain transactions The payload for signing transactions is now more human friendly. Each message is different depending on the transaction type, providing some details extracted from the transaction. Signed-off-by: p4u --- crypto/ethereum/ethereum.go | 104 +----------- crypto/ethereum/vocdoni_sik.go | 75 ++++++++ crypto/ethereum/vocdoni_test.go | 71 ++++---- crypto/ethereum/vocdoni_tx.go | 189 +++++++++++++++++++++ vochain/transaction/vochaintx/vochaintx.go | 14 +- 5 files changed, 313 insertions(+), 140 deletions(-) create mode 100644 crypto/ethereum/vocdoni_sik.go create mode 100644 crypto/ethereum/vocdoni_tx.go diff --git a/crypto/ethereum/ethereum.go b/crypto/ethereum/ethereum.go index 13e5df3c3..396cd46df 100644 --- a/crypto/ethereum/ethereum.go +++ b/crypto/ethereum/ethereum.go @@ -8,14 +8,10 @@ import ( "encoding/hex" "errors" "fmt" - "math/big" "sync" ethcommon "github.com/ethereum/go-ethereum/common" ethcrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/iden3/go-iden3-crypto/poseidon" - "go.vocdoni.io/dvote/crypto/zk" - "go.vocdoni.io/dvote/tree/arbo" "go.vocdoni.io/dvote/types" "go.vocdoni.io/dvote/util" ) @@ -164,42 +160,18 @@ func (k *SignKeys) SignEthereum(message []byte) ([]byte, error) { return signature, nil } -// SignVocdoniTx signs a vocdoni transaction. TxData is the full transaction payload (no HexString nor a Hash) -func (k *SignKeys) SignVocdoniTx(txData []byte, chainID string) ([]byte, error) { +// Sign signs a raw message. TxData is the full transaction payload (no HexString nor a Hash) +func (k *SignKeys) Sign(txData []byte) ([]byte, error) { if k.Private.D == nil { return nil, errors.New("no private key available") } - signature, err := ethcrypto.Sign(Hash(BuildVocdoniTransaction(txData, chainID)), &k.Private) + signature, err := ethcrypto.Sign(Hash(txData), &k.Private) if err != nil { return nil, err } return signature, nil } -// SignVocdoniMsg signs a vocdoni message. Message is the full payload (no HexString nor a Hash) -func (k *SignKeys) SignVocdoniMsg(message []byte) ([]byte, error) { - if k.Private.D == nil { - return nil, errors.New("no private key available") - } - signature, err := ethcrypto.Sign(Hash(BuildVocdoniMessage(message)), &k.Private) - if err != nil { - return nil, err - } - return signature, nil -} - -// SIKsignature signs the default vocdoni sik payload. It envolves the -// SignEthereum method, using the DefaultSIKPayload and discarding the last -// byte of the signature (used for recovery), different that the same byte of a -// signature generated with javascript. -func (k *SignKeys) SIKsignature() ([]byte, error) { - sign, err := k.SignEthereum([]byte(DefaultSIKPayload)) - if err != nil { - return nil, err - } - return sign[:SignatureLength-1], nil -} - // VerifySender verifies if a message is sent by some Authorized address key func (k *SignKeys) VerifySender(message, signature []byte) (bool, ethcommon.Address, error) { recoveredAddr, err := AddrFromSignature(message, signature) @@ -214,60 +186,6 @@ func (k *SignKeys) VerifySender(message, signature []byte) (bool, ethcommon.Addr return false, recoveredAddr, nil } -// AccountSIK method generates the Secret Identity Key for the current SignKeys -// with the signature of the DefaultSIKPayload and the user secret (if it is -// provided) following the definition: -// -// SIK = poseidon(address, signature, secret) -// -// The secret could be nil. -func (k *SignKeys) AccountSIK(secret []byte) ([]byte, error) { - if secret == nil { - secret = []byte{0} - } - sign, err := k.SIKsignature() - if err != nil { - return nil, fmt.Errorf("error signing sik payload: %w", err) - } - seed := []*big.Int{ - arbo.BytesToBigInt(k.Address().Bytes()), - zk.BigToFF(new(big.Int).SetBytes(secret)), - zk.BigToFF(new(big.Int).SetBytes(sign)), - } - hash, err := poseidon.Hash(seed) - if err != nil { - return nil, err - } - return arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), hash), nil -} - -// AccountSIKnullifier method composes the nullifier of the current SignKeys -// for the desired election id and the secret provided. -func (k *SignKeys) AccountSIKnullifier(electionId, secret []byte) ([]byte, error) { - // sign the default Secret Identity Key seed - sign, err := k.SIKsignature() - if err != nil { - return nil, fmt.Errorf("error signing default sik seed: %w", err) - } - // get the representation of the signature on the finite field and repeat - // the same with the secret if it is provided, if not add a zero - seed := []*big.Int{zk.BigToFF(new(big.Int).SetBytes(sign))} - if secret != nil { - seed = append(seed, zk.BigToFF(new(big.Int).SetBytes(secret))) - } else { - seed = append(seed, big.NewInt(0)) - } - // encode the election id for circom and include it into the nullifier - encElectionId := zk.BytesToArbo(electionId) - seed = append(seed, encElectionId...) - // calculate the poseidon image --> H(signature + secret + electionId) - hash, err := poseidon.Hash(seed) - if err != nil { - return nil, err - } - return hash.Bytes(), nil -} - // AddrFromPublicKey standaolone function to obtain the Ethereum address from a ECDSA public key func AddrFromPublicKey(pub []byte) (ethcommon.Address, error) { var err error @@ -336,22 +254,6 @@ func Hash(data []byte) []byte { return HashRaw(buf.Bytes()) } -// BuildVocdoniTransaction builds the payload of a vochain transaction (txData) -// ready to be signed -func BuildVocdoniTransaction(txData []byte, chainID string) []byte { - var buf bytes.Buffer - fmt.Fprintf(&buf, "Vocdoni signed transaction:\n%s\n%x", chainID, HashRaw(txData)) - return buf.Bytes() -} - -// BuildVocdoniMessage builds the payload of a vocdoni message -// ready to be signed -func BuildVocdoniMessage(message []byte) []byte { - var buf bytes.Buffer - fmt.Fprintf(&buf, "Vocdoni signed message:\n%x", HashRaw(message)) - return buf.Bytes() -} - // HashRaw hashes data with no prefix func HashRaw(data []byte) []byte { return ethcrypto.Keccak256(data) diff --git a/crypto/ethereum/vocdoni_sik.go b/crypto/ethereum/vocdoni_sik.go new file mode 100644 index 000000000..2af87ca40 --- /dev/null +++ b/crypto/ethereum/vocdoni_sik.go @@ -0,0 +1,75 @@ +package ethereum + +import ( + "fmt" + "math/big" + + "github.com/iden3/go-iden3-crypto/poseidon" + "go.vocdoni.io/dvote/crypto/zk" + "go.vocdoni.io/dvote/tree/arbo" +) + +// SIKsignature signs the default vocdoni sik payload. It envolves the +// SignEthereum method, using the DefaultSIKPayload and discarding the last +// byte of the signature (used for recovery), different that the same byte of a +// signature generated with javascript. +func (k *SignKeys) SIKsignature() ([]byte, error) { + sign, err := k.SignEthereum([]byte(DefaultSIKPayload)) + if err != nil { + return nil, err + } + return sign[:SignatureLength-1], nil +} + +// AccountSIK method generates the Secret Identity Key for the current SignKeys +// with the signature of the DefaultSIKPayload and the user secret (if it is +// provided) following the definition: +// +// SIK = poseidon(address, signature, secret) +// +// The secret could be nil. +func (k *SignKeys) AccountSIK(secret []byte) ([]byte, error) { + if secret == nil { + secret = []byte{0} + } + sign, err := k.SIKsignature() + if err != nil { + return nil, fmt.Errorf("error signing sik payload: %w", err) + } + seed := []*big.Int{ + arbo.BytesToBigInt(k.Address().Bytes()), + zk.BigToFF(new(big.Int).SetBytes(secret)), + zk.BigToFF(new(big.Int).SetBytes(sign)), + } + hash, err := poseidon.Hash(seed) + if err != nil { + return nil, err + } + return arbo.BigIntToBytes(arbo.HashFunctionPoseidon.Len(), hash), nil +} + +// AccountSIKnullifier method composes the nullifier of the current SignKeys +// for the desired election id and the secret provided. +func (k *SignKeys) AccountSIKnullifier(electionID, secret []byte) ([]byte, error) { + // sign the default Secret Identity Key seed + sign, err := k.SIKsignature() + if err != nil { + return nil, fmt.Errorf("error signing default sik seed: %w", err) + } + // get the representation of the signature on the finite field and repeat + // the same with the secret if it is provided, if not add a zero + seed := []*big.Int{zk.BigToFF(new(big.Int).SetBytes(sign))} + if secret != nil { + seed = append(seed, zk.BigToFF(new(big.Int).SetBytes(secret))) + } else { + seed = append(seed, big.NewInt(0)) + } + // encode the election id for circom and include it into the nullifier + seed = append(seed, zk.BytesToArbo(electionID)...) + // calculate the poseidon image --> H(signature + secret + electionId) + hash, err := poseidon.Hash(seed) + if err != nil { + return nil, err + } + return hash.Bytes(), nil +} diff --git a/crypto/ethereum/vocdoni_test.go b/crypto/ethereum/vocdoni_test.go index bbf4e0745..f3925c795 100644 --- a/crypto/ethereum/vocdoni_test.go +++ b/crypto/ethereum/vocdoni_test.go @@ -1,12 +1,13 @@ package ethereum import ( - "encoding/hex" - "encoding/json" "fmt" "testing" qt "github.com/frankban/quicktest" + "go.vocdoni.io/dvote/log" + "go.vocdoni.io/proto/build/go/models" + "google.golang.org/protobuf/proto" ) func TestVocdoniSignature(t *testing.T) { @@ -19,50 +20,60 @@ func TestVocdoniSignature(t *testing.T) { pub, priv := s.HexString() t.Logf("Generated pub:%s priv:%s", pub, priv) - msg := testVocdoniSignatureMessage{Method: "getVisibility", Timestamp: 1582196988222} - message, err := json.Marshal(msg) - qt.Assert(t, err, qt.IsNil) + signer2 := SignKeys{} + signer2.Generate() + toAddr := signer2.Address().Bytes() + value := uint64(100) + nonce := uint32(123) + + tx := &models.Tx{Payload: &models.Tx_SendTokens{ + SendTokens: &models.SendTokensTx{ + Txtype: models.TxType_SEND_TOKENS, + From: s.Address().Bytes(), + To: toAddr, + Value: value, + Nonce: 123, + }}} - t.Logf("Message to sign: %s", message) - signature, err := s.SignVocdoniMsg(message) + message, err := proto.Marshal(tx) qt.Assert(t, err, qt.IsNil) - t.Logf("Signature for message is %x", signature) + t.Logf("Proto to sign: %s", log.FormatProto(tx)) - extractedPubKey, err := PubKeyFromSignature(BuildVocdoniMessage(message), signature) + msgToSign, _, err := BuildVocdoniTransaction(message, "chain-123") qt.Assert(t, err, qt.IsNil) - qt.Assert(t, fmt.Sprintf("%x", extractedPubKey), qt.DeepEquals, pub) + t.Logf("Message to sign: %s", msgToSign) - t.Logf("Transaction to sign: %s", message) - signature, err = s.SignVocdoniTx(message, "chain-123") + signature, err := s.SignVocdoniTx(message, "chain-123") qt.Assert(t, err, qt.IsNil) t.Logf("Signature for transaction is %x", signature) - extractedPubKey, err = PubKeyFromSignature(BuildVocdoniTransaction(message, "chain-123"), signature) + extractedPubKey, err := PubKeyFromSignature(msgToSign, signature) qt.Assert(t, err, qt.IsNil) qt.Assert(t, fmt.Sprintf("%x", extractedPubKey), qt.DeepEquals, pub) - // Test compatibility with JS (message) - msg = testVocdoniSignatureMessage{Method: "getVisibility", Timestamp: 1582196988554} - message, err = json.Marshal(msg) - qt.Assert(t, err, qt.IsNil) - signature, err = hex.DecodeString("2aab382d8cf025f55d8c3f7597e83dc878939ef63f1a27b818fa0814d79e91d66dc8d8112fbdcc89d2355d58a74ad227a2a9603ef7eb2321283a8ea93fb90ee11b") - qt.Assert(t, err, qt.IsNil) + infoUri := "ipfs://vocdoni.io" - extractedPubKey, err = PubKeyFromSignature(BuildVocdoniMessage(message), signature) - qt.Assert(t, err, qt.IsNil) - qt.Assert(t, fmt.Sprintf("%x", extractedPubKey), qt.DeepEquals, "02cb3cabb521d84fc998b5649d6b59e27a3e27633d31cc0ca6083a00d68833d5ca") + tx = &models.Tx{Payload: &models.Tx_SetAccount{ + SetAccount: &models.SetAccountTx{ + Txtype: models.TxType_SET_ACCOUNT_INFO_URI, + InfoURI: &infoUri, + Account: s.Address().Bytes(), + Nonce: &nonce, + }}} - // Test compatibility with JS (transaction) - signature, err = hex.DecodeString("44d31937862e1f835f92d8b3f93858636bad075bd2429cf79404a81e42bd3d00196613c37a99b054c32339dde53ce3c25932c5d5f89483ccdf93234d3c4808b81b") + message, err = proto.Marshal(tx) qt.Assert(t, err, qt.IsNil) + t.Logf("Proto to sign: %s", log.FormatProto(tx)) - extractedPubKey, err = PubKeyFromSignature(BuildVocdoniTransaction(message, "production"), signature) + msgToSign, _, err = BuildVocdoniTransaction(message, "chain-123") qt.Assert(t, err, qt.IsNil) - qt.Assert(t, fmt.Sprintf("%x", extractedPubKey), qt.DeepEquals, "02cb3cabb521d84fc998b5649d6b59e27a3e27633d31cc0ca6083a00d68833d5ca") + t.Logf("Message to sign: %s", msgToSign) -} + signature, err = s.SignVocdoniTx(message, "chain-123") + qt.Assert(t, err, qt.IsNil) + t.Logf("Signature for transaction is %x", signature) -type testVocdoniSignatureMessage struct { - Method string `json:"method"` - Timestamp int `json:"timestamp"` + extractedPubKey, err = PubKeyFromSignature(msgToSign, signature) + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, fmt.Sprintf("%x", extractedPubKey), qt.DeepEquals, pub) } diff --git a/crypto/ethereum/vocdoni_tx.go b/crypto/ethereum/vocdoni_tx.go new file mode 100644 index 000000000..665347082 --- /dev/null +++ b/crypto/ethereum/vocdoni_tx.go @@ -0,0 +1,189 @@ +package ethereum + +import ( + "bytes" + "errors" + "fmt" + "strings" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "go.vocdoni.io/proto/build/go/models" + "google.golang.org/protobuf/proto" +) + +const ( + createAccountTemplate = "You are signing a Vocdoni transaction of type CREATE_ACCOUNT for address %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + setAccountInfoURITemplate = "You are signing a Vocdoni transaction of type SET_ACCOUNT_INFO_URI for address %x with URI %s.\n\nThe hash of this transaction is %x and the destination chain is %s." + setAccountDefaultTemplate = "You are signing a Vocdoni transaction of type SET_ACCOUNT/%s.\n\nThe hash of this transaction is %x and the destination chain is %s." + + sendTokensTemplate = "You are signing a Vocdoni transaction of type SEND_TOKENS for an amount of %d VOC tokens to destination address %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + voteTemplate = "You are signing a Vocdoni transaction of type VOTE for process ID %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + collectFaucetTemplate = "You are signing a Vocdoni transaction of type COLLECT_FAUCET.\n\nThe hash of this transaction is %x and the destination chain is %s." + + newProcessTemplate = "You are signing a Vocdoni transaction of type NEW_PROCESS.\n\nThe hash of this transaction is %x and the destination chain is %s." + setProcessTemplate = "You are signing a Vocdoni transaction of type SET_PROCESS/%s with process ID %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + setProcessCensusTemplate = "You are signing a Vocdoni transaction of type SET_PROCESS_CENSUS for process ID %x and census %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + setProcessStatusTemplate = "You are signing a Vocdoni transaction of type SET_PROCESS_STATUS for process ID %x and status %s.\n\nThe hash of this transaction is %x and the destination chain is %s." + setProcessDefaultTemplate = "You are signing a Vocdoni transaction of type SET_PROCESS/%s with process ID %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + + registerSIKTemplate = "You are signing a Vocdoni transaction of type REGISTER_SIK for secret identity key %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + setSIKTemplate = "You are signing a Vocdoni transaction of type SET_SIK for secret identity key %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + delSIKTemplate = "You are signing a Vocdoni transaction of type DEL_SIK for secret identity key %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + + addDelegateAccountTemplate = "You are signing a Vocdoni transaction of type ADD_DELEGATE_FOR_ACCOUNT for address %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + delDelegateAccountTemplte = "You are signing a Vocdoni transaction of type DEL_DELEGATE_FOR_ACCOUNT for address %x.\n\nThe hash of this transaction is %x and the destination chain is %s." + + defaultTemplate = "Vocdoni signed transaction:\n%s\n%x" +) + +// BuildVocdoniProtoTxMessage builds the message to be signed for a vocdoni transaction. +// It takes an optional transaction hash, if it is not provided it will be computed. +// It returns the payload that needs to be signed. +func BuildVocdoniProtoTxMessage(tx *models.Tx, chainID string, hash []byte) ([]byte, error) { + var msg string + + marshaledTx, err := proto.Marshal(tx) + if err != nil { + return nil, fmt.Errorf("failed to marshal Tx: %v", err) + } + if hash == nil { + hash = HashRaw(marshaledTx) + } + + switch tx.Payload.(type) { + case *models.Tx_SendTokens: + t := tx.GetSendTokens() + if t == nil { + return nil, fmt.Errorf("send tokens payload is nil") + } + msg = fmt.Sprintf(sendTokensTemplate, t.Value, t.To, hash, chainID) + case *models.Tx_SetProcess: + t := tx.GetSetProcess() + if t == nil { + return nil, fmt.Errorf("set process payload is nil") + } + switch t.Txtype { + case models.TxType_SET_PROCESS_CENSUS: + msg = fmt.Sprintf(setProcessCensusTemplate, t.ProcessId, t.GetCensusRoot(), hash, chainID) + case models.TxType_SET_PROCESS_STATUS: + msg = fmt.Sprintf(setProcessStatusTemplate, t.ProcessId, strings.ToLower(t.GetStatus().String()), hash, chainID) + default: + msg = fmt.Sprintf(setProcessDefaultTemplate, t.Txtype.String(), t.ProcessId, hash, chainID) + } + case *models.Tx_Vote: + t := tx.GetVote() + if t == nil { + return nil, fmt.Errorf("vote payload is nil") + } + msg = fmt.Sprintf(voteTemplate, t.ProcessId, hash, chainID) + case *models.Tx_NewProcess: + t := tx.GetNewProcess().GetProcess() + if t == nil { + return nil, fmt.Errorf("new process payload is nil") + } + msg = fmt.Sprintf(newProcessTemplate, hash, chainID) + case *models.Tx_SetAccount: + t := tx.GetSetAccount() + if t == nil { + return nil, fmt.Errorf("set account payload is nil") + } + switch t.Txtype { + case models.TxType_CREATE_ACCOUNT: + msg = fmt.Sprintf(createAccountTemplate, t.Account, hash, chainID) + case models.TxType_SET_ACCOUNT_INFO_URI: + msg = fmt.Sprintf(setAccountInfoURITemplate, t.Account, t.GetInfoURI(), hash, chainID) + case models.TxType_ADD_DELEGATE_FOR_ACCOUNT: + msg = fmt.Sprintf(addDelegateAccountTemplate, t.Account, hash, chainID) + case models.TxType_DEL_DELEGATE_FOR_ACCOUNT: + msg = fmt.Sprintf(delDelegateAccountTemplte, t.Account, hash, chainID) + default: + msg = fmt.Sprintf(setAccountDefaultTemplate, t.Txtype.String(), hash, chainID) + } + case *models.Tx_CollectFaucet: + t := tx.GetCollectFaucet() + if t == nil { + return nil, fmt.Errorf("collect faucet payload is nil") + } + msg = fmt.Sprintf(collectFaucetTemplate, hash, chainID) + case *models.Tx_SetSIK: + t := tx.GetSetSIK() + if t == nil { + return nil, fmt.Errorf("set SIK payload is nil") + } + msg = fmt.Sprintf(setSIKTemplate, t.SIK, hash, chainID) + case *models.Tx_DelSIK: + t := tx.GetSetSIK() + if t == nil { + return nil, fmt.Errorf("del SIK payload is nil") + } + msg = fmt.Sprintf(delSIKTemplate, t.SIK, hash, chainID) + case *models.Tx_RegisterSIK: + t := tx.GetRegisterSIK() + if t == nil { + return nil, fmt.Errorf("register SIK payload is nil") + } + msg = fmt.Sprintf(registerSIKTemplate, t.SIK, hash, chainID) + default: + msg = fmt.Sprintf(defaultTemplate, chainID, hash) + } + return []byte(msg), nil +} + +// SignVocdoniTx signs a vocdoni transaction. TxData is the full transaction payload (no HexString nor a Hash) +func (k *SignKeys) SignVocdoniTx(txData []byte, chainID string) ([]byte, error) { + if k.Private.D == nil { + return nil, errors.New("no private key available") + } + // TODO: this unmarshal is not necessary, but it is required to keep compatibility with the old code + // the SIgnVocdoniTx method should take directly a models.Tx as parameter and return the signature + // and marhaled tx. + tx := &models.Tx{} + if err := proto.Unmarshal(txData, tx); err != nil { + return nil, fmt.Errorf("failed to unmarshal Tx: %v", err) + } + + payloadToSign, err := BuildVocdoniProtoTxMessage(tx, chainID, HashRaw(txData)) + if err != nil { + return nil, fmt.Errorf("failed to build transaction message: %v", err) + } + + signature, err := ethcrypto.Sign(Hash(payloadToSign), &k.Private) + if err != nil { + return nil, err + } + return signature, nil +} + +// SignVocdoniMsg signs a vocdoni message. Message is the full payload (no HexString nor a Hash) +func (k *SignKeys) SignVocdoniMsg(message []byte) ([]byte, error) { + if k.Private.D == nil { + return nil, errors.New("no private key available") + } + signature, err := ethcrypto.Sign(Hash(BuildVocdoniMessage(message)), &k.Private) + if err != nil { + return nil, err + } + return signature, nil +} + +// BuildVocdoniTransaction builds the payload for a Vocdoni transaction. +// It returns the payload that needs to be signed and the unmarshaled transaction struct. +func BuildVocdoniTransaction(marshaledTx []byte, chainID string) ([]byte, *models.Tx, error) { + var tx models.Tx + if err := proto.Unmarshal(marshaledTx, &tx); err != nil { + return nil, nil, fmt.Errorf("failed to unmarshal Tx: %v", err) + } + message, err := BuildVocdoniProtoTxMessage(&tx, chainID, HashRaw(marshaledTx)) + if err != nil { + return nil, nil, fmt.Errorf("failed to build transaction message: %v", err) + } + return message, &tx, nil +} + +// BuildVocdoniMessage builds the payload of a vocdoni message +// ready to be signed +func BuildVocdoniMessage(message []byte) []byte { + var buf bytes.Buffer + fmt.Fprintf(&buf, "Vocdoni signed message:\n%x", HashRaw(message)) + return buf.Bytes() +} diff --git a/vochain/transaction/vochaintx/vochaintx.go b/vochain/transaction/vochaintx/vochaintx.go index 139eeaedd..4bb91412f 100644 --- a/vochain/transaction/vochaintx/vochaintx.go +++ b/vochain/transaction/vochaintx/vochaintx.go @@ -26,20 +26,16 @@ type Tx struct { func (tx *Tx) Unmarshal(content []byte, chainID string) error { stx := new(models.SignedTx) if err := proto.Unmarshal(content, stx); err != nil { - return err + return fmt.Errorf("failed to unmarshal signed transaction: %w", err) } - tx.Tx = new(models.Tx) - if stx.GetTx() == nil { - return fmt.Errorf("nil transaction") + var err error + tx.SignedBody, tx.Tx, err = ethereum.BuildVocdoniTransaction(stx.GetTx(), chainID) + if err != nil { + return fmt.Errorf("failed to unmarshal transaction: %w", err) } - if err := proto.Unmarshal(stx.GetTx(), tx.Tx); err != nil { - return err - } - tx.TxModelType = string(tx.Tx.ProtoReflect().WhichOneof(tx.Tx.ProtoReflect().Descriptor().Oneofs().Get(0)).Name()) tx.Signature = stx.GetSignature() tx.TxID = TxKey(content) - tx.SignedBody = ethereum.BuildVocdoniTransaction(stx.GetTx(), chainID) return nil } From a2f259fa32b1bfab39c34d1892e0cbd5124d27c6 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 25 Sep 2023 15:54:12 +0200 Subject: [PATCH 12/12] dockerfiles: several fixes and refactor * `docker-compose` -> `docker compose` * vocdoninode: use compose profiles instead of different docker-compose.yml * vocdoninode: watchtower --revive-stopped * testsuite: avoid unneeded container starts, using compose profiles * testsuite: fix zkCircuits path after 7c854c5 * testsuite: update README.md * remove unmaintained testsuite ansible playbook * remove unmaintained docker-compose-traefik-cloudflare.yml * remove unmaintained scripts/update_and_deploy*.sh --- .github/workflows/main.yml | 2 +- README.md | 6 +- dockerfiles/testsuite/README.md | 35 +++---- dockerfiles/testsuite/ansible/.gitignore | 4 - dockerfiles/testsuite/ansible/README.md | 21 ----- dockerfiles/testsuite/ansible/ansible.cfg | 10 -- .../testsuite/ansible/generate_testnet.yml | 86 ----------------- dockerfiles/testsuite/ansible/hosts | 1 - .../ansible/templates/docker-compose.yml | 92 ------------------- .../testsuite/ansible/templates/env.gateway | 17 ---- .../testsuite/ansible/templates/env.miner | 8 -- .../testsuite/ansible/templates/env.oracle | 11 --- .../ansible/templates/env.oracle0key | 1 - .../testsuite/ansible/templates/env.seed | 7 -- .../testsuite/ansible/templates/env.seeds | 5 - dockerfiles/testsuite/docker-compose.yml | 20 ++-- dockerfiles/testsuite/start_test.sh | 5 +- dockerfiles/vocdoninode/README.md | 14 +-- .../docker-compose-traefik-cloudflare.yml | 68 -------------- .../vocdoninode/docker-compose.watchtower.yml | 9 -- dockerfiles/vocdoninode/docker-compose.yml | 12 ++- scripts/update_and_deploy.sh | 27 ------ scripts/update_and_deploy_compose.sh | 32 ------- 23 files changed, 53 insertions(+), 440 deletions(-) delete mode 100644 dockerfiles/testsuite/ansible/.gitignore delete mode 100644 dockerfiles/testsuite/ansible/README.md delete mode 100644 dockerfiles/testsuite/ansible/ansible.cfg delete mode 100644 dockerfiles/testsuite/ansible/generate_testnet.yml delete mode 100644 dockerfiles/testsuite/ansible/hosts delete mode 100644 dockerfiles/testsuite/ansible/templates/docker-compose.yml delete mode 100755 dockerfiles/testsuite/ansible/templates/env.gateway delete mode 100644 dockerfiles/testsuite/ansible/templates/env.miner delete mode 100755 dockerfiles/testsuite/ansible/templates/env.oracle delete mode 100644 dockerfiles/testsuite/ansible/templates/env.oracle0key delete mode 100755 dockerfiles/testsuite/ansible/templates/env.seed delete mode 100644 dockerfiles/testsuite/ansible/templates/env.seeds delete mode 100644 dockerfiles/vocdoninode/docker-compose-traefik-cloudflare.yml delete mode 100644 dockerfiles/vocdoninode/docker-compose.watchtower.yml delete mode 100755 scripts/update_and_deploy.sh delete mode 100755 scripts/update_and_deploy_compose.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 65120d15a..bf628999a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -109,7 +109,7 @@ jobs: - name: Run compose script env: TESTSUITE_BUILD_TAG: ${{ github.sha }} - COMPOSE_PROJECT_NAME: testsuite_${{ github.run_id }} # unique name for docker-compose (needed for concurrent job runs) + COMPOSE_PROJECT_NAME: testsuite_${{ github.run_id }} # unique name for docker compose (needed for concurrent job runs) COMPOSE_DVOTE_PORT_MAPPING: "9090" # this binds gateway0 to a random available port on docker host (needed for concurrent job runs) COMPOSE_HOST_PATH: ${{ github.workspace }}/dockerfiles/testsuite LOG_PANIC_ON_INVALIDCHARS: true # check that log lines contains no invalid chars (evidence of format mismatch) diff --git a/README.md b/README.md index b3a271a73..eeb66cce5 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,13 @@ go build ./cmd/node #### Docker -You can run vocdoni node as a standalone container with docker-compose (recommended). +You can run vocdoni node as a standalone container with docker compose (recommended). It is recommended to also start `watchtower` to automatically update the container when a new version is released. ```bash cd dockerfiles/vocdoninode cp env.example env -docker-compose -f docker-compose.yml -f docker-compose.watchtower.yml up -d +COMPOSE_PROFILES=watchtower docker compose up -d ``` All data will be stored in the shared volume `run` and the API will be available at `http://127.0.0.1:9090/v2`. @@ -71,7 +71,7 @@ If the computer has the port 443 available and mapped to a public IP, you might To stop the container: ```bash -docker-compose -f docker-compose.yml -f docker-compose.watchtower.yml down +docker compose down ``` #### Connecting diff --git a/dockerfiles/testsuite/README.md b/dockerfiles/testsuite/README.md index ea1b77d43..30ef9fa33 100644 --- a/dockerfiles/testsuite/README.md +++ b/dockerfiles/testsuite/README.md @@ -3,8 +3,8 @@ To run the tests: ``` -docker-compose build -docker-compose up -d +docker compose build +docker compose up -d go run ../../cmd/end2endtest/ ``` @@ -18,15 +18,13 @@ NOTTY=1 ./start_test.sh the testnet is composed of: * one [seed node](https://docs.tendermint.com/master/nodes/#seed-nodes) - * seven [miners](https://docs.vocdoni.io/architecture/services/vochain.html#miner) (aka [validator nodes](https://docs.tendermint.com/master/nodes/#validators) in tendermint jargon) - * one [oracle](https://docs.vocdoni.io/architecture/components.html#oracle) + * four [miners](https://docs.vocdoni.io/architecture/services/vochain.html#miner) (aka [validator nodes](https://docs.tendermint.com/master/nodes/#validators) in tendermint jargon) * one [gateway](https://docs.vocdoni.io/architecture/components.html#gateway) the `genesis.json` file lists the public keys of all the miners, since vochain is a Proof-of-Authority. -it also specifies which nodes are trusted oracles. the seed node will serve to bootstrap the network: it'll just wait for incoming connections from other nodes, and provide them a list of peers which they can connect to. -the miners will first connect to the seed node, get the list of peers, and connect to each other. when there are at least 4 miners online, they can reach consensus and start producing blocks. +the miners will first connect to the seed node, get the list of peers, and connect to each other. when there are at least 3 miners online, they can reach consensus and start producing blocks. when the network is up and running, the tool `end2endtest` is used to simulate a voting process, interacting with the gateway node. To create the voting process (a transaction that costs a certain amount of tokens), `end2endtest` uses a faucet offered by the testsuite (passed in `--faucet`) to fund the accounts. @@ -62,16 +60,6 @@ docker ps -a docker logs ``` -## Generate custom testnet - -if you want to generate a custom-sized testnet (with X miners, Y gateways, Z oracles, and so on), check the `ansible` directory: -```sh -cd ansible -cat README.md -ansible-playbook generate_testnet.yml -../start_test.sh -``` - ## Troubleshooting if you run this interactively in a headless environment (remote server), you might face the following error: @@ -85,3 +73,18 @@ you can simply install `pass`, or an even more simple workaround is to make a du # ln -s /bin/true /usr/local/bin/pass ``` since `docker login` just checks that `pass` is available, but doesn't actually need it for login, all of the repos accesed are public. + +## Grafana + +if you want to collect Prometheus metrics and visualize them in a local Grafana, enable `grafana` [profile](https://docs.docker.com/compose/profiles/): + +```sh +export COMPOSE_PROFILES=grafana +docker compose up -d +``` + +or simply + +```sh +docker compose --profile grafana up -d +``` diff --git a/dockerfiles/testsuite/ansible/.gitignore b/dockerfiles/testsuite/ansible/.gitignore deleted file mode 100644 index 42f0197f2..000000000 --- a/dockerfiles/testsuite/ansible/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/docker-compose.yml -/dvotecli -/env.* -/genesis.json diff --git a/dockerfiles/testsuite/ansible/README.md b/dockerfiles/testsuite/ansible/README.md deleted file mode 100644 index 7ab6fbd3f..000000000 --- a/dockerfiles/testsuite/ansible/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Generate a docker-compose.yml for a custom-sized testnet - -you need to have [ansible](https://ansible.com) installed, then you can run: - -```sh -ansible-playbook generate_testnet.yml -``` - -you can also pass parameters like this: - -```sh -ansible-playbook generate_testnet.yml -e gateways=3 -e seeds=2 -e oracles=2 -e miners=10 -``` - -this will generate various files, mainly a `docker-compose.yml`, `genesis.json` and some `env` files. - -afterwards, you can run the usual test script but using this freshly generated custom environment: - -```sh -../start_test.sh -``` diff --git a/dockerfiles/testsuite/ansible/ansible.cfg b/dockerfiles/testsuite/ansible/ansible.cfg deleted file mode 100644 index 0e453fdd3..000000000 --- a/dockerfiles/testsuite/ansible/ansible.cfg +++ /dev/null @@ -1,10 +0,0 @@ -[defaults] -inventory = ./hosts -interpreter_python = auto_silent - -# Human-readable error output per https://stackoverflow.com/a/45086602 -stdout_callback = yaml -stderr_callback = yaml - -#[diff] -#always = yes diff --git a/dockerfiles/testsuite/ansible/generate_testnet.yml b/dockerfiles/testsuite/ansible/generate_testnet.yml deleted file mode 100644 index d3101b119..000000000 --- a/dockerfiles/testsuite/ansible/generate_testnet.yml +++ /dev/null @@ -1,86 +0,0 @@ ---- -# USAGE: -# $ ansible-playbook generate_testnet.yml -# you can also tweak parameters -# $ ansible-playbook generate_testnet.yml -e gateways=3 -e seeds=2 -e oracles=2 -e miners=10 - -- name: generate a testnet using parameters - - hosts: localhost - connection: local - gather_facts: no - - vars: - gateways: 1 - seeds: 1 - oracles: 1 - miners: 5 - chain_id: "vocdoni-testsuite-01" - dest: "{{playbook_dir}}/" - docker_compose_file: "{{dest}}/docker-compose.yml" - genesis_file: "{{dest}}/genesis.json" - - tasks: - - name: register var for git root (1/2) # used also in docker-compose.yml template - command: git root - register: result - changed_when: false # always show `ok` - - name: register var for git root (2/2) - set_fact: - git_root: "{{ result.stdout }}" - - - name: ensure dest directory ({{dest}}) exists - file: path={{dest}} state=directory - - - name: build dvotecli - tags: build - shell: go build -o {{dest}}/dvotecli {{git_root}}/cmd/dvotecli/ - - - name: generate random genesis.json - command: | - {{dest}}/dvotecli genesis \ - --chainId {{chain_id}} \ - --seeds {{seeds}} \ - --oracles {{oracles}} \ - --miners {{miners}} \ - -w {{dest}}/genesis.json \ - --json - register: privkeys - - - name: tweak genesis.json max_age_{num_blocks,duration} - replace: - path: "{{dest}}/genesis.json" - after: '"evidence": {' - before: '}' - regexp: '"{{item.var}}": "0"' - replace: '"{{item.var}}": "{{item.value}}"' - with_items: - - { var: max_age_num_blocks, value: 100000 } - - { var: max_age_duration, value: 10000 } - - - name: parse privkeys - set_fact: - seed_keys: "{{ (privkeys.stdout | from_json).seeds }}" - oracle_keys: "{{ (privkeys.stdout | from_json).oracles }}" - miner_keys: "{{ (privkeys.stdout | from_json).miners }}" - - - name: generate docker_compose.yml - template: - dest: "{{docker_compose_file}}" - src: docker-compose.yml - - - name: generate env files - template: - src: "{{item}}" - dest: "{{dest}}" - with_items: - - env.gateway - - env.miner - - env.oracle - - env.oracle0key - - env.seed - - env.seeds - - - name: output privkeys to console for debugging - tags: debug - debug: var=privkeys.stdout diff --git a/dockerfiles/testsuite/ansible/hosts b/dockerfiles/testsuite/ansible/hosts deleted file mode 100644 index 2fbb50c4a..000000000 --- a/dockerfiles/testsuite/ansible/hosts +++ /dev/null @@ -1 +0,0 @@ -localhost diff --git a/dockerfiles/testsuite/ansible/templates/docker-compose.yml b/dockerfiles/testsuite/ansible/templates/docker-compose.yml deleted file mode 100644 index 166e58798..000000000 --- a/dockerfiles/testsuite/ansible/templates/docker-compose.yml +++ /dev/null @@ -1,92 +0,0 @@ -version: "3.7" - -services: -{% for key in seed_keys %} - seed{{loop.index0}}: - image: "vocdoninode:${TESTSUITE_BUILD_TAG:-latest}" -{% if loop.first %} - build: # reused by the other services - context: {{git_root}} -{% endif %} - environment: - - DVOTE_VOCHAINCONFIG_PUBLICADDR=seed{{loop.index0}}:26656 - - DVOTE_VOCHAINCONFIG_NODEKEY=0x{{key.priv_key}} - env_file: - - "${COMPOSE_HOST_PATH:-.}/env.seed" - volumes: - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - networks: - - seed{{loop.index0}} - -{% endfor %} -{% for key in miner_keys %} - miner{{loop.index0}}: - image: "vocdoninode:${TESTSUITE_BUILD_TAG:-latest}" - environment: - - DVOTE_VOCHAINCONFIG_PUBLICADDR=miner{{loop.index0}}:26656 - - DVOTE_VOCHAINCONFIG_MINERKEY=0x{{key.priv_key}} - env_file: - - "${COMPOSE_HOST_PATH:-.}/env.miner" - - "${COMPOSE_HOST_PATH:-.}/env.seeds" - volumes: - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - networks: -{% for id in range(seeds|int) %} - - seed{{id}} -{% endfor %} - -{% endfor %} -{% for key in oracle_keys %} - oracle{{loop.index0}}: - image: "vocdoninode:${TESTSUITE_BUILD_TAG:-latest}" - environment: - - DVOTE_VOCHAINCONFIG_PUBLICADDR=oracle{{loop.index0}}:26656 - - DVOTE_ETHCONFIG_SIGNINGKEY={{key.priv_key}} - env_file: - - "${COMPOSE_HOST_PATH:-.}/env.oracle" - - "${COMPOSE_HOST_PATH:-.}/env.seeds" - volumes: - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - networks: -{% for id in range(seeds|int) %} - - seed{{id}} -{% endfor %} - -{% endfor %} -{% for id in range(gateways|int) %} - gateway{{id}}: - image: "vocdoninode:${TESTSUITE_BUILD_TAG:-latest}" - environment: - - DVOTE_VOCHAINCONFIG_PUBLICADDR=gateway{{id}}:26656 - env_file: - - "${COMPOSE_HOST_PATH:-.}/env.gateway" - - "${COMPOSE_HOST_PATH:-.}/env.seeds" - ports: - - "${COMPOSE_DVOTE_PORT_MAPPING:-{{9090+id}}:9090}" - volumes: - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - networks: - - gateway{{id}} -{% for id in range(seeds|int) %} - - seed{{id}} -{% endfor %} - -{% endfor %} - test: - image: "vocdoninode-test:${TESTSUITE_BUILD_TAG:-latest}" - build: - context: {{git_root}} - target: test - networks: -{% for id in range(gateways|int) %} - - gateway{{id}} -{% endfor %} - command: "true" - -networks: -{% for id in range(seeds|int) %} - seed{{id}}: -{% endfor %} -{% for id in range(gateways|int) %} - gateway{{id}}: -{% endfor %} diff --git a/dockerfiles/testsuite/ansible/templates/env.gateway b/dockerfiles/testsuite/ansible/templates/env.gateway deleted file mode 100755 index 93dd93a1b..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.gateway +++ /dev/null @@ -1,17 +0,0 @@ -DVOTE_DATADIR=/app/run -DVOTE_LOGLEVEL=debug -DVOTE_DEV=True -DVOTE_API_FILE=True -DVOTE_API_CENSUS=True -DVOTE_API_VOTE=True -DVOTE_API_RESULTS=True -DVOTE_API_INDEXER=True -DVOTE_API_URL=True -DVOTE_API_ALLOWPRIVATE=True -DVOTE_ETHCONFIG_NOWAITSYNC=True -DVOTE_ETHEVENTCONFIG_CENSUSSYNC=False -DVOTE_W3CONFIG_ENABLED=False -DVOTE_VOCHAINCONFIG_GENESIS=/app/misc/genesis.json -DVOTE_VOCHAINCONFIG_NOWAITSYNC=True -DVOTE_METRICS_ENABLED=True -DVOTE_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/ansible/templates/env.miner b/dockerfiles/testsuite/ansible/templates/env.miner deleted file mode 100644 index 5670958d3..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.miner +++ /dev/null @@ -1,8 +0,0 @@ -DVOTE_DATADIR=/app/run -DVOTE_MODE=miner -DVOTE_LOGLEVEL=debug -DVOTE_DEV=True -DVOTE_VOCHAINCONFIG_GENESIS=/app/misc/genesis.json -DVOTE_METRICS_ENABLED=True -DVOTE_METRICS_REFRESHINTERVAL=5 -DVOTE_VOCHAINCONFIG_MINERTARGETBLOCKTIMESECONDS=5 diff --git a/dockerfiles/testsuite/ansible/templates/env.oracle b/dockerfiles/testsuite/ansible/templates/env.oracle deleted file mode 100755 index 8ad712f11..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.oracle +++ /dev/null @@ -1,11 +0,0 @@ -DVOTE_DATADIR=/app/run -DVOTE_MODE=oracle -DVOTE_LOGLEVEL=debug -DVOTE_DEV=True -DVOTE_ETHCONFIG_CHAINTYPE=goerli -DVOTE_ETHCONFIG_LIGHTMODE=True -DVOTE_ETHEVENTCONFIG_SUBSCRIBEONLY=False -DVOTE_VOCHAINCONFIG_GENESIS=/app/misc/genesis.json -DVOTE_VOCHAINCONFIG_KEYKEEPERINDEX=4 -DVOTE_METRICS_ENABLED=True -DVOTE_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/ansible/templates/env.oracle0key b/dockerfiles/testsuite/ansible/templates/env.oracle0key deleted file mode 100644 index 377512882..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.oracle0key +++ /dev/null @@ -1 +0,0 @@ -DVOTE_ETHCONFIG_SIGNINGKEY={{oracle_keys[0].priv_key}} diff --git a/dockerfiles/testsuite/ansible/templates/env.seed b/dockerfiles/testsuite/ansible/templates/env.seed deleted file mode 100755 index 22575fc7d..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.seed +++ /dev/null @@ -1,7 +0,0 @@ -DVOTE_DATADIR=/app/run -DVOTE_MODE=seed -DVOTE_LOGLEVEL=debug -DVOTE_DEV=True -DVOTE_VOCHAINCONFIG_GENESIS=/app/misc/genesis.json -DVOTE_METRICS_ENABLED=True -DVOTE_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/ansible/templates/env.seeds b/dockerfiles/testsuite/ansible/templates/env.seeds deleted file mode 100644 index 2edb3726f..000000000 --- a/dockerfiles/testsuite/ansible/templates/env.seeds +++ /dev/null @@ -1,5 +0,0 @@ -DVOTE_VOCHAINCONFIG_SEEDS=" - {%- for key in seed_keys -%} - {{key.address}}@seed{{loop.index0}}:26656{% if not loop.last %},{% endif %} - {%- endfor %} -" diff --git a/dockerfiles/testsuite/docker-compose.yml b/dockerfiles/testsuite/docker-compose.yml index 47928153e..b52defbbf 100644 --- a/dockerfiles/testsuite/docker-compose.yml +++ b/dockerfiles/testsuite/docker-compose.yml @@ -13,7 +13,7 @@ services: volumes: - data-seed:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-seed:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -26,7 +26,7 @@ services: volumes: - data-miner0:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-miner0:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -39,7 +39,7 @@ services: volumes: - data-miner1:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-miner1:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -52,7 +52,7 @@ services: volumes: - data-miner2:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-miner2:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -65,7 +65,7 @@ services: volumes: - data-miner3:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-miner3:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -80,7 +80,7 @@ services: volumes: - data-gateway0:/app/run/ - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/root/.cache/vocdoni/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/zkCircuits/ - gocoverage-gateway0:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -99,6 +99,8 @@ services: environment: - GOCOVERDIR=/app/run/gocoverage command: "true" + profiles: + - tools # this container is only intended to be `docker compose run`, no need to start on `docker compose up` gocoverage: image: golang:1.21 @@ -110,6 +112,8 @@ services: - gocoverage-miner3:/app/run/gocoverage/miner3 - gocoverage-gateway0:/app/run/gocoverage/gateway0 - gocoverage-test:/app/run/gocoverage/test + profiles: + - tools # this container is only intended to be `docker compose run`, no need to start on `docker compose up` prometheus: image: prom/prometheus:v2.26.0 @@ -124,6 +128,8 @@ services: - "--log.level=debug" networks: - blockchain + profiles: + - grafana grafana: image: grafana/grafana @@ -142,6 +148,8 @@ services: environment: - GF_AUTH_ANONYMOUS_ENABLED=true - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + profiles: + - grafana networks: blockchain: diff --git a/dockerfiles/testsuite/start_test.sh b/dockerfiles/testsuite/start_test.sh index 8662b2cd2..4276d6782 100755 --- a/dockerfiles/testsuite/start_test.sh +++ b/dockerfiles/testsuite/start_test.sh @@ -14,7 +14,7 @@ # e2etest_dynamicensuselection: run dynamic census test export COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 COMPOSE_INTERACTIVE_NO_CLI=1 -[ -n "$GOCOVERDIR" ] && export BUILDARGS="-cover" # docker-compose build passes this to go 1.20 so that binaries collect code coverage +[ -n "$GOCOVERDIR" ] && export BUILDARGS="-cover" # docker compose build passes this to go 1.20 so that binaries collect code coverage COMPOSE_CMD=${COMPOSE_CMD:-"docker compose"} COMPOSE_CMD_RUN="$COMPOSE_CMD run" @@ -156,9 +156,6 @@ e2etest_ballotelection() { ### end tests definition -# useful for debugging bash flow -# docker-compose() { echo "# would do: docker-compose $@" ; sleep 0.2 ;} - log "### Starting test suite ###" $COMPOSE_CMD build $COMPOSE_CMD up -d seed # start the seed first so the nodes can properly bootstrap diff --git a/dockerfiles/vocdoninode/README.md b/dockerfiles/vocdoninode/README.md index 61b10209c..5ac2618f3 100644 --- a/dockerfiles/vocdoninode/README.md +++ b/dockerfiles/vocdoninode/README.md @@ -5,28 +5,22 @@ To deploy a standalone node copy the `env.example` file as `env`, define the vars according to your settings, and start the node with: ```bash -RESTART=always docker-compose up -d +RESTART=always docker compose up -d ``` This command will download the precompiled remote image of the vocdoni node. For compiling your own image, you can run as previous step the following command: ```bash -docker-compose build +docker compose build ``` ## With Watchtower -If you want to add watchtower (for automatic remote image upgrade), provide all compose files involved: +If you want to add watchtower (for automatic remote image upgrade), use the relevant profile: ```bash -RESTART=always docker-compose -f docker-compose.yml -f docker-compose.watchtower.yml up -d -``` - -Similarly, you need to provide all compose files to tear it down: - -```bash -RESTART=always docker-compose -f docker-compose.yml -f docker-compose.watchtower.yml down +RESTART=always COMPOSE_PROFILES=watchtower docker compose up -d ``` Note that the RESTART variable can be set to change the behaviour across restarts. To ensure the node is started after a reboot, set it to `always` or `unless-stopped` diff --git a/dockerfiles/vocdoninode/docker-compose-traefik-cloudflare.yml b/dockerfiles/vocdoninode/docker-compose-traefik-cloudflare.yml deleted file mode 100644 index ecacad4e0..000000000 --- a/dockerfiles/vocdoninode/docker-compose-traefik-cloudflare.yml +++ /dev/null @@ -1,68 +0,0 @@ -version: "3.4" -services: - traefik: - image: "traefik:v2.5" - container_name: "traefik" - command: - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--entrypoints.web.address=:80" - - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - - "--entrypoints.web.http.redirections.entryPoint.scheme=https" - - "--entrypoints.web.http.redirections.entrypoint.permanent=true" - - "--entrypoints.websecure.address=:443" - - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" - - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" - - "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_LE_EMAIL}" - - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - - "--log.level=DEBUG" - ports: - - "80:80" - - "443:443" - environment: - - "CLOUDFLARE_EMAIL=${TRAEFIK_CF_EMAIL}" - - "CLOUDFLARE_DNS_API_TOKEN=${TRAEFIK_CF_TOKEN}" - volumes: - - "./letsencrypt:/letsencrypt" - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - dvotenode: - image: "ghcr.io/vocdoni/vocdoni-node:${VOCDONI_NODE_TAG:-main}" - env_file: env - volumes: - - "run:/app/run" - ports: - - "9090:9090" - - "9091:9091" - - "4001:4001" - - "4171:4171" - - "5001:5001" - - "30303:30303" - - "30303:30303/udp" - - "9096:9096" - - "26656:26656" - - "26657:26657" - - "26658:26658" - - "61000-61100:61000-61100" - sysctls: - net.core.somaxconn: 8128 - restart: ${RESTART:-always} - labels: - - "traefik.enable=true" - - "traefik.http.routers.dvotenode.rule=Host(`${DOMAIN}`)" - - "traefik.http.routers.dvotenode.entrypoints=websecure" - - "traefik.http.routers.dvotenode.tls.certresolver=letsencrypt" - - "traefik.http.routers.dvotenode.service=dvotenode" - - "traefik.http.services.dvotenode.loadbalancer.server.port=9090" - - watchtower: - image: containrrr/watchtower - volumes: - - /var/run/docker.sock:/var/run/docker.sock - labels: - - com.centurylinklabs.watchtower.enable="false" - command: --interval 30 - -volumes: - run: {} - diff --git a/dockerfiles/vocdoninode/docker-compose.watchtower.yml b/dockerfiles/vocdoninode/docker-compose.watchtower.yml deleted file mode 100644 index af28d1656..000000000 --- a/dockerfiles/vocdoninode/docker-compose.watchtower.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: "3.4" -services: - watchtower: - image: containrrr/watchtower - volumes: - - /var/run/docker.sock:/var/run/docker.sock - labels: - - com.centurylinklabs.watchtower.enable="false" - command: --interval 30 diff --git a/dockerfiles/vocdoninode/docker-compose.yml b/dockerfiles/vocdoninode/docker-compose.yml index 24f456aae..6ea067dd9 100644 --- a/dockerfiles/vocdoninode/docker-compose.yml +++ b/dockerfiles/vocdoninode/docker-compose.yml @@ -3,7 +3,7 @@ services: vocdoninode: build: context: ../../ - image: "ghcr.io/vocdoni/vocdoni-node:main" + image: "ghcr.io/vocdoni/vocdoni-node:${VOCDONI_NODE_TAG:-main}" env_file: env volumes: - "run:/app/run" @@ -26,6 +26,16 @@ services: net.core.somaxconn: 8128 restart: ${RESTART:-no} + watchtower: + image: containrrr/watchtower + volumes: + - /var/run/docker.sock:/var/run/docker.sock + labels: + - com.centurylinklabs.watchtower.enable="false" + command: --interval 30 --include-stopped --revive-stopped + profiles: + - watchtower + volumes: run: {} eth: {} diff --git a/scripts/update_and_deploy.sh b/scripts/update_and_deploy.sh deleted file mode 100755 index d51178554..000000000 --- a/scripts/update_and_deploy.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -BRANCH=${BRANCH:-main} -CMD=${CMD:-vocdoninode} -NAME="$CMD-$BRANCH" - -[ ! -d dockerfiles/$CMD ] && { - echo "dockerfiles/$CMD does not exist" - echo "please execute this script from repository root: bash scripts/update_and_deploy.sh" - exit 1 -} - -check_git() { # 0=no | 1=yes - [ -n "$FORCE" ] && echo "Force is enabled" && return 1 - git fetch origin - local is_updated=$(git log HEAD..origin/$BRANCH --oneline | wc -c) # 0=yes - [ $is_updated -gt 0 ] && git pull origin $BRANCH --force && return 1 - return 0 -} - -check_git || { - echo "Updating and deploying container" - for f in `docker container ls | grep $NAME | awk '{print $1}' | grep -v CONTAINER`; do docker container stop $f; done - docker container prune -f - NAME="$NAME" EXTRA_OPTS="$EXTRA_OPTS" dockerfiles/$CMD/dockerlaunch.sh - exit $? -} -echo "nothing to do, use FORCE=1 $0 if you want to force the update" diff --git a/scripts/update_and_deploy_compose.sh b/scripts/update_and_deploy_compose.sh deleted file mode 100755 index 7b9ab83de..000000000 --- a/scripts/update_and_deploy_compose.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -BRANCH=${BRANCH:-main} -CMD=${CMD:-vocdoninode} -NAME="$CMD-$BRANCH" - -export COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 - -[ ! -d dockerfiles/$CMD ] && { - echo "dockerfiles/$CMD does not exist" - echo "please execute this script from repository root: bash scripts/update_and_deploy_compose.sh" - exit 1 -} - -check_git() { # 0=no | 1=yes - [ -n "$FORCE" ] && echo "Force is enabled" && return 1 - git fetch origin - local is_updated=$(git log HEAD..origin/$BRANCH --oneline | wc -c) # 0=yes - [ $is_updated -gt 0 ] && git pull origin $BRANCH --force && return 1 - return 0 -} - -deploy() { - echo "Updating and deploying container" - cd dockerfiles/$CMD - docker-compose build - docker-compose up -d - exit $? -} - -check_git || deploy - -echo "nothing to do, use FORCE=1 $0 if you want to force the update"