summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTsachi Herman <tsachi.herman@algorand.com>2022-03-01 13:31:07 -0500
committerTsachi Herman <tsachi.herman@algorand.com>2022-03-01 13:31:07 -0500
commitdf44eaf0adeb5c0deb08af973691e69440b7e0c8 (patch)
tree1aa79573fe07fde4c63c36d5f20161c67baa8082
parent6f26b29f96ed15d8a911a4138c6adea302d1c993 (diff)
parent6b7dfb635d100d20c67259f091f0390e4498d9c3 (diff)
Merge branch 'master' into feature/unlimited-assets
-rw-r--r--agreement/pseudonode_test.go11
-rw-r--r--cmd/algokey/keyreg.go258
-rw-r--r--cmd/algokey/part.go1
-rw-r--r--crypto/compactcert/structs.go2
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--ledger/internal/compactcert.go2
-rwxr-xr-xtest/scripts/e2e_subs/keyreg.sh33
8 files changed, 301 insertions, 12 deletions
diff --git a/agreement/pseudonode_test.go b/agreement/pseudonode_test.go
index b4209ffde..dfb9f2ce1 100644
--- a/agreement/pseudonode_test.go
+++ b/agreement/pseudonode_test.go
@@ -520,11 +520,12 @@ func TestPseudonodeFailedEnqueuedTasks(t *testing.T) {
for i := 0; i < pseudonodeVerificationBacklog*2; i++ {
ch, err = pb.MakeProposals(context.Background(), startRound, period(i))
if err != nil {
- require.Subset(t, []int{pseudonodeVerificationBacklog, pseudonodeVerificationBacklog + 1}, []int{i})
+ require.ErrorAs(t, errPseudonodeBacklogFull, &err)
break
}
channels = append(channels, ch)
}
+ enqueuedProposals := len(channels)
require.Error(t, err, "MakeProposals did not returned an error when being overflowed with requests")
require.True(t, errors.Is(err, errPseudonodeBacklogFull))
@@ -533,17 +534,17 @@ func TestPseudonodeFailedEnqueuedTasks(t *testing.T) {
for i := 0; i < pseudonodeVerificationBacklog*2; i++ {
ch, err = pb.MakeVotes(context.Background(), startRound, period(i), step(i%5), makeProposalValue(period(i), accounts[0].Address()), persist)
if err != nil {
- require.Subset(t, []int{pseudonodeVerificationBacklog, pseudonodeVerificationBacklog + 1}, []int{i})
+ require.ErrorAs(t, errPseudonodeBacklogFull, &err)
break
}
channels = append(channels, ch)
}
require.Error(t, err, "MakeVotes did not returned an error when being overflowed with requests")
-
+ enqueuedVotes := len(channels) - enqueuedProposals
// drain output channels.
for _, ch := range channels {
drainChannel(ch)
}
- require.Equal(t, 330, subStrLogger.instancesFound[0])
- require.Equal(t, 330, subStrLogger.instancesFound[1])
+ require.Equal(t, enqueuedVotes*len(accounts), subStrLogger.instancesFound[0])
+ require.Equal(t, enqueuedProposals*len(accounts), subStrLogger.instancesFound[1])
}
diff --git a/cmd/algokey/keyreg.go b/cmd/algokey/keyreg.go
new file mode 100644
index 000000000..445a07827
--- /dev/null
+++ b/cmd/algokey/keyreg.go
@@ -0,0 +1,258 @@
+// Copyright (C) 2019-2022 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/spf13/cobra"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/data/account"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/util"
+ "github.com/algorand/go-algorand/util/db"
+)
+
+var keyregCmd *cobra.Command
+
+type keyregCmdParams struct {
+ fee uint64
+ firstValid uint64
+ lastValid uint64
+ network string
+ offline bool
+ txFile string
+ partkeyFile string
+ addr string
+}
+
+// There is no node to query, so we do our best here.
+const (
+ txnLife uint64 = 1000
+ minFee uint64 = 1000
+)
+
+var validNetworks map[string]crypto.Digest
+var validNetworkList []string
+
+func init() {
+ var params keyregCmdParams
+
+ keyregCmd = &cobra.Command{
+ Use: "keyreg",
+ Short: "Make key registration transaction",
+ Args: cobra.NoArgs,
+ Run: func(cmd *cobra.Command, _ []string) {
+ err := run(params)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n\n", err.Error())
+ os.Exit(1)
+ }
+ },
+ }
+
+ keyregCmd.Flags().Uint64Var(&params.fee, "fee", minFee, "transaction fee")
+ keyregCmd.Flags().Uint64Var(&params.firstValid, "firstvalid", 0, "first round where the transaction may be committed to the ledger")
+ keyregCmd.MarkFlagRequired("firstvalid") // nolint:errcheck
+ keyregCmd.Flags().Uint64Var(&params.lastValid, "lastvalid", 0, fmt.Sprintf("last round where the generated transaction may be committed to the ledger, defaults to firstvalid + %d", txnLife))
+ keyregCmd.Flags().StringVar(&params.network, "network", "mainnet", "the network where the provided keys will be registered, one of mainnet/testnet/betanet")
+ keyregCmd.MarkFlagRequired("network") // nolint:errcheck
+ keyregCmd.Flags().BoolVar(&params.offline, "offline", false, "set to bring an account offline")
+ keyregCmd.Flags().StringVarP(&params.txFile, "outputFile", "o", "", fmt.Sprintf("write signed transaction to this file, or '%s' to write to stdout", stdoutFilenameValue))
+ keyregCmd.Flags().StringVar(&params.partkeyFile, "keyfile", "", "participation keys to register, file is opened to fetch metadata for the transaction; only specify when bringing an account online to vote in Algorand consensus")
+ keyregCmd.Flags().StringVar(&params.addr, "account", "", "account address to bring offline; only specify when taking an account offline from voting in Algorand consensus")
+
+ // TODO: move 'bundleGenesisInject' into something that can be imported here instead of using constants.
+ validNetworks = map[string]crypto.Digest{
+ "mainnet": mustConvertB64ToDigest("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="),
+ "testnet": mustConvertB64ToDigest("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="),
+ "betanet": mustConvertB64ToDigest("mFgazF+2uRS1tMiL9dsj01hJGySEmPN28B/TjjvpVW0="),
+ "devnet": mustConvertB64ToDigest("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E="),
+ }
+ validNetworkList = make([]string, 0, len(validNetworks))
+ for k := range validNetworks {
+ validNetworkList = append(validNetworkList, k)
+ }
+}
+
+func mustConvertB64ToDigest(b64 string) (digest crypto.Digest) {
+ data, err := base64.StdEncoding.DecodeString(b64)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to decode digest '%s': %s\n\n", b64, err)
+ os.Exit(1)
+ }
+ if len(data) != len(digest[:]) {
+ fmt.Fprintf(os.Stderr, "Unexpected decoded digest length decoding '%s'.\n\n", b64)
+ os.Exit(1)
+ }
+ copy(digest[:], data)
+ return
+}
+
+func getGenesisInformation(network string) (crypto.Digest, error) {
+ // For testing purposes, there is a secret option to override the genesis information.
+ hashOverride := os.Getenv("ALGOKEY_GENESIS_HASH")
+ if hashOverride != "" {
+ return mustConvertB64ToDigest(hashOverride), nil
+ }
+
+ // Otherwise check that network matches one of the known networks.
+ gen, ok := validNetworks[strings.ToLower(network)]
+ if !ok {
+ return crypto.Digest{}, fmt.Errorf("unknown network '%s' provided. Supported networks: %s",
+ network,
+ strings.Join(validNetworkList, ", "))
+ }
+
+ return gen, nil
+}
+
+func run(params keyregCmdParams) error {
+ // Implicit last valid
+ if params.lastValid == 0 {
+ params.lastValid = params.firstValid + txnLife
+ }
+
+ if params.fee < minFee {
+ return fmt.Errorf("the provided transaction fee (%d) is too low, the minimum fee is %d", params.fee, minFee)
+ }
+
+ if !params.offline {
+ if params.partkeyFile == "" {
+ return errors.New("must provide --keyfile when registering participation keys")
+ }
+ if params.addr != "" {
+ return errors.New("do not provide --address when registering participation keys")
+ }
+ } else {
+ if params.addr == "" {
+ return errors.New("must provide --address when bringing an account offline")
+ }
+ if params.partkeyFile != "" {
+ return errors.New("do not provide --keyfile when bringing an account offline")
+ }
+ }
+
+ var accountAddress basics.Address
+ if params.addr != "" {
+ var err error
+ accountAddress, err = basics.UnmarshalChecksumAddress(params.addr)
+ if err != nil {
+ return fmt.Errorf("unable to parse --address: %w", err)
+ }
+ }
+
+ if params.partkeyFile != "" && !util.FileExists(params.partkeyFile) {
+ return fmt.Errorf("cannot access keyfile '%s'", params.partkeyFile)
+ }
+
+ if params.txFile == "" {
+ params.txFile = fmt.Sprintf("%s.tx", params.partkeyFile)
+ }
+
+ if util.FileExists(params.txFile) || params.txFile == stdoutFilenameValue {
+ return fmt.Errorf("outputFile '%s' already exists", params.partkeyFile)
+ }
+
+ // Lookup information from partkey file
+ var part *account.Participation
+ if params.partkeyFile != "" {
+ partDB, err := db.MakeErasableAccessor(params.partkeyFile)
+ if err != nil {
+ return fmt.Errorf("cannot open keyfile %s: %v", params.partkeyFile, err)
+ }
+
+ partkey, err := account.RestoreParticipation(partDB)
+ if err != nil {
+ return fmt.Errorf("cannot load keyfile %s: %v", params.partkeyFile, err)
+ }
+ defer partkey.Close()
+
+ part = &partkey.Participation
+
+ if params.firstValid < uint64(part.FirstValid) {
+ return fmt.Errorf("the transaction's firstvalid round (%d) field should be set greater than or equal to the participation key's first valid round (%d). The network will reject key registration transactions that are set to take effect before the participation key's first valid round", params.firstValid, part.FirstValid)
+ }
+ }
+
+ validRange := params.lastValid - params.firstValid
+ if validRange > txnLife {
+ return fmt.Errorf("the transaction's specified validity range must be less than or equal to 1000 rounds due to security constraints. Please enter a first valid round (%d) and last valid round (%d) whose difference is no more than 1000 rounds", params.firstValid, params.lastValid)
+ }
+
+ var txn transactions.Transaction
+ if !params.offline {
+ // Generate go-online transaction
+ txn = part.GenerateRegistrationTransaction(
+ basics.MicroAlgos{Raw: params.fee},
+ basics.Round(params.firstValid),
+ basics.Round(params.lastValid),
+ [32]byte{},
+ part.StateProofSecrets != nil)
+ } else {
+ // Generate go-offline transaction
+ txn = transactions.Transaction{
+ Type: protocol.KeyRegistrationTx,
+ Header: transactions.Header{
+ Sender: accountAddress,
+ Fee: basics.MicroAlgos{Raw: params.fee},
+ FirstValid: basics.Round(params.firstValid),
+ LastValid: basics.Round(params.lastValid),
+ },
+ }
+ }
+
+ var err error
+ txn.GenesisHash, err = getGenesisInformation(params.network)
+ if err != nil {
+ return err
+ }
+
+ // Wrap in a transactions.SignedTxn with an empty sig.
+ // This way protocol.Encode will encode the transaction type
+ stxn, err := transactions.AssembleSignedTxn(txn, crypto.Signature{}, crypto.MultisigSig{})
+ if err != nil {
+ return fmt.Errorf("failed to assemble transaction: %w", err)
+ }
+
+ data := protocol.Encode(&stxn)
+ if params.txFile == stdoutFilenameValue {
+ // Write to Stdout
+ if _, err = os.Stdout.Write(data); err != nil {
+ return fmt.Errorf("failed to write transaction to stdout: %w", err)
+ }
+ } else {
+ if err = ioutil.WriteFile(params.txFile, data, 0600); err != nil {
+ return fmt.Errorf("failed to write transaction to '%s': %w", params.txFile, err)
+ }
+ }
+
+ if params.offline {
+ fmt.Printf("Account key go offline transaction written to '%s'.\n", params.txFile)
+ } else {
+ fmt.Printf("Key registration transaction written to '%s'.\n", params.txFile)
+ }
+ return nil
+}
diff --git a/cmd/algokey/part.go b/cmd/algokey/part.go
index 5a29f9d71..57a4ddedc 100644
--- a/cmd/algokey/part.go
+++ b/cmd/algokey/part.go
@@ -175,6 +175,7 @@ func init() {
partCmd.AddCommand(partGenerateCmd)
partCmd.AddCommand(partInfoCmd)
partCmd.AddCommand(partReparentCmd)
+ partCmd.AddCommand(keyregCmd)
partGenerateCmd.Flags().StringVar(&partKeyfile, "keyfile", "", "Participation key filename")
partGenerateCmd.Flags().Uint64Var(&partFirstRound, "first", 0, "First round for participation key")
diff --git a/crypto/compactcert/structs.go b/crypto/compactcert/structs.go
index d8a6e2100..dd1a4f315 100644
--- a/crypto/compactcert/structs.go
+++ b/crypto/compactcert/structs.go
@@ -29,8 +29,6 @@ type Params struct {
ProvenWeight uint64 // Weight threshold proven by the certificate
SigRound basics.Round // The round for which the ephemeral key is committed to
SecKQ uint64 // Security parameter (k+q) from analysis document
-
- EnableBatchVerification bool // whether ED25519 batch verification is enabled
}
// CompactOneTimeSignature is crypto.OneTimeSignature with omitempty
diff --git a/go.mod b/go.mod
index 19a67f811..8db89a7b8 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
github.com/algorand/falcon v0.0.0-20220130164023-c9e1d466f123
github.com/algorand/go-codec/codec v0.0.0-20190507210007-269d70b6135d
github.com/algorand/go-deadlock v0.2.1
- github.com/algorand/go-sumhash v0.0.0-20211021081112-0ea867c5153a
+ github.com/algorand/go-sumhash v0.1.0
github.com/algorand/graphtrace v0.0.0-20201117160756-e524ed1a6f64
github.com/algorand/msgp v1.1.49
github.com/algorand/oapi-codegen v1.3.5-algorand5
diff --git a/go.sum b/go.sum
index 1e73be8d0..92f339d4f 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ github.com/algorand/go-codec/codec v0.0.0-20190507210007-269d70b6135d h1:W9MgGUo
github.com/algorand/go-codec/codec v0.0.0-20190507210007-269d70b6135d/go.mod h1:qm6LyXvDa1+uZJxaVg8X+OEjBqt/zDinDa2EohtTDxU=
github.com/algorand/go-deadlock v0.2.1 h1:TQPQwWAB133bS5uwHpmrgH5hCMyZK5hnUW26aqWMvq4=
github.com/algorand/go-deadlock v0.2.1/go.mod h1:HgdF2cwtBIBCL7qmUaozuG/UIZFR6PLpSMR58pvWiXE=
-github.com/algorand/go-sumhash v0.0.0-20211021081112-0ea867c5153a h1:H+gCngy1bT6m8OB6IGzv/UXbAorPoqefa4J2NMuK0v8=
-github.com/algorand/go-sumhash v0.0.0-20211021081112-0ea867c5153a/go.mod h1:OOe7jdDWUhLkuP1XytkK5gnLu9entAviN5DfDZh6XAc=
+github.com/algorand/go-sumhash v0.1.0 h1:b/QRhyLuF//vOcicBIxBXYW8bERNoeLxieht/dUYpVg=
+github.com/algorand/go-sumhash v0.1.0/go.mod h1:OOe7jdDWUhLkuP1XytkK5gnLu9entAviN5DfDZh6XAc=
github.com/algorand/graphtrace v0.0.0-20201117160756-e524ed1a6f64 h1:yvKeJdS/mvLRiyIxu8j5BQDXIzs1XbC9/22KycJnt3A=
github.com/algorand/graphtrace v0.0.0-20201117160756-e524ed1a6f64/go.mod h1:qFtQmC+kmsfnLfS9j3xgKtzsWyozemL5ek1R4dWZa5c=
github.com/algorand/msgp v1.1.49 h1:YBFRcYZNsD2WgzXONvzFrjv1/887pWzJSx874VL4P6g=
diff --git a/ledger/internal/compactcert.go b/ledger/internal/compactcert.go
index 623cdc526..ca2be6d19 100644
--- a/ledger/internal/compactcert.go
+++ b/ledger/internal/compactcert.go
@@ -136,8 +136,6 @@ func CompactCertParams(votersHdr bookkeeping.BlockHeader, hdr bookkeeping.BlockH
ProvenWeight: provenWeight,
SigRound: hdr.Round,
SecKQ: proto.CompactCertSecKQ,
-
- EnableBatchVerification: proto.EnableBatchVerification,
}
return
}
diff --git a/test/scripts/e2e_subs/keyreg.sh b/test/scripts/e2e_subs/keyreg.sh
new file mode 100755
index 000000000..b3d852f26
--- /dev/null
+++ b/test/scripts/e2e_subs/keyreg.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+date '+e2e_subs/keyreg.sh start %Y%m%d_%H%M%S'
+
+set -exo pipefail
+export SHELLOPTS
+
+WALLET=$1
+
+gcmd="goal -w ${WALLET}"
+
+ACCOUNT=$(${gcmd} account list|awk '{ print $3 }')
+
+# secret algokey override
+ALGOKEY_GENESIS_HASH=$(goal node status | grep 'Genesis hash:'|awk '{ print $3 }')
+export ALGOKEY_GENESIS_HASH
+# Test key registration
+KEYS="${TEMPDIR}/foo.keys"
+TXN="${TEMPDIR}/keyreg.txn"
+STXN="${TEMPDIR}/keyreg.stxn"
+algokey part generate --first 1 --last 1000 --parent "${ACCOUNT}" --keyfile "${KEYS}"
+algokey part keyreg --network placeholder --keyfile "${KEYS}" --firstvalid 1 --outputFile "${TXN}"
+# technically algokey could be used to sign at this point, that would require
+# exporting secrets from the wallet.
+${gcmd} clerk sign -i "${TXN}" -o "${STXN}"
+${gcmd} clerk rawsend -f "${STXN}"
+
+TXN2="${TEMPDIR}/keydereg.txn"
+STXN2="${TEMPDIR}/keydereg.stxn"
+# Test key de-registration
+algokey part keyreg --network placeholder --offline --account "${ACCOUNT}" --firstvalid 1 --outputFile "${TXN2}"
+${gcmd} clerk sign -i "${TXN2}" -o "${STXN2}"
+${gcmd} clerk rawsend -f "${STXN2}"