From f8c84a2a0618cc3de62d1f7360c493469e150dc7 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Wed, 8 Jul 2020 13:09:39 +0000 Subject: [PATCH] go/common/crypto/multisig: Initial multisig implementation This adds preliminary internal support required for the new multi-signature accounts, and changes everything to use the new multi-signature account addresses and envelope. Notes: * Tooling to actually generate multi-signature accounts, and to sign transactions with accounts containing more than 1 public key is not yet implemented. * The `registry node` subcommand option `node.entity_id` has been renamed to `node.entity_address`. --- .changelog/3094.breaking.md | 14 + go/.gitignore | 3 + go/common/crypto/multisig/multisig.go | 306 ++++++++++++++++ go/common/crypto/multisig/multisig_test.go | 196 +++++++++++ go/common/entity/entity.go | 104 +++--- go/common/node/node.go | 16 +- go/consensus/api/api.go | 3 +- go/consensus/api/submission.go | 14 +- .../transaction/testvectors/testvectors.go | 4 +- go/consensus/api/transaction/transaction.go | 76 ++-- go/consensus/tendermint/abci/mux.go | 23 +- go/consensus/tendermint/abci/state.go | 11 +- go/consensus/tendermint/api/context.go | 18 +- go/consensus/tendermint/api/state.go | 7 +- .../tendermint/apps/keymanager/keymanager.go | 7 +- .../apps/keymanager/transactions.go | 3 +- .../tendermint/apps/registry/genesis.go | 9 +- .../tendermint/apps/registry/query.go | 7 +- .../tendermint/apps/registry/registry.go | 4 +- .../tendermint/apps/registry/state/state.go | 57 ++- .../apps/registry/state/state_test.go | 7 +- .../tendermint/apps/registry/transactions.go | 79 +++-- .../apps/registry/transactions_test.go | 95 +++-- .../tendermint/apps/roothash/roothash.go | 7 +- .../tendermint/apps/scheduler/genesis.go | 19 +- .../tendermint/apps/scheduler/scheduler.go | 15 +- go/consensus/tendermint/apps/staking/auth.go | 2 +- go/consensus/tendermint/apps/staking/fees.go | 42 +-- .../apps/staking/proposing_rewards.go | 18 +- .../apps/staking/signing_rewards.go | 13 +- .../tendermint/apps/staking/slashing.go | 11 +- .../tendermint/apps/staking/slashing_test.go | 21 +- .../tendermint/apps/staking/staking.go | 14 +- .../apps/staking/state/accumulator_test.go | 4 +- .../tendermint/apps/staking/state/gas.go | 16 +- .../tendermint/apps/staking/state/state.go | 52 +-- .../apps/staking/state/state_test.go | 53 +-- .../tendermint/apps/staking/transactions.go | 10 +- .../apps/staking/transactions_test.go | 3 +- go/consensus/tendermint/apps/staking/votes.go | 10 +- go/consensus/tendermint/registry/registry.go | 2 +- go/consensus/tendermint/tendermint.go | 10 +- go/consensus/tests/tester.go | 9 +- go/extra/stats/cmd/stats.go | 19 +- go/genesis/genesis_test.go | 79 +++-- go/oasis-node/cmd/common/common.go | 7 +- .../cmd/common/consensus/consensus.go | 4 +- go/oasis-node/cmd/consensus/consensus.go | 7 +- go/oasis-node/cmd/debug/byzantine/registry.go | 11 +- .../cmd/debug/consim/xfer_workload.go | 20 +- go/oasis-node/cmd/debug/txsource/txsource.go | 9 +- .../cmd/debug/txsource/workload/commission.go | 25 +- .../cmd/debug/txsource/workload/delegation.go | 19 +- .../cmd/debug/txsource/workload/oversized.go | 10 +- .../cmd/debug/txsource/workload/parallel.go | 32 +- .../cmd/debug/txsource/workload/queries.go | 14 +- .../debug/txsource/workload/registration.go | 69 ++-- .../cmd/debug/txsource/workload/runtime.go | 4 +- .../cmd/debug/txsource/workload/transfer.go | 21 +- .../cmd/debug/txsource/workload/workload.go | 42 ++- go/oasis-node/cmd/genesis/genesis.go | 32 +- go/oasis-node/cmd/registry/entity/entity.go | 48 +-- go/oasis-node/cmd/registry/node/node.go | 32 +- go/oasis-node/cmd/registry/runtime/runtime.go | 81 ++--- go/oasis-node/cmd/stake/stake.go | 5 +- go/oasis-node/node_test.go | 38 +- go/oasis-test-runner/oasis/entity.go | 4 +- go/oasis-test-runner/oasis/runtime.go | 24 +- go/oasis-test-runner/scenario/e2e/debond.go | 2 +- .../scenario/e2e/gas_fees_staking.go | 24 +- .../scenario/e2e/registry_cli.go | 39 ++- .../scenario/e2e/runtime/runtime_dynamic.go | 8 +- .../scenario/e2e/stake_cli.go | 23 +- go/oasis-test-runner/scenario/e2e/upgrade.go | 6 +- go/registry/api/api.go | 67 ++-- go/registry/api/grpc.go | 8 +- go/registry/api/runtime.go | 50 +-- go/registry/api/sanity_check.go | 28 +- go/registry/gen_vectors/main.go | 12 +- go/registry/tests/tester.go | 129 ++++--- go/roothash/api/commitment/pool_test.go | 48 ++- go/staking/api/address.go | 20 +- go/staking/api/address_test.go | 3 +- go/staking/gen_vectors/main.go | 10 +- go/staking/tests/debug/debug_stake.go | 7 +- go/staking/tests/tester.go | 8 +- go/upgrade/migrations/dummy.go | 17 +- go/worker/registration/worker.go | 47 +-- tests/fixture-data/consim/genesis.json | 329 +----------------- .../fixture-data/debond/staking-genesis.json | 6 +- .../gas-fees-runtimes/staking-genesis.json | 18 +- .../gas-fees/staking-genesis.json | 2 +- .../txsource/staking-genesis.json | 20 +- 93 files changed, 1722 insertions(+), 1259 deletions(-) create mode 100644 .changelog/3094.breaking.md create mode 100644 go/common/crypto/multisig/multisig.go create mode 100644 go/common/crypto/multisig/multisig_test.go diff --git a/.changelog/3094.breaking.md b/.changelog/3094.breaking.md new file mode 100644 index 00000000000..2a09b60ae3f --- /dev/null +++ b/.changelog/3094.breaking.md @@ -0,0 +1,14 @@ +go/consensus/api/transaction: Support multi-sig accounts + +This adds preliminary internal support required for the new +multi-signature accounts, and changes everything to use the new +multi-signature account addresses and envelope. + +Notes: + +- Tooling to actually generate multi-signature accounts, and to sign + transactions with accounts containing more than 1 public key is not + yet implemented. + +- The `registry node` subcommand option `node.entity_id` has been + renamed to `node.entity_address`. diff --git a/go/.gitignore b/go/.gitignore index 83ecca1ba15..a5acfa10769 100644 --- a/go/.gitignore +++ b/go/.gitignore @@ -11,3 +11,6 @@ storage/mkvs/interop/mkvs-test-helpers extra/extract-metrics/extract-metrics extra/stats/stats + +registry/gen_vectors/gen_vectors +staking/gen_vectors/gen_vectors diff --git a/go/common/crypto/multisig/multisig.go b/go/common/crypto/multisig/multisig.go new file mode 100644 index 00000000000..72d75b073f5 --- /dev/null +++ b/go/common/crypto/multisig/multisig.go @@ -0,0 +1,306 @@ +// Package multisig implements the multisig envelope format and +// associated types. +package multisig + +import ( + "context" + "encoding/json" + "fmt" + "io" + "math/bits" + + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/prettyprint" +) + +var ( + _ prettyprint.PrettyPrinter = (*AccountSigner)(nil) + _ prettyprint.PrettyPrinter = (*Account)(nil) + _ prettyprint.PrettyPrinter = (*PrettyEnvelope)(nil) +) + +// AccountSigner is a signer associated with an account. +type AccountSigner struct { + // PublicKey is the account signer's public key. + PublicKey signature.PublicKey `json:"public_key"` + + // Weight is the account signer's signing weight. + Weight uint64 `json:"weight"` +} + +// PrettyPrint writes a pretty-printed representation of the type to +// the given writer. +func (signer AccountSigner) PrettyPrint(context context.Context, prefix string, w io.Writer) { + data, err := json.MarshalIndent(signer, prefix, " ") + if err != nil { + fmt.Fprintf(w, "%s\n", prefix, err) + return + } + fmt.Fprintf(w, "%s%s\n", prefix, data) +} + +// PrettyType returns a representation of the type that can be used for +// pretty printing. +func (signer AccountSigner) PrettyType() (interface{}, error) { + return signer, nil +} + +// Account is an account descriptor. +type Account struct { + cbor.Versioned + + // Signers are the account signers associated with the given + // account. + Signers []AccountSigner `json:"signers,omitempty"` + + // Threshold is the minimum combined weight that must be + // met (>=) for a payload to be considered valid. + Threshold uint64 `json:"threshold"` +} + +// Verify validates an account descriptor for well-formedness. +func (acc *Account) Verify() error { + if acc.V != 0 { + return fmt.Errorf("crypto/multisig: invalid version: %v", acc.V) + } + if len(acc.Signers) == 0 { + return fmt.Errorf("crypto/multisig: no account signers") + } + + pkMap := make(map[signature.PublicKey]bool) + var sum, carry uint64 + for _, v := range acc.Signers { + pk := v.PublicKey + if !pk.IsValid() { + return fmt.Errorf("crypto/multisig: invalid account signer: '%s'", pk) + } + if pkMap[pk] { + return fmt.Errorf("crypto/multisig: duplicate account signer: '%s'", pk) + } + pkMap[pk] = true + + if v.Weight == 0 { + return fmt.Errorf("crypto/multisig: invalid account signing weight: '%s'", pk) + } + + sum, carry = bits.Add64(sum, v.Weight, 0) + if carry != 0 { + return fmt.Errorf("crypto/multisig: total signing weight overflow") + } + } + + if acc.Threshold == 0 { + return fmt.Errorf("crypto/multisig: invalid account threshold") + } + if sum < acc.Threshold { + return fmt.Errorf("crypto/multisig: threshold %d exceeds available signing power %d", acc.Threshold, sum) + } + + // Strictly speaking this isn't required, but forcing all single + // signer accounts to be of the form created by NewAccountFromPublicKey + // seems like a reasonable thing to do. + if len(acc.Signers) == 1 && sum != 1 { + return fmt.Errorf("crypto/multisig: invalid signing power %d for single signer account", sum) + } + + return nil +} + +// Hash returns the hash of the account descriptor. +func (acc *Account) Hash() []byte { + h := hash.NewFrom(acc) + return h[:] +} + +// PrettyPrint writes a pretty-printed representation of the type to +// the given writer. +func (acc Account) PrettyPrint(context context.Context, prefix string, w io.Writer) { + data, err := json.MarshalIndent(acc, prefix, " ") + if err != nil { + fmt.Fprintf(w, "%s\n", prefix, err) + return + } + fmt.Fprintf(w, "%s%s\n", prefix, data) +} + +// PrettyType returns a representation of the type that can be used for +// pretty printing. +func (acc Account) PrettyType() (interface{}, error) { + return acc, nil +} + +// NewAccountFromPublicKey creates an account descriptor containing a +// single public key. +func NewAccountFromPublicKey(pk signature.PublicKey) *Account { + return &Account{ + Signers: []AccountSigner{ + { + PublicKey: pk, + Weight: 1, + }, + }, + Threshold: 1, + } +} + +// Envelope is a multisig envelope. +type Envelope struct { + // Account is the account signing this payload. + Account Account `json:"account"` + + // Signatures are the signatures covering `Account.Address () || Payload` + // for this envelope, sorted in the order that the public keys + // appear in the account descriptor. + // + // If a signature is missing for a given signer, the corresponding + // entry in the vector will be `nil`. + Signatures []*signature.RawSignature `json:"signatures,omitempty"` + + // Payload is the raw payload. + Payload []byte `json:"payload,omitempty"` +} + +// Verify verifies an envelope and its signature(s). +func (e *Envelope) Verify(context signature.Context) error { + if err := e.Account.Verify(); err != nil { + return err + } + + if len(e.Signatures) != len(e.Account.Signers) { + return fmt.Errorf("crypto/multisig: invalid number of signatures") + } + + var ( + sigs []signature.Signature + sum uint64 + ) + for i, v := range e.Signatures { + if v == nil { + continue + } + if len(v) != signature.SignatureSize { + return fmt.Errorf("crypto/multisig: malformed signature(s)") + } + + signer := e.Account.Signers[i] + sigs = append(sigs, signature.Signature{ + PublicKey: signer.PublicKey, + Signature: *v, + }) + sum += signer.Weight // Overflow checked in Account.Verify. + } + + msg := append(e.Account.Hash(), e.Payload...) + if !signature.VerifyManyToOne(context, msg, sigs) { + // Note: Envelopes with any invalid signatures will be rejected + // by this. In theory, there could be enough valid signatures + // with sufficient signing power, but in practice "don't do that + // then". + return fmt.Errorf("crypto/multisig: invalid signature(s)") + } + + if sum < e.Account.Threshold { + return fmt.Errorf("crypto/multisig: insufficent signing power") + } + + return nil +} + +// Open opens an envelope, and returns the payload after verifying the +// Account and signatures. +func (e *Envelope) Open(context signature.Context, dst interface{}) error { + if err := e.Verify(context); err != nil { + return err + } + + if err := cbor.Unmarshal(e.Payload, dst); err != nil { + return fmt.Errorf("crypto/multisig: malformed payload: %w", err) + } + + return nil +} + +// Sign signs a raw serialized payload for inclusion in an envelope. +func Sign(signer signature.Signer, account *Account, context signature.Context, payload []byte) (*signature.Signature, error) { + // This could check if the signer is part of the account, but + // leaving that to the caller is fine ("Don't do that then"). + msg := append(account.Hash(), payload...) + return signature.Sign(signer, context, msg) +} + +// NewEnvelope creates a new envelope from its components. +func NewEnvelope(account *Account, signatures []*signature.Signature, payload []byte) (*Envelope, error) { + env := &Envelope{ + Account: *account, + Payload: payload, + } + + sigMap := make(map[signature.PublicKey]*signature.RawSignature) + for _, v := range signatures { + if sigMap[v.PublicKey] != nil { + return nil, fmt.Errorf("crypto/multisig: redundant signature for '%s'", v.PublicKey) + } + sigMap[v.PublicKey] = &v.Signature + } + + for _, v := range account.Signers { + sig := sigMap[v.PublicKey] + env.Signatures = append(env.Signatures, sig) + delete(sigMap, v.PublicKey) + } + + if len(sigMap) != 0 { + return nil, fmt.Errorf("crypto/multisig: signatures not part of the account") + } + + // Note: This could validate the account and final envelope but, + // "don't do that then". + + return env, nil +} + +// PrettyEnvelope is used for pretty-printing envelopes so that the actual +// content is displayed instead of the binary blob. +// +// It should only be used for pretty-printing. +type PrettyEnvelope struct { + Account Account `json:"account"` + Signatures []*signature.RawSignature `json:"signatures,omitempty"` + Payload interface{} `json:"payload,omitempty"` +} + +// PrettyPrint writes a pretty-printed representation of the type to +// the given writer. +func (pe PrettyEnvelope) PrettyPrint(context context.Context, prefix string, w io.Writer) { + data, err := json.MarshalIndent(pe, prefix, " ") + if err != nil { + fmt.Fprintf(w, "%s\n", prefix, err) + return + } + fmt.Fprintf(w, "%s%s\n", prefix, data) +} + +// PrettyType returns a representation of the type that can be used for +// pretty printing. +func (pe PrettyEnvelope) PrettyType() (interface{}, error) { + return pe, nil +} + +// NewPrettyEnvelope creates a new PrettyEnvelope instance that can be +// used for pretty-printing envelopes. +func NewPrettyEnvelope(e Envelope, b interface{}) (*PrettyEnvelope, error) { + if pp, ok := b.(prettyprint.PrettyPrinter); ok { + var err error + if b, err = pp.PrettyType(); err != nil { + return nil, fmt.Errorf("failed to pretty print body: %w", err) + } + } + + return &PrettyEnvelope{ + Account: e.Account, + Signatures: e.Signatures, + Payload: b, + }, nil +} diff --git a/go/common/crypto/multisig/multisig_test.go b/go/common/crypto/multisig/multisig_test.go new file mode 100644 index 00000000000..ad6257fb71a --- /dev/null +++ b/go/common/crypto/multisig/multisig_test.go @@ -0,0 +1,196 @@ +package multisig + +import ( + "crypto/rand" + "math" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" +) + +func TestAccount(t *testing.T) { + require := require.New(t) + + signer, err := memory.NewSigner(rand.Reader) + require.NoError(err, "NewSigner") + + // Bad version + badVersion := &Account{ + Versioned: cbor.Versioned{ + V: 1, + }, + } + err = badVersion.Verify() + require.Error(err, "Account.Verify(): bad version") + + // No signers + noSigners := &Account{ + Threshold: 1, + } + err = noSigners.Verify() + require.Error(err, "Account.Verify(): no signers") + + // Bad threshold + badThreshold := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer.Public(), + Weight: 1, + }, + }, + Threshold: 0, + } + err = badThreshold.Verify() + require.Error(err, "Account.Verify(): zero threshold") + badThreshold.Threshold = 5 + err = badThreshold.Verify() + require.Error(err, "Account.Verify(): impossible threshold") + + // Bad weight + badWeight := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer.Public(), + Weight: 0, + }, + }, + Threshold: 1, + } + err = badWeight.Verify() + require.Error(err, "Account.Verify(): invalid signing weight") + + // Duplicate signer + dupSigner := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer.Public(), + Weight: 1, + }, + { + PublicKey: signer.Public(), + Weight: 2, + }, + }, + Threshold: 1, + } + err = dupSigner.Verify() + require.Error(err, "Account.Verify(): duplicate signer") + + // Weight overflow + signer2, err := memory.NewSigner(rand.Reader) + require.NoError(err, "NewSigner") + weightOverflow := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer.Public(), + Weight: math.MaxUint64, + }, + { + PublicKey: signer2.Public(), + Weight: 1, + }, + }, + Threshold: 1, + } + err = weightOverflow.Verify() + require.Error(err, "Account.Verify(): weight overflow") + + // Ok + account := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer.Public(), + Weight: 1, + }, + { + PublicKey: signer2.Public(), + Weight: 1, + }, + }, + Threshold: 1, + } + err = account.Verify() + require.NoError(err, "Account.Verify()") +} + +func TestEnvelope(t *testing.T) { + require := require.New(t) + + // Define a random test payload + type testPayload struct { + TestData string + } + data := testPayload{ + TestData: "Hasta el suelo, Mi amigo linoleo, Linoleo", + } + payload := cbor.Marshal(data) + + // Define a test account + signer1, err := memory.NewSigner(rand.Reader) + require.NoError(err, "NewSigner") + signer2, err := memory.NewSigner(rand.Reader) + require.NoError(err, "NewSigner") + signer3, err := memory.NewSigner(rand.Reader) + require.NoError(err, "NewSigner") + account := &Account{ + Signers: []AccountSigner{ + { + PublicKey: signer1.Public(), + Weight: 1, + }, + { + PublicKey: signer2.Public(), + Weight: 1, + }, + { + PublicKey: signer3.Public(), + Weight: 1, + }, + }, + Threshold: 2, + } + + // Make the signatures we'll need + signature.SetChainContext("test: oasis-core tests") + context := signature.NewContext("oasis-core/crypto/multisig: tests", signature.WithChainSeparation()) + sig1, err := Sign(signer1, account, context, payload) + require.NoError(err, "Sign: signer1") + sig2, err := Sign(signer2, account, context, payload) + require.NoError(err, "Sign: signer2") + sig3, err := Sign(signer3, account, context, payload) + require.NoError(err, "Sign: signer3") + + // All signatures + var dest testPayload + envelope, err := NewEnvelope(account, []*signature.Signature{sig1, sig2, sig3}, payload) + require.NoError(err, "NewEnvelope: all signatures") + err = envelope.Open(context, &dest) + require.NoError(err, "Open: all signatures") + require.Equal(data, dest, "envelope roundtrips") + + // 2 signatures + envelope, err = NewEnvelope(account, []*signature.Signature{sig2, sig3}, payload) + require.NoError(err, "NewEnvelope: 2 signatures") + err = envelope.Open(context, &dest) + require.NoError(err, "Open: 2 signatures") + + // 1 signature + envelope, err = NewEnvelope(account, []*signature.Signature{sig1}, payload) + require.NoError(err, "NewEnvelope: 1 signature") + err = envelope.Open(context, &dest) + require.Error(err, "Open: 1 signatures") + + // Bad signature + envelope.Signatures[0][0] ^= 0xa5 + err = envelope.Open(context, &dest) + require.Error(err, "Open: bad signature") + + // Invalid number of signatures/sentinels + envelope.Signatures = envelope.Signatures[:2] + err = envelope.Open(context, &dest) + require.Error(err, "Open: invalid number of signatures/sentinels") +} diff --git a/go/common/entity/entity.go b/go/common/entity/entity.go index ea3a7087e39..95a05bc57c7 100644 --- a/go/common/entity/entity.go +++ b/go/common/entity/entity.go @@ -11,9 +11,11 @@ import ( "path/filepath" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/prettyprint" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) const ( @@ -23,8 +25,9 @@ const ( ) var ( - testEntity Entity - testEntitySigner signature.Signer + testEntity Entity + testEntitySigner signature.Signer + testEntityAccount *multisig.Account _ prettyprint.PrettyPrinter = (*SignedEntity)(nil) ) @@ -42,13 +45,10 @@ const ( // Entity represents an entity that controls one or more Nodes and or // services. type Entity struct { // nolint: maligned - // DescriptorVersion is the entity descriptor version. - // - // It should be bumped whenever breaking changes are made to the descriptor. - DescriptorVersion uint16 `json:"v,omitempty"` + cbor.Versioned - // ID is the public key identifying the entity. - ID signature.PublicKey `json:"id"` + // AccountAddress is the account address identifying the entity. + AccountAddress staking.Address `json:"account_address"` // Nodes is the vector of node identity keys owned by this entity, that // will sign the descriptor with the node signing key rather than the @@ -65,15 +65,15 @@ func (e *Entity) ValidateBasic(strictVersion bool) error { switch strictVersion { case true: // Only the latest version is allowed. - if e.DescriptorVersion != LatestEntityDescriptorVersion { + if e.Versioned.V != LatestEntityDescriptorVersion { return fmt.Errorf("invalid entity descriptor version (expected: %d got: %d)", LatestEntityDescriptorVersion, - e.DescriptorVersion, + e.Versioned.V, ) } case false: // A range of versions is allowed. - if e.DescriptorVersion < minEntityDescriptorVersion || e.DescriptorVersion > maxEntityDescriptorVersion { + if e.Versioned.V < minEntityDescriptorVersion || e.Versioned.V > maxEntityDescriptorVersion { return fmt.Errorf("invalid entity descriptor version (min: %d max: %d)", minEntityDescriptorVersion, maxEntityDescriptorVersion, @@ -85,7 +85,7 @@ func (e *Entity) ValidateBasic(strictVersion bool) error { // String returns a string representation of itself. func (e Entity) String() string { - return "" + return "" } // Save saves the JSON serialized entity descriptor. @@ -101,27 +101,30 @@ func (e *Entity) Save(baseDir string) error { } // Load loads an existing entity from disk. -func Load(baseDir string, signerFactory signature.SignerFactory) (*Entity, signature.Signer, error) { +func Load(baseDir string, signerFactory signature.SignerFactory) (*Entity, signature.Signer, *multisig.Account, error) { entityPath := filepath.Join(baseDir, entityFilename) // Load the entity signer. signer, err := signerFactory.Load(signature.SignerEntity) if err != nil { - return nil, nil, err + return nil, nil, nil, err } ent, err := LoadDescriptor(entityPath) if err != nil { signer.Reset() - return nil, nil, err + return nil, nil, nil, err } - if !ent.ID.Equal(signer.Public()) { + signerAccount := multisig.NewAccountFromPublicKey(signer.Public()) + signerAccountAddr := staking.NewAddress(signerAccount) + + if !ent.AccountAddress.Equal(signerAccountAddr) { signer.Reset() - return nil, nil, fmt.Errorf("public key mismatch (signer: %s, entity: %s)", signer.Public(), ent.ID) + return nil, nil, nil, fmt.Errorf("account mismatch (signer: %s, entity: %s)", signerAccountAddr, ent.AccountAddress) } - return ent, signer, nil + return ent, signer, signerAccount, nil } // LoadDescriptor loads an existing entity from disk, without loading the signer. @@ -141,11 +144,14 @@ func LoadDescriptor(f string) (*Entity, error) { } // GenerateWithSigner generates a new entity using an existing signer and serializes it to disk. -func GenerateWithSigner(baseDir string, signer signature.Signer, template *Entity) (*Entity, error) { +func GenerateWithSigner(baseDir string, signer signature.Signer, template *Entity) (*Entity, *multisig.Account, error) { // Generate a new entity. + account := multisig.NewAccountFromPublicKey(signer.Public()) ent := &Entity{ - DescriptorVersion: LatestEntityDescriptorVersion, - ID: signer.Public(), + Versioned: cbor.Versioned{ + V: LatestEntityDescriptorVersion, + }, + AccountAddress: staking.NewAddress(account), } if template != nil { ent.Nodes = template.Nodes @@ -153,38 +159,38 @@ func GenerateWithSigner(baseDir string, signer signature.Signer, template *Entit } if err := ent.Save(baseDir); err != nil { - return nil, err + return nil, nil, err } - return ent, nil + return ent, account, nil } // Generate generates a new entity and serializes it to disk. -func Generate(baseDir string, signerFactory signature.SignerFactory, template *Entity) (*Entity, signature.Signer, error) { +func Generate(baseDir string, signerFactory signature.SignerFactory, template *Entity) (*Entity, signature.Signer, *multisig.Account, error) { // Generate a new entity. signer, err := signerFactory.Generate(signature.SignerEntity, rand.Reader) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - ent, err := GenerateWithSigner(baseDir, signer, template) + ent, account, err := GenerateWithSigner(baseDir, signer, template) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return ent, signer, nil + return ent, signer, account, nil } // TestEntity returns the built-in test entity and signer. -func TestEntity() (*Entity, signature.Signer, error) { - return &testEntity, testEntitySigner, nil +func TestEntity() (*Entity, signature.Signer, *multisig.Account, error) { + return &testEntity, testEntitySigner, testEntityAccount, nil } // SignedEntity is a signed blob containing a CBOR-serialized Entity. type SignedEntity struct { - signature.Signed + multisig.Envelope } // Open first verifies the blob signature and then unmarshals the blob. func (s *SignedEntity) Open(context signature.Context, entity *Entity) error { // nolint: interfacer - return s.Signed.Open(context, entity) + return s.Envelope.Open(context, entity) } // PrettyPrint writes a pretty-printed representation of the type @@ -202,28 +208,38 @@ func (s SignedEntity) PrettyPrint(ctx context.Context, prefix string, w io.Write // PrettyType returns a representation of the type that can be used for pretty printing. func (s SignedEntity) PrettyType() (interface{}, error) { var e Entity - if err := cbor.Unmarshal(s.Signed.Blob, &e); err != nil { - return nil, fmt.Errorf("malformed signed blob: %w", err) + if err := cbor.Unmarshal(s.Envelope.Payload, &e); err != nil { + return nil, fmt.Errorf("malformed signed payload: %w", err) } - return signature.NewPrettySigned(s.Signed, e) + return multisig.NewPrettyEnvelope(s.Envelope, e) } -// SignEntity serializes the Entity and signs the result. -func SignEntity(signer signature.Signer, context signature.Context, entity *Entity) (*SignedEntity, error) { - signed, err := signature.SignSigned(signer, context, entity) +// SingleSignEntity serializes the Entity and signs the result. +// +// Note: This is a convenience routine that does not support entities +// backed by accounts with more than 1 signer. +func SingleSignEntity(signer signature.Signer, account *multisig.Account, context signature.Context, entity *Entity) (*SignedEntity, error) { + if len(account.Signers) != 1 { + return nil, fmt.Errorf("attemtped to single-sign multi-sig entity") + } + rawEntity := cbor.Marshal(entity) + entitySig, err := multisig.Sign(signer, account, context, rawEntity) if err != nil { return nil, err } - - return &SignedEntity{ - Signed: *signed, - }, nil + envelope, err := multisig.NewEnvelope(account, []*signature.Signature{entitySig}, rawEntity) + if err != nil { + return nil, err + } + return &SignedEntity{*envelope}, nil } func init() { testEntitySigner = memorySigner.NewTestSigner("ekiden test entity key seed") - testEntity.DescriptorVersion = LatestEntityDescriptorVersion - testEntity.ID = testEntitySigner.Public() + testEntityAccount = multisig.NewAccountFromPublicKey(testEntitySigner.Public()) + + testEntity.Versioned.V = LatestEntityDescriptorVersion + testEntity.AccountAddress = staking.NewAddress(testEntityAccount) testEntity.AllowEntitySignedNodes = true } diff --git a/go/common/node/node.go b/go/common/node/node.go index 7f680d47a25..e75e1348bc2 100644 --- a/go/common/node/node.go +++ b/go/common/node/node.go @@ -18,6 +18,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/prettyprint" "github.com/oasisprotocol/oasis-core/go/common/sgx/ias" "github.com/oasisprotocol/oasis-core/go/common/version" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) var ( @@ -46,17 +47,14 @@ const ( // Node represents public connectivity information about an Oasis node. type Node struct { // nolint: maligned - // DescriptorVersion is the node descriptor version. - // - // It should be bumped whenever breaking changes are made to the descriptor. - DescriptorVersion uint16 `json:"v,omitempty"` + cbor.Versioned // ID is the public key identifying the node. ID signature.PublicKey `json:"id"` - // EntityID is the public key identifying the Entity controlling + // EntityAddress is the account address of the entity controlling // the node. - EntityID signature.PublicKey `json:"entity_id"` + EntityAddress staking.Address `json:"entity_address"` // Expiration is the epoch in which this node's commitment expires. Expiration uint64 `json:"expiration"` @@ -134,15 +132,15 @@ func (n *Node) ValidateBasic(strictVersion bool) error { switch strictVersion { case true: // Only the latest version is allowed. - if n.DescriptorVersion != LatestNodeDescriptorVersion { + if n.Versioned.V != LatestNodeDescriptorVersion { return fmt.Errorf("invalid node descriptor version (expected: %d got: %d)", LatestNodeDescriptorVersion, - n.DescriptorVersion, + n.Versioned.V, ) } case false: // A range of versions is allowed. - if n.DescriptorVersion < minNodeDescriptorVersion || n.DescriptorVersion > maxNodeDescriptorVersion { + if n.Versioned.V < minNodeDescriptorVersion || n.Versioned.V > maxNodeDescriptorVersion { return fmt.Errorf("invalid node descriptor version (min: %d max: %d)", minNodeDescriptorVersion, maxNodeDescriptorVersion, diff --git a/go/consensus/api/api.go b/go/consensus/api/api.go index c146c5c7b48..3d3f1124ef2 100644 --- a/go/consensus/api/api.go +++ b/go/consensus/api/api.go @@ -8,6 +8,7 @@ import ( beacon "github.com/oasisprotocol/oasis-core/go/beacon/api" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/errors" "github.com/oasisprotocol/oasis-core/go/common/node" @@ -246,7 +247,7 @@ func NewConsensusEvidence(inner interface{}) ConsensusEvidence { // EstimateGasRequest is a EstimateGas request. type EstimateGasRequest struct { - Signer signature.PublicKey `json:"signer"` + Account multisig.Account `json:"account"` Transaction *transaction.Transaction `json:"transaction"` } diff --git a/go/consensus/api/submission.go b/go/consensus/api/submission.go index 5f842ad0ac7..5b35e394974 100644 --- a/go/consensus/api/submission.go +++ b/go/consensus/api/submission.go @@ -7,6 +7,7 @@ import ( "github.com/cenkalti/backoff/v4" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/errors" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -64,9 +65,10 @@ type submissionManager struct { func (m *submissionManager) signAndSubmitTx(ctx context.Context, signer signature.Signer, tx *transaction.Transaction) error { // Update transaction nonce. var err error - signerAddr := staking.NewAddress(signer.Public()) + account := multisig.NewAccountFromPublicKey(signer.Public()) + accountAddr := staking.NewAddress(account) - tx.Nonce, err = m.backend.GetSignerNonce(ctx, &GetSignerNonceRequest{AccountAddress: signerAddr, Height: HeightLatest}) + tx.Nonce, err = m.backend.GetSignerNonce(ctx, &GetSignerNonceRequest{AccountAddress: accountAddr, Height: HeightLatest}) if err != nil { if errors.Is(err, ErrNoCommittedBlocks) { // No committed blocks available, retry submission. @@ -80,7 +82,7 @@ func (m *submissionManager) signAndSubmitTx(ctx context.Context, signer signatur if tx.Fee == nil { // Estimate amount of gas needed to perform the update. var gas transaction.Gas - gas, err = m.backend.EstimateGas(ctx, &EstimateGasRequest{Signer: signer.Public(), Transaction: tx}) + gas, err = m.backend.EstimateGas(ctx, &EstimateGasRequest{Account: *account, Transaction: tx}) if err != nil { return fmt.Errorf("failed to estimate gas: %w", err) } @@ -114,7 +116,7 @@ func (m *submissionManager) signAndSubmitTx(ctx context.Context, signer signatur } // Sign the transaction. - sigTx, err := transaction.Sign(signer, tx) + signedTx, err := transaction.SingleSign(signer, account, tx) if err != nil { m.logger.Error("failed to sign transaction", "err", err, @@ -122,11 +124,11 @@ func (m *submissionManager) signAndSubmitTx(ctx context.Context, signer signatur return backoff.Permanent(err) } - if err = m.backend.SubmitTx(ctx, sigTx); err != nil { + if err = m.backend.SubmitTx(ctx, signedTx); err != nil { if errors.Is(err, transaction.ErrInvalidNonce) { // Invalid nonce, retry submission. m.logger.Debug("retrying transaction submission due to invalid nonce", - "account_address", signerAddr, + "account_address", accountAddr, "nonce", tx.Nonce, ) return err diff --git a/go/consensus/api/transaction/testvectors/testvectors.go b/go/consensus/api/transaction/testvectors/testvectors.go index 0ec95704809..c2f4d7e9b28 100644 --- a/go/consensus/api/transaction/testvectors/testvectors.go +++ b/go/consensus/api/transaction/testvectors/testvectors.go @@ -4,6 +4,7 @@ import ( "reflect" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" @@ -32,7 +33,8 @@ func MakeTestVector(kind string, tx *transaction.Transaction) TestVector { // MakeTestVectorWithSigner generates a new test vector from a transction using a specific signer. func MakeTestVectorWithSigner(kind string, tx *transaction.Transaction, signer signature.Signer) TestVector { - sigTx, err := transaction.Sign(signer, tx) + account := multisig.NewAccountFromPublicKey(signer.Public()) + sigTx, err := transaction.SingleSign(signer, account, tx) if err != nil { panic(err) } diff --git a/go/consensus/api/transaction/transaction.go b/go/consensus/api/transaction/transaction.go index 22fd3db30a1..c924416e5b1 100644 --- a/go/consensus/api/transaction/transaction.go +++ b/go/consensus/api/transaction/transaction.go @@ -11,6 +11,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/errors" "github.com/oasisprotocol/oasis-core/go/common/prettyprint" @@ -149,7 +150,7 @@ type PrettyTransaction struct { // SignedTransaction is a signed transaction. type SignedTransaction struct { - signature.Signed + multisig.Envelope } // Hash returns the cryptographic hash of the encoded transaction. @@ -157,56 +158,67 @@ func (s *SignedTransaction) Hash() hash.Hash { return hash.NewFrom(s) } +// Open first verifies the blob signature and then unmarshals the blob. +func (s *SignedTransaction) Open(tx *Transaction) error { // nolint: interfacer + return s.Envelope.Open(SignatureContext, tx) +} + +// PrettyType returns a representation of the type that can be used for pretty printing. +func (s SignedTransaction) PrettyType() (interface{}, error) { + var tx Transaction + if err := cbor.Unmarshal(s.Envelope.Payload, &tx); err != nil { + return nil, fmt.Errorf("malformed signed payload: %w", err) + } + return multisig.NewPrettyEnvelope(s.Envelope, tx) +} + +// SingleSign serializes the Transaction and signs the result. +// +// Note: This is a convenience routine that does not support transactions +// backed by accounts with more than 1 signer. +func SingleSign(signer signature.Signer, account *multisig.Account, tx *Transaction) (*SignedTransaction, error) { + if len(account.Signers) != 1 { + return nil, fmt.Errorf("attemtped to single-sign multi-sig transaction") + } + rawTx := cbor.Marshal(tx) + txSig, err := multisig.Sign(signer, account, SignatureContext, rawTx) + if err != nil { + return nil, err + } + envelope, err := multisig.NewEnvelope(account, []*signature.Signature{txSig}, rawTx) + if err != nil { + return nil, err + } + return &SignedTransaction{*envelope}, nil +} + // PrettyPrint writes a pretty-printed representation of the type // to the given writer. func (s SignedTransaction) PrettyPrint(ctx context.Context, prefix string, w io.Writer) { fmt.Fprintf(w, "%sHash: %s\n", prefix, s.Hash()) - fmt.Fprintf(w, "%sSigner: %s\n", prefix, s.Signature.PublicKey) - fmt.Fprintf(w, "%s (signature: %s)\n", prefix, s.Signature.Signature) + // TODO: Someone that cares probably can figure out a better way + // to display the account and signatures. + fmt.Fprintf(w, "%sAccount: %s\n", prefix, base64.StdEncoding.EncodeToString(s.Account.Hash())) - // Check if signature is valid. - if !s.Signature.Verify(SignatureContext, s.Blob) { - fmt.Fprintf(w, "%s [INVALID SIGNATURE]\n", prefix) + // Check if the envelope is valid. + if err := s.Envelope.Verify(SignatureContext); err != nil { + fmt.Fprintf(w, "%s [INVALID ENVELOPE: %s]\n", prefix, err) } // Display the blob even if signature verification failed as it may // be useful to look into it regardless. var tx Transaction fmt.Fprintf(w, "%sContent:\n", prefix) - if err := cbor.Unmarshal(s.Blob, &tx); err != nil { + if err := cbor.Unmarshal(s.Envelope.Payload, &tx); err != nil { fmt.Fprintf(w, "%s \n", prefix, err) - fmt.Fprintf(w, "%s \n", prefix, base64.StdEncoding.EncodeToString(s.Blob)) + fmt.Fprintf(w, "%s \n", prefix, base64.StdEncoding.EncodeToString(s.Envelope.Payload)) return } tx.PrettyPrint(ctx, prefix+" ", w) } -// PrettyType returns a representation of the type that can be used for pretty printing. -func (s SignedTransaction) PrettyType() (interface{}, error) { - var tx Transaction - if err := cbor.Unmarshal(s.Blob, &tx); err != nil { - return nil, fmt.Errorf("malformed signed blob: %w", err) - } - return signature.NewPrettySigned(s.Signed, tx) -} - -// Open first verifies the blob signature and then unmarshals the blob. -func (s *SignedTransaction) Open(tx *Transaction) error { // nolint: interfacer - return s.Signed.Open(SignatureContext, tx) -} - -// Sign signs a transaction. -func Sign(signer signature.Signer, tx *Transaction) (*SignedTransaction, error) { - signed, err := signature.SignSigned(signer, SignatureContext, tx) - if err != nil { - return nil, err - } - - return &SignedTransaction{Signed: *signed}, nil -} - // MethodSeparator is the separator used to separate backend name from method name. const MethodSeparator = "." diff --git a/go/consensus/tendermint/abci/mux.go b/go/consensus/tendermint/abci/mux.go index 405d8800620..47e34f14268 100644 --- a/go/consensus/tendermint/abci/mux.go +++ b/go/consensus/tendermint/abci/mux.go @@ -19,6 +19,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/errors" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -259,7 +260,7 @@ func (a *ApplicationServer) WatchInvalidatedTx(txHash hash.Hash) (<-chan error, } // EstimateGas calculates the amount of gas required to execute the given transaction. -func (a *ApplicationServer) EstimateGas(caller signature.PublicKey, tx *transaction.Transaction) (transaction.Gas, error) { +func (a *ApplicationServer) EstimateGas(caller *multisig.Account, tx *transaction.Transaction) (transaction.Gas, error) { return a.mux.EstimateGas(caller, tx) } @@ -603,7 +604,7 @@ func (mux *abciMux) processTx(ctx *api.Context, tx *transaction.Transaction, txS if err := txAuthHandler.AuthenticateTx(ctx, tx); err != nil { ctx.Logger().Debug("failed to authenticate transaction", "tx", tx, - "tx_signer", ctx.TxSigner(), + "tx_account_hash", base64.StdEncoding.EncodeToString(ctx.TxAccount().Hash()), "method", tx.Method, "err", err, ) @@ -658,12 +659,12 @@ func (mux *abciMux) executeTx(ctx *api.Context, rawTx []byte) error { } // Set authenticated transaction signer. - ctx.SetTxSigner(sigTx.Signature.PublicKey) + ctx.SetTxAccount(&sigTx.Account) return mux.processTx(ctx, tx, len(rawTx)) } -func (mux *abciMux) EstimateGas(caller signature.PublicKey, tx *transaction.Transaction) (transaction.Gas, error) { +func (mux *abciMux) EstimateGas(caller *multisig.Account, tx *transaction.Transaction) (transaction.Gas, error) { // As opposed to other transaction dispatch entry points (CheckTx/DeliverTx), this method can // be called in parallel to the consensus layer and to other invocations. // @@ -679,11 +680,17 @@ func (mux *abciMux) EstimateGas(caller signature.PublicKey, tx *transaction.Tran } _ = tx.Fee.Amount.FromUint64(math.MaxUint64) - ctx.SetTxSigner(caller) + mockSigs := make([]*signature.RawSignature, 0, len(caller.Signers)) + for i := 0; i < len(caller.Signers); i++ { + mockSigs = append(mockSigs, new(signature.RawSignature)) + } + + ctx.SetTxAccount(caller) mockSignedTx := transaction.SignedTransaction{ - Signed: signature.Signed{ - Blob: cbor.Marshal(tx), - // Signature is fixed-size, so we can leave it as default. + Envelope: multisig.Envelope{ + Account: *caller, + Signatures: mockSigs, + Payload: cbor.Marshal(tx), }, } txSize := len(cbor.Marshal(mockSignedTx)) diff --git a/go/consensus/tendermint/abci/state.go b/go/consensus/tendermint/abci/state.go index e5cf7d3f683..3963a7c01a0 100644 --- a/go/consensus/tendermint/abci/state.go +++ b/go/consensus/tendermint/abci/state.go @@ -14,7 +14,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/quantity" consensusGenesis "github.com/oasisprotocol/oasis-core/go/consensus/genesis" @@ -64,7 +64,6 @@ type applicationState struct { // nolint: maligned haltEpochHeight epochtime.EpochTime minGasPrice quantity.Quantity - ownTxSigner signature.PublicKey ownTxSignerAddress staking.Address disableCheckTx bool @@ -203,10 +202,6 @@ func (s *applicationState) MinGasPrice() *quantity.Quantity { return &s.minGasPrice } -func (s *applicationState) OwnTxSigner() signature.PublicKey { - return s.ownTxSigner -} - func (s *applicationState) OwnTxSignerAddress() staking.Address { return s.ownTxSignerAddress } @@ -478,6 +473,7 @@ func newApplicationState(ctx context.Context, cfg *ApplicationConfig) (*applicat ctx, cancelCtx := context.WithCancel(ctx) + ownAccount := multisig.NewAccountFromPublicKey(cfg.OwnTxSigner) s := &applicationState{ logger: logging.GetLogger("abci-mux/state"), ctx: ctx, @@ -491,8 +487,7 @@ func newApplicationState(ctx context.Context, cfg *ApplicationConfig) (*applicat prunerNotifyCh: channels.NewRingChannel(1), haltEpochHeight: cfg.HaltEpochHeight, minGasPrice: minGasPrice, - ownTxSigner: cfg.OwnTxSigner, - ownTxSignerAddress: staking.NewAddress(cfg.OwnTxSigner), + ownTxSignerAddress: staking.NewAddress(ownAccount), disableCheckTx: cfg.DisableCheckTx, metricsClosedCh: make(chan struct{}), } diff --git a/go/consensus/tendermint/api/context.go b/go/consensus/tendermint/api/context.go index 8d14d23eec2..e34457acef1 100644 --- a/go/consensus/tendermint/api/context.go +++ b/go/consensus/tendermint/api/context.go @@ -8,7 +8,7 @@ import ( "github.com/tendermint/tendermint/abci/types" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/storage/mkvs" ) @@ -66,7 +66,7 @@ type Context struct { events []types.Event gasAccountant GasAccountant - txSigner signature.PublicKey + txAccount *multisig.Account appState ApplicationState state mkvs.Tree @@ -146,29 +146,29 @@ func (c *Context) Data() interface{} { return c.data } -// TxSigner returns the authenticated transaction signer. +// TxAccount returns the authenticated transaction account. // // In case the method is called on a non-transaction context, this method // will panic. -func (c *Context) TxSigner() signature.PublicKey { +func (c *Context) TxAccount() *multisig.Account { switch c.mode { case ContextCheckTx, ContextDeliverTx, ContextSimulateTx: - return c.txSigner + return c.txAccount default: panic("context: only available in transaction context") } } -// SetTxSigner sets the authenticated transaction signer. +// SetTxAccount sets the authenticated transaction account. // -// This must only be done after verifying the transaction signature. +// This must only be done after verifying the transaction signature(s). // // In case the method is called on a non-transaction context, this method // will panic. -func (c *Context) SetTxSigner(txSigner signature.PublicKey) { +func (c *Context) SetTxAccount(txAccount *multisig.Account) { switch c.mode { case ContextCheckTx, ContextDeliverTx, ContextSimulateTx: - c.txSigner = txSigner + c.txAccount = txAccount default: panic("context: only available in transaction context") } diff --git a/go/consensus/tendermint/api/state.go b/go/consensus/tendermint/api/state.go index 6a03a70a2ff..3f22d134efa 100644 --- a/go/consensus/tendermint/api/state.go +++ b/go/consensus/tendermint/api/state.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -54,9 +55,6 @@ type ApplicationState interface { // MinGasPrice returns the configured minimum gas price. MinGasPrice() *quantity.Quantity - // OwnTxSigner returns the transaction signer identity of the local node. - OwnTxSigner() signature.PublicKey - // OwnTxSignerAddress returns the transaction signer's staking address of the local node. OwnTxSignerAddress() staking.Address @@ -177,11 +175,12 @@ func NewMockApplicationState(cfg *MockApplicationStateConfig) ApplicationState { blockCtx.Set(GasAccountantKey{}, NewNopGasAccountant()) } + ownAccount := multisig.NewAccountFromPublicKey(cfg.OwnTxSigner) return &mockApplicationState{ cfg: cfg, blockCtx: blockCtx, tree: tree, - ownTxSignerAddress: staking.NewAddress(cfg.OwnTxSigner), + ownTxSignerAddress: staking.NewAddress(ownAccount), } } diff --git a/go/consensus/tendermint/apps/keymanager/keymanager.go b/go/consensus/tendermint/apps/keymanager/keymanager.go index d56dbfa88a7..70c025f4cfe 100644 --- a/go/consensus/tendermint/apps/keymanager/keymanager.go +++ b/go/consensus/tendermint/apps/keymanager/keymanager.go @@ -21,7 +21,6 @@ import ( "github.com/oasisprotocol/oasis-core/go/keymanager/api" keymanager "github.com/oasisprotocol/oasis-core/go/keymanager/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" - staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) var emptyHashSha3 = sha3.Sum256(nil) @@ -124,12 +123,10 @@ func (app *keymanagerApplication) onEpochChange(ctx *tmapi.Context, epoch epocht // Suspend the runtime in case the registering entity no longer has enough stake to cover // the entity and runtime deposits. if !params.DebugBypassStake { - acctAddr := staking.NewAddress(rt.EntityID) - if err = stakeAcc.CheckStakeClaims(acctAddr); err != nil { + if err = stakeAcc.CheckStakeClaims(rt.EntityAddress); err != nil { ctx.Logger().Warn("insufficient stake for key manager runtime operation", "err", err, - "entity", rt.EntityID, - "account", acctAddr, + "entity_address", rt.EntityAddress, ) // Suspend runtime. diff --git a/go/consensus/tendermint/apps/keymanager/transactions.go b/go/consensus/tendermint/apps/keymanager/transactions.go index 52419e9a5af..6a8ebec1f68 100644 --- a/go/consensus/tendermint/apps/keymanager/transactions.go +++ b/go/consensus/tendermint/apps/keymanager/transactions.go @@ -9,6 +9,7 @@ import ( registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state" "github.com/oasisprotocol/oasis-core/go/keymanager/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) func (app *keymanagerApplication) updatePolicy( @@ -27,7 +28,7 @@ func (app *keymanagerApplication) updatePolicy( } // Ensure that the tx signer is the key manager owner. - if !rt.EntityID.Equal(ctx.TxSigner()) { + if !rt.EntityAddress.Equal(staking.NewAddress(ctx.TxAccount())) { return fmt.Errorf("keymanager: invalid update signer: %s", sigPol.Policy.ID) } diff --git a/go/consensus/tendermint/apps/registry/genesis.go b/go/consensus/tendermint/apps/registry/genesis.go index 89c308b9c47..5c414bcc245 100644 --- a/go/consensus/tendermint/apps/registry/genesis.go +++ b/go/consensus/tendermint/apps/registry/genesis.go @@ -14,6 +14,7 @@ import ( registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state" genesis "github.com/oasisprotocol/oasis-core/go/genesis/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.RequestInitChain, doc *genesis.Document) error { @@ -34,7 +35,7 @@ func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.Re return fmt.Errorf("registry: genesis entity index %d is nil", i) } ctx.Logger().Debug("InitChain: Registering genesis entity", - "entity", v.Signature.PublicKey, + "entity", staking.NewAddress(&v.Account), ) if err := app.registerEntity(ctx, state, v); err != nil { ctx.Logger().Error("InitChain: failed to register entity", @@ -58,7 +59,7 @@ func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.Re continue } ctx.Logger().Debug("InitChain: Registering genesis runtime", - "runtime_owner", v.Signature.PublicKey, + "runtime_owner", staking.NewAddress(&v.Account), ) if err := app.registerRuntime(ctx, state, v); err != nil { ctx.Logger().Error("InitChain: failed to register runtime", @@ -74,7 +75,7 @@ func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.Re return fmt.Errorf("registry: genesis suspended runtime index %d is nil", i) } ctx.Logger().Debug("InitChain: Registering genesis suspended runtime", - "runtime_owner", v.Signature.PublicKey, + "runtime_owner", staking.NewAddress(&v.Account), ) if err := app.registerRuntime(ctx, state, v); err != nil { ctx.Logger().Error("InitChain: failed to register runtime", @@ -84,7 +85,7 @@ func (app *registryApplication) InitChain(ctx *abciAPI.Context, request types.Re return fmt.Errorf("registry: genesis suspended runtime registration failure: %w", err) } var rt registry.Runtime - if err := cbor.Unmarshal(v.Blob, &rt); err != nil { + if err := cbor.Unmarshal(v.Payload, &rt); err != nil { return fmt.Errorf("registry: malformed genesis suspended runtime: %w", err) } if err := state.SuspendRuntime(ctx, rt.ID); err != nil { diff --git a/go/consensus/tendermint/apps/registry/query.go b/go/consensus/tendermint/apps/registry/query.go index 7374909094e..bb2f7e82fbd 100644 --- a/go/consensus/tendermint/apps/registry/query.go +++ b/go/consensus/tendermint/apps/registry/query.go @@ -11,11 +11,12 @@ import ( abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) // Query is the registry query interface. type Query interface { - Entity(context.Context, signature.PublicKey) (*entity.Entity, error) + Entity(context.Context, staking.Address) (*entity.Entity, error) Entities(context.Context) ([]*entity.Entity, error) Node(context.Context, signature.PublicKey) (*node.Node, error) NodeByConsensusAddress(context.Context, []byte) (*node.Node, error) @@ -46,8 +47,8 @@ type registryQuerier struct { height int64 } -func (rq *registryQuerier) Entity(ctx context.Context, id signature.PublicKey) (*entity.Entity, error) { - return rq.state.Entity(ctx, id) +func (rq *registryQuerier) Entity(ctx context.Context, address staking.Address) (*entity.Entity, error) { + return rq.state.Entity(ctx, address) } func (rq *registryQuerier) Entities(ctx context.Context) ([]*entity.Entity, error) { diff --git a/go/consensus/tendermint/apps/registry/registry.go b/go/consensus/tendermint/apps/registry/registry.go index 177ba75feb7..bfd883dc96d 100644 --- a/go/consensus/tendermint/apps/registry/registry.go +++ b/go/consensus/tendermint/apps/registry/registry.go @@ -18,7 +18,6 @@ import ( stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state" epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" - staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) var _ abci.Application = (*registryApplication)(nil) @@ -191,8 +190,7 @@ func (app *registryApplication) onRegistryEpochChanged(ctx *api.Context, registr // Remove the stake claim for the given node. if !params.DebugBypassStake { - acctAddr := staking.NewAddress(node.EntityID) - if err = stakeAcc.RemoveStakeClaim(acctAddr, registry.StakeClaimForNode(node.ID)); err != nil { + if err = stakeAcc.RemoveStakeClaim(node.EntityAddress, registry.StakeClaimForNode(node.ID)); err != nil { return fmt.Errorf("registry: onRegistryEpochChanged: couldn't remove stake claim: %w", err) } } diff --git a/go/consensus/tendermint/apps/registry/state/state.go b/go/consensus/tendermint/apps/registry/state/state.go index fdb268cbca7..a35b567e143 100644 --- a/go/consensus/tendermint/apps/registry/state/state.go +++ b/go/consensus/tendermint/apps/registry/state/state.go @@ -13,6 +13,7 @@ import ( abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" tmcrypto "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/crypto" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" "github.com/oasisprotocol/oasis-core/go/storage/mkvs" ) @@ -23,7 +24,7 @@ var ( // signedEntityKeyFmt is the key format used for signed entities. // // Value is CBOR-serialized signed entity. - signedEntityKeyFmt = keyformat.New(0x10, keyformat.H(&signature.PublicKey{})) + signedEntityKeyFmt = keyformat.New(0x10, &staking.Address{}) // signedNodeKeyFmt is the key format used for signed nodes. // // Value is CBOR-serialized signed node. @@ -32,7 +33,7 @@ var ( // index. // // Value is empty. - signedNodeByEntityKeyFmt = keyformat.New(0x12, keyformat.H(&signature.PublicKey{}), keyformat.H(&signature.PublicKey{})) + signedNodeByEntityKeyFmt = keyformat.New(0x12, &staking.Address{}, keyformat.H(&signature.PublicKey{})) // signedRuntimeKeyFmt is the key format used for signed runtimes. // // Value is CBOR-serialized signed runtime. @@ -68,7 +69,7 @@ var ( // index. // // Value is empty. - signedRuntimeByEntityKeyFmt = keyformat.New(0x19, keyformat.H(&signature.PublicKey{}), keyformat.H(&common.Namespace{})) + signedRuntimeByEntityKeyFmt = keyformat.New(0x19, &staking.Address{}, keyformat.H(&common.Namespace{})) ) // ImmutableState is the immutable registry state wrapper. @@ -76,14 +77,14 @@ type ImmutableState struct { is *abciAPI.ImmutableState } -func (s *ImmutableState) getSignedEntityRaw(ctx context.Context, id signature.PublicKey) ([]byte, error) { - data, err := s.is.Get(ctx, signedEntityKeyFmt.Encode(&id)) +func (s *ImmutableState) getSignedEntityRaw(ctx context.Context, address staking.Address) ([]byte, error) { + data, err := s.is.Get(ctx, signedEntityKeyFmt.Encode(&address)) return data, abciAPI.UnavailableStateError(err) } // Entity looks up a registered entity by its identifier. -func (s *ImmutableState) Entity(ctx context.Context, id signature.PublicKey) (*entity.Entity, error) { - signedEntityRaw, err := s.getSignedEntityRaw(ctx, id) +func (s *ImmutableState) Entity(ctx context.Context, address staking.Address) (*entity.Entity, error) { + signedEntityRaw, err := s.getSignedEntityRaw(ctx, address) if err != nil { return nil, err } @@ -96,7 +97,7 @@ func (s *ImmutableState) Entity(ctx context.Context, id signature.PublicKey) (*e return nil, abciAPI.UnavailableStateError(err) } var entity entity.Entity - if err = cbor.Unmarshal(signedEntity.Blob, &entity); err != nil { + if err = cbor.Unmarshal(signedEntity.Payload, &entity); err != nil { return nil, abciAPI.UnavailableStateError(err) } return &entity, nil @@ -118,7 +119,7 @@ func (s *ImmutableState) Entities(ctx context.Context) ([]*entity.Entity, error) return nil, abciAPI.UnavailableStateError(err) } var entity entity.Entity - if err := cbor.Unmarshal(signedEntity.Blob, &entity); err != nil { + if err := cbor.Unmarshal(signedEntity.Payload, &entity); err != nil { return nil, abciAPI.UnavailableStateError(err) } @@ -272,7 +273,7 @@ func (s *ImmutableState) getRuntime(ctx context.Context, keyFmt *keyformat.KeyFo return nil, err } var runtime registry.Runtime - if err = cbor.Unmarshal(signedRuntime.Blob, &runtime); err != nil { + if err = cbor.Unmarshal(signedRuntime.Payload, &runtime); err != nil { return nil, abciAPI.UnavailableStateError(err) } return &runtime, nil @@ -394,7 +395,7 @@ func (s *ImmutableState) Runtimes(ctx context.Context) ([]*registry.Runtime, err var runtimes []*registry.Runtime err := s.iterateRuntimes(ctx, signedRuntimeKeyFmt, func(sigRt *registry.SignedRuntime) error { var rt registry.Runtime - if err := cbor.Unmarshal(sigRt.Blob, &rt); err != nil { + if err := cbor.Unmarshal(sigRt.Payload, &rt); err != nil { return abciAPI.UnavailableStateError(err) } runtimes = append(runtimes, &rt) @@ -411,7 +412,7 @@ func (s *ImmutableState) AllRuntimes(ctx context.Context) ([]*registry.Runtime, var runtimes []*registry.Runtime unpackFn := func(sigRt *registry.SignedRuntime) error { var rt registry.Runtime - if err := cbor.Unmarshal(sigRt.Blob, &rt); err != nil { + if err := cbor.Unmarshal(sigRt.Payload, &rt); err != nil { return abciAPI.UnavailableStateError(err) } runtimes = append(runtimes, &rt) @@ -444,14 +445,13 @@ func (s *ImmutableState) NodeStatus(ctx context.Context, id signature.PublicKey) } // HasEntityNodes checks whether an entity has any registered nodes. -func (s *ImmutableState) HasEntityNodes(ctx context.Context, id signature.PublicKey) (bool, error) { +func (s *ImmutableState) HasEntityNodes(ctx context.Context, address staking.Address) (bool, error) { it := s.is.NewIterator(ctx) defer it.Close() - hID := keyformat.PreHashed(id.Hash()) - if it.Seek(signedNodeByEntityKeyFmt.Encode(&id)); it.Valid() { - var hEntityID keyformat.PreHashed - if !signedNodeByEntityKeyFmt.Decode(it.Key(), &hEntityID) || !hEntityID.Equal(&hID) { + if it.Seek(signedNodeByEntityKeyFmt.Encode(&address)); it.Valid() { + var entityAddr staking.Address + if !signedNodeByEntityKeyFmt.Decode(it.Key(), &entityAddr) || !entityAddr.Equal(address) { return false, nil } return true, nil @@ -460,14 +460,13 @@ func (s *ImmutableState) HasEntityNodes(ctx context.Context, id signature.Public } // HasEntityRuntimes checks whether an entity has any registered runtimes. -func (s *ImmutableState) HasEntityRuntimes(ctx context.Context, id signature.PublicKey) (bool, error) { +func (s *ImmutableState) HasEntityRuntimes(ctx context.Context, address staking.Address) (bool, error) { it := s.is.NewIterator(ctx) defer it.Close() - hID := keyformat.PreHashed(id.Hash()) - if it.Seek(signedRuntimeByEntityKeyFmt.Encode(&id)); it.Valid() { - var hEntityID keyformat.PreHashed - if !signedRuntimeByEntityKeyFmt.Decode(it.Key(), &hEntityID) || !hEntityID.Equal(&hID) { + if it.Seek(signedRuntimeByEntityKeyFmt.Encode(&address)); it.Valid() { + var entityAddr staking.Address + if !signedRuntimeByEntityKeyFmt.Decode(it.Key(), &entityAddr) || !entityAddr.Equal(address) { return false, nil } return true, nil @@ -527,13 +526,13 @@ type MutableState struct { // SetEntity sets a signed entity descriptor for a registered entity. func (s *MutableState) SetEntity(ctx context.Context, ent *entity.Entity, sigEnt *entity.SignedEntity) error { - err := s.ms.Insert(ctx, signedEntityKeyFmt.Encode(&ent.ID), cbor.Marshal(sigEnt)) + err := s.ms.Insert(ctx, signedEntityKeyFmt.Encode(&ent.AccountAddress), cbor.Marshal(sigEnt)) return abciAPI.UnavailableStateError(err) } // RemoveEntity removes a previously registered entity. -func (s *MutableState) RemoveEntity(ctx context.Context, id signature.PublicKey) (*entity.Entity, error) { - data, err := s.ms.RemoveExisting(ctx, signedEntityKeyFmt.Encode(&id)) +func (s *MutableState) RemoveEntity(ctx context.Context, address staking.Address) (*entity.Entity, error) { + data, err := s.ms.RemoveExisting(ctx, signedEntityKeyFmt.Encode(&address)) if err != nil { return nil, abciAPI.UnavailableStateError(err) } @@ -543,7 +542,7 @@ func (s *MutableState) RemoveEntity(ctx context.Context, id signature.PublicKey) return nil, abciAPI.UnavailableStateError(err) } var removedEntity entity.Entity - if err = cbor.Unmarshal(removedSignedEntity.Blob, &removedEntity); err != nil { + if err = cbor.Unmarshal(removedSignedEntity.Payload, &removedEntity); err != nil { return nil, abciAPI.UnavailableStateError(err) } return &removedEntity, nil @@ -561,7 +560,7 @@ func (s *MutableState) SetNode(ctx context.Context, existingNode, node *node.Nod if err = s.ms.Insert(ctx, signedNodeKeyFmt.Encode(&node.ID), cbor.Marshal(signedNode)); err != nil { return abciAPI.UnavailableStateError(err) } - if err = s.ms.Insert(ctx, signedNodeByEntityKeyFmt.Encode(&node.EntityID, &node.ID), []byte("")); err != nil { + if err = s.ms.Insert(ctx, signedNodeByEntityKeyFmt.Encode(&node.EntityAddress, &node.ID), []byte("")); err != nil { return abciAPI.UnavailableStateError(err) } @@ -616,7 +615,7 @@ func (s *MutableState) RemoveNode(ctx context.Context, node *node.Node) error { if err := s.ms.Remove(ctx, signedNodeKeyFmt.Encode(&node.ID)); err != nil { return abciAPI.UnavailableStateError(err) } - if err := s.ms.Remove(ctx, signedNodeByEntityKeyFmt.Encode(&node.EntityID, &node.ID)); err != nil { + if err := s.ms.Remove(ctx, signedNodeByEntityKeyFmt.Encode(&node.EntityAddress, &node.ID)); err != nil { return abciAPI.UnavailableStateError(err) } if err := s.ms.Remove(ctx, nodeStatusKeyFmt.Encode(&node.ID)); err != nil { @@ -643,7 +642,7 @@ func (s *MutableState) RemoveNode(ctx context.Context, node *node.Node) error { // SetRuntime sets a signed runtime descriptor for a registered runtime. func (s *MutableState) SetRuntime(ctx context.Context, rt *registry.Runtime, sigRt *registry.SignedRuntime, suspended bool) error { - if err := s.ms.Insert(ctx, signedRuntimeByEntityKeyFmt.Encode(&rt.EntityID, &rt.ID), []byte("")); err != nil { + if err := s.ms.Insert(ctx, signedRuntimeByEntityKeyFmt.Encode(&rt.EntityAddress, &rt.ID), []byte("")); err != nil { return abciAPI.UnavailableStateError(err) } diff --git a/go/consensus/tendermint/apps/registry/state/state_test.go b/go/consensus/tendermint/apps/registry/state/state_test.go index 4d6f938afe2..5db6b7e27ed 100644 --- a/go/consensus/tendermint/apps/registry/state/state_test.go +++ b/go/consensus/tendermint/apps/registry/state/state_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/node" @@ -42,8 +43,10 @@ func TestNodeUpdate(t *testing.T) { // Create a new node. n := node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nodeSigner.Public(), + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nodeSigner.Public(), P2P: node.P2PInfo{ ID: p2pSigner1.Public(), }, diff --git a/go/consensus/tendermint/apps/registry/transactions.go b/go/consensus/tendermint/apps/registry/transactions.go index 30e6b6c45f2..b3af0273929 100644 --- a/go/consensus/tendermint/apps/registry/transactions.go +++ b/go/consensus/tendermint/apps/registry/transactions.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" @@ -45,12 +46,12 @@ func (app *registryApplication) registerEntity( // Make sure the signer of the transaction matches the signer of the entity. // NOTE: If this is invoked during InitChain then there is no actual transaction // and thus no transaction signer so we must skip this check. - if !ctx.IsInitChain() && !sigEnt.Signature.PublicKey.Equal(ctx.TxSigner()) { - return registry.ErrIncorrectTxSigner + acctAddr := staking.NewAddress(&sigEnt.Account) + if !ctx.IsInitChain() && !acctAddr.Equal(staking.NewAddress(ctx.TxAccount())) { + return registry.ErrIncorrectTxAccount } if !params.DebugBypassStake { - acctAddr := staking.NewAddress(ent.ID) if err = stakingState.AddStakeClaim( ctx, acctAddr, @@ -59,7 +60,6 @@ func (app *registryApplication) registerEntity( ); err != nil { ctx.Logger().Error("RegisterEntity: Insufficent stake", "err", err, - "entity", ent.ID, "account", acctAddr, ) return err @@ -96,10 +96,10 @@ func (app *registryApplication) deregisterEntity(ctx *api.Context, state *regist return err } - id := ctx.TxSigner() + acctAddr := staking.NewAddress(ctx.TxAccount()) // Prevent entity deregistration if there are any registered nodes. - hasNodes, err := state.HasEntityNodes(ctx, id) + hasNodes, err := state.HasEntityNodes(ctx, acctAddr) if err != nil { ctx.Logger().Error("DeregisterEntity: failed to check for nodes", "err", err, @@ -108,12 +108,12 @@ func (app *registryApplication) deregisterEntity(ctx *api.Context, state *regist } if hasNodes { ctx.Logger().Error("DeregisterEntity: entity still has nodes", - "entity_id", id, + "entity_address", acctAddr, ) return registry.ErrEntityHasNodes } // Prevent entity deregistration if there are any registered runtimes. - hasRuntimes, err := state.HasEntityRuntimes(ctx, id) + hasRuntimes, err := state.HasEntityRuntimes(ctx, acctAddr) if err != nil { ctx.Logger().Error("DeregisterEntity: failed to check for runtimes", "err", err, @@ -122,12 +122,12 @@ func (app *registryApplication) deregisterEntity(ctx *api.Context, state *regist } if hasRuntimes { ctx.Logger().Error("DeregisterEntity: entity still has runtimes", - "entity_id", id, + "entity_address", acctAddr, ) return registry.ErrEntityHasRuntimes } - removedEntity, err := state.RemoveEntity(ctx, id) + removedEntity, err := state.RemoveEntity(ctx, acctAddr) switch err { case nil: case registry.ErrNoSuchEntity: @@ -137,14 +137,13 @@ func (app *registryApplication) deregisterEntity(ctx *api.Context, state *regist } if !params.DebugBypassStake { - acctAddr := staking.NewAddress(id) if err = stakingState.RemoveStakeClaim(ctx, acctAddr, registry.StakeClaimRegisterEntity); err != nil { panic(fmt.Errorf("DeregisterEntity: failed to remove stake claim: %w", err)) } } ctx.Logger().Debug("DeregisterEntity: complete", - "entity_id", id, + "entity_addr", acctAddr, ) tagV := &EntityDeregistration{ @@ -173,7 +172,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo ) return err } - untrustedEntity, err := state.Entity(ctx, untrustedNode.EntityID) + untrustedEntity, err := state.Entity(ctx, untrustedNode.EntityAddress) if err != nil { ctx.Logger().Error("RegisterNode: failed to query owning entity", "err", err, @@ -217,7 +216,18 @@ func (app *registryApplication) registerNode( // nolint: gocyclo // Charge gas for node registration if signed by entity. For node-signed // registrations, the gas charges are pre-paid by the entity. - isEntitySigned := sigNode.MultiSigned.IsSignedBy(newNode.EntityID) + var isEntitySigned bool + if params.DebugAllowEntitySignedNodeRegistration && untrustedEntity.AllowEntitySignedNodes { + // Yes, the detection logic is awful. See VerifyRegisterNodeArgs, + // where the same thing is done for a justification. + for _, v := range sigNode.MultiSigned.Signatures { + addr := staking.NewAddress(multisig.NewAccountFromPublicKey(v.PublicKey)) + if addr == newNode.EntityAddress { + isEntitySigned = true + break + } + } + } if isEntitySigned { if err = ctx.Gas().UseGas(1, registry.GasOpRegisterNode, params.GasCosts); err != nil { return err @@ -229,20 +239,20 @@ func (app *registryApplication) registerNode( // nolint: gocyclo // NOTE: If this is invoked during InitChain then there is no actual transaction // and thus no transaction signer so we must skip this check. if !ctx.IsInitChain() { - expectedTxSigner := newNode.ID + expectedTxAddress := staking.NewAddress(multisig.NewAccountFromPublicKey(newNode.ID)) if isEntitySigned { - expectedTxSigner = newNode.EntityID + expectedTxAddress = newNode.EntityAddress } - if !ctx.TxSigner().Equal(expectedTxSigner) { - return registry.ErrIncorrectTxSigner + if !expectedTxAddress.Equal(staking.NewAddress(ctx.TxAccount())) { + return registry.ErrIncorrectTxAccount } } // Check runtime's whitelist. for _, rt := range paidRuntimes { - if rt.AdmissionPolicy.EntityWhitelist != nil && !rt.AdmissionPolicy.EntityWhitelist.Entities[newNode.EntityID] { + if rt.AdmissionPolicy.EntityWhitelist != nil && !rt.AdmissionPolicy.EntityWhitelist.Entities[newNode.EntityAddress] { ctx.Logger().Error("RegisterNode: node's entity not in a runtime's whitelist", - "entity", newNode.EntityID, + "entity_address", newNode.EntityAddress, "runtime", rt.ID, ) return registry.ErrForbidden @@ -278,7 +288,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo "err", err, "new_node", newNode, "existing_node", existingNode, - "entity", newNode.EntityID, + "entity_address", newNode.EntityAddress, ) return registry.ErrInvalidArgument } @@ -315,13 +325,11 @@ func (app *registryApplication) registerNode( // nolint: gocyclo claim := registry.StakeClaimForNode(newNode.ID) thresholds := registry.StakeThresholdsForNode(newNode, paidRuntimes) - acctAddr := staking.NewAddress(newNode.EntityID) - if err = stakeAcc.AddStakeClaim(acctAddr, claim, thresholds); err != nil { + if err = stakeAcc.AddStakeClaim(newNode.EntityAddress, claim, thresholds); err != nil { ctx.Logger().Error("RegisterNode: insufficient stake for new node", "err", err, - "entity", newNode.EntityID, - "account", acctAddr, + "entity_address", newNode.EntityAddress, ) return err } @@ -337,7 +345,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo "err", err, "new_node", newNode, "existing_node", existingNode, - "entity", newNode.EntityID, + "entity_address", newNode.EntityAddress, ) return err } @@ -346,7 +354,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo ctx.Logger().Error("RegisterNode: failed to create/update node", "err", err, "node", newNode, - "entity", newNode.EntityID, + "entity_address", newNode.EntityAddress, "is_creation", existingNode == nil, ) return fmt.Errorf("failed to set node: %w", err) @@ -385,8 +393,7 @@ func (app *registryApplication) registerNode( // nolint: gocyclo // Only resume a runtime if the entity has enough stake to avoid having the runtime be // suspended again on the next epoch transition. if !params.DebugBypassStake { - acctAddr := staking.NewAddress(rt.EntityID) - if err = stakeAcc.CheckStakeClaims(acctAddr); err != nil { + if err = stakeAcc.CheckStakeClaims(rt.EntityAddress); err != nil { continue } } @@ -453,7 +460,7 @@ func (app *registryApplication) unfreezeNode( return err } // Make sure that the unfreeze request was signed by the owning entity. - if !ctx.TxSigner().Equal(node.EntityID) { + if !node.EntityAddress.Equal(staking.NewAddress(ctx.TxAccount())) { return registry.ErrBadEntityForNode } @@ -463,7 +470,7 @@ func (app *registryApplication) unfreezeNode( ctx.Logger().Error("UnfreezeNode: failed to fetch node status", "err", err, "node_id", unfreeze.NodeID, - "entity_id", node.EntityID, + "entity_address", node.EntityAddress, ) return err } @@ -536,8 +543,9 @@ func (app *registryApplication) registerRuntime( // nolint: gocyclo // Make sure the signer of the transaction matches the signer of the runtime. // NOTE: If this is invoked during InitChain then there is no actual transaction // and thus no transaction signer so we must skip this check. - if !ctx.IsInitChain() && !sigRt.Signature.PublicKey.Equal(ctx.TxSigner()) { - return registry.ErrIncorrectTxSigner + acctAddr := staking.NewAddress(&sigRt.Account) + if !ctx.IsInitChain() && !acctAddr.Equal(staking.NewAddress(ctx.TxAccount())) { + return registry.ErrIncorrectTxAccount } // If TEE is required, check if runtime provided at least one enclave ID. @@ -584,12 +592,11 @@ func (app *registryApplication) registerRuntime( // nolint: gocyclo if !params.DebugBypassStake { claim := registry.StakeClaimForRuntime(rt.ID) thresholds := registry.StakeThresholdsForRuntime(rt) - acctAddr := staking.NewAddress(rt.EntityID) if err = stakingState.AddStakeClaim(ctx, acctAddr, claim, thresholds); err != nil { ctx.Logger().Error("RegisterRuntime: Insufficient stake", "err", err, - "entity", rt.EntityID, + "entity_address", rt.EntityAddress, "account", acctAddr, ) return err @@ -600,7 +607,7 @@ func (app *registryApplication) registerRuntime( // nolint: gocyclo ctx.Logger().Error("RegisterRuntime: failed to create runtime", "err", err, "runtime", rt, - "entity", rt.EntityID, + "entity_address", rt.EntityAddress, ) return fmt.Errorf("failed to set runtime: %w", err) } diff --git a/go/consensus/tendermint/apps/registry/transactions_test.go b/go/consensus/tendermint/apps/registry/transactions_test.go index 7418c91dc9c..e95e7f006db 100644 --- a/go/consensus/tendermint/apps/registry/transactions_test.go +++ b/go/consensus/tendermint/apps/registry/transactions_test.go @@ -7,6 +7,8 @@ import ( requirePkg "github.com/stretchr/testify/require" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -120,12 +122,16 @@ func TestRegisterNode(t *testing.T) { func(tcd *testCaseData) { // Create a new runtime. rtSigner := memorySigner.NewTestSigner("consensus/tendermint/apps/registry: runtime signer: ComputeNode") + rtAccount := multisig.NewAccountFromPublicKey(rtSigner.Public()) rt := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNode"), 0), - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNode"), 0), + EntityAddress: staking.NewAddress(rtAccount), + Kind: registry.KindCompute, } - sigRt, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt) + sigRt, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt) _ = state.SetRuntime(ctx, &rt, sigRt, false) tcd.node.AddRoles(node.RoleComputeWorker) @@ -143,17 +149,21 @@ func TestRegisterNode(t *testing.T) { func(tcd *testCaseData) { // Create a new runtime. rtSigner := memorySigner.NewTestSigner("consensus/tendermint/apps/registry: runtime signer: ComputeNodeWithoutPerRuntimeStake") + rtAccount := multisig.NewAccountFromPublicKey(rtSigner.Public()) rt := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutPerRuntimeStake"), 0), - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutPerRuntimeStake"), 0), + EntityAddress: staking.NewAddress(rtAccount), + Kind: registry.KindCompute, Staking: registry.RuntimeStakingParameters{ Thresholds: map[staking.ThresholdKind]quantity.Quantity{ staking.KindNodeCompute: *quantity.NewFromUint64(1000), }, }, } - sigRt, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt) + sigRt, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt) _ = state.SetRuntime(ctx, &rt, sigRt, false) tcd.node.AddRoles(node.RoleComputeWorker) @@ -171,21 +181,25 @@ func TestRegisterNode(t *testing.T) { func(tcd *testCaseData) { // Create a new runtime. rtSigner := memorySigner.NewTestSigner("consensus/tendermint/apps/registry: runtime signer: ComputeNodeWithPerRuntimeStake") + rtAccount := multisig.NewAccountFromPublicKey(rtSigner.Public()) rt := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithPerRuntimeStake"), 0), - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithPerRuntimeStake"), 0), + EntityAddress: staking.NewAddress(rtAccount), + Kind: registry.KindCompute, Staking: registry.RuntimeStakingParameters{ Thresholds: map[staking.ThresholdKind]quantity.Quantity{ staking.KindNodeCompute: *quantity.NewFromUint64(1000), }, }, } - sigRt, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt) + sigRt, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt) _ = state.SetRuntime(ctx, &rt, sigRt, false) // Add bonded stake (hacky, without a self-delegation). - _ = stakeState.SetAccount(ctx, staking.NewAddress(tcd.node.EntityID), &staking.Account{ + _ = stakeState.SetAccount(ctx, tcd.node.EntityAddress, &staking.Account{ Escrow: staking.EscrowAccount{ Active: staking.SharePool{ Balance: *quantity.NewFromUint64(10_000), @@ -207,29 +221,33 @@ func TestRegisterNode(t *testing.T) { "ComputeNodeWithoutPerRuntimeStakeMulti", func(tcd *testCaseData) { rtSigner := memorySigner.NewTestSigner("consensus/tendermint/apps/registry: runtime signer: ComputeNodeWithoutPerRuntimeStakeMulti") + rtAccount := multisig.NewAccountFromPublicKey(rtSigner.Public()) // Create a new runtime. rt1 := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutPerRuntimeStakeMulti 1"), 0), - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutPerRuntimeStakeMulti 1"), 0), + EntityAddress: staking.NewAddress(rtAccount), + Kind: registry.KindCompute, Staking: registry.RuntimeStakingParameters{ Thresholds: map[staking.ThresholdKind]quantity.Quantity{ staking.KindNodeCompute: *quantity.NewFromUint64(1000), }, }, } - sigRt1, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt1) + sigRt1, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt1) _ = state.SetRuntime(ctx, &rt1, sigRt1, false) // Create another runtime with a different identifier. rt2 := rt1 rt2.ID = common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutPerRuntimeStakeMulti 2"), 0) - sigRt2, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt2) + sigRt2, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt2) _ = state.SetRuntime(ctx, &rt2, sigRt2, false) // Add bonded stake (hacky, without a self-delegation). - _ = stakeState.SetAccount(ctx, staking.NewAddress(tcd.node.EntityID), &staking.Account{ + _ = stakeState.SetAccount(ctx, tcd.node.EntityAddress, &staking.Account{ Escrow: staking.EscrowAccount{ Active: staking.SharePool{ Balance: *quantity.NewFromUint64(1000), @@ -252,24 +270,28 @@ func TestRegisterNode(t *testing.T) { "ComputeNodeWithoutGlobalStakeMulti", func(tcd *testCaseData) { rtSigner := memorySigner.NewTestSigner("consensus/tendermint/apps/registry: runtime signer: ComputeNodeWithoutGlobalStakeMulti") + rtAccount := multisig.NewAccountFromPublicKey(rtSigner.Public()) // Create a new runtime. rt1 := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutGlobalStakeMulti 1"), 0), - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutGlobalStakeMulti 1"), 0), + EntityAddress: staking.NewAddress(rtAccount), + Kind: registry.KindCompute, } - sigRt1, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt1) + sigRt1, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt1) _ = state.SetRuntime(ctx, &rt1, sigRt1, false) // Create another runtime with a different identifier. rt2 := rt1 rt2.ID = common.NewTestNamespaceFromSeed([]byte("consensus/tendermint/apps/registry: runtime: ComputeNodeWithoutGlobalStakeMulti 2"), 0) - sigRt2, _ := registry.SignRuntime(rtSigner, registry.RegisterRuntimeSignatureContext, &rt2) + sigRt2, _ := registry.SingleSignRuntime(rtSigner, rtAccount, registry.RegisterRuntimeSignatureContext, &rt2) _ = state.SetRuntime(ctx, &rt2, sigRt2, false) // Add bonded stake (hacky, without a self-delegation). - _ = stakeState.SetAccount(ctx, staking.NewAddress(tcd.node.EntityID), &staking.Account{ + _ = stakeState.SetAccount(ctx, tcd.node.EntityAddress, &staking.Account{ Escrow: staking.EscrowAccount{ Active: staking.SharePool{ Balance: *quantity.NewFromUint64(1000), @@ -367,12 +389,15 @@ func TestRegisterNode(t *testing.T) { } // Prepare a test entity that owns the nodes. + entityAccount := multisig.NewAccountFromPublicKey(tcd.entitySigner.Public()) ent := entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, - ID: tcd.entitySigner.Public(), - Nodes: []signature.PublicKey{tcd.nodeSigner.Public()}, + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, + AccountAddress: staking.NewAddress(entityAccount), + Nodes: []signature.PublicKey{tcd.nodeSigner.Public()}, } - sigEnt, err := entity.SignEntity(tcd.entitySigner, registry.RegisterEntitySignatureContext, &ent) + sigEnt, err := entity.SingleSignEntity(tcd.entitySigner, entityAccount, registry.RegisterEntitySignatureContext, &ent) require.NoError(err, "SignEntity") err = state.SetEntity(ctx, &ent, sigEnt) require.NoError(err, "SetEntity") @@ -383,10 +408,12 @@ func TestRegisterNode(t *testing.T) { require.NoError(err, "address.UnmarshalText") tcd.node = node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: tcd.nodeSigner.Public(), - EntityID: ent.ID, - Expiration: 3, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: tcd.nodeSigner.Public(), + EntityAddress: ent.AccountAddress, + Expiration: 3, P2P: node.P2PInfo{ ID: tcd.p2pSigner.Public(), Addresses: []node.Address{address}, @@ -414,7 +441,7 @@ func TestRegisterNode(t *testing.T) { require.NoError(err, "MultiSignNode") // Attempt to register the node. - ctx.SetTxSigner(tcd.nodeSigner.Public()) + ctx.SetTxAccount(multisig.NewAccountFromPublicKey(tcd.nodeSigner.Public())) err = app.registerNode(ctx, state, sigNode) switch tc.valid { case true: diff --git a/go/consensus/tendermint/apps/roothash/roothash.go b/go/consensus/tendermint/apps/roothash/roothash.go index d607f4985e8..73383ca70e4 100644 --- a/go/consensus/tendermint/apps/roothash/roothash.go +++ b/go/consensus/tendermint/apps/roothash/roothash.go @@ -29,7 +29,6 @@ import ( "github.com/oasisprotocol/oasis-core/go/roothash/api/block" "github.com/oasisprotocol/oasis-core/go/roothash/api/commitment" scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api" - staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) // timerKindRound is the round timer kind. @@ -136,12 +135,10 @@ func (app *rootHashApplication) onCommitteeChanged(ctx *tmapi.Context, epoch epo // suspended anyway due to nobody being there to pay maintenance fees). sufficientStake := true if !empty && !params.DebugBypassStake { - acctAddr := staking.NewAddress(rt.EntityID) - if err = stakeAcc.CheckStakeClaims(acctAddr); err != nil { + if err = stakeAcc.CheckStakeClaims(rt.EntityAddress); err != nil { ctx.Logger().Warn("insufficient stake for runtime operation", "err", err, - "entity", rt.EntityID, - "account", acctAddr, + "entity_address", rt.EntityAddress, ) sufficientStake = false } diff --git a/go/consensus/tendermint/apps/scheduler/genesis.go b/go/consensus/tendermint/apps/scheduler/genesis.go index ddc1c22edfc..7d57c52c278 100644 --- a/go/consensus/tendermint/apps/scheduler/genesis.go +++ b/go/consensus/tendermint/apps/scheduler/genesis.go @@ -134,19 +134,16 @@ func (app *schedulerApplication) InitChain(ctx *abciAPI.Context, req types.Reque expectedPower = 1 } else { var account *staking.Account - acctAddr := staking.NewAddress(n.EntityID) - account, err = stakeState.Account(ctx, acctAddr) + account, err = stakeState.Account(ctx, n.EntityAddress) if err != nil { ctx.Logger().Error("couldn't get account for genesis validator entity", "err", err, "node", n.ID, - "entity", n.EntityID, - "accont", acctAddr, + "entity_address", n.EntityAddress, ) - return fmt.Errorf("scheduler: getting account %s for genesis validator %s of entity %s: %w", - acctAddr, + return fmt.Errorf("scheduler: getting account %s for genesis validator %s: %w", + n.EntityAddress, n.ID, - n.EntityID, err, ) } @@ -155,15 +152,13 @@ func (app *schedulerApplication) InitChain(ctx *abciAPI.Context, req types.Reque ctx.Logger().Error("computing voting power from stake failed", "err", err, "node", n.ID, - "entity", n.EntityID, - "account", acctAddr, + "entity_address", n.EntityAddress, "stake", &account.Escrow.Active.Balance, ) return fmt.Errorf( - "scheduler: computing voting power from stake (node %s entity %s account %s stake %v): %w", + "scheduler: computing voting power from stake (node %s entity %s stake %v): %w", n.ID, - n.EntityID, - acctAddr, + n.EntityAddress, &account.Escrow.Active.Balance, err, ) diff --git a/go/consensus/tendermint/apps/scheduler/scheduler.go b/go/consensus/tendermint/apps/scheduler/scheduler.go index 480284db435..5559c2d0709 100644 --- a/go/consensus/tendermint/apps/scheduler/scheduler.go +++ b/go/consensus/tendermint/apps/scheduler/scheduler.go @@ -429,16 +429,15 @@ func (app *schedulerApplication) electCommittee( for _, n := range nodes { // Check if an entity has enough stake. - entAddr := staking.NewAddress(n.EntityID) if stakeAcc != nil { - if err = stakeAcc.CheckStakeClaims(entAddr); err != nil { + if err = stakeAcc.CheckStakeClaims(n.EntityAddress); err != nil { continue } } if isSuitableFn(ctx, n, rt) { nodeList = append(nodeList, n) if entitiesEligibleForReward != nil { - entitiesEligibleForReward[entAddr] = true + entitiesEligibleForReward[n.EntityAddress] = true } } } @@ -555,14 +554,13 @@ func (app *schedulerApplication) electValidators( if !n.HasRoles(node.RoleValidator) { continue } - entAddr := staking.NewAddress(n.EntityID) if stakeAcc != nil { - if err := stakeAcc.CheckStakeClaims(entAddr); err != nil { + if err := stakeAcc.CheckStakeClaims(n.EntityAddress); err != nil { continue } } nodeList = append(nodeList, n) - entities[entAddr] = true + entities[n.EntityAddress] = true } // Sort all of the entities that are actually running eligible validator @@ -587,11 +585,10 @@ func (app *schedulerApplication) electValidators( entityNodesMap := make(map[staking.Address][]*node.Node) for i := 0; i < len(idxs); i++ { n := nodeList[idxs[i]] - entAddr := staking.NewAddress(n.EntityID) - entNodes := entityNodesMap[entAddr] + entNodes := entityNodesMap[n.EntityAddress] entNodes = append(entNodes, n) - entityNodesMap[entAddr] = entNodes + entityNodesMap[n.EntityAddress] = entNodes } // Go down the list of entities running nodes by stake, picking one node diff --git a/go/consensus/tendermint/apps/staking/auth.go b/go/consensus/tendermint/apps/staking/auth.go index 87d706021b3..145779458f0 100644 --- a/go/consensus/tendermint/apps/staking/auth.go +++ b/go/consensus/tendermint/apps/staking/auth.go @@ -28,5 +28,5 @@ func (app *stakingApplication) GetSignerNonce(ctx context.Context, req *api.GetS // Implements abci.TransactionAuthHandler. func (app *stakingApplication) AuthenticateTx(ctx *abciAPI.Context, tx *transaction.Transaction) error { - return stakingState.AuthenticateAndPayFees(ctx, ctx.TxSigner(), tx.Nonce, tx.Fee) + return stakingState.AuthenticateAndPayFees(ctx, ctx.TxAccount(), tx.Nonce, tx.Fee) } diff --git a/go/consensus/tendermint/apps/staking/fees.go b/go/consensus/tendermint/apps/staking/fees.go index 5430f2f14f8..369f96618fd 100644 --- a/go/consensus/tendermint/apps/staking/fees.go +++ b/go/consensus/tendermint/apps/staking/fees.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/quantity" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state" @@ -17,7 +16,7 @@ import ( func (app *stakingApplication) disburseFeesP( ctx *abciAPI.Context, stakeState *stakingState.MutableState, - proposerEntity *signature.PublicKey, + proposerEntity *staking.Address, totalFees *quantity.Quantity, ) error { ctx.Logger().Debug("disbursing proposer fees", @@ -64,22 +63,21 @@ func (app *stakingApplication) disburseFeesP( // Pay the proposer. feeProposerAmt := totalFees.Clone() if proposerEntity != nil && !feeProposerAmt.IsZero() { - proposerAddr := staking.NewAddress(*proposerEntity) - proposerAcct, err := stakeState.Account(ctx, proposerAddr) + proposerAcct, err := stakeState.Account(ctx, *proposerEntity) if err != nil { return fmt.Errorf("failed to fetch proposer account: %w", err) } if err = quantity.Move(&proposerAcct.General.Balance, totalFees, feeProposerAmt); err != nil { return fmt.Errorf("move feeProposerAmt: %w", err) } - if err = stakeState.SetAccount(ctx, proposerAddr, proposerAcct); err != nil { + if err = stakeState.SetAccount(ctx, *proposerEntity, proposerAcct); err != nil { return fmt.Errorf("failed to set account: %w", err) } // Emit transfer event. evt := &staking.TransferEvent{ From: staking.FeeAccumulatorAddress, - To: proposerAddr, + To: *proposerEntity, Amount: *feeProposerAmt, } ctx.EmitEvent(abciAPI.NewEventBuilder(app.Name()).Attribute(KeyTransfer, cbor.Marshal(evt))) @@ -117,9 +115,9 @@ func (app *stakingApplication) disburseFeesP( func (app *stakingApplication) disburseFeesVQ( ctx *abciAPI.Context, stakeState *stakingState.MutableState, - proposerEntity *signature.PublicKey, + proposerEntity *staking.Address, numEligibleValidators int, - votingEntities []signature.PublicKey, + votingAddresses []staking.Address, ) error { lastBlockFees, err := stakeState.LastBlockFees(ctx) if err != nil { @@ -129,7 +127,7 @@ func (app *stakingApplication) disburseFeesVQ( ctx.Logger().Debug("disbursing signer and next proposer fees", "total_amount", lastBlockFees, "num_eligible_validators", numEligibleValidators, - "num_voting_entities", len(votingEntities), + "num_voting_addresses", len(votingAddresses), ) if lastBlockFees.IsZero() { // Nothing to disburse. @@ -168,10 +166,10 @@ func (app *stakingApplication) disburseFeesVQ( } // Multiply to get the next proposer's total payment. - numVotingEntities := len(votingEntities) + numVotingAddresses := len(votingAddresses) var nVEQ quantity.Quantity - if err = nVEQ.FromInt64(int64(numVotingEntities)); err != nil { - return fmt.Errorf("import numVotingEntities %d: %w", numVotingEntities, err) + if err = nVEQ.FromInt64(int64(numVotingAddresses)); err != nil { + return fmt.Errorf("import numVotingAddresses %d: %w", numVotingAddresses, err) } nextProposerTotal := shareNextProposer.Clone() if err = nextProposerTotal.Mul(&nVEQ); err != nil { @@ -180,22 +178,21 @@ func (app *stakingApplication) disburseFeesVQ( // Pay the next proposer. if !nextProposerTotal.IsZero() && proposerEntity != nil { - proposerAddr := staking.NewAddress(*proposerEntity) - proposerAcct, err := stakeState.Account(ctx, proposerAddr) + proposerAcct, err := stakeState.Account(ctx, *proposerEntity) if err != nil { return fmt.Errorf("failed to fetch next proposer account: %w", err) } if err = quantity.Move(&proposerAcct.General.Balance, lastBlockFees, nextProposerTotal); err != nil { return fmt.Errorf("move nextProposerTotal: %w", err) } - if err = stakeState.SetAccount(ctx, proposerAddr, proposerAcct); err != nil { + if err = stakeState.SetAccount(ctx, *proposerEntity, proposerAcct); err != nil { return fmt.Errorf("failed to set next proposer account: %w", err) } // Emit transfer event. evt := &staking.TransferEvent{ From: staking.FeeAccumulatorAddress, - To: proposerAddr, + To: *proposerEntity, Amount: *nextProposerTotal, } ctx.EmitEvent(abciAPI.NewEventBuilder(app.Name()).Attribute(KeyTransfer, cbor.Marshal(evt))) @@ -203,23 +200,22 @@ func (app *stakingApplication) disburseFeesVQ( // Pay the voters. if !shareVote.IsZero() { - for _, voterEntity := range votingEntities { - voterAddr := staking.NewAddress(voterEntity) - voterAcct, err := stakeState.Account(ctx, voterAddr) + for _, voterAddress := range votingAddresses { + voterAcct, err := stakeState.Account(ctx, voterAddress) if err != nil { - return fmt.Errorf("failed to fetch voter account %s: %w", voterAddr, err) + return fmt.Errorf("failed to fetch voter account %s: %w", voterAddress, err) } if err = quantity.Move(&voterAcct.General.Balance, lastBlockFees, shareVote); err != nil { return fmt.Errorf("move shareVote: %w", err) } - if err = stakeState.SetAccount(ctx, voterAddr, voterAcct); err != nil { - return fmt.Errorf("failed to set voter account %s: %w", voterAddr, err) + if err = stakeState.SetAccount(ctx, voterAddress, voterAcct); err != nil { + return fmt.Errorf("failed to set voter account %s: %w", voterAddress, err) } // Emit transfer event. evt := &staking.TransferEvent{ From: staking.FeeAccumulatorAddress, - To: voterAddr, + To: voterAddress, Amount: *shareVote, } ctx.EmitEvent(abciAPI.NewEventBuilder(app.Name()).Attribute(KeyTransfer, cbor.Marshal(evt))) diff --git a/go/consensus/tendermint/apps/staking/proposing_rewards.go b/go/consensus/tendermint/apps/staking/proposing_rewards.go index 53805aac281..a799443798d 100644 --- a/go/consensus/tendermint/apps/staking/proposing_rewards.go +++ b/go/consensus/tendermint/apps/staking/proposing_rewards.go @@ -6,7 +6,6 @@ import ( "github.com/tendermint/tendermint/abci/types" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state" @@ -14,12 +13,12 @@ import ( staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) -func (app *stakingApplication) resolveEntityIDFromProposer( +func (app *stakingApplication) resolveEntityAddressFromProposer( ctx *abciAPI.Context, regState *registryState.MutableState, request types.RequestBeginBlock, -) *signature.PublicKey { - var proposingEntity *signature.PublicKey +) *staking.Address { + var proposingAddress *staking.Address proposerNode, err := regState.NodeByConsensusAddress(ctx, request.Header.ProposerAddress) if err != nil { ctx.Logger().Warn("failed to get proposer node", @@ -27,21 +26,20 @@ func (app *stakingApplication) resolveEntityIDFromProposer( "address", hex.EncodeToString(request.Header.ProposerAddress), ) } else { - proposingEntity = &proposerNode.EntityID + proposingAddress = &proposerNode.EntityAddress } - return proposingEntity + return proposingAddress } func (app *stakingApplication) rewardBlockProposing( ctx *abciAPI.Context, stakeState *stakingState.MutableState, - proposingEntity *signature.PublicKey, + proposingAddress *staking.Address, numEligibleValidators, numSigningEntities int, ) error { - if proposingEntity == nil { + if proposingAddress == nil { return nil } - proposerAddr := staking.NewAddress(*proposingEntity) params, err := stakeState.ConsensusParameters(ctx) if err != nil { @@ -64,7 +62,7 @@ func (app *stakingApplication) rewardBlockProposing( ¶ms.RewardFactorBlockProposed, numSigningEntities, numEligibleValidators, - proposerAddr, + *proposingAddress, ); err != nil { return fmt.Errorf("adding rewards: %w", err) } diff --git a/go/consensus/tendermint/apps/staking/signing_rewards.go b/go/consensus/tendermint/apps/staking/signing_rewards.go index 3b54f6bd284..af79415df0e 100644 --- a/go/consensus/tendermint/apps/staking/signing_rewards.go +++ b/go/consensus/tendermint/apps/staking/signing_rewards.go @@ -3,7 +3,6 @@ package staking import ( "fmt" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state" epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api" @@ -13,14 +12,14 @@ import ( func (app *stakingApplication) updateEpochSigning( ctx *abciAPI.Context, stakeState *stakingState.MutableState, - signingEntities []signature.PublicKey, + signingAddresses []staking.Address, ) error { epochSigning, err := stakeState.EpochSigning(ctx) if err != nil { return fmt.Errorf("loading epoch signing info: %w", err) } - if err := epochSigning.Update(signingEntities); err != nil { + if err := epochSigning.Update(signingAddresses); err != nil { return err } @@ -58,19 +57,15 @@ func (app *stakingApplication) rewardEpochSigning(ctx *abciAPI.Context, time epo return nil } - eligibleEntities, err := epochSigning.EligibleEntities( + eligibleAddresses, err := epochSigning.EligibleAddresses( params.SigningRewardThresholdNumerator, params.SigningRewardThresholdDenominator, ) if err != nil { return fmt.Errorf("determining eligibility: %w", err) } - var eligibleEntitiesAddrs []staking.Address - for _, entity := range eligibleEntities { - eligibleEntitiesAddrs = append(eligibleEntitiesAddrs, staking.NewAddress(entity)) - } - if err := stakeState.AddRewards(ctx, time, ¶ms.RewardFactorEpochSigned, eligibleEntitiesAddrs); err != nil { + if err := stakeState.AddRewards(ctx, time, ¶ms.RewardFactorEpochSigned, eligibleAddresses); err != nil { return fmt.Errorf("adding rewards: %w", err) } diff --git a/go/consensus/tendermint/apps/staking/slashing.go b/go/consensus/tendermint/apps/staking/slashing.go index 85b4c731418..4b99c47c529 100644 --- a/go/consensus/tendermint/apps/staking/slashing.go +++ b/go/consensus/tendermint/apps/staking/slashing.go @@ -51,7 +51,7 @@ func onEvidenceDoubleSign( if nodeStatus.IsFrozen() { ctx.Logger().Debug("not slashing frozen validator", "node_id", node.ID, - "entity_id", node.EntityID, + "entity_address", node.EntityAddress, "freeze_end_time", nodeStatus.FreezeEndTime, ) return nil @@ -86,13 +86,12 @@ func onEvidenceDoubleSign( } // Slash validator. - entityAddr := staking.NewAddress(node.EntityID) - _, err = stakeState.SlashEscrow(ctx, entityAddr, &penalty.Amount) + _, err = stakeState.SlashEscrow(ctx, node.EntityAddress, &penalty.Amount) if err != nil { ctx.Logger().Error("failed to slash validator entity", "err", err, "node_id", node.ID, - "entity_id", node.EntityID, + "entity_address", node.EntityAddress, ) return err } @@ -101,14 +100,14 @@ func onEvidenceDoubleSign( ctx.Logger().Error("failed to set validator node status", "err", err, "node_id", node.ID, - "entity_id", node.EntityID, + "entity_address", node.EntityAddress, ) return err } ctx.Logger().Warn("slashed validator for double signing", "node_id", node.ID, - "entity_id", node.EntityID, + "entity_address", node.EntityAddress, ) return nil diff --git a/go/consensus/tendermint/apps/staking/slashing_test.go b/go/consensus/tendermint/apps/staking/slashing_test.go index b9b488cefca..1fd8e0d32bc 100644 --- a/go/consensus/tendermint/apps/staking/slashing_test.go +++ b/go/consensus/tendermint/apps/staking/slashing_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -42,17 +43,20 @@ func TestOnEvidenceDoubleSign(t *testing.T) { require.NoError(err, "should not fail when validator address is not known") // Add entity. - ent, entitySigner, _ := entity.TestEntity() - sigEntity, err := entity.SignEntity(entitySigner, registry.RegisterEntitySignatureContext, ent) + ent, entitySigner, entityAccount, _ := entity.TestEntity() + sigEntity, err := entity.SingleSignEntity(entitySigner, entityAccount, registry.RegisterEntitySignatureContext, ent) require.NoError(err, "SignEntity") err = regState.SetEntity(ctx, ent, sigEntity) require.NoError(err, "SetEntity") + entityAddress := staking.NewAddress(entityAccount) // Add node. nodeSigner := memorySigner.NewTestSigner("node test signer") nod := &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nodeSigner.Public(), - EntityID: ent.ID, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nodeSigner.Public(), + EntityAddress: entityAddress, Consensus: node.ConsensusInfo{ ID: consensusID, }, @@ -92,15 +96,12 @@ func TestOnEvidenceDoubleSign(t *testing.T) { err = onEvidenceDoubleSign(ctx, validatorAddress, 1, now, 1) require.Error(err, "should fail when validator has no stake") - // Computes entity's staking address. - addr := staking.NewAddress(ent.ID) - // Get the validator some stake. var balance quantity.Quantity _ = balance.FromUint64(200) var totalShares quantity.Quantity _ = totalShares.FromUint64(200) - err = stakeState.SetAccount(ctx, addr, &staking.Account{ + err = stakeState.SetAccount(ctx, entityAddress, &staking.Account{ Escrow: staking.EscrowAccount{ Active: staking.SharePool{ Balance: balance, @@ -115,7 +116,7 @@ func TestOnEvidenceDoubleSign(t *testing.T) { require.NoError(err, "slashing should succeed") // Entity stake should be slashed. - acct, err := stakeState.Account(ctx, addr) + acct, err := stakeState.Account(ctx, entityAddress) require.NoError(err, "Account") _ = balance.Sub(&slashAmount) require.EqualValues(balance, acct.Escrow.Active.Balance, "entity stake should be slashed") diff --git a/go/consensus/tendermint/apps/staking/staking.go b/go/consensus/tendermint/apps/staking/staking.go index fdd50e85dba..c37a5091f25 100644 --- a/go/consensus/tendermint/apps/staking/staking.go +++ b/go/consensus/tendermint/apps/staking/staking.go @@ -55,30 +55,30 @@ func (app *stakingApplication) BeginBlock(ctx *api.Context, request types.Reques regState := registryState.NewMutableState(ctx.State()) stakeState := stakingState.NewMutableState(ctx.State()) - // Look up the proposer's entity. - proposingEntity := app.resolveEntityIDFromProposer(ctx, regState, request) + // Look up the proposer's entity address. + proposingAddress := app.resolveEntityAddressFromProposer(ctx, regState, request) // Go through all voters of the previous block and resolve entities. // numEligibleValidators is how many total validators are in the validator set, while // votingEntities is from the validators which actually voted. numEligibleValidators := len(request.GetLastCommitInfo().Votes) - votingEntities := app.resolveEntityIDsFromVotes(ctx, regState, request.GetLastCommitInfo()) + votingAddresses := app.resolveEntityAddressesFromVotes(ctx, regState, request.GetLastCommitInfo()) // Disburse fees from previous block. - if err := app.disburseFeesVQ(ctx, stakeState, proposingEntity, numEligibleValidators, votingEntities); err != nil { + if err := app.disburseFeesVQ(ctx, stakeState, proposingAddress, numEligibleValidators, votingAddresses); err != nil { return fmt.Errorf("disburse fees voters and next proposer: %w", err) } // Save block proposer for fee disbursements. - stakingState.SetBlockProposer(ctx, proposingEntity) + stakingState.SetBlockProposer(ctx, proposingAddress) // Add rewards for proposer. - if err := app.rewardBlockProposing(ctx, stakeState, proposingEntity, numEligibleValidators, len(votingEntities)); err != nil { + if err := app.rewardBlockProposing(ctx, stakeState, proposingAddress, numEligibleValidators, len(votingAddresses)); err != nil { return fmt.Errorf("staking: block proposing reward: %w", err) } // Track signing for rewards. - if err := app.updateEpochSigning(ctx, stakeState, votingEntities); err != nil { + if err := app.updateEpochSigning(ctx, stakeState, votingAddresses); err != nil { return fmt.Errorf("staking: failed to update epoch signing info: %w", err) } diff --git a/go/consensus/tendermint/apps/staking/state/accumulator_test.go b/go/consensus/tendermint/apps/staking/state/accumulator_test.go index 65ee2f5efc7..fdcbdd9956b 100644 --- a/go/consensus/tendermint/apps/staking/state/accumulator_test.go +++ b/go/consensus/tendermint/apps/staking/state/accumulator_test.go @@ -40,8 +40,8 @@ func TestStakeAccumulatorCache(t *testing.T) { // escrow account instance which is tested separately. Here we just make sure that state // changes are propagated correctly. - ent, _, _ := entity.TestEntity() - addr := staking.NewAddress(ent.ID) + ent, _, _, _ := entity.TestEntity() + addr := ent.AccountAddress var acct staking.Account acct.Escrow.Active.Balance = *q.Clone() err = stakeState.SetAccount(ctx, addr, &acct) diff --git a/go/consensus/tendermint/apps/staking/state/gas.go b/go/consensus/tendermint/apps/staking/state/gas.go index bbb3d9a3245..94649e4d11c 100644 --- a/go/consensus/tendermint/apps/staking/state/gas.go +++ b/go/consensus/tendermint/apps/staking/state/gas.go @@ -5,7 +5,7 @@ import ( "math" "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/quantity" "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" @@ -25,14 +25,14 @@ type feeAccumulator struct { balance quantity.Quantity } -// AuthenticateAndPayFees authenticates the message signer and makes sure that +// AuthenticateAndPayFees authenticates the message signer(s) and makes sure that // any gas fees are paid. // // This method transfers the fees to the per-block fee accumulator which is // persisted at the end of the block. func AuthenticateAndPayFees( ctx *abciAPI.Context, - signer signature.PublicKey, + signer *multisig.Account, nonce uint64, fee *transaction.Fee, ) error { @@ -46,7 +46,7 @@ func AuthenticateAndPayFees( return nil } - // Convert signer's public key to account address. + // Convert the account descriptor to account address. addr := staking.NewAddress(signer) if addr.IsReserved() { return fmt.Errorf("using reserved account address %s is prohibited", addr) @@ -133,14 +133,14 @@ func BlockFees(ctx *abciAPI.Context) quantity.Quantity { type proposerKey struct{} func (pk proposerKey) NewDefault() interface{} { - var empty *signature.PublicKey + var empty *staking.Address return empty } -func SetBlockProposer(ctx *abciAPI.Context, p *signature.PublicKey) { +func SetBlockProposer(ctx *abciAPI.Context, p *staking.Address) { ctx.BlockContext().Set(proposerKey{}, p) } -func BlockProposer(ctx *abciAPI.Context) *signature.PublicKey { - return ctx.BlockContext().Get(proposerKey{}).(*signature.PublicKey) +func BlockProposer(ctx *abciAPI.Context) *staking.Address { + return ctx.BlockContext().Get(proposerKey{}).(*staking.Address) } diff --git a/go/consensus/tendermint/apps/staking/state/state.go b/go/consensus/tendermint/apps/staking/state/state.go index abde62f4c47..d10d9bb6e75 100644 --- a/go/consensus/tendermint/apps/staking/state/state.go +++ b/go/consensus/tendermint/apps/staking/state/state.go @@ -5,10 +5,10 @@ import ( "context" "fmt" "math" + "math/bits" "sort" "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/keyformat" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -377,11 +377,11 @@ func (s *ImmutableState) DebondingDelegation( } type DebondingQueueEntry struct { - Epoch epochtime.EpochTime - DelegatorAddr staking.Address - EscrowAddr staking.Address - Seq uint64 - Delegation *staking.DebondingDelegation + Epoch epochtime.EpochTime `json:"epoch"` + DelegatorAddr staking.Address `json:"delegator_addr"` + EscrowAddr staking.Address `json:"escrow_addr"` + Seq uint64 `json:"seq"` + Delegation *staking.DebondingDelegation `json:"delegation,omitempty"` } func (s *ImmutableState) ExpiredDebondingQueue(ctx context.Context, epoch epochtime.EpochTime) ([]*DebondingQueueEntry, error) { @@ -441,47 +441,49 @@ func (s *ImmutableState) LastBlockFees(ctx context.Context) (*quantity.Quantity, } type EpochSigning struct { - Total uint64 - ByEntity map[signature.PublicKey]uint64 + Total uint64 `json:"total"` + ByAddress map[staking.Address]uint64 `json:"by_address"` } -func (es *EpochSigning) Update(signingEntities []signature.PublicKey) error { +func (es *EpochSigning) Update(signingAddresses []staking.Address) error { + var carry uint64 + oldTotal := es.Total - es.Total = oldTotal + 1 - if es.Total <= oldTotal { + es.Total, carry = bits.Add64(oldTotal, 1, 0) + if carry != 0 { return fmt.Errorf("incrementing total blocks count: overflow, old_total=%d", oldTotal) } - for _, entityID := range signingEntities { - oldCount := es.ByEntity[entityID] - es.ByEntity[entityID] = oldCount + 1 - if es.ByEntity[entityID] <= oldCount { - return fmt.Errorf("incrementing count for entity %s: overflow, old_count=%d", entityID, oldCount) + for _, address := range signingAddresses { + oldCount := es.ByAddress[address] + es.ByAddress[address], carry = bits.Add64(oldCount, 1, 0) + if carry != 0 { + return fmt.Errorf("incrementing count for address %s: overflow, old_count=%d", address, oldCount) } } return nil } -func (es *EpochSigning) EligibleEntities(thresholdNumerator, thresholdDenominator uint64) ([]signature.PublicKey, error) { - var eligibleEntities []signature.PublicKey +func (es *EpochSigning) EligibleAddresses(thresholdNumerator, thresholdDenominator uint64) ([]staking.Address, error) { + var eligibleAddresses []staking.Address if es.Total > math.MaxUint64/thresholdNumerator { return nil, fmt.Errorf("overflow in total blocks, total=%d", es.Total) } thresholdPremultiplied := es.Total * thresholdNumerator - for entityID, count := range es.ByEntity { + for address, count := range es.ByAddress { if count > math.MaxUint64/thresholdDenominator { - return nil, fmt.Errorf("entity %s: overflow in threshold comparison, count=%d", entityID, count) + return nil, fmt.Errorf("address %s: overflow in threshold comparison, count=%d", address, count) } if count*thresholdDenominator < thresholdPremultiplied { continue } - eligibleEntities = append(eligibleEntities, entityID) + eligibleAddresses = append(eligibleAddresses, address) } - sort.Slice(eligibleEntities, func(i, j int) bool { - return bytes.Compare(eligibleEntities[i][:], eligibleEntities[j][:]) < 0 + sort.Slice(eligibleAddresses, func(i, j int) bool { + return bytes.Compare(eligibleAddresses[i][:], eligibleAddresses[j][:]) < 0 }) - return eligibleEntities, nil + return eligibleAddresses, nil } func (s *ImmutableState) EpochSigning(ctx context.Context) (*EpochSigning, error) { @@ -492,7 +494,7 @@ func (s *ImmutableState) EpochSigning(ctx context.Context) (*EpochSigning, error if value == nil { // Not present means zero everything. return &EpochSigning{ - ByEntity: make(map[signature.PublicKey]uint64), + ByAddress: make(map[staking.Address]uint64), }, nil } diff --git a/go/consensus/tendermint/apps/staking/state/state_test.go b/go/consensus/tendermint/apps/staking/state/state_test.go index 04b9edffc38..a57ae383ceb 100644 --- a/go/consensus/tendermint/apps/staking/state/state_test.go +++ b/go/consensus/tendermint/apps/staking/state/state_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -44,7 +45,7 @@ func TestDelegationQueries(t *testing.T) { // Generate escrow account. escrowSigner, err := fac.Generate(signature.SignerEntity, rand.Reader) require.NoError(err, "generating escrow signer") - escrowAddr := staking.NewAddress(escrowSigner.Public()) + escrowAddr := staking.NewAddress(multisig.NewAccountFromPublicKey(escrowSigner.Public())) var escrowAccount staking.Account err = s.SetAccount(ctx, escrowAddr, &escrowAccount) @@ -61,7 +62,7 @@ func TestDelegationQueries(t *testing.T) { for i := int64(1); i <= int64(numDelegatorAccounts); i++ { signer, serr := fac.Generate(signature.SignerEntity, rand.Reader) require.NoError(serr, "memory signer factory Generate account") - addr := staking.NewAddress(signer.Public()) + addr := staking.NewAddress(multisig.NewAccountFromPublicKey(signer.Public())) delegatorAddrs = append(delegatorAddrs, addr) @@ -125,7 +126,7 @@ func TestRewardAndSlash(t *testing.T) { delegatorSigner, err := memorySigner.NewSigner(rand.Reader) require.NoError(err, "generating delegator signer") - delegatorAddr := staking.NewAddress(delegatorSigner.Public()) + delegatorAddr := staking.NewAddress(multisig.NewAccountFromPublicKey(delegatorSigner.Public())) delegatorAccount := &staking.Account{} delegatorAccount.General.Nonce = 10 err = delegatorAccount.General.Balance.FromBigInt(big.NewInt(300)) @@ -133,7 +134,7 @@ func TestRewardAndSlash(t *testing.T) { escrowSigner, err := memorySigner.NewSigner(rand.Reader) require.NoError(err, "generating escrow signer") - escrowAddr := staking.NewAddress(escrowSigner.Public()) + escrowAddr := staking.NewAddress(multisig.NewAccountFromPublicKey(escrowSigner.Public())) escrowAddrAsList := []staking.Address{escrowAddr} escrowAccount := &staking.Account{} escrowAccount.Escrow.CommissionSchedule = staking.CommissionSchedule{ @@ -292,26 +293,30 @@ func TestEpochSigning(t *testing.T) { es, err := s.EpochSigning(ctx) require.NoError(err, "load epoch signing info") require.Zero(es.Total, "empty epoch signing info total") - require.Empty(es.ByEntity, "empty epoch signing info by entity") + require.Empty(es.ByAddress, "empty epoch signing info by address") - var truant, exact, perfect signature.PublicKey - err = truant.UnmarshalHex("1111111111111111111111111111111111111111111111111111111111111111") - require.NoError(err, "initializing 'truant' ID") - err = exact.UnmarshalHex("3333333333333333333333333333333333333333333333333333333333333333") - require.NoError(err, "initializing 'exact' ID") - err = perfect.UnmarshalHex("4444444444444444444444444444444444444444444444444444444444444444") - require.NoError(err, "initializing 'perfect' ID") + var truantPub, exactPub, perfectPub signature.PublicKey + err = truantPub.UnmarshalHex("1111111111111111111111111111111111111111111111111111111111111111") + require.NoError(err, "initializing 'truant' PublicKey") + err = exactPub.UnmarshalHex("3333333333333333333333333333333333333333333333333333333333333333") + require.NoError(err, "initializing 'exact' PublicKey") + err = perfectPub.UnmarshalHex("4444444444444444444444444444444444444444444444444444444444444444") + require.NoError(err, "initializing 'perfect' PublicKey") - err = es.Update([]signature.PublicKey{truant, exact, perfect}) + truant := staking.NewAddress(multisig.NewAccountFromPublicKey(truantPub)) + exact := staking.NewAddress(multisig.NewAccountFromPublicKey(exactPub)) + perfect := staking.NewAddress(multisig.NewAccountFromPublicKey(perfectPub)) + + err = es.Update([]staking.Address{truant, exact, perfect}) require.NoError(err, "updating epoch signing info") - err = es.Update([]signature.PublicKey{exact, perfect}) + err = es.Update([]staking.Address{exact, perfect}) require.NoError(err, "updating epoch signing info") - err = es.Update([]signature.PublicKey{exact, perfect}) + err = es.Update([]staking.Address{exact, perfect}) require.NoError(err, "updating epoch signing info") - err = es.Update([]signature.PublicKey{perfect}) + err = es.Update([]staking.Address{perfect}) require.NoError(err, "updating epoch signing info") require.EqualValues(4, es.Total, "populated epoch signing info total") - require.Len(es.ByEntity, 3, "populated epoch signing info by entity") + require.Len(es.ByAddress, 3, "populated epoch signing info by address") err = s.SetEpochSigning(ctx, es) require.NoError(err, "SetEpochSigning") @@ -319,17 +324,17 @@ func TestEpochSigning(t *testing.T) { require.NoError(err, "load epoch signing info 2") require.Equal(es, esRoundTrip, "epoch signing info round trip") - eligibleEntities, err := es.EligibleEntities(3, 4) - require.NoError(err, "determining eligible entities") - require.Len(eligibleEntities, 2, "eligible entities") - require.NotContains(eligibleEntities, truant, "'truant' not eligible") - require.Contains(eligibleEntities, exact, "'exact' eligible") - require.Contains(eligibleEntities, perfect, "'perfect' eligible") + eligibleAddresses, err := es.EligibleAddresses(3, 4) + require.NoError(err, "determining eligible addresses") + require.Len(eligibleAddresses, 2, "eligible addresses") + require.NotContains(eligibleAddresses, truant, "'truant' not eligible") + require.Contains(eligibleAddresses, exact, "'exact' eligible") + require.Contains(eligibleAddresses, perfect, "'perfect' eligible") err = s.ClearEpochSigning(ctx) require.NoError(err, "ClearEpochSigning") esClear, err := s.EpochSigning(ctx) require.NoError(err, "load cleared epoch signing info") require.Zero(esClear.Total, "cleared epoch signing info total") - require.Empty(esClear.ByEntity, "cleared epoch signing info by entity") + require.Empty(esClear.ByAddress, "cleared epoch signing info by address") } diff --git a/go/consensus/tendermint/apps/staking/transactions.go b/go/consensus/tendermint/apps/staking/transactions.go index 49e1fd112ea..e2e533b1b6b 100644 --- a/go/consensus/tendermint/apps/staking/transactions.go +++ b/go/consensus/tendermint/apps/staking/transactions.go @@ -35,7 +35,7 @@ func (app *stakingApplication) transfer(ctx *api.Context, state *stakingState.Mu return err } - fromAddr := staking.NewAddress(ctx.TxSigner()) + fromAddr := staking.NewAddress(ctx.TxAccount()) if fromAddr.IsReserved() || !isTransferPermitted(params, fromAddr) { return staking.ErrForbidden } @@ -114,7 +114,7 @@ func (app *stakingApplication) burn(ctx *api.Context, state *stakingState.Mutabl return err } - fromAddr := staking.NewAddress(ctx.TxSigner()) + fromAddr := staking.NewAddress(ctx.TxAccount()) if fromAddr.IsReserved() { return staking.ErrForbidden } @@ -180,7 +180,7 @@ func (app *stakingApplication) addEscrow(ctx *api.Context, state *stakingState.M return staking.ErrInvalidArgument } - fromAddr := staking.NewAddress(ctx.TxSigner()) + fromAddr := staking.NewAddress(ctx.TxAccount()) if fromAddr.IsReserved() { return staking.ErrForbidden } @@ -272,7 +272,7 @@ func (app *stakingApplication) reclaimEscrow(ctx *api.Context, state *stakingSta return err } - toAddr := staking.NewAddress(ctx.TxSigner()) + toAddr := staking.NewAddress(ctx.TxAccount()) if toAddr.IsReserved() { return staking.ErrForbidden } @@ -396,7 +396,7 @@ func (app *stakingApplication) amendCommissionSchedule( return err } - fromAddr := staking.NewAddress(ctx.TxSigner()) + fromAddr := staking.NewAddress(ctx.TxAccount()) if fromAddr.IsReserved() { return staking.ErrForbidden } diff --git a/go/consensus/tendermint/apps/staking/transactions_test.go b/go/consensus/tendermint/apps/staking/transactions_test.go index 1d270700a37..f80ed5f0eb2 100644 --- a/go/consensus/tendermint/apps/staking/transactions_test.go +++ b/go/consensus/tendermint/apps/staking/transactions_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/quantity" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" @@ -81,7 +82,7 @@ func TestReservedAddresses(t *testing.T) { // Create a new test public key, set it as the tx signer and create a new reserved address from it. testPK := signature.NewPublicKey("badfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - ctx.SetTxSigner(testPK) + ctx.SetTxAccount(multisig.NewAccountFromPublicKey(testPK)) _ = staking.NewReservedAddress(testPK) // Make sure all transaction types fail for the reserved address. diff --git a/go/consensus/tendermint/apps/staking/votes.go b/go/consensus/tendermint/apps/staking/votes.go index bdc44f54cdd..46ffe875301 100644 --- a/go/consensus/tendermint/apps/staking/votes.go +++ b/go/consensus/tendermint/apps/staking/votes.go @@ -5,13 +5,13 @@ import ( "github.com/tendermint/tendermint/abci/types" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api" registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) -func (app *stakingApplication) resolveEntityIDsFromVotes(ctx *abciAPI.Context, regState *registryState.MutableState, lastCommitInfo types.LastCommitInfo) []signature.PublicKey { - var entityIDs []signature.PublicKey +func (app *stakingApplication) resolveEntityAddressesFromVotes(ctx *abciAPI.Context, regState *registryState.MutableState, lastCommitInfo types.LastCommitInfo) []staking.Address { + var entityAddresses []staking.Address for _, a := range lastCommitInfo.Votes { if !a.SignedLastBlock { continue @@ -28,8 +28,8 @@ func (app *stakingApplication) resolveEntityIDsFromVotes(ctx *abciAPI.Context, r continue } - entityIDs = append(entityIDs, node.EntityID) + entityAddresses = append(entityAddresses, node.EntityAddress) } - return entityIDs + return entityAddresses } diff --git a/go/consensus/tendermint/registry/registry.go b/go/consensus/tendermint/registry/registry.go index 3247b48cebe..6a6b818e106 100644 --- a/go/consensus/tendermint/registry/registry.go +++ b/go/consensus/tendermint/registry/registry.go @@ -56,7 +56,7 @@ func (sc *serviceClient) Querier() *app.QueryFactory { return sc.querier } -func (sc *serviceClient) GetEntity(ctx context.Context, query *api.IDQuery) (*entity.Entity, error) { +func (sc *serviceClient) GetEntity(ctx context.Context, query *api.AccountQuery) (*entity.Entity, error) { q, err := sc.querier.QueryAt(ctx, query.Height) if err != nil { return nil, err diff --git a/go/consensus/tendermint/tendermint.go b/go/consensus/tendermint/tendermint.go index d3a58004194..bc5f1c2ffc1 100644 --- a/go/consensus/tendermint/tendermint.go +++ b/go/consensus/tendermint/tendermint.go @@ -587,7 +587,7 @@ func (t *tendermintService) SubmitEvidence(ctx context.Context, evidence consens } func (t *tendermintService) EstimateGas(ctx context.Context, req *consensusAPI.EstimateGasRequest) (transaction.Gas, error) { - return t.mux.EstimateGas(req.Signer, req.Transaction) + return t.mux.EstimateGas(&req.Account, req.Transaction) } func (t *tendermintService) subscribe(subscriber string, query tmpubsub.Query) (tmtypes.Subscription, error) { @@ -1359,8 +1359,7 @@ func genesisToTendermint(d *genesisAPI.Document) (*tmtypes.GenesisDoc, error) { power = 1 } else { var stake *quantity.Quantity - acctAddr := stakingAPI.NewAddress(openedNode.EntityID) - if account, ok := d.Staking.Ledger[acctAddr]; ok { + if account, ok := d.Staking.Ledger[openedNode.EntityAddress]; ok { stake = account.Escrow.Active.Balance.Clone() } else { // If all balances and stuff are zero, it's permitted not to have an account in the ledger at all. @@ -1368,9 +1367,8 @@ func genesisToTendermint(d *genesisAPI.Document) (*tmtypes.GenesisDoc, error) { } power, err = schedulerAPI.VotingPowerFromStake(stake) if err != nil { - return nil, fmt.Errorf("tendermint: computing voting power for entity %s with account %s and stake %v: %w", - openedNode.EntityID, - acctAddr, + return nil, fmt.Errorf("tendermint: computing voting power for entity %s and stake %v: %w", + openedNode.EntityAddress, stake, err, ) diff --git a/go/consensus/tests/tester.go b/go/consensus/tests/tester.go index 00560d332b9..a941aa8b871 100644 --- a/go/consensus/tests/tester.go +++ b/go/consensus/tests/tester.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" @@ -89,14 +90,18 @@ func ConsensusImplementationTests(t *testing.T, backend consensus.ClientBackend) require.True(epoch > 0, "epoch height should be greater than zero") _, err = backend.EstimateGas(ctx, &consensus.EstimateGasRequest{ - Signer: memorySigner.NewTestSigner("estimate gas signer").Public(), + Account: *multisig.NewAccountFromPublicKey( + memorySigner.NewTestSigner("estimate gas signer").Public(), + ), Transaction: transaction.NewTransaction(0, nil, epochtimemock.MethodSetEpoch, 0), }) require.NoError(err, "EstimateGas") nonce, err := backend.GetSignerNonce(ctx, &consensus.GetSignerNonceRequest{ AccountAddress: staking.NewAddress( - signature.NewPublicKey("badfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + multisig.NewAccountFromPublicKey( + signature.NewPublicKey("badfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + ), ), Height: consensus.HeightLatest, }) diff --git a/go/extra/stats/cmd/stats.go b/go/extra/stats/cmd/stats.go index 86495dd73a3..682c20e1abf 100644 --- a/go/extra/stats/cmd/stats.go +++ b/go/extra/stats/cmd/stats.go @@ -24,6 +24,7 @@ import ( nodeCmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common" cmdGrpc "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/grpc" registryAPI "github.com/oasisprotocol/oasis-core/go/registry/api" + stakingAPI "github.com/oasisprotocol/oasis-core/go/staking/api" ) const ( @@ -56,20 +57,20 @@ type nodeStats struct { // entityStats are per entity stats. type entityStats struct { - id signature.PublicKey + id stakingAPI.Address nodes map[signature.PublicKey]*nodeStats } // nodeIDs are node identifiers. type nodeIDs struct { - entityID signature.PublicKey + entityID stakingAPI.Address nodeID signature.PublicKey } // stats are gathered entity stats. type stats struct { // Per entity stats. - entities map[signature.PublicKey]*entityStats + entities map[stakingAPI.Address]*entityStats // Tendermint stores the validator addresses (which are the truncated SHA-256 // of the node consensus public keys) in Commit data instead of the actual @@ -80,7 +81,7 @@ type stats struct { // printEntityAvailability prints topN entities by availability score. func (s stats) printEntityAvailability(topN int) { type results struct { - entityID signature.PublicKey + entityID stakingAPI.Address elections int64 signatures int64 selections int64 @@ -150,7 +151,7 @@ func (s stats) getNodeStats(nodeAddr string) (*nodeStats, error) { // newStats initializes empty stats. func newStats() *stats { b := &stats{ - entities: make(map[signature.PublicKey]*entityStats), + entities: make(map[stakingAPI.Address]*entityStats), nodeAddressMap: make(map[string]nodeIDs), } return b @@ -168,12 +169,12 @@ func (s *stats) addRegistryData(ctx context.Context, registry registryAPI.Backen var es *entityStats var ok bool // Get or create node entity. - if es, ok = s.entities[n.EntityID]; !ok { + if es, ok = s.entities[n.EntityAddress]; !ok { es = &entityStats{ - id: n.EntityID, + id: n.EntityAddress, nodes: make(map[signature.PublicKey]*nodeStats), } - s.entities[n.EntityID] = es + s.entities[n.EntityAddress] = es } // Initialize node stats if missing. @@ -182,7 +183,7 @@ func (s *stats) addRegistryData(ctx context.Context, registry registryAPI.Backen cID := n.Consensus.ID tmAddr := tmcrypto.PublicKeyToTendermint(&cID).Address().String() s.nodeAddressMap[tmAddr] = nodeIDs{ - entityID: n.EntityID, + entityID: n.EntityAddress, nodeID: n.ID, } } diff --git a/go/genesis/genesis_test.go b/go/genesis/genesis_test.go index f18475535e2..a9b1cf6f26b 100644 --- a/go/genesis/genesis_test.go +++ b/go/genesis/genesis_test.go @@ -11,7 +11,9 @@ import ( "github.com/stretchr/testify/require" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -70,16 +72,16 @@ var testDoc = &genesis.Document{ Staking: stakingTests.DebugGenesisState, } -func signEntityOrDie(signer signature.Signer, e *entity.Entity) *entity.SignedEntity { - signedEntity, err := entity.SignEntity(signer, registry.RegisterGenesisEntitySignatureContext, e) +func signEntityOrDie(signer signature.Signer, account *multisig.Account, e *entity.Entity) *entity.SignedEntity { + signedEntity, err := entity.SingleSignEntity(signer, account, registry.RegisterGenesisEntitySignatureContext, e) if err != nil { panic(err) } return signedEntity } -func signRuntimeOrDie(signer signature.Signer, rt *registry.Runtime) *registry.SignedRuntime { - signedRuntime, err := registry.SignRuntime(signer, registry.RegisterGenesisRuntimeSignatureContext, rt) +func signRuntimeOrDie(signer signature.Signer, account *multisig.Account, rt *registry.Runtime) *registry.SignedRuntime { + signedRuntime, err := registry.SingleSignRuntime(signer, account, registry.RegisterGenesisRuntimeSignatureContext, rt) if err != nil { panic(err) } @@ -159,36 +161,43 @@ func TestGenesisSanityCheck(t *testing.T) { // Note that this test entity has no nodes by design, those will be added // later by various tests. + validAccount := multisig.NewAccountFromPublicKey(validPK) testEntity := &entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, - ID: validPK, + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, + AccountAddress: staking.NewAddress(validAccount), AllowEntitySignedNodes: true, } - signedTestEntity := signEntityOrDie(signer, testEntity) + signedTestEntity := signEntityOrDie(signer, validAccount, testEntity) kmRuntimeID := hex2ns("4000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", false) testKMRuntime := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: kmRuntimeID, - EntityID: testEntity.ID, - Kind: registry.KindKeyManager, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: kmRuntimeID, + EntityAddress: testEntity.AccountAddress, + Kind: registry.KindKeyManager, AdmissionPolicy: registry.RuntimeAdmissionPolicy{ EntityWhitelist: ®istry.EntityWhitelistRuntimeAdmissionPolicy{ - Entities: map[signature.PublicKey]bool{ - validPK: true, + Entities: map[staking.Address]bool{ + testEntity.AccountAddress: true, }, }, }, } - signedTestKMRuntime := signRuntimeOrDie(signer, testKMRuntime) + signedTestKMRuntime := signRuntimeOrDie(signer, validAccount, testKMRuntime) testRuntimeID := hex2ns("0000000000000000000000000000000000000000000000000000000000000001", false) testRuntime := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: testRuntimeID, - EntityID: testEntity.ID, - Kind: registry.KindCompute, - KeyManager: &testKMRuntime.ID, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: testRuntimeID, + EntityAddress: testEntity.AccountAddress, + Kind: registry.KindCompute, + KeyManager: &testKMRuntime.ID, Executor: registry.ExecutorParameters{ GroupSize: 1, RoundTimeout: 1 * time.Second, @@ -216,18 +225,20 @@ func TestGenesisSanityCheck(t *testing.T) { AnyNode: ®istry.AnyNodeRuntimeAdmissionPolicy{}, }, } - signedTestRuntime := signRuntimeOrDie(signer, testRuntime) + signedTestRuntime := signRuntimeOrDie(signer, validAccount, testRuntime) var testConsensusAddress node.ConsensusAddress _ = testConsensusAddress.UnmarshalText([]byte("AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBA=@127.0.0.1:1234")) var testAddress node.Address _ = testAddress.UnmarshalText([]byte("127.0.0.1:1234")) testNode := &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nodeSigner.Public(), - EntityID: testEntity.ID, - Expiration: 10, - Roles: node.RoleValidator, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nodeSigner.Public(), + EntityAddress: testEntity.AccountAddress, + Expiration: 10, + Roles: node.RoleValidator, TLS: node.TLSInfo{ PubKey: nodeTLSSigner.Public(), Addresses: []node.TLSAddress{ @@ -398,23 +409,24 @@ func TestGenesisSanityCheck(t *testing.T) { d.Registry.Entities = []*entity.SignedEntity{signedTestEntity} require.NoError(d.SanityCheck(), "test entity should pass") + invalidAccount := multisig.NewAccountFromPublicKey(invalidPK) d = *testDoc te := *testEntity - te.ID = invalidPK - signedBrokenEntity := signEntityOrDie(signer, &te) + te.AccountAddress = staking.NewAddress(invalidAccount) + signedBrokenEntity := signEntityOrDie(signer, validAccount, &te) d.Registry.Entities = []*entity.SignedEntity{signedBrokenEntity} require.Error(d.SanityCheck(), "invalid test entity ID should be rejected") d = *testDoc te = *testEntity te.Nodes = []signature.PublicKey{invalidPK} - signedBrokenEntity = signEntityOrDie(signer, &te) + signedBrokenEntity = signEntityOrDie(signer, validAccount, &te) d.Registry.Entities = []*entity.SignedEntity{signedBrokenEntity} require.Error(d.SanityCheck(), "test entity's invalid node public key should be rejected") d = *testDoc te = *testEntity - signedBrokenEntity, err := entity.SignEntity(signer, signature.NewContext("genesis sanity check invalid ctx"), &te) + signedBrokenEntity, err := entity.SingleSignEntity(signer, validAccount, signature.NewContext("genesis sanity check invalid ctx"), &te) if err != nil { panic(err) } @@ -451,7 +463,7 @@ func TestGenesisSanityCheck(t *testing.T) { d = *testDoc te = *testEntity te.Nodes = []signature.PublicKey{testNode.ID} - signedEntityWithTestNode := signEntityOrDie(signer, &te) + signedEntityWithTestNode := signEntityOrDie(signer, validAccount, &te) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{} d.Registry.Nodes = []*node.MultiSignedNode{signedTestNode} @@ -461,7 +473,7 @@ func TestGenesisSanityCheck(t *testing.T) { te = *testEntity te.Nodes = []signature.PublicKey{unknownPK} te.AllowEntitySignedNodes = false - signedEntityWithBrokenNode := signEntityOrDie(signer, &te) + signedEntityWithBrokenNode := signEntityOrDie(signer, validAccount, &te) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithBrokenNode} d.Registry.Runtimes = []*registry.SignedRuntime{} d.Registry.Nodes = []*node.MultiSignedNode{signedTestNode} @@ -471,15 +483,16 @@ func TestGenesisSanityCheck(t *testing.T) { te = *testEntity te.Nodes = []signature.PublicKey{unknownPK} te.AllowEntitySignedNodes = true - signedEntityWithBrokenNode = signEntityOrDie(signer, &te) + signedEntityWithBrokenNode = signEntityOrDie(signer, validAccount, &te) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithBrokenNode} d.Registry.Runtimes = []*registry.SignedRuntime{} d.Registry.Nodes = []*node.MultiSignedNode{entitySignedTestNode} require.NoError(d.SanityCheck(), "node not listed among controlling entity's nodes should still be accepted if the entity allows entity-signed nodes") + unknownAccount := multisig.NewAccountFromPublicKey(unknownPK) d = *testDoc tn := *testNode - tn.EntityID = unknownPK + tn.EntityAddress = staking.NewAddress(unknownAccount) signedBrokenTestNode := signNodeOrDie(nodeSigners, &tn) d.Registry.Entities = []*entity.SignedEntity{signedEntityWithTestNode} d.Registry.Runtimes = []*registry.SignedRuntime{signedTestKMRuntime} diff --git a/go/oasis-node/cmd/common/common.go b/go/oasis-node/cmd/common/common.go index c2fe45940a1..e5bff61c1c8 100644 --- a/go/oasis-node/cmd/common/common.go +++ b/go/oasis-node/cmd/common/common.go @@ -13,6 +13,7 @@ import ( "github.com/spf13/viper" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -248,14 +249,14 @@ func GetInputReader(cmd *cobra.Command, cfg string) (io.ReadCloser, bool, error) } // LoadEntity loads the entity and it's signer. -func LoadEntity(signerBackend, entityDir string) (*entity.Entity, signature.Signer, error) { +func LoadEntity(signerBackend, entityDir string) (*entity.Entity, signature.Signer, *multisig.Account, error) { if flags.DebugTestEntity() { return entity.TestEntity() } factory, err := cmdSigner.NewFactory(signerBackend, entityDir, signature.SignerEntity) if err != nil { - return nil, nil, err + return nil, nil, nil, err } return entity.Load(entityDir, factory) @@ -273,6 +274,6 @@ func ExportEntity(signerBackend, entityDir string) error { return err } - _, err = entity.GenerateWithSigner(entityDir, signer, nil) + _, _, err = entity.GenerateWithSigner(entityDir, signer, nil) return err } diff --git a/go/oasis-node/cmd/common/consensus/consensus.go b/go/oasis-node/cmd/common/consensus/consensus.go index ee2ccde2cdc..fd57e535521 100644 --- a/go/oasis-node/cmd/common/consensus/consensus.go +++ b/go/oasis-node/cmd/common/consensus/consensus.go @@ -108,7 +108,7 @@ func SignAndSaveTx(tx *transaction.Transaction) { ) os.Exit(1) } - _, signer, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) + _, signer, account, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) if err != nil { logger.Error("failed to load account entity", "err", err, @@ -117,7 +117,7 @@ func SignAndSaveTx(tx *transaction.Transaction) { } defer signer.Reset() - sigTx, err := transaction.Sign(signer, tx) + sigTx, err := transaction.SingleSign(signer, account, tx) if err != nil { logger.Error("failed to sign transaction", "err", err, diff --git a/go/oasis-node/cmd/consensus/consensus.go b/go/oasis-node/cmd/consensus/consensus.go index b445dff046b..a2c4a16dcf4 100644 --- a/go/oasis-node/cmd/consensus/consensus.go +++ b/go/oasis-node/cmd/consensus/consensus.go @@ -13,6 +13,8 @@ import ( "google.golang.org/grpc" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/logging" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" @@ -149,13 +151,16 @@ func doEstimateGas(cmd *cobra.Command, args []string) { req := consensus.EstimateGasRequest{ Transaction: loadUnsignedTx(), } - if err := req.Signer.UnmarshalText([]byte(signerPub)); err != nil { + + var pk signature.PublicKey + if err := pk.UnmarshalText([]byte(signerPub)); err != nil { logger.Error("failed to unmarshal signer public key", "err", err, "signer_pub_str", signerPub, ) os.Exit(1) } + req.Account = *multisig.NewAccountFromPublicKey(pk) gas, err := client.EstimateGas(context.Background(), &req) if err != nil { logger.Error("failed to estimate gas", diff --git a/go/oasis-node/cmd/debug/byzantine/registry.go b/go/oasis-node/cmd/debug/byzantine/registry.go index 1bde8fa876e..86f711a89a6 100644 --- a/go/oasis-node/cmd/debug/byzantine/registry.go +++ b/go/oasis-node/cmd/debug/byzantine/registry.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/identity" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -42,10 +43,12 @@ func registryRegisterNode(svc service.TendermintService, id *identity.Identity, } nodeDesc := &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: id.NodeSigner.Public(), - EntityID: entityID, - Expiration: 1000, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: id.NodeSigner.Public(), + EntityAddress: entityID, + Expiration: 1000, TLS: node.TLSInfo{ PubKey: id.GetTLSSigner().Public(), Addresses: tlsAddresses, diff --git a/go/oasis-node/cmd/debug/consim/xfer_workload.go b/go/oasis-node/cmd/debug/consim/xfer_workload.go index deee88a22a2..310a4efbd57 100644 --- a/go/oasis-node/cmd/debug/consim/xfer_workload.go +++ b/go/oasis-node/cmd/debug/consim/xfer_workload.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/viper" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -42,6 +43,7 @@ type xferWorkload struct { type xferAccount struct { signer signature.Signer + account *multisig.Account address staking.Address nonce uint64 balance quantity.Quantity @@ -65,8 +67,8 @@ func (w *xferWorkload) Init(doc *genesis.Document) error { // Ensure the genesis doc has the debug test entity to be used to // fund the accounts. - testEntity, _, _ := entity.TestEntity() - testAccount := doc.Staking.Ledger[staking.NewAddress(testEntity.ID)] + testEntity, _, _, _ := entity.TestEntity() + testAccount := doc.Staking.Ledger[testEntity.AccountAddress] if testAccount == nil { return fmt.Errorf("consim/workload/xfer: test entity not present in genesis") } @@ -79,12 +81,12 @@ func (w *xferWorkload) Init(doc *genesis.Document) error { func (w *xferWorkload) Start(initialState *genesis.Document, cancelCh <-chan struct{}, errCh chan<- error) (<-chan []BlockTx, error) { // Initialize the funding account. - testEntity, testSigner, _ := entity.TestEntity() - testAccountAddr := staking.NewAddress(testEntity.ID) - testAccount := initialState.Staking.Ledger[testAccountAddr] + testEntity, testSigner, testEntityAccount, _ := entity.TestEntity() + testAccount := initialState.Staking.Ledger[testEntity.AccountAddress] w.fundingAccount = &xferAccount{ signer: testSigner, - address: testAccountAddr, + account: testEntityAccount, + address: testEntity.AccountAddress, nonce: testAccount.General.Nonce, balance: testAccount.General.Balance, } @@ -96,9 +98,11 @@ func (w *xferWorkload) Start(initialState *genesis.Document, cancelCh <-chan str return nil, fmt.Errorf("consim/workload/xfer: failed to create signer: %w", err) } + account := multisig.NewAccountFromPublicKey(accSigner.Public()) acc := &xferAccount{ signer: accSigner, - address: staking.NewAddress(accSigner.Public()), + account: account, + address: staking.NewAddress(account), } if lacc := initialState.Staking.Ledger[acc.address]; lacc != nil { acc.nonce = lacc.General.Nonce @@ -205,7 +209,7 @@ func xferGenTx(from, to *xferAccount, amount uint64) ([]byte, error) { var fee transaction.Fee tx := staking.NewTransferTx(from.nonce, &fee, xfer) - signedTx, err := transaction.Sign(from.signer, tx) + signedTx, err := transaction.SingleSign(from.signer, from.account, tx) if err != nil { return nil, err } diff --git a/go/oasis-node/cmd/debug/txsource/txsource.go b/go/oasis-node/cmd/debug/txsource/txsource.go index c4976e4a8b7..479e5616d8d 100644 --- a/go/oasis-node/cmd/debug/txsource/txsource.go +++ b/go/oasis-node/cmd/debug/txsource/txsource.go @@ -12,6 +12,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/crypto/drbg" "github.com/oasisprotocol/oasis-core/go/common/crypto/mathrand" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -22,6 +23,7 @@ import ( cmdFlags "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags" cmdGrpc "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/grpc" "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/debug/txsource/workload" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) const ( @@ -107,16 +109,17 @@ func doRun(cmd *cobra.Command, args []string) error { // NOTE: we don't use Test Entity account directly in the workloads // as using the same account in all runs would lead to a lot of // contention and nonce mismatches. - fundingAccount, err := memorySigner.NewFactory().Generate(signature.SignerEntity, rng) + fundingSigner, err := memorySigner.NewFactory().Generate(signature.SignerEntity, rng) if err != nil { return fmt.Errorf("memory signer factory generate funding account %w", err) } - if err = workload.FundAccountFromTestEntity(ctx, logger, cnsc, fundingAccount); err != nil { + fundingAccount := multisig.NewAccountFromPublicKey(fundingSigner.Public()) + if err = workload.FundAccountFromTestEntity(ctx, logger, cnsc, staking.NewAddress(fundingAccount)); err != nil { return fmt.Errorf("test entity account funding failure: %w", err) } logger.Debug("entering workload", "name", name) - if err = w.Run(ctx, rng, conn, cnsc, fundingAccount); err != nil { + if err = w.Run(ctx, rng, conn, cnsc, fundingAccount, fundingSigner); err != nil { logger.Error("workload error", "err", err) return fmt.Errorf("workload %s: %w", name, err) } diff --git a/go/oasis-node/cmd/debug/txsource/workload/commission.go b/go/oasis-node/cmd/debug/txsource/workload/commission.go index 66f55bf64a1..0abcaf740c8 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/commission.go +++ b/go/oasis-node/cmd/debug/txsource/workload/commission.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -33,9 +34,11 @@ type commission struct { rules staking.CommissionScheduleRules signer signature.Signer + account *multisig.Account address staking.Address reckonedNonce uint64 - fundingAccount signature.Signer + fundingAccount *multisig.Account + fundingSigner signature.Signer } // currentBound returns the rate bounds at the latest bound step that has @@ -318,7 +321,7 @@ func (c *commission) doAmendCommissionSchedule(ctx context.Context, rng *rand.Ra // Estimate gas. gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ - Signer: c.signer.Public(), + Account: *c.account, Transaction: tx, }) if err != nil { @@ -332,12 +335,12 @@ func (c *commission) doAmendCommissionSchedule(ctx context.Context, rng *rand.Ra // Fund account to cover AmendCommissionSchedule transaction fees. fundAmount := int64(gas) * gasPrice // transaction costs - if err = transferFunds(ctx, c.logger, cnsc, c.fundingAccount, c.address, fundAmount); err != nil { + if err = transferFunds(ctx, c.logger, cnsc, c.fundingAccount, c.fundingSigner, c.address, fundAmount); err != nil { return fmt.Errorf("account funding failure: %w", err) } // Sign transaction. - signedTx, err := transaction.Sign(c.signer, tx) + signedTx, err := transaction.SingleSign(c.signer, c.account, tx) if err != nil { return fmt.Errorf("transaction.Sign: %w", err) } @@ -357,19 +360,29 @@ func (c *commission) doAmendCommissionSchedule(ctx context.Context, rng *rand.Ra return nil } -func (c *commission) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, fundingAccount signature.Signer) error { +func (c *commission) Run( + gracefulExit context.Context, + rng *rand.Rand, + conn *grpc.ClientConn, + cnsc consensus.ClientBackend, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, +) error { var err error + ctx := context.Background() c.logger = logging.GetLogger("cmd/txsource/workload/commission") c.fundingAccount = fundingAccount + c.fundingSigner = fundingSigner fac := memorySigner.NewFactory() c.signer, err = fac.Generate(signature.SignerEntity, rng) if err != nil { return fmt.Errorf("memory signer factory Generate account: %w", err) } - c.address = staking.NewAddress(c.signer.Public()) + c.account = multisig.NewAccountFromPublicKey(c.signer.Public()) + c.address = staking.NewAddress(c.account) stakingClient := staking.NewStakingClient(conn) diff --git a/go/oasis-node/cmd/debug/txsource/workload/delegation.go b/go/oasis-node/cmd/debug/txsource/workload/delegation.go index e97910c5ce3..2a29f54a172 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/delegation.go +++ b/go/oasis-node/cmd/debug/txsource/workload/delegation.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -31,10 +32,12 @@ type delegation struct { signer signature.Signer reckonedNonce uint64 debondEndTime uint64 + account *multisig.Account address staking.Address delegatedTo staking.Address } - fundingAccount signature.Signer + fundingAccount *multisig.Account + fundingSigner signature.Signer } func (d *delegation) doEscrowTx(ctx context.Context, rng *rand.Rand, cnsc consensus.ClientBackend) error { @@ -82,7 +85,7 @@ func (d *delegation) doEscrowTx(ctx context.Context, rng *rand.Rand, cnsc consen d.accounts[selectedIdx].reckonedNonce++ // We only do one escrow per account at a time, so `delegateAmount` // funds (that are Escrowed) should already be in the balance. - if err := fundSignAndSubmitTx(ctx, d.logger, cnsc, d.accounts[selectedIdx].signer, tx, d.fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, d.logger, cnsc, d.accounts[selectedIdx].account, d.accounts[selectedIdx].signer, tx, d.fundingAccount, d.fundingSigner); err != nil { d.logger.Error("failed to sign and submit escrow transaction", "tx", tx, "signer", d.accounts[selectedIdx].signer.Public(), @@ -138,7 +141,7 @@ func (d *delegation) doReclaimEscrowTx(ctx context.Context, rng *rand.Rand, cnsc } tx := staking.NewReclaimEscrowTx(d.accounts[selectedIdx].reckonedNonce, &transaction.Fee{}, reclaim) d.accounts[selectedIdx].reckonedNonce++ - if err = fundSignAndSubmitTx(ctx, d.logger, cnsc, d.accounts[selectedIdx].signer, tx, d.fundingAccount); err != nil { + if err = fundSignAndSubmitTx(ctx, d.logger, cnsc, d.accounts[selectedIdx].account, d.accounts[selectedIdx].signer, tx, d.fundingAccount, d.fundingSigner); err != nil { d.logger.Error("failed to sign and submit reclaim escrow transaction", "tx", tx, "signer", d.accounts[selectedIdx].signer.Public(), @@ -178,18 +181,21 @@ func (d *delegation) Run( rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { ctx := context.Background() d.logger = logging.GetLogger("cmd/txsource/workload/delegation") d.fundingAccount = fundingAccount + d.fundingSigner = fundingSigner fac := memorySigner.NewFactory() d.accounts = make([]struct { signer signature.Signer reckonedNonce uint64 debondEndTime uint64 + account *multisig.Account address staking.Address delegatedTo staking.Address }, delegationNumAccounts) @@ -200,11 +206,12 @@ func (d *delegation) Run( return fmt.Errorf("memory signer factory Generate account %d: %w", i, err) } d.accounts[i].signer = signer - d.accounts[i].address = staking.NewAddress(signer.Public()) + d.accounts[i].account = multisig.NewAccountFromPublicKey(signer.Public()) + d.accounts[i].address = staking.NewAddress(d.accounts[i].account) // Fund the account with delegation amount. // Funds for fees will be transferred before making transactions. - if err = transferFunds(ctx, d.logger, cnsc, fundingAccount, d.accounts[i].address, delegateAmount); err != nil { + if err = transferFunds(ctx, d.logger, cnsc, d.fundingAccount, d.fundingSigner, d.accounts[i].address, delegateAmount); err != nil { return fmt.Errorf("account funding failure: %w", err) } } diff --git a/go/oasis-node/cmd/debug/txsource/workload/oversized.go b/go/oasis-node/cmd/debug/txsource/workload/oversized.go index 31dc9ceeffe..19341799c7f 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/oversized.go +++ b/go/oasis-node/cmd/debug/txsource/workload/oversized.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -33,14 +34,16 @@ func (oversized) Run( rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { txSignerFactory := memorySigner.NewFactory() txSigner, err := txSignerFactory.Generate(signature.SignerEntity, rng) if err != nil { return fmt.Errorf("failed to generate signer key: %w", err) } - txSignerAddr := staking.NewAddress(txSigner.Public()) + txSignerAccount := multisig.NewAccountFromPublicKey(txSigner.Public()) + txSignerAddr := staking.NewAddress(txSignerAccount) ctx := context.Background() @@ -81,6 +84,7 @@ func (oversized) Run( oversizedLogger, cnsc, fundingAccount, + fundingSigner, txSignerAddr, int64(oversizedTxGasAmount*gasPrice), ); err != nil { @@ -88,7 +92,7 @@ func (oversized) Run( } tx := transaction.NewTransaction(nonce, &fee, staking.MethodTransfer, &xfer) - signedTx, err := transaction.Sign(txSigner, tx) + signedTx, err := transaction.SingleSign(txSigner, txSignerAccount, tx) if err != nil { return fmt.Errorf("transaction.Sign: %w", err) } diff --git a/go/oasis-node/cmd/debug/txsource/workload/parallel.go b/go/oasis-node/cmd/debug/txsource/workload/parallel.go index 82dbd808a53..8e4a24a8993 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/parallel.go +++ b/go/oasis-node/cmd/debug/txsource/workload/parallel.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -37,7 +38,8 @@ func (parallel) Run( rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { ctx := context.Background() var err error @@ -45,32 +47,36 @@ func (parallel) Run( // Estimate gas needed for the used transfer transaction. var txGasAmount transaction.Gas xfer := &staking.Transfer{ - To: staking.NewAddress(fundingAccount.Public()), + To: staking.NewAddress(fundingAccount), } if err = xfer.Amount.FromInt64(parallelTxTransferAmount); err != nil { return fmt.Errorf("transfer base units FromInt64 %d: %w", parallelTxTransferAmount, err) } txGasAmount, err = cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ - Signer: fundingAccount.Public(), + Account: *fundingAccount, Transaction: staking.NewTransferTx(0, nil, xfer), }) if err != nil { return fmt.Errorf("failed to estimate gas: %w", err) } - accounts := make([]signature.Signer, parallelConcurency) + accounts := make([]struct { + account *multisig.Account + signer signature.Signer + }, parallelConcurency) fac := memorySigner.NewFactory() for i := range accounts { - accounts[i], err = fac.Generate(signature.SignerEntity, rng) + accounts[i].signer, err = fac.Generate(signature.SignerEntity, rng) if err != nil { return fmt.Errorf("memory signer factory Generate account %d: %w", i, err) } + accounts[i].account = multisig.NewAccountFromPublicKey(accounts[i].signer.Public()) // Initial funding of accounts. fundAmount := parallelTxTransferAmount + // self transfer amount parallelTxFundInterval*txGasAmount*gasPrice // gas for `parallelTxFundInterval` transfers. - addr := staking.NewAddress(accounts[i].Public()) - if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, addr, int64(fundAmount)); err != nil { + addr := staking.NewAddress(accounts[i].account) + if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, fundingSigner, addr, int64(fundAmount)); err != nil { return fmt.Errorf("account funding failure: %w", err) } } @@ -91,10 +97,10 @@ func (parallel) Run( var wg sync.WaitGroup wg.Add(parallelConcurency) for c := 0; c < parallelConcurency; c++ { - go func(txSigner signature.Signer, nonce uint64) { + go func(txAccount *multisig.Account, txSigner signature.Signer, nonce uint64) { defer wg.Done() - addr := staking.NewAddress(txSigner.Public()) + addr := staking.NewAddress(txAccount) // Transfer tx. transfer := staking.Transfer{ @@ -107,7 +113,7 @@ func (parallel) Run( tx := staking.NewTransferTx(nonce, &fee, &transfer) var signedTx *transaction.SignedTransaction - signedTx, err = transaction.Sign(txSigner, tx) + signedTx, err = transaction.SingleSign(txSigner, txAccount, tx) if err != nil { parallelLogger.Error("transaction.Sign error", "err", err) errCh <- fmt.Errorf("transaction.Sign: %w", err) @@ -122,7 +128,7 @@ func (parallel) Run( errCh <- fmt.Errorf("cnsc.SubmitTx: %w", err) return } - }(accounts[c], nonce) + }(accounts[c].account, accounts[c].signer, nonce) } // Wait for transactions. @@ -154,8 +160,8 @@ func (parallel) Run( // Re-fund accounts for next `parallelTxFundInterval` transfers. for i := range accounts { fundAmount := parallelTxFundInterval * txGasAmount * gasPrice // gas for `parallelTxFundInterval` transfers. - addr := staking.NewAddress(accounts[i].Public()) - if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, addr, int64(fundAmount)); err != nil { + addr := staking.NewAddress(accounts[i].account) + if err = transferFunds(ctx, parallelLogger, cnsc, fundingAccount, fundingSigner, addr, int64(fundAmount)); err != nil { return fmt.Errorf("account funding failure: %w", err) } } diff --git a/go/oasis-node/cmd/debug/txsource/workload/queries.go b/go/oasis-node/cmd/debug/txsource/workload/queries.go index 117ab263b2a..5997cd59018 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/queries.go +++ b/go/oasis-node/cmd/debug/txsource/workload/queries.go @@ -14,6 +14,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -345,11 +346,11 @@ func (q *queries) doRegistryQueries(ctx context.Context, rng *rand.Rand, height // Query each entity individually. for _, ent := range ents { var entity *entity.Entity - entity, err = q.registry.GetEntity(ctx, ®istry.IDQuery{ID: ent.ID, Height: height}) + entity, err = q.registry.GetEntity(ctx, ®istry.AccountQuery{ID: ent.AccountAddress, Height: height}) if err != nil { return fmt.Errorf("GetEntity error at height %d: %w", height, err) } - if !ent.ID.Equal(entity.ID) { + if !ent.AccountAddress.Equal(entity.AccountAddress) { return fmt.Errorf("GetEntity mismatch, expected: %s, got: %s", ent, entity) } } @@ -693,7 +694,14 @@ func (q *queries) doQueries(ctx context.Context, rng *rand.Rand) error { return nil } -func (q *queries) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, fundingAccount signature.Signer) error { +func (q *queries) Run( + gracefulExit context.Context, + rng *rand.Rand, + conn *grpc.ClientConn, + cnsc consensus.ClientBackend, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, +) error { ctx := context.Background() q.logger = logging.GetLogger("cmd/txsource/workload/queries") diff --git a/go/oasis-node/cmd/debug/txsource/workload/registration.go b/go/oasis-node/cmd/debug/txsource/workload/registration.go index f7e1e50d436..b58e747bb2b 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/registration.go +++ b/go/oasis-node/cmd/debug/txsource/workload/registration.go @@ -13,6 +13,8 @@ import ( "google.golang.org/grpc" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -41,12 +43,14 @@ type registration struct { ns common.Namespace } -func getRuntime(entityID signature.PublicKey, id common.Namespace) *registry.Runtime { +func getRuntime(entityAddress staking.Address, id common.Namespace) *registry.Runtime { rt := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: id, - EntityID: entityID, - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: id, + EntityAddress: entityAddress, + Kind: registry.KindCompute, Executor: registry.ExecutorParameters{ GroupSize: 1, RoundTimeout: 1 * time.Second, @@ -78,7 +82,7 @@ func getRuntime(entityID signature.PublicKey, id common.Namespace) *registry.Run return rt } -func getNodeDesc(rng *rand.Rand, nodeIdentity *identity.Identity, entityID signature.PublicKey, runtimeID common.Namespace) *node.Node { +func getNodeDesc(rng *rand.Rand, nodeIdentity *identity.Identity, entityAddress staking.Address, runtimeID common.Namespace) *node.Node { nodeAddr := node.Address{ TCPAddr: net.TCPAddr{ IP: net.IPv4(127, 0, 0, 1), @@ -97,11 +101,13 @@ func getNodeDesc(rng *rand.Rand, nodeIdentity *identity.Identity, entityID signa } nodeDesc := node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nodeIdentity.NodeSigner.Public(), - EntityID: entityID, - Expiration: 0, - Roles: availableRoles[rng.Intn(len(availableRoles))], + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nodeIdentity.NodeSigner.Public(), + EntityAddress: entityAddress, + Expiration: 0, + Roles: availableRoles[rng.Intn(len(availableRoles))], TLS: node.TLSInfo{ PubKey: nodeIdentity.GetTLSSigner().Public(), Addresses: []node.TLSAddress{ @@ -159,7 +165,8 @@ func (r *registration) Run( // nolint: gocyclo rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { ctx := context.Background() var err error @@ -178,11 +185,13 @@ func (r *registration) Run( // nolint: gocyclo // Load all accounts. type nodeAcc struct { id *identity.Identity + nodeAccount *multisig.Account nodeDesc *node.Node reckonedNonce uint64 } entityAccs := make([]struct { signer signature.Signer + account *multisig.Account address staking.Address reckonedNonce uint64 nodeIdentities []*nodeAcc @@ -195,7 +204,8 @@ func (r *registration) Run( // nolint: gocyclo return fmt.Errorf("memory signer factory Generate account %d: %w", i, err2) } entityAccs[i].signer = signer - entityAccs[i].address = staking.NewAddress(signer.Public()) + entityAccs[i].account = multisig.NewAccountFromPublicKey(signer.Public()) + entityAccs[i].address = staking.NewAddress(entityAccs[i].account) } // Register entities. @@ -211,8 +221,10 @@ func (r *registration) Run( // nolint: gocyclo } ent := &entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, - ID: entityAccs[i].signer.Public(), + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, + AccountAddress: entityAccs[i].address, } // Generate entity node identities. @@ -225,10 +237,11 @@ func (r *registration) Run( // nolint: gocyclo if err != nil { return fmt.Errorf("failed generating account node identity: %w", err) } - nodeDesc := getNodeDesc(rng, ident, entityAccs[i].signer.Public(), r.ns) + nodeDesc := getNodeDesc(rng, ident, entityAccs[i].address, r.ns) var nodeAccNonce uint64 - nodeAccAddress := staking.NewAddress(ident.NodeSigner.Public()) + nodeAcco := multisig.NewAccountFromPublicKey(ident.NodeSigner.Public()) + nodeAccAddress := staking.NewAddress(nodeAcco) nodeAccNonce, err = cnsc.GetSignerNonce(ctx, &consensus.GetSignerNonceRequest{ AccountAddress: nodeAccAddress, Height: consensus.HeightLatest, @@ -237,12 +250,20 @@ func (r *registration) Run( // nolint: gocyclo return fmt.Errorf("GetSignerNonce error for accout %s: %w", nodeAccAddress, err) } - entityAccs[i].nodeIdentities = append(entityAccs[i].nodeIdentities, &nodeAcc{ident, nodeDesc, nodeAccNonce}) + entityAccs[i].nodeIdentities = append( + entityAccs[i].nodeIdentities, + &nodeAcc{ + ident, + nodeAcco, + nodeDesc, + nodeAccNonce, + }, + ) ent.Nodes = append(ent.Nodes, ident.NodeSigner.Public()) } // Register entity. - sigEntity, err := entity.SignEntity(entityAccs[i].signer, registry.RegisterEntitySignatureContext, ent) + sigEntity, err := entity.SingleSignEntity(entityAccs[i].signer, entityAccs[i].account, registry.RegisterEntitySignatureContext, ent) if err != nil { return fmt.Errorf("failed to sign entity: %w", err) } @@ -250,7 +271,7 @@ func (r *registration) Run( // nolint: gocyclo // Estimate gas and submit transaction. tx := registry.NewRegisterEntityTx(entityAccs[i].reckonedNonce, &transaction.Fee{}, sigEntity) entityAccs[i].reckonedNonce++ - if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, entityAccs[i].signer, tx, fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, entityAccs[i].account, entityAccs[i].signer, tx, fundingAccount, fundingSigner); err != nil { registrationLogger.Error("failed to sign and submit regsiter entity transaction", "tx", tx, "signer", entityAccs[i].signer, @@ -262,15 +283,15 @@ func (r *registration) Run( // nolint: gocyclo // XXX: currently only a single runtime is registered at start. Could // also periodically register new runtimes. if i == 0 { - runtimeDesc := getRuntime(entityAccs[i].signer.Public(), r.ns) - sigRuntime, err := registry.SignRuntime(entityAccs[i].signer, registry.RegisterRuntimeSignatureContext, runtimeDesc) + runtimeDesc := getRuntime(entityAccs[i].address, r.ns) + sigRuntime, err := registry.SingleSignRuntime(entityAccs[i].signer, entityAccs[i].account, registry.RegisterRuntimeSignatureContext, runtimeDesc) if err != nil { return fmt.Errorf("failed to sign entity: %w", err) } tx := registry.NewRegisterRuntimeTx(entityAccs[i].reckonedNonce, &transaction.Fee{}, sigRuntime) entityAccs[i].reckonedNonce++ - if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, entityAccs[i].signer, tx, fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, entityAccs[i].account, entityAccs[i].signer, tx, fundingAccount, fundingSigner); err != nil { registrationLogger.Error("failed to sign and submit register runtime transaction", "tx", tx, "signer", entityAccs[i].signer, @@ -303,7 +324,7 @@ func (r *registration) Run( // nolint: gocyclo // Register node. tx := registry.NewRegisterNodeTx(selectedNode.reckonedNonce, &transaction.Fee{}, sigNode) selectedNode.reckonedNonce++ - if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, selectedNode.id.NodeSigner, tx, fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, registrationLogger, cnsc, selectedNode.nodeAccount, selectedNode.id.NodeSigner, tx, fundingAccount, fundingSigner); err != nil { registrationLogger.Error("failed to sign and submit register node transaction", "tx", tx, "signer", selectedNode.id.NodeSigner, diff --git a/go/oasis-node/cmd/debug/txsource/workload/runtime.go b/go/oasis-node/cmd/debug/txsource/workload/runtime.go index 6db3b884841..485467aa6cf 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/runtime.go +++ b/go/oasis-node/cmd/debug/txsource/workload/runtime.go @@ -12,6 +12,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/logging" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" @@ -261,7 +262,8 @@ func (r *runtime) Run( rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { ctx := context.Background() diff --git a/go/oasis-node/cmd/debug/txsource/workload/transfer.go b/go/oasis-node/cmd/debug/txsource/workload/transfer.go index 1cacc4bada9..ae8bafd4f41 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/transfer.go +++ b/go/oasis-node/cmd/debug/txsource/workload/transfer.go @@ -7,6 +7,7 @@ import ( "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -34,11 +35,13 @@ type transfer struct { accounts []struct { signer signature.Signer + account *multisig.Account address staking.Address reckonedNonce uint64 reckonedBalance quantity.Quantity } - fundingAccount signature.Signer + fundingAccount *multisig.Account + fundingSigner signature.Signer } func (t *transfer) doTransferTx(ctx context.Context, fromIdx, toIdx int) error { @@ -57,7 +60,7 @@ func (t *transfer) doTransferTx(ctx context.Context, fromIdx, toIdx int) error { "to", to.address, "base_units", transferAmount, ) - if err := fundSignAndSubmitTx(ctx, t.logger, t.consensus, from.signer, tx, t.fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, t.logger, t.consensus, from.account, from.signer, tx, t.fundingAccount, t.fundingSigner); err != nil { t.logger.Error("failed to sign and submit transfer transaction", "tx", tx, "signer", from.signer.Public(), @@ -84,7 +87,7 @@ func (t *transfer) doBurnTx(ctx context.Context, idx int) error { acc := &t.accounts[idx] // Fund account with stake that will be burned. - if err := transferFunds(ctx, t.logger, t.consensus, t.fundingAccount, acc.address, int64(transferBurnAmount)); err != nil { + if err := transferFunds(ctx, t.logger, t.consensus, t.fundingAccount, t.fundingSigner, acc.address, int64(transferBurnAmount)); err != nil { return fmt.Errorf("workload/transfer: account funding failure: %w", err) } @@ -99,7 +102,7 @@ func (t *transfer) doBurnTx(ctx context.Context, idx int) error { "account", acc.address, "base_units", transferBurnAmount, ) - if err := fundSignAndSubmitTx(ctx, t.logger, t.consensus, acc.signer, tx, t.fundingAccount); err != nil { + if err := fundSignAndSubmitTx(ctx, t.logger, t.consensus, acc.account, acc.signer, tx, t.fundingAccount, t.fundingSigner); err != nil { t.logger.Error("failed to sign and submit transfer transaction", "tx", tx, "signer", acc.signer.Public(), @@ -115,7 +118,8 @@ func (t *transfer) Run( rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { ctx := context.Background() @@ -123,11 +127,13 @@ func (t *transfer) Run( t.consensus = cnsc t.accounts = make([]struct { signer signature.Signer + account *multisig.Account address staking.Address reckonedNonce uint64 reckonedBalance quantity.Quantity }, transferNumAccounts) t.fundingAccount = fundingAccount + t.fundingSigner = fundingSigner fac := memorySigner.NewFactory() // Load all the keys up front. Like, how annoyed would you be if down the line one of them turned out to be @@ -138,14 +144,15 @@ func (t *transfer) Run( return fmt.Errorf("memory signer factory Generate account %d: %w", i, err) } t.accounts[i].signer = signer - t.accounts[i].address = staking.NewAddress(signer.Public()) + t.accounts[i].account = multisig.NewAccountFromPublicKey(signer.Public()) + t.accounts[i].address = staking.NewAddress(t.accounts[i].account) } // Read all the account info up front. stakingClient := staking.NewStakingClient(conn) for i := range t.accounts { fundAmount := transferAmount // funds for for a transfer - if err := transferFunds(ctx, t.logger, cnsc, t.fundingAccount, t.accounts[i].address, int64(fundAmount)); err != nil { + if err := transferFunds(ctx, t.logger, cnsc, t.fundingAccount, t.fundingSigner, t.accounts[i].address, int64(fundAmount)); err != nil { return fmt.Errorf("workload/transfer: account funding failure: %w", err) } var account *staking.Account diff --git a/go/oasis-node/cmd/debug/txsource/workload/workload.go b/go/oasis-node/cmd/debug/txsource/workload/workload.go index e073a87d063..4c7b70aca3b 100644 --- a/go/oasis-node/cmd/debug/txsource/workload/workload.go +++ b/go/oasis-node/cmd/debug/txsource/workload/workload.go @@ -10,6 +10,7 @@ import ( flag "github.com/spf13/pflag" "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -32,25 +33,26 @@ func FundAccountFromTestEntity( ctx context.Context, logger *logging.Logger, cnsc consensus.ClientBackend, - to signature.Signer, + to staking.Address, ) error { - _, testEntitySigner, _ := entity.TestEntity() - toAddr := staking.NewAddress(to.Public()) - return transferFunds(ctx, logger, cnsc, testEntitySigner, toAddr, fundAccountAmount) + _, testEntitySigner, testEntityAccount, _ := entity.TestEntity() + return transferFunds(ctx, logger, cnsc, testEntityAccount, testEntitySigner, to, fundAccountAmount) } func fundSignAndSubmitTx( ctx context.Context, logger *logging.Logger, cnsc consensus.ClientBackend, - caller signature.Signer, + caller *multisig.Account, + callerSigner signature.Signer, tx *transaction.Transaction, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error { // Estimate gas needed if not set. if tx.Fee.Gas == 0 { gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ - Signer: caller.Public(), + Account: *caller, Transaction: tx, }) if err != nil { @@ -64,13 +66,13 @@ func fundSignAndSubmitTx( if err := tx.Fee.Amount.FromInt64(feeAmount); err != nil { return fmt.Errorf("fee amount from int64: %w", err) } - callerAddr := staking.NewAddress(caller.Public()) - if err := transferFunds(ctx, logger, cnsc, fundingAccount, callerAddr, feeAmount); err != nil { + callerAddr := staking.NewAddress(caller) + if err := transferFunds(ctx, logger, cnsc, fundingAccount, fundingSigner, callerAddr, feeAmount); err != nil { return fmt.Errorf("account funding failure: %w", err) } // Sign tx. - signedTx, err := transaction.Sign(caller, tx) + signedTx, err := transaction.SingleSign(callerSigner, caller, tx) if err != nil { return fmt.Errorf("transaction.Sign: %w", err) } @@ -78,7 +80,7 @@ func fundSignAndSubmitTx( logger.Debug("submitting transaction", "tx", tx, "signed_tx", signedTx, - "tx_caller", caller.Public(), + "tx_caller", caller, ) // SubmitTx. @@ -95,7 +97,7 @@ func fundSignAndSubmitTx( "err", err, "tx", tx, "signed_tx", signedTx, - "tx_caller", caller.Public(), + "tx_caller", caller, ) return fmt.Errorf("cnsc.SubmitTx: %w", err) } @@ -106,7 +108,8 @@ func transferFunds( ctx context.Context, logger *logging.Logger, cnsc consensus.ClientBackend, - from signature.Signer, + from *multisig.Account, + fromSigner signature.Signer, to staking.Address, transferAmount int64, ) error { @@ -120,7 +123,7 @@ func transferFunds( // Maybe just expose the SignAndSubmit() method in the // consensus.ClientBackend? return backoff.Retry(func() error { - fromAddr := staking.NewAddress(from.Public()) + fromAddr := staking.NewAddress(from) // Get test entity nonce. nonce, err := cnsc.GetSignerNonce(ctx, &consensus.GetSignerNonceRequest{ AccountAddress: fromAddr, @@ -136,13 +139,13 @@ func transferFunds( if err = transfer.Amount.FromInt64(transferAmount); err != nil { return backoff.Permanent(fmt.Errorf("transfer base units FromInt64 %d: %w", transferAmount, err)) } - logger.Debug("transfering funds", "from", from.Public(), "to", to, "amount", transferAmount, "nonce", nonce) + logger.Debug("transfering funds", "from", fromAddr, "to", to, "amount", transferAmount, "nonce", nonce) var fee transaction.Fee tx := staking.NewTransferTx(nonce, &fee, &transfer) // Estimate fee. gas, err := cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{ - Signer: from.Public(), + Account: *from, Transaction: tx, }) if err != nil { @@ -154,7 +157,7 @@ func transferFunds( return fmt.Errorf("fee amount from int64: %w", err) } - signedTx, err := transaction.Sign(from, tx) + signedTx, err := transaction.SingleSign(fromSigner, from, tx) if err != nil { return backoff.Permanent(fmt.Errorf("transaction.Sign: %w", err)) } @@ -170,7 +173,7 @@ func transferFunds( // In any case no it doesn't hurt to retry on all submission errors. logger.Debug("SubmitTX error, retrying...", "err", err, - "from", from.Public(), + "from", fromAddr, "to", to, "nonce", tx.Nonce, ) @@ -191,7 +194,8 @@ type Workload interface { rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, - fundingAccount signature.Signer, + fundingAccount *multisig.Account, + fundingSigner signature.Signer, ) error } diff --git a/go/oasis-node/cmd/genesis/genesis.go b/go/oasis-node/cmd/genesis/genesis.go index 321edea2b68..0b7c7a1f2a8 100644 --- a/go/oasis-node/cmd/genesis/genesis.go +++ b/go/oasis-node/cmd/genesis/genesis.go @@ -294,12 +294,12 @@ func AppendRegistryState(doc *genesis.Document, entities, runtimes, nodes []stri Nodes: make([]*node.MultiSignedNode, 0, len(nodes)), } - entMap := make(map[signature.PublicKey]bool) + entMap := make(map[staking.Address]bool) appendToEntities := func(signedEntity *entity.SignedEntity, ent *entity.Entity) error { - if entMap[ent.ID] { + if entMap[ent.AccountAddress] { return errors.New("genesis: duplicate entity registration") } - entMap[ent.ID] = true + entMap[ent.AccountAddress] = true regSt.Entities = append(regSt.Entities, signedEntity) @@ -345,15 +345,8 @@ func AppendRegistryState(doc *genesis.Document, entities, runtimes, nodes []stri if flags.DebugTestEntity() { l.Warn("registering debug test entity") - ent, signer, err := entity.TestEntity() - if err != nil { - l.Error("failed to retrive test entity", - "err", err, - ) - return err - } - - signedEntity, err := entity.SignEntity(signer, registry.RegisterGenesisEntitySignatureContext, ent) + ent, signer, account, _ := entity.TestEntity() + signedEntity, err := entity.SingleSignEntity(signer, account, registry.RegisterGenesisEntitySignatureContext, ent) if err != nil { l.Error("failed to sign test entity", "err", err, @@ -557,14 +550,7 @@ func AppendStakingState(doc *genesis.Document, state string, l *logging.Logger) if flags.DebugTestEntity() { l.Warn("granting stake to the debug test entity") - ent, _, err := entity.TestEntity() - if err != nil { - l.Error("failed to retrieve test entity", - "err", err, - ) - return err - } - entAddr := staking.NewAddress(ent.ID) + ent, _, _, _ := entity.TestEntity() // Ok then, we hold the world ransom for One Hundred Billion Dollars. var q quantity.Quantity @@ -575,7 +561,7 @@ func AppendStakingState(doc *genesis.Document, state string, l *logging.Logger) return err } - stakingSt.Ledger[entAddr] = &staking.Account{ + stakingSt.Ledger[ent.AccountAddress] = &staking.Account{ General: staking.GeneralAccount{ Balance: q, Nonce: 0, @@ -588,8 +574,8 @@ func AppendStakingState(doc *genesis.Document, state string, l *logging.Logger) }, } stakingSt.Delegations = map[staking.Address]map[staking.Address]*staking.Delegation{ - entAddr: { - entAddr: { + ent.AccountAddress: { + ent.AccountAddress: { Shares: stakingTests.QtyFromInt(1), }, }, diff --git a/go/oasis-node/cmd/registry/entity/entity.go b/go/oasis-node/cmd/registry/entity/entity.go index 7148624ba79..3e42d4160b5 100644 --- a/go/oasis-node/cmd/registry/entity/entity.go +++ b/go/oasis-node/cmd/registry/entity/entity.go @@ -14,6 +14,8 @@ import ( "github.com/spf13/viper" "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -108,7 +110,7 @@ func doInit(cmd *cobra.Command, args []string) { // Loosely check to see if there is an existing entity. This isn't // perfect, just "oopsie" avoidance. - if _, _, err = loadOrGenerateEntity(dataDir, false); err == nil { + if _, _, _, err = loadOrGenerateEntity(dataDir, false); err == nil { switch cmdFlags.Force() { case true: logger.Warn("overwriting existing entity") @@ -119,7 +121,7 @@ func doInit(cmd *cobra.Command, args []string) { } // Generate a new entity. - ent, signer, err := loadOrGenerateEntity(dataDir, true) + ent, signer, account, err := loadOrGenerateEntity(dataDir, true) if err != nil { logger.Error("failed to generate entity", "err", err, @@ -127,12 +129,12 @@ func doInit(cmd *cobra.Command, args []string) { os.Exit(1) } - if err = signAndWriteEntityGenesis(dataDir, signer, ent); err != nil { + if err = signAndWriteEntityGenesis(dataDir, signer, account, ent); err != nil { os.Exit(1) } logger.Info("generated entity", - "entity", ent.ID, + "entity", ent.AccountAddress, ) } @@ -150,7 +152,7 @@ func doUpdate(cmd *cobra.Command, args []string) { } // Load the existing entity. - ent, signer, err := loadOrGenerateEntity(dataDir, false) + ent, signer, account, err := loadOrGenerateEntity(dataDir, false) if err != nil { logger.Error("failed to load entity", "err", err, @@ -203,10 +205,10 @@ func doUpdate(cmd *cobra.Command, args []string) { os.Exit(1) } - if !ent.ID.Equal(n.EntityID) { + if !ent.AccountAddress.Equal(n.EntityAddress) { logger.Error("entity ID mismatch, node does not belong to this entity", - "entity_id", ent.ID, - "node_entity_id", n.EntityID, + "entity_address", ent.AccountAddress, + "node_entity_address", n.EntityAddress, ) os.Exit(1) } @@ -238,18 +240,18 @@ func doUpdate(cmd *cobra.Command, args []string) { } // Regenerate the genesis document entity registration. - if err = signAndWriteEntityGenesis(dataDir, signer, ent); err != nil { + if err = signAndWriteEntityGenesis(dataDir, signer, account, ent); err != nil { os.Exit(1) } logger.Info("updated entity", - "entity", ent.ID, + "entity", ent.AccountAddress, ) } -func signAndWriteEntityGenesis(dataDir string, signer signature.Signer, ent *entity.Entity) error { +func signAndWriteEntityGenesis(dataDir string, signer signature.Signer, account *multisig.Account, ent *entity.Entity) error { // Sign the entity registration for use in a genesis document. - signed, err := entity.SignEntity(signer, registry.RegisterGenesisEntitySignatureContext, ent) + signed, err := entity.SingleSignEntity(signer, account, registry.RegisterGenesisEntitySignatureContext, ent) if err != nil { logger.Error("failed to sign entity for genesis registration", "err", err, @@ -285,7 +287,7 @@ func doGenRegister(cmd *cobra.Command, args []string) { os.Exit(1) } - ent, signer, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) + ent, signer, account, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) if err != nil { logger.Error("failed to load entity", "err", err, @@ -294,7 +296,7 @@ func doGenRegister(cmd *cobra.Command, args []string) { } defer signer.Reset() - signed, err := entity.SignEntity(signer, registry.RegisterEntitySignatureContext, ent) + signed, err := entity.SingleSignEntity(signer, account, registry.RegisterEntitySignatureContext, ent) if err != nil { logger.Error("failed to sign entity descriptor", "err", err, @@ -345,20 +347,20 @@ func doList(cmd *cobra.Command, args []string) { b, _ := json.Marshal(ent) s = string(b) default: - s = ent.ID.String() + s = ent.AccountAddress.String() } fmt.Printf("%v\n", s) } } -func loadOrGenerateEntity(dataDir string, generate bool) (*entity.Entity, signature.Signer, error) { +func loadOrGenerateEntity(dataDir string, generate bool) (*entity.Entity, signature.Signer, *multisig.Account, error) { if cmdFlags.DebugTestEntity() { return entity.TestEntity() } if viper.GetBool(cfgAllowEntitySignedNodes) && !cmdFlags.DebugDontBlameOasis() { - return nil, nil, fmt.Errorf("loadOrGenerateEntity: sanity check failed: one or more unsafe debug flags set") + return nil, nil, nil, fmt.Errorf("loadOrGenerateEntity: sanity check failed: one or more unsafe debug flags set") } entityDir, err := cmdSigner.CLIDirOrPwd() @@ -370,22 +372,24 @@ func loadOrGenerateEntity(dataDir string, generate bool) (*entity.Entity, signat } entitySignerFactory, err := cmdSigner.NewFactory(cmdSigner.Backend(), entityDir, signature.SignerEntity) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if generate { template := &entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, AllowEntitySignedNodes: viper.GetBool(cfgAllowEntitySignedNodes), } if viper.GetBool(CfgReuseSigner) { signer, err := entitySignerFactory.Load(signature.SignerEntity) if err != nil { - return nil, nil, fmt.Errorf("loadOrGenerateEntity: failed to load existing signer: %w", err) + return nil, nil, nil, fmt.Errorf("loadOrGenerateEntity: failed to load existing signer: %w", err) } - ent, err := entity.GenerateWithSigner(dataDir, signer, template) - return ent, signer, err + ent, account, err := entity.GenerateWithSigner(dataDir, signer, template) + return ent, signer, account, err } return entity.Generate(dataDir, entitySignerFactory, template) } diff --git a/go/oasis-node/cmd/registry/node/node.go b/go/oasis-node/cmd/registry/node/node.go index 300d684dba4..6a9561552d9 100644 --- a/go/oasis-node/cmd/registry/node/node.go +++ b/go/oasis-node/cmd/registry/node/node.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/viper" "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" fileSigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/file" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -27,11 +28,12 @@ import ( cmdGrpc "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/grpc" cmdSigner "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/signer" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" "github.com/oasisprotocol/oasis-core/go/worker/common/configparser" ) const ( - CfgEntityID = "node.entity_id" + CfgEntityAddress = "node.entity_address" CfgExpiration = "node.expiration" CfgTLSAddress = "node.tls_address" CfgP2PAddress = "node.p2p_address" @@ -107,8 +109,8 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo // Get the entity ID or entity. var ( - entityDir string - entityID signature.PublicKey + entityDir string + entityAddr staking.Address entity *entity.Entity entitySigner signature.Signer @@ -116,14 +118,14 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo isSelfSigned bool ) - if idStr := viper.GetString(CfgEntityID); idStr != "" { - if err = entityID.UnmarshalText([]byte(idStr)); err != nil { - logger.Error("malformed entity ID", + if addrStr := viper.GetString(CfgEntityAddress); addrStr != "" { + if err = entityAddr.UnmarshalText([]byte(addrStr)); err != nil { + logger.Error("malformed entity address", "err", err, ) os.Exit(1) } - logger.Info("entity ID provided, assuming self-signed node registrations") + logger.Info("entity address provided, assuming self-signed node registrations") isSelfSigned = true } else { @@ -134,7 +136,7 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo ) os.Exit(1) } - entity, entitySigner, err = cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) + entity, entitySigner, _, err = cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) if err != nil { logger.Error("failed to load entity", "err", err, @@ -142,7 +144,7 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo os.Exit(1) } - entityID = entity.ID + entityAddr = entity.AccountAddress isSelfSigned = !entity.AllowEntitySignedNodes if viper.GetBool(CfgSelfSigned) { isSelfSigned = true @@ -182,10 +184,12 @@ func doInit(cmd *cobra.Command, args []string) { // nolint: gocyclo } n := &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nodeIdentity.NodeSigner.Public(), - EntityID: entityID, - Expiration: viper.GetUint64(CfgExpiration), + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nodeIdentity.NodeSigner.Public(), + EntityAddress: entityAddr, + Expiration: viper.GetUint64(CfgExpiration), TLS: node.TLSInfo{ PubKey: nodeIdentity.GetTLSSigner().Public(), NextPubKey: nextPubKey, @@ -423,7 +427,7 @@ func Register(parentCmd *cobra.Command) { } func init() { - flags.String(CfgEntityID, "", "Entity ID that controls this node") + flags.String(CfgEntityAddress, "", "Entity address that controls this node") flags.Uint64(CfgExpiration, 0, "Epoch that the node registration should expire") flags.StringSlice(CfgTLSAddress, nil, "Address(es) the node can be reached over TLS of the form [PubKey@]ip:port (where PubKey@ part is optional and represents base64 encoded node TLS public key)") flags.StringSlice(CfgP2PAddress, nil, "Address(es) the node can be reached over the P2P transport") diff --git a/go/oasis-node/cmd/registry/runtime/runtime.go b/go/oasis-node/cmd/registry/runtime/runtime.go index 0db168b690c..676b060752a 100644 --- a/go/oasis-node/cmd/registry/runtime/runtime.go +++ b/go/oasis-node/cmd/registry/runtime/runtime.go @@ -18,6 +18,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/node" @@ -150,12 +151,12 @@ func doInitGenesis(cmd *cobra.Command, args []string) { os.Exit(1) } - rt, signer, err := runtimeFromFlags() + rt, signer, account, err := runtimeFromFlags() if err != nil { os.Exit(1) } - signed, err := signForRegistration(rt, signer, true) + signed, err := signForRegistration(rt, signer, account, true) if err != nil { os.Exit(1) } @@ -182,7 +183,7 @@ func doGenRegister(cmd *cobra.Command, args []string) { cmdConsensus.InitGenesis() cmdConsensus.AssertTxFileOK() - rt, signer, err := runtimeFromFlags() + rt, signer, account, err := runtimeFromFlags() if err != nil { logger.Info("failed to get runtime", "err", err, @@ -190,7 +191,7 @@ func doGenRegister(cmd *cobra.Command, args []string) { os.Exit(1) } - signed, err := signForRegistration(rt, signer, false) + signed, err := signForRegistration(rt, signer, account, false) if err != nil { logger.Info("failed to sign runtime descriptor", "err", err, @@ -238,13 +239,13 @@ func doList(cmd *cobra.Command, args []string) { } } -func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint: gocyclo +func runtimeFromFlags() (*registry.Runtime, signature.Signer, *multisig.Account, error) { // nolint: gocyclo var id common.Namespace if err := id.UnmarshalHex(viper.GetString(CfgID)); err != nil { logger.Error("failed to parse runtime ID", "err", err, ) - return nil, nil, err + return nil, nil, nil, err } var teeHardware node.TEEHardware @@ -253,7 +254,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("invalid TEE hardware", CfgTEEHardware, s, ) - return nil, nil, fmt.Errorf("invalid TEE hardware") + return nil, nil, nil, fmt.Errorf("invalid TEE hardware") } entityDir, err := cmdSigner.CLIDirOrPwd() @@ -261,14 +262,14 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("failed to retrieve signer dir", "err", err, ) - return nil, nil, fmt.Errorf("failed to retrive signer dir") + return nil, nil, nil, fmt.Errorf("failed to retrive signer dir") } - _, signer, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) + _, signer, account, err := cmdCommon.LoadEntity(cmdSigner.Backend(), entityDir) if err != nil { logger.Error("failed to load owning entity", "err", err, ) - return nil, nil, err + return nil, nil, nil, err } var ( @@ -280,7 +281,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("invalid runtime kind", CfgKind, s, ) - return nil, nil, fmt.Errorf("invalid runtime kind") + return nil, nil, nil, fmt.Errorf("invalid runtime kind") } switch kind { case registry.KindCompute: @@ -290,7 +291,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("failed to parse key manager ID", "err", err, ) - return nil, nil, err + return nil, nil, nil, err } kmID = &tmpKmID } @@ -298,7 +299,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("runtime ID has the key manager flag set", "id", id, ) - return nil, nil, fmt.Errorf("invalid runtime flags") + return nil, nil, nil, fmt.Errorf("invalid runtime flags") } case registry.KindKeyManager: // Key managers don't have their own key manager. @@ -306,10 +307,10 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("runtime ID does not have the key manager flag set", "id", id, ) - return nil, nil, fmt.Errorf("invalid runtime flags") + return nil, nil, nil, fmt.Errorf("invalid runtime flags") } case registry.KindInvalid: - return nil, nil, fmt.Errorf("cannot create runtime with invalid kind") + return nil, nil, nil, fmt.Errorf("cannot create runtime with invalid kind") } // TODO: Support root upload when registering. @@ -326,7 +327,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint "err", err, "filename", state, ) - return nil, nil, err + return nil, nil, nil, err } var log storage.WriteLog @@ -335,7 +336,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint "err", err, "filename", state, ) - return nil, nil, err + return nil, nil, nil, err } // Use in-memory MKVS tree to calculate the new root. @@ -348,7 +349,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint "err", err, "filename", state, ) - return nil, nil, err + return nil, nil, nil, err } } @@ -359,7 +360,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint "err", err, "filename", state, ) - return nil, nil, err + return nil, nil, nil, err } gen.StateRoot = newRoot @@ -367,12 +368,14 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint } rt := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: id, - EntityID: signer.Public(), - Genesis: gen, - Kind: kind, - TEEHardware: teeHardware, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: id, + EntityAddress: staking.NewAddress(account), + Genesis: gen, + Kind: kind, + TEEHardware: teeHardware, Version: registry.VersionInfo{ Version: version.FromU64(viper.GetUint64(CfgVersion)), }, @@ -416,7 +419,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("failed to parse SGX enclave identity", "err", err, ) - return nil, nil, err + return nil, nil, nil, err } vi.Enclaves = append(vi.Enclaves, enclaveID) } @@ -426,15 +429,15 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint case AdmissionPolicyNameAnyNode: rt.AdmissionPolicy.AnyNode = ®istry.AnyNodeRuntimeAdmissionPolicy{} case AdmissionPolicyNameEntityWhitelist: - entities := make(map[signature.PublicKey]bool) + entities := make(map[staking.Address]bool) for _, se := range viper.GetStringSlice(CfgAdmissionPolicyEntityWhitelist) { - var e signature.PublicKey + var e staking.Address if err = e.UnmarshalText([]byte(se)); err != nil { logger.Error("failed to parse entity ID", "err", err, CfgAdmissionPolicyEntityWhitelist, se, ) - return nil, nil, fmt.Errorf("entity whitelist runtime admission policy parse entity ID: %w", err) + return nil, nil, nil, fmt.Errorf("entity whitelist runtime admission policy parse entity ID: %w", err) } entities[e] = true } @@ -445,7 +448,7 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint logger.Error("invalid runtime admission policy", CfgAdmissionPolicy, sap, ) - return nil, nil, fmt.Errorf("invalid runtime admission policy") + return nil, nil, nil, fmt.Errorf("invalid runtime admission policy") } // Staking parameters. @@ -458,14 +461,14 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint ) if err = kind.UnmarshalText([]byte(kindRaw)); err != nil { - return nil, nil, fmt.Errorf("staking: bad threshold kind (%s): %w", kindRaw, err) + return nil, nil, nil, fmt.Errorf("staking: bad threshold kind (%s): %w", kindRaw, err) } if err = value.UnmarshalText([]byte(valueRaw)); err != nil { - return nil, nil, fmt.Errorf("staking: bad threshold value (%s): %w", valueRaw, err) + return nil, nil, nil, fmt.Errorf("staking: bad threshold value (%s): %w", valueRaw, err) } if _, ok := rt.Staking.Thresholds[kind]; ok { - return nil, nil, fmt.Errorf("staking: duplicate value for threshold '%s'", kind) + return nil, nil, nil, fmt.Errorf("staking: duplicate value for threshold '%s'", kind) } rt.Staking.Thresholds[kind] = value } @@ -473,17 +476,17 @@ func runtimeFromFlags() (*registry.Runtime, signature.Signer, error) { // nolint // Validate descriptor. if err = rt.ValidateBasic(true); err != nil { - return nil, nil, fmt.Errorf("invalid runtime descriptor: %w", err) + return nil, nil, nil, fmt.Errorf("invalid runtime descriptor: %w", err) } // Validate storage configuration. if err = registry.VerifyRegisterRuntimeStorageArgs(rt, logger); err != nil { - return nil, nil, fmt.Errorf("invalid runtime storage configuration: %w", err) + return nil, nil, nil, fmt.Errorf("invalid runtime storage configuration: %w", err) } - return rt, signer, nil + return rt, signer, account, nil } -func signForRegistration(rt *registry.Runtime, signer signature.Signer, isGenesis bool) (*registry.SignedRuntime, error) { +func signForRegistration(rt *registry.Runtime, signer signature.Signer, account *multisig.Account, isGenesis bool) (*registry.SignedRuntime, error) { var ctx signature.Context switch isGenesis { case false: @@ -492,7 +495,7 @@ func signForRegistration(rt *registry.Runtime, signer signature.Signer, isGenesi ctx = registry.RegisterGenesisRuntimeSignatureContext } - signed, err := registry.SignRuntime(signer, ctx, rt) + signed, err := registry.SingleSignRuntime(signer, account, ctx, rt) if err != nil { logger.Error("failed to sign runtime descriptor", "err", err, @@ -579,7 +582,7 @@ func init() { // Init Admission policy flags. runtimeFlags.String(CfgAdmissionPolicy, "", "What type of node admission policy to have") - runtimeFlags.StringSlice(CfgAdmissionPolicyEntityWhitelist, nil, "For entity whitelist node admission policies, the IDs (hex) of the entities in the whitelist") + runtimeFlags.StringSlice(CfgAdmissionPolicyEntityWhitelist, nil, "For entity whitelist node admission policies, the addresses (Bech32) of the entities in the whitelist") // Init Staking flags. runtimeFlags.StringToString(CfgStakingThreshold, nil, "Additional staking threshold for this runtime (=)") diff --git a/go/oasis-node/cmd/stake/stake.go b/go/oasis-node/cmd/stake/stake.go index 86ce6233fd9..cffb41d6c55 100644 --- a/go/oasis-node/cmd/stake/stake.go +++ b/go/oasis-node/cmd/stake/stake.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/viper" "google.golang.org/grpc" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/errors" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -46,7 +47,7 @@ var ( pubkey2AddressCmd = &cobra.Command{ Use: "pubkey2address", - Short: "convert a public key (e.g. entity's ID) to an account address", + Short: "convert a public key to a single-signature account address", Run: doPubkey2Address, } @@ -238,7 +239,7 @@ func doPubkey2Address(cmd *cobra.Command, args []string) { os.Exit(1) } - fmt.Printf("%v\n", staking.NewAddress(pk)) + fmt.Printf("%v\n", staking.NewAddress(multisig.NewAccountFromPublicKey(pk))) } // Register registers the stake sub-command and all of it's children. diff --git a/go/oasis-node/node_test.go b/go/oasis-node/node_test.go index 5627e1d1b83..c1299ccf409 100644 --- a/go/oasis-node/node_test.go +++ b/go/oasis-node/node_test.go @@ -15,6 +15,8 @@ import ( beaconTests "github.com/oasisprotocol/oasis-core/go/beacon/tests" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" fileSigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/file" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -79,7 +81,9 @@ var ( } testRuntime = ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, // ID: default value, // EntityID: test entity, Kind: registry.KindCompute, @@ -125,8 +129,9 @@ type testNode struct { executorCommitteeNode *executorCommittee.Node txnschedulerCommitteeNode *txnschedulerCommittee.Node - entity *entity.Entity - entitySigner signature.Signer + entity *entity.Entity + entitySigner signature.Signer + entityAccount *multisig.Account dataDir string start time.Time @@ -159,7 +164,7 @@ func newTestNode(t *testing.T) *testNode { signerFactory, err := fileSigner.NewFactory(dataDir, signature.SignerEntity) require.NoError(err, "create file signer") - entity, entitySigner, err := entity.Generate(dataDir, signerFactory, nil) + entity, entitySigner, entityAccount, err := entity.Generate(dataDir, signerFactory, nil) require.NoError(err, "create test entity") viper.Set("datadir", dataDir) @@ -175,11 +180,12 @@ func newTestNode(t *testing.T) *testNode { } n := &testNode{ - runtimeID: testRuntime.ID, - dataDir: dataDir, - entity: entity, - entitySigner: entitySigner, - start: time.Now(), + runtimeID: testRuntime.ID, + dataDir: dataDir, + entity: entity, + entitySigner: entitySigner, + entityAccount: entityAccount, + start: time.Now(), } t.Logf("starting node, data directory: %v", dataDir) n.Node, err = node.NewTestNode() @@ -274,22 +280,22 @@ func testRegisterEntityRuntime(t *testing.T, node *testNode) { require := require.New(t) // Register node entity. - signedEnt, err := entity.SignEntity(node.entitySigner, registry.RegisterEntitySignatureContext, node.entity) + signedEnt, err := entity.SingleSignEntity(node.entitySigner, node.entityAccount, registry.RegisterEntitySignatureContext, node.entity) require.NoError(err, "sign node entity") tx := registry.NewRegisterEntityTx(0, nil, signedEnt) err = consensusAPI.SignAndSubmitTx(context.Background(), node.Consensus, node.entitySigner, tx) require.NoError(err, "register node entity") // Register the test entity. - testEntity, testEntitySigner, _ := entity.TestEntity() - signedEnt, err = entity.SignEntity(testEntitySigner, registry.RegisterEntitySignatureContext, testEntity) + testEntity, testEntitySigner, testEntityAccount, _ := entity.TestEntity() + signedEnt, err = entity.SingleSignEntity(testEntitySigner, testEntityAccount, registry.RegisterEntitySignatureContext, testEntity) require.NoError(err, "sign test entity") tx = registry.NewRegisterEntityTx(0, nil, signedEnt) err = consensusAPI.SignAndSubmitTx(context.Background(), node.Consensus, testEntitySigner, tx) require.NoError(err, "register test entity") // Register the test runtime. - signedRt, err := registry.SignRuntime(testEntitySigner, registry.RegisterRuntimeSignatureContext, testRuntime) + signedRt, err := registry.SingleSignRuntime(testEntitySigner, testEntityAccount, registry.RegisterRuntimeSignatureContext, testRuntime) require.NoError(err, "sign runtime descriptor") tx = registry.NewRegisterRuntimeTx(0, nil, signedRt) err = consensusAPI.SignAndSubmitTx(context.Background(), node.Consensus, testEntitySigner, tx) @@ -357,7 +363,7 @@ WaitLoop: } // Deregistering the test entity should fail as it has runtimes. - _, testEntitySigner, _ := entity.TestEntity() + _, testEntitySigner, _, _ := entity.TestEntity() tx = registry.NewDeregisterEntityTx(0, nil) err = consensusAPI.SignAndSubmitTx(context.Background(), node.Consensus, testEntitySigner, tx) require.Error(t, err, "deregister should fail when an entity has runtimes") @@ -508,11 +514,11 @@ func testStorageClientWithoutNode(t *testing.T, node *testNode) { } func init() { - testEntity, _, _ := entity.TestEntity() + testEntity, _, _, _ := entity.TestEntity() testRuntimeID = common.NewTestNamespaceFromSeed([]byte("oasis node test namespace"), 0) testRuntime.ID = testRuntimeID - testRuntime.EntityID = testEntity.ID + testRuntime.EntityAddress = testEntity.AccountAddress testRuntime.Genesis.StateRoot.Empty() } diff --git a/go/oasis-test-runner/oasis/entity.go b/go/oasis-test-runner/oasis/entity.go index f610e75ac8d..57b384b1fd7 100644 --- a/go/oasis-test-runner/oasis/entity.go +++ b/go/oasis-test-runner/oasis/entity.go @@ -130,7 +130,7 @@ func (net *Network) NewEntity(cfg *EntityCfg) (*Entity, error) { ent = &Entity{ isDebugTestEntity: true, } - ent.entity, ent.entitySigner, _ = entity.TestEntity() + ent.entity, ent.entitySigner, _, _ = entity.TestEntity() } else { entName := fmt.Sprintf("entity-%d", len(net.entities)) entityDir, err := net.baseDir.NewSubDir(entName) @@ -196,7 +196,7 @@ func (net *Network) NewEntity(cfg *EntityCfg) (*Entity, error) { ) return nil, fmt.Errorf("oasis/entity: failed to create entity file signer: %w", err) } - ent.entity, ent.entitySigner, err = entity.Load(entityDir.String(), signerFactory) + ent.entity, ent.entitySigner, _, err = entity.Load(entityDir.String(), signerFactory) if err != nil { net.logger.Error("failed to load newly provisoned entity", "err", err, diff --git a/go/oasis-test-runner/oasis/runtime.go b/go/oasis-test-runner/oasis/runtime.go index d838be3df1d..6fc433f5840 100644 --- a/go/oasis-test-runner/oasis/runtime.go +++ b/go/oasis-test-runner/oasis/runtime.go @@ -115,17 +115,19 @@ func (rt *Runtime) GetGenesisStatePath() string { // NewRuntime provisions a new runtime and adds it to the network. func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { descriptor := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: cfg.ID, - EntityID: cfg.Entity.entity.ID, - Kind: cfg.Kind, - TEEHardware: cfg.TEEHardware, - Executor: cfg.Executor, - Merge: cfg.Merge, - TxnScheduler: cfg.TxnScheduler, - Storage: cfg.Storage, - AdmissionPolicy: cfg.AdmissionPolicy, - Staking: cfg.Staking, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: cfg.ID, + EntityAddress: cfg.Entity.entity.AccountAddress, + Kind: cfg.Kind, + TEEHardware: cfg.TEEHardware, + Executor: cfg.Executor, + Merge: cfg.Merge, + TxnScheduler: cfg.TxnScheduler, + Storage: cfg.Storage, + AdmissionPolicy: cfg.AdmissionPolicy, + Staking: cfg.Staking, } rtDir, err := net.baseDir.NewSubDir("runtime-" + cfg.ID.String()) diff --git a/go/oasis-test-runner/scenario/e2e/debond.go b/go/oasis-test-runner/scenario/e2e/debond.go index aec6b81cd7f..3eab1b8fb5c 100644 --- a/go/oasis-test-runner/scenario/e2e/debond.go +++ b/go/oasis-test-runner/scenario/e2e/debond.go @@ -58,7 +58,7 @@ func (s *debondImpl) Run(*env.Env) error { lockupQuery := staking.OwnerQuery{ Height: consensus.HeightLatest, } - if err := lockupQuery.Owner.UnmarshalText([]byte("oasis1qpt202cf6t0s5ugkk34p83yf0c30gpjkny92u7dh")); err != nil { + if err := lockupQuery.Owner.UnmarshalText([]byte("oasis1qz92ljfknlc2nqzwdwhk0lqqe4rxzuthfcvj49hs")); err != nil { return fmt.Errorf("failed to unmarshal lockup account address: %w", err) } s.Logger.Info("checking balance at beginning") diff --git a/go/oasis-test-runner/scenario/e2e/gas_fees_staking.go b/go/oasis-test-runner/scenario/e2e/gas_fees_staking.go index 5a79d088964..bc1aea51052 100644 --- a/go/oasis-test-runner/scenario/e2e/gas_fees_staking.go +++ b/go/oasis-test-runner/scenario/e2e/gas_fees_staking.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -34,11 +35,15 @@ var ( // Testing destination account address. dstAddr = staking.NewAddress( - signature.NewPublicKey("badfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + multisig.NewAccountFromPublicKey( + signature.NewPublicKey("badfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + ), ) // Testing escrow account address. escrowAddr = staking.NewAddress( - signature.NewPublicKey("badbadffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + multisig.NewAccountFromPublicKey( + signature.NewPublicKey("badbadffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + ), ) ) @@ -240,7 +245,7 @@ func (sc *gasFeesImpl) getTotalEntityBalance(ctx context.Context) (*quantity.Qua var total quantity.Quantity for _, e := range sc.Net.Entities()[1:] { // Only count entities with validators. ent, _ := e.Inner() - addr := staking.NewAddress(ent.ID) + addr := ent.AccountAddress acct, err := st.Account(ctx, &staking.OwnerQuery{Owner: addr, Height: consensus.HeightLatest}) if err != nil { @@ -248,8 +253,7 @@ func (sc *gasFeesImpl) getTotalEntityBalance(ctx context.Context) (*quantity.Qua } sc.Logger.Debug("fetched balance", - "entity", ent.ID, - "address", addr, + "entity_address", addr, "balance", acct.General.Balance, ) @@ -267,7 +271,7 @@ func (sc *gasFeesImpl) testTransfer(ctx context.Context, signer signature.Signer _ = transfer.Amount.FromInt64(amount) tx := staking.NewTransferTx(acct.General.Nonce, &fee, &transfer) - sigTx, err := transaction.Sign(signer, tx) + sigTx, err := transaction.SingleSign(signer, multisig.NewAccountFromPublicKey(signer.Public()), tx) if err != nil { return fmt.Errorf("failed to sign transfer: %w", err) } @@ -281,7 +285,7 @@ func (sc *gasFeesImpl) testBurn(ctx context.Context, signer signature.Signer) (* _ = burn.Amount.FromInt64(amount) tx := staking.NewBurnTx(acct.General.Nonce, &fee, &burn) - sigTx, err := transaction.Sign(signer, tx) + sigTx, err := transaction.SingleSign(signer, multisig.NewAccountFromPublicKey(signer.Public()), tx) if err != nil { return fmt.Errorf("failed to sign burn: %w", err) } @@ -297,7 +301,7 @@ func (sc *gasFeesImpl) testAddEscrow(ctx context.Context, signer signature.Signe _ = escrow.Amount.FromInt64(amount) tx := staking.NewAddEscrowTx(acct.General.Nonce, &fee, &escrow) - sigTx, err := transaction.Sign(signer, tx) + sigTx, err := transaction.SingleSign(signer, multisig.NewAccountFromPublicKey(signer.Public()), tx) if err != nil { return fmt.Errorf("failed to sign escrow: %w", err) } @@ -313,7 +317,7 @@ func (sc *gasFeesImpl) testReclaimEscrow(ctx context.Context, signer signature.S _ = escrow.Shares.FromInt64(shares) tx := staking.NewReclaimEscrowTx(acct.General.Nonce, &fee, &escrow) - sigTx, err := transaction.Sign(signer, tx) + sigTx, err := transaction.SingleSign(signer, multisig.NewAccountFromPublicKey(signer.Public()), tx) if err != nil { return fmt.Errorf("failed to sign reclaim escrow: %w", err) } @@ -411,7 +415,7 @@ func (sc *gasFeesImpl) testStakingGasOp( st := sc.Net.Controller().Staking // Fetch initial account info. - addr := staking.NewAddress(signer.Public()) + addr := staking.NewAddress(multisig.NewAccountFromPublicKey(signer.Public())) acct, err := st.Account(ctx, &staking.OwnerQuery{Owner: addr, Height: consensus.HeightLatest}) if err != nil { return fmt.Errorf("failed to get account info: %w", err) diff --git a/go/oasis-test-runner/scenario/e2e/registry_cli.go b/go/oasis-test-runner/scenario/e2e/registry_cli.go index 27dc2107fdf..fee31ff1752 100644 --- a/go/oasis-test-runner/scenario/e2e/registry_cli.go +++ b/go/oasis-test-runner/scenario/e2e/registry_cli.go @@ -15,6 +15,7 @@ import ( "time" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" fileSigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/file" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -219,7 +220,7 @@ func (sc *registryCLIImpl) testEntityAndNode(childEnv *env.Env, cli *cli.Helpers } // listEntities lists currently registered entities. -func (sc *registryCLIImpl) listEntities(childEnv *env.Env) ([]signature.PublicKey, error) { +func (sc *registryCLIImpl) listEntities(childEnv *env.Env) ([]staking.Address, error) { sc.Logger.Info("listing all entities") args := []string{ "registry", "entity", "list", @@ -231,14 +232,14 @@ func (sc *registryCLIImpl) listEntities(childEnv *env.Env) ([]signature.PublicKe } entitiesStr := strings.Split(out.String(), "\n") - var entities []signature.PublicKey + var entities []staking.Address for _, entStr := range entitiesStr { // Ignore last newline. if entStr == "" { continue } - var ent signature.PublicKey + var ent staking.Address if err = ent.UnmarshalText([]byte(entStr)); err != nil { return nil, fmt.Errorf("failed to parse entity ID (%s): error: %w output: %s", entStr, err, out.String()) } @@ -254,7 +255,7 @@ func (sc *registryCLIImpl) loadEntity(entDir string) (*entity.Entity, error) { if err != nil { return nil, fmt.Errorf("failed to create entity file signer: %w", err) } - ent, _, err := entity.Load(entDir, entitySignerFactory) + ent, _, _, err := entity.Load(entDir, entitySignerFactory) if err != nil { return nil, fmt.Errorf("failed to load entity: %w", err) } @@ -349,7 +350,7 @@ func (sc *registryCLIImpl) isRegistered(childEnv *env.Env, nodeName, nodeDataDir } // newTestNode returns a test node instance given the entityID. -func (sc *registryCLIImpl) newTestNode(entityID signature.PublicKey) (*node.Node, []string, []string, []string, error) { +func (sc *registryCLIImpl) newTestNode(entityAddress staking.Address) (*node.Node, []string, []string, []string, error) { // Addresses. testAddresses := []node.Address{ {TCPAddr: net.TCPAddr{ @@ -403,10 +404,12 @@ func (sc *registryCLIImpl) newTestNode(entityID signature.PublicKey) (*node.Node } testNode := node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: signature.PublicKey{}, // ID is generated afterwards. - EntityID: entityID, - Expiration: 42, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: signature.PublicKey{}, // ID is generated afterwards. + EntityAddress: entityAddress, + Expiration: 42, TLS: node.TLSInfo{ PubKey: signature.PublicKey{}, // Public key is generated afterwards. Addresses: testTLSAddresses, @@ -436,7 +439,7 @@ func (sc *registryCLIImpl) initNode(childEnv *env.Env, ent *entity.Entity, entDi sc.Logger.Info("initializing new node") // testNode will be our fixture for testing the CLI. - testNode, testAddressesStr, testConsensusAddressesStr, testTLSAddressesStr, err := sc.newTestNode(ent.ID) + testNode, testAddressesStr, testConsensusAddressesStr, testTLSAddressesStr, err := sc.newTestNode(ent.AccountAddress) if err != nil { return nil, err } @@ -447,7 +450,7 @@ func (sc *registryCLIImpl) initNode(childEnv *env.Env, ent *entity.Entity, entDi "registry", "node", "init", "--" + cmdRegNode.CfgTLSAddress, strings.Join(testTLSAddressesStr, ","), "--" + cmdRegNode.CfgConsensusAddress, strings.Join(testConsensusAddressesStr, ","), - "--" + cmdRegNode.CfgEntityID, testNode.EntityID.String(), + "--" + cmdRegNode.CfgEntityAddress, testNode.EntityAddress.String(), "--" + cmdRegNode.CfgExpiration, strconv.FormatUint(testNode.Expiration, 10), "--" + cmdRegNode.CfgSelfSigned, "1", "--" + cmdRegNode.CfgP2PAddress, strings.Join(testAddressesStr, ","), @@ -602,16 +605,18 @@ func (sc *registryCLIImpl) testRuntime(ctx context.Context, childEnv *env.Env, c } // Create runtime descriptor instance. - testEntity, _, err := entity.TestEntity() + testEntity, _, _, err := entity.TestEntity() if err != nil { return fmt.Errorf("TestEntity: %w", err) } var q quantity.Quantity _ = q.FromUint64(100) testRuntime := registry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - EntityID: testEntity.ID, - Kind: registry.KindCompute, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + EntityAddress: testEntity.AccountAddress, + Kind: registry.KindCompute, Executor: registry.ExecutorParameters{ GroupSize: 1, GroupBackupSize: 2, @@ -641,8 +646,8 @@ func (sc *registryCLIImpl) testRuntime(ctx context.Context, childEnv *env.Env, c }, AdmissionPolicy: registry.RuntimeAdmissionPolicy{ EntityWhitelist: ®istry.EntityWhitelistRuntimeAdmissionPolicy{ - Entities: map[signature.PublicKey]bool{ - testEntity.ID: true, + Entities: map[staking.Address]bool{ + testEntity.AccountAddress: true, }, }, }, diff --git a/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go b/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go index a5b25359223..8630b681926 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go @@ -8,6 +8,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/quantity" "github.com/oasisprotocol/oasis-core/go/common/sgx" @@ -346,7 +347,8 @@ func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { // nolint: gocyclo // Now reclaim all stake from the debug entity which owns the runtime. sc.Logger.Info("reclaiming stake from entity which owns the runtime") entSigner := sc.Net.Entities()[0].Signer() - entAddr := staking.NewAddress(entSigner.Public()) + entAccount := multisig.NewAccountFromPublicKey(entSigner.Public()) + entAddr := staking.NewAddress(entAccount) var oneShare quantity.Quantity _ = oneShare.FromUint64(1) tx := staking.NewReclaimEscrowTx(nonce, &transaction.Fee{Gas: 10000}, &staking.ReclaimEscrow{ @@ -354,7 +356,7 @@ func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { // nolint: gocyclo Shares: oneShare, }) nonce++ - sigTx, err := transaction.Sign(entSigner, tx) + sigTx, err := transaction.SingleSign(entSigner, entAccount, tx) if err != nil { return fmt.Errorf("failed to sign reclaim: %w", err) } @@ -449,7 +451,7 @@ func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { // nolint: gocyclo Amount: enoughStake, }) nonce++ // nolint: ineffassign - sigTx, err = transaction.Sign(entSigner, tx) + sigTx, err = transaction.SingleSign(entSigner, entAccount, tx) if err != nil { return fmt.Errorf("failed to sign escrow: %w", err) } diff --git a/go/oasis-test-runner/scenario/e2e/stake_cli.go b/go/oasis-test-runner/scenario/e2e/stake_cli.go index 76c7f013895..d60e57d37f2 100644 --- a/go/oasis-test-runner/scenario/e2e/stake_cli.go +++ b/go/oasis-test-runner/scenario/e2e/stake_cli.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -60,13 +61,25 @@ const ( var ( // Testing source account address. - srcAddress = api.NewAddress(signature.NewPublicKey(srcPubkeyHex)) + srcAddress = api.NewAddress( + multisig.NewAccountFromPublicKey( + signature.NewPublicKey(srcPubkeyHex), + ), + ) // Testing destination account address. - dstAddress = api.NewAddress(signature.NewPublicKey(dstPubkeyHex)) + dstAddress = api.NewAddress( + multisig.NewAccountFromPublicKey( + signature.NewPublicKey(dstPubkeyHex), + ), + ) // Testing escrow account address. - escrowAddress = api.NewAddress(signature.NewPublicKey(escrowPubkeyHex)) + escrowAddress = api.NewAddress( + multisig.NewAccountFromPublicKey( + signature.NewPublicKey(escrowPubkeyHex), + ), + ) // StakeCLI is the staking scenario. StakeCLI scenario.Scenario = &stakeCLIImpl{ @@ -269,12 +282,12 @@ func (sc *stakeCLIImpl) testTransfer(childEnv *env.Env, cli *cli.Helpers, src, d if err := sc.genUnsignedTransferTx(childEnv, transferAmount, 0, dst, unsignedTransferTxPath); err != nil { return fmt.Errorf("genUnsignedTransferTx: %w", err) } - _, teSigner, err := entity.TestEntity() + _, teSigner, _, err := entity.TestEntity() if err != nil { return fmt.Errorf("obtain test entity: %w", err) } - expectedGasEstimate := transaction.Gas(262) + expectedGasEstimate := transaction.Gas(282) // TODO: Derive or document this. gas, err := cli.Consensus.EstimateGas(unsignedTransferTxPath, teSigner.Public()) if err != nil { return fmt.Errorf("estimate gas on unsigned transfer tx: %w", err) diff --git a/go/oasis-test-runner/scenario/e2e/upgrade.go b/go/oasis-test-runner/scenario/e2e/upgrade.go index cddc6c6cb9a..18a851eb04c 100644 --- a/go/oasis-test-runner/scenario/e2e/upgrade.go +++ b/go/oasis-test-runner/scenario/e2e/upgrade.go @@ -310,11 +310,11 @@ func (sc *nodeUpgradeImpl) Run(childEnv *env.Env) error { } // Check the entity set during consensus upgrade. - idQuery := ®istry.IDQuery{ + accountQuery := ®istry.AccountQuery{ Height: consensus.HeightLatest, - ID: migrations.TestEntity.ID, + ID: migrations.TestEntity.AccountAddress, } - _, err = sc.Net.Controller().Registry.GetEntity(sc.ctx, idQuery) + _, err = sc.Net.Controller().Registry.GetEntity(sc.ctx, accountQuery) if err != nil { return fmt.Errorf("can't get registered test entity: %w", err) } diff --git a/go/registry/api/api.go b/go/registry/api/api.go index cd54b41cee3..ea69d4e5c1f 100644 --- a/go/registry/api/api.go +++ b/go/registry/api/api.go @@ -12,6 +12,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/entity" "github.com/oasisprotocol/oasis-core/go/common/errors" @@ -101,9 +102,9 @@ var ( // ErrNoSuchRuntime is the error returned when an runtime does not exist. ErrNoSuchRuntime = errors.New(ModuleName, 11, "registry: no such runtime") - // ErrIncorrectTxSigner is the error returned when the signer of the transaction + // ErrIncorrectTxAccount is the error returned when the account of the transaction // is not the correct one. - ErrIncorrectTxSigner = errors.New(ModuleName, 12, "registry: incorrect tx signer") + ErrIncorrectTxAccount = errors.New(ModuleName, 12, "registry: incorrect tx account") // ErrNodeExpired is the error returned when a node is expired. ErrNodeExpired = errors.New(ModuleName, 13, "registry: node expired") @@ -178,8 +179,8 @@ var ( // Backend is a registry implementation. type Backend interface { - // GetEntity gets an entity by ID. - GetEntity(context.Context, *IDQuery) (*entity.Entity, error) + // GetEntity gets an entity by staking account address. + GetEntity(context.Context, *AccountQuery) (*entity.Entity, error) // GetEntities gets a list of all registered entities. GetEntities(context.Context, int64) ([]*entity.Entity, error) @@ -235,6 +236,12 @@ type Backend interface { Cleanup() } +// AccountQuery is a registry query by account address. +type AccountQuery struct { + Height int64 `json:"height"` + ID staking.Address `json:"id"` +} + // IDQuery is a registry query by ID. type IDQuery struct { Height int64 `json:"height"` @@ -376,11 +383,11 @@ func VerifyRegisterEntityArgs(logger *logging.Logger, sigEnt *entity.SignedEntit ) return nil, ErrInvalidSignature } - if err := sigEnt.Signed.Signature.SanityCheck(ent.ID); err != nil { - logger.Error("RegisterEntity: invalid argument(s)", + signerAccount := staking.NewAddress(&sigEnt.Envelope.Account) + if !signerAccount.Equal(ent.AccountAddress) { + logger.Error("RegisterEntity: entity signed by unexpected account", "signed_entity", sigEnt, "entity", ent, - "err", err, ) return nil, ErrInvalidArgument } @@ -458,8 +465,8 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo } // This should never happen, unless there's a bug in the caller. - if !entity.ID.Equal(n.EntityID) { - logger.Error("RegisterNode: node entity ID does not match expected entity", + if !entity.AccountAddress.Equal(n.EntityAddress) { + logger.Error("RegisterNode: node entity address does not match expected entity", "node", n, "entity", entity, ) @@ -498,14 +505,32 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo // If we are using entity signing, descriptors will also be signed // by the entity signing key. - if !sigNode.MultiSigned.IsSignedBy(entity.ID) { + // + // Naturally with the transition to multisig accounts, the notion + // of a singular entity signing key is somewhat nonsensical. + // + // Thankfully this is a debug-only feature, so abuse the fact that + // all uses of this will always be in tests, where the entity in + // question only has one signer internally. + findEntityPublicKey := func() (signature.PublicKey, error) { + for _, sig := range sigNode.MultiSigned.Signatures { + addr := staking.NewAddress(multisig.NewAccountFromPublicKey(sig.PublicKey)) + if addr.Equal(n.EntityAddress) { + return sig.PublicKey, nil + } + } + return signature.PublicKey{}, fmt.Errorf("%w: failed to find entity signature in registration", ErrInvalidArgument) + } + + entityPk, err := findEntityPublicKey() + if err != nil { logger.Error("RegisterNode: registration not signed by entity", "signed_node", sigNode, "node", n, ) return nil, nil, fmt.Errorf("%w: registration not signed by entity", ErrInvalidArgument) } - expectedSigners = append(expectedSigners, entity.ID) + expectedSigners = append(expectedSigners, entityPk) } // Expired registrations are allowed here because this routine is abused @@ -947,10 +972,10 @@ func VerifyNodeUpdate(logger *logging.Logger, currentNode, newNode *node.Node) e ) return ErrNodeUpdateNotAllowed } - if !currentNode.EntityID.Equal(newNode.EntityID) { - logger.Error("RegisterNode: trying to update node entity ID", - "current_id", currentNode.EntityID, - "new_id", newNode.EntityID, + if !currentNode.EntityAddress.Equal(newNode.EntityAddress) { + logger.Error("RegisterNode: trying to update node entity address", + "current_address", currentNode.EntityAddress, + "new_address", newNode.EntityAddress, ) return ErrNodeUpdateNotAllowed } @@ -1022,11 +1047,11 @@ func VerifyRegisterRuntimeArgs( ) return nil, ErrInvalidSignature } - if err := sigRt.Signed.Signature.SanityCheck(rt.EntityID); err != nil { - logger.Error("RegisterRuntime: invalid argument(s)", + signerAccount := staking.NewAddress(&sigRt.Envelope.Account) + if !signerAccount.Equal(rt.EntityAddress) { + logger.Error("RegisterRuntime: runtime signed by unexpected account", "signed_runtime", sigRt, "runtime", rt, - "err", err, ) return nil, ErrInvalidArgument } @@ -1228,10 +1253,10 @@ func VerifyRegisterComputeRuntimeArgs(ctx context.Context, logger *logging.Logge // VerifyRuntimeUpdate verifies changes while updating the runtime. func VerifyRuntimeUpdate(logger *logging.Logger, currentRt, newRt *Runtime) error { - if !currentRt.EntityID.Equal(newRt.EntityID) { + if !currentRt.EntityAddress.Equal(newRt.EntityAddress) { logger.Error("RegisterRuntime: trying to change runtime owner", - "current_owner", currentRt.EntityID, - "new_owner", newRt.EntityID, + "current_owner", currentRt.EntityAddress, + "new_owner", newRt.EntityAddress, ) return ErrRuntimeUpdateNotAllowed } diff --git a/go/registry/api/grpc.go b/go/registry/api/grpc.go index 27971b1e67d..ead3b8ca037 100644 --- a/go/registry/api/grpc.go +++ b/go/registry/api/grpc.go @@ -16,7 +16,7 @@ var ( serviceName = cmnGrpc.NewServiceName("Registry") // methodGetEntity is the GetEntity method. - methodGetEntity = serviceName.NewMethod("GetEntity", IDQuery{}) + methodGetEntity = serviceName.NewMethod("GetEntity", AccountQuery{}) // methodGetEntities is the GetEntities method. methodGetEntities = serviceName.NewMethod("GetEntities", int64(0)) // methodGetNode is the GetNode method. @@ -122,7 +122,7 @@ func handlerGetEntity( // nolint: golint dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor, ) (interface{}, error) { - var query IDQuery + var query AccountQuery if err := dec(&query); err != nil { return nil, err } @@ -134,7 +134,7 @@ func handlerGetEntity( // nolint: golint FullMethod: methodGetEntity.FullName(), } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(Backend).GetEntity(ctx, req.(*IDQuery)) + return srv.(Backend).GetEntity(ctx, req.(*AccountQuery)) } return interceptor(ctx, &query, info, handler) } @@ -467,7 +467,7 @@ type registryClient struct { conn *grpc.ClientConn } -func (c *registryClient) GetEntity(ctx context.Context, query *IDQuery) (*entity.Entity, error) { +func (c *registryClient) GetEntity(ctx context.Context, query *AccountQuery) (*entity.Entity, error) { var rsp entity.Entity if err := c.conn.Invoke(ctx, methodGetEntity.FullName(), query, &rsp); err != nil { return nil, err diff --git a/go/registry/api/runtime.go b/go/registry/api/runtime.go index 4c9cee0d388..0ab690fe946 100644 --- a/go/registry/api/runtime.go +++ b/go/registry/api/runtime.go @@ -11,6 +11,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/prettyprint" @@ -166,7 +167,7 @@ type AnyNodeRuntimeAdmissionPolicy struct{} // EntityWhitelistRuntimeAdmissionPolicy allows only whitelisted entities' nodes to register. type EntityWhitelistRuntimeAdmissionPolicy struct { - Entities map[signature.PublicKey]bool `json:"entities"` + Entities map[staking.Address]bool `json:"entities"` } // RuntimeAdmissionPolicy is a specification of which nodes are allowed to register for a runtime. @@ -220,17 +221,14 @@ const ( // Runtime represents a runtime. type Runtime struct { // nolint: maligned - // DescriptorVersion is the runtime descriptor version. - // - // It should be bumped whenever breaking changes are made to the descriptor. - DescriptorVersion uint16 `json:"v,omitempty"` + cbor.Versioned // ID is a globally unique long term identifier of the runtime. ID common.Namespace `json:"id"` - // EntityID is the public key identifying the Entity controlling + // EntityAddress is the account address of the entity controlling // the runtime. - EntityID signature.PublicKey `json:"entity_id"` + EntityAddress staking.Address `json:"account_address"` // Genesis is the runtime genesis information. Genesis RuntimeGenesis `json:"genesis"` @@ -272,15 +270,15 @@ func (r *Runtime) ValidateBasic(strictVersion bool) error { switch strictVersion { case true: // Only the latest version is allowed. - if r.DescriptorVersion != LatestRuntimeDescriptorVersion { + if r.Versioned.V != LatestRuntimeDescriptorVersion { return fmt.Errorf("invalid runtime descriptor version (expected: %d got: %d)", LatestRuntimeDescriptorVersion, - r.DescriptorVersion, + r.Versioned.V, ) } case false: // A range of versions is allowed. - if r.DescriptorVersion < minRuntimeDescriptorVersion || r.DescriptorVersion > maxRuntimeDescriptorVersion { + if r.Versioned.V < minRuntimeDescriptorVersion || r.Versioned.V > maxRuntimeDescriptorVersion { return fmt.Errorf("invalid runtime descriptor version (min: %d max: %d)", minRuntimeDescriptorVersion, maxRuntimeDescriptorVersion, @@ -306,12 +304,12 @@ func (r *Runtime) IsCompute() bool { // SignedRuntime is a signed blob containing a CBOR-serialized Runtime. type SignedRuntime struct { - signature.Signed + multisig.Envelope } // Open first verifies the blob signature and then unmarshals the blob. func (s *SignedRuntime) Open(context signature.Context, runtime *Runtime) error { // nolint: interfacer - return s.Signed.Open(context, runtime) + return s.Envelope.Open(context, runtime) } // PrettyPrint writes a pretty-printed representation of the type @@ -329,22 +327,30 @@ func (s SignedRuntime) PrettyPrint(ctx context.Context, prefix string, w io.Writ // PrettyType returns a representation of the type that can be used for pretty printing. func (s SignedRuntime) PrettyType() (interface{}, error) { var rt Runtime - if err := cbor.Unmarshal(s.Signed.Blob, &rt); err != nil { - return nil, fmt.Errorf("malformed signed blob: %w", err) + if err := cbor.Unmarshal(s.Envelope.Payload, &rt); err != nil { + return nil, fmt.Errorf("malformed signed payload: %w", err) } - return signature.NewPrettySigned(s.Signed, rt) + return multisig.NewPrettyEnvelope(s.Envelope, rt) } -// SignRuntime serializes the Runtime and signs the result. -func SignRuntime(signer signature.Signer, context signature.Context, runtime *Runtime) (*SignedRuntime, error) { - signed, err := signature.SignSigned(signer, context, runtime) +// SingleSignRuntime serializes the Runtime and signs the result. +// +// Note: This is a convenience routine that does not support runtimes +// backed by accounts with more than 1 signer. +func SingleSignRuntime(signer signature.Signer, account *multisig.Account, context signature.Context, runtime *Runtime) (*SignedRuntime, error) { + if len(account.Signers) != 1 { + return nil, fmt.Errorf("attemtped to single-sign multi-sig runtime") + } + rawRuntime := cbor.Marshal(runtime) + runtimeSig, err := multisig.Sign(signer, account, context, rawRuntime) if err != nil { return nil, err } - - return &SignedRuntime{ - Signed: *signed, - }, nil + envelope, err := multisig.NewEnvelope(account, []*signature.Signature{runtimeSig}, rawRuntime) + if err != nil { + return nil, err + } + return &SignedRuntime{*envelope}, nil } // VersionInfo is the per-runtime version information. diff --git a/go/registry/api/sanity_check.go b/go/registry/api/sanity_check.go index 01f7a3fc682..c18a2f2cfdd 100644 --- a/go/registry/api/sanity_check.go +++ b/go/registry/api/sanity_check.go @@ -54,9 +54,9 @@ func (g *Genesis) SanityCheck( // Check for blacklisted public keys. entities := []*entity.Entity{} - for k, ent := range seenEntities { - if publicKeyBlacklist[k] { - return fmt.Errorf("registry: sanity check failed: entity public key blacklisted: '%s'", k) + for addr, ent := range seenEntities { + if !addr.IsValid() { + return fmt.Errorf("registry: sanity check failed: entity address invalid: '%s'", addr) } entities = append(entities, ent) } @@ -65,8 +65,8 @@ func (g *Genesis) SanityCheck( return fmt.Errorf("registry: sanity check failed: could not obtain all runtimes from runtimesLookup: %w", err) } for _, rt := range runtimes { - if publicKeyBlacklist[rt.EntityID] { - return fmt.Errorf("registry: sanity check failed: runtime '%s' owned by blacklisted entity: '%s'", rt.ID, rt.EntityID) + if !rt.EntityAddress.IsValid() { + return fmt.Errorf("registry: sanity check failed: runtime '%s' owned by invalid address: '%s'", rt.ID, rt.EntityAddress) } } for k := range publicKeyBlacklist { @@ -89,14 +89,14 @@ func (g *Genesis) SanityCheck( // SanityCheckEntities examines the entities table. // Returns lookup of entity ID to the entity record for use in other checks. -func SanityCheckEntities(logger *logging.Logger, entities []*entity.SignedEntity) (map[signature.PublicKey]*entity.Entity, error) { - seenEntities := make(map[signature.PublicKey]*entity.Entity) +func SanityCheckEntities(logger *logging.Logger, entities []*entity.SignedEntity) (map[staking.Address]*entity.Entity, error) { + seenEntities := make(map[staking.Address]*entity.Entity) for _, signedEnt := range entities { entity, err := VerifyRegisterEntityArgs(logger, signedEnt, true, true) if err != nil { return nil, fmt.Errorf("entity sanity check failed: %w", err) } - seenEntities[entity.ID] = entity + seenEntities[entity.AccountAddress] = entity } return seenEntities, nil @@ -155,7 +155,7 @@ func SanityCheckNodes( logger *logging.Logger, params *ConsensusParameters, nodes []*node.MultiSignedNode, - seenEntities map[signature.PublicKey]*entity.Entity, + seenEntities map[staking.Address]*entity.Entity, runtimesLookup RuntimeLookup, isGenesis bool, epoch epochtime.EpochTime, @@ -175,7 +175,7 @@ func SanityCheckNodes( if !n.ID.IsValid() { return nil, fmt.Errorf("registry: node sanity check failed: ID %s is invalid", n.ID.String()) } - entity, ok := seenEntities[n.EntityID] + entity, ok := seenEntities[n.EntityAddress] if !ok { return nil, fmt.Errorf("registry: node sanity check failed node: %s references a missing entity", n.ID.String()) } @@ -224,7 +224,7 @@ func SanityCheckStake( // Generate escrow account for all entities. for _, entity := range entities { var escrow *staking.EscrowAccount - addr := staking.NewAddress(entity.ID) + addr := entity.AccountAddress acct, ok := accounts[addr] if ok { // Generate an escrow account with the same active balance and shares number. @@ -255,19 +255,19 @@ func SanityCheckStake( nodeRts = append(nodeRts, runtimeMap[rt.ID]) } // Add node stake claims. - addr := staking.NewAddress(node.EntityID) + addr := node.EntityAddress generatedEscrows[addr].StakeAccumulator.AddClaimUnchecked(StakeClaimForNode(node.ID), StakeThresholdsForNode(node, nodeRts)) } for _, rt := range runtimes { // Add runtime stake claims. - addr := staking.NewAddress(rt.EntityID) + addr := rt.EntityAddress generatedEscrows[addr].StakeAccumulator.AddClaimUnchecked(StakeClaimForRuntime(rt.ID), StakeThresholdsForRuntime(rt)) } // Compare entities' generated escrow accounts with actual ones. for _, entity := range entities { var generatedEscrow, actualEscrow *staking.EscrowAccount - addr := staking.NewAddress(entity.ID) + addr := entity.AccountAddress generatedEscrow = generatedEscrows[addr] acct, ok := accounts[addr] if ok { diff --git a/go/registry/gen_vectors/main.go b/go/registry/gen_vectors/main.go index 274ea8b4e5c..1ac1a5ea49b 100644 --- a/go/registry/gen_vectors/main.go +++ b/go/registry/gen_vectors/main.go @@ -6,7 +6,9 @@ import ( "fmt" "math" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -14,6 +16,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction/testvectors" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) func main() { @@ -35,16 +38,19 @@ func main() { for _, nonce := range []uint64{0, 1, 10, 42, 1000, 1_000_000, 10_000_000, math.MaxUint64} { // Valid register entity transactions. entitySigner := memorySigner.NewTestSigner("oasis-core registry test vectors: RegisterEntity signer") + entityAccount := multisig.NewAccountFromPublicKey(entitySigner.Public()) for _, numNodes := range []int{0, 1, 2, 5} { ent := entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, - ID: entitySigner.Public(), + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, + AccountAddress: staking.NewAddress(entityAccount), } for i := 0; i < numNodes; i++ { nodeSigner := memorySigner.NewTestSigner(fmt.Sprintf("oasis core registry test vectors: node signer %d", i)) ent.Nodes = append(ent.Nodes, nodeSigner.Public()) } - sigEnt, err := entity.SignEntity(entitySigner, registry.RegisterEntitySignatureContext, &ent) + sigEnt, err := entity.SingleSignEntity(entitySigner, entityAccount, registry.RegisterEntitySignatureContext, &ent) if err != nil { panic(err) } diff --git a/go/registry/tests/tester.go b/go/registry/tests/tester.go index efe7e0054ac..e928b9eb6e7 100644 --- a/go/registry/tests/tester.go +++ b/go/registry/tests/tester.go @@ -12,7 +12,9 @@ import ( "github.com/stretchr/testify/require" "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/drbg" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/crypto/tls" @@ -102,7 +104,7 @@ func testRegistryEntityNodes( // nolint: gocyclo var gotIt bool for _, evt := range evts { if evt.EntityEvent != nil { - if evt.EntityEvent.Entity.ID.Equal(ev.Entity.ID) && evt.EntityEvent.IsRegistration { + if evt.EntityEvent.Entity.AccountAddress.Equal(ev.Entity.AccountAddress) && evt.EntityEvent.IsRegistration { require.False(evt.TxHash.IsEmpty(), "Event transaction hash should not be empty") require.Greater(evt.Height, int64(0), "Event height should be greater than zero") gotIt = true @@ -118,7 +120,7 @@ func testRegistryEntityNodes( // nolint: gocyclo for _, v := range entities { var ent *entity.Entity - ent, err = backend.GetEntity(ctx, &api.IDQuery{ID: v.Entity.ID, Height: consensusAPI.HeightLatest}) + ent, err = backend.GetEntity(ctx, &api.AccountQuery{ID: v.Entity.AccountAddress, Height: consensusAPI.HeightLatest}) require.NoError(err, "GetEntity") require.EqualValues(v.Entity, ent, "retrieved entity") } @@ -127,20 +129,20 @@ func testRegistryEntityNodes( // nolint: gocyclo registeredEntities, err = backend.GetEntities(ctx, consensusAPI.HeightLatest) require.NoError(err, "GetEntities") // NOTE: The test entity is alway present as it controls a runtime and cannot be removed. - testEntity, _, _ := entity.TestEntity() + testEntity, _, _, _ := entity.TestEntity() require.Len(registeredEntities, len(entities)+1, "entities after registration") - seen := make(map[signature.PublicKey]bool) + seen := make(map[staking.Address]bool) for _, ent := range registeredEntities { - if ent.ID.Equal(testEntity.ID) { + if ent.AccountAddress.Equal(testEntity.AccountAddress) { continue } var isValid bool for _, v := range entities { - if v.Entity.ID.Equal(ent.ID) { + if v.Entity.AccountAddress.Equal(ent.AccountAddress) { require.EqualValues(v.Entity, ent, "bulk retrieved entity") - seen[ent.ID] = true + seen[ent.AccountAddress] = true isValid = true break } @@ -429,7 +431,7 @@ func testRegistryEntityNodes( // nolint: gocyclo var gotIt bool for _, evt := range evts { if evt.EntityEvent != nil { - if evt.EntityEvent.Entity.ID.Equal(ev.Entity.ID) && !evt.EntityEvent.IsRegistration { + if evt.EntityEvent.Entity.AccountAddress.Equal(ev.Entity.AccountAddress) && !evt.EntityEvent.IsRegistration { require.False(evt.TxHash.IsEmpty(), "Event transaction hash should not be empty") require.Greater(evt.Height, int64(0), "Event height should be greater than zero") gotIt = true @@ -468,7 +470,7 @@ func testRegistryEntityNodes( // nolint: gocyclo var gotIt bool for _, evt := range evts { if evt.EntityEvent != nil { - if evt.EntityEvent.Entity.ID.Equal(ev.Entity.ID) && !evt.EntityEvent.IsRegistration { + if evt.EntityEvent.Entity.AccountAddress.Equal(ev.Entity.AccountAddress) && !evt.EntityEvent.IsRegistration { require.False(evt.TxHash.IsEmpty(), "Event transaction hash should not be empty") require.Greater(evt.Height, int64(0), "Event height should be greater than zero") gotIt = true @@ -484,7 +486,7 @@ func testRegistryEntityNodes( // nolint: gocyclo // There should be no more entities. for _, v := range entities { - _, err := backend.GetEntity(ctx, &api.IDQuery{ID: v.Entity.ID, Height: consensusAPI.HeightLatest}) + _, err := backend.GetEntity(ctx, &api.AccountQuery{ID: v.Entity.AccountAddress, Height: consensusAPI.HeightLatest}) require.Equal(api.ErrNoSuchEntity, err, "GetEntity") } }) @@ -538,10 +540,11 @@ func testRegistryRuntime(t *testing.T, backend api.Backend, consensus consensusA // We must use the test entity for runtime registrations as registering a runtime will prevent // the entity from being deregistered and the other node tests already use the test entity for // deregistration. - testEntity, testEntitySigner, _ := entity.TestEntity() + testEntity, testEntitySigner, testEntityAccount, _ := entity.TestEntity() entity := &TestEntity{ - Entity: testEntity, - Signer: testEntitySigner, + Entity: testEntity, + Signer: testEntitySigner, + Account: testEntityAccount, } // Runtime registration test cases. @@ -563,8 +566,8 @@ func testRegistryRuntime(t *testing.T, backend api.Backend, consensus consensusA require.NoError(err, "NewTestEntities with entity node seed") rt.AdmissionPolicy = api.RuntimeAdmissionPolicy{ EntityWhitelist: &api.EntityWhitelistRuntimeAdmissionPolicy{ - Entities: map[signature.PublicKey]bool{ - nodeEntities[1].Entity.ID: true, + Entities: map[staking.Address]bool{ + nodeEntities[1].Entity.AccountAddress: true, }, }, } @@ -731,9 +734,9 @@ func EnsureRegistryEmpty(t *testing.T, backend api.Backend) { registeredEntities, err := backend.GetEntities(context.Background(), consensusAPI.HeightLatest) require.NoError(t, err, "GetEntities") // Allow one runtime-controlling entity (the test entity). - testEntity, _, _ := entity.TestEntity() + testEntity, _, _, _ := entity.TestEntity() require.Len(t, registeredEntities, 1, "registered entities") - require.Equal(t, testEntity.ID, registeredEntities[0].ID, "only the test entity can remain registered") + require.Equal(t, testEntity.AccountAddress, registeredEntities[0].AccountAddress, "only the test entity can remain registered") registeredNodes, err := backend.GetNodes(context.Background(), consensusAPI.HeightLatest) require.NoError(t, err, "GetNodes") @@ -743,8 +746,9 @@ func EnsureRegistryEmpty(t *testing.T, backend api.Backend) { // TestEntity is a testing Entity and some common pre-generated/signed // blobs useful for testing. type TestEntity struct { - Entity *entity.Entity - Signer signature.Signer + Entity *entity.Entity + Signer signature.Signer + Account *multisig.Account SignedRegistration *entity.SignedEntity @@ -826,7 +830,7 @@ func (ent *TestEntity) NewTestNodes(nCompute, nStorage int, idNonce []byte, runt } n := nCompute + nStorage - rng, err := drbg.New(crypto.SHA512, hashForDrbg(ent.Entity.ID[:]), idNonce, []byte("TestNodes")) + rng, err := drbg.New(crypto.SHA512, hashForDrbg(ent.Entity.AccountAddress[:]), idNonce, []byte("TestNodes")) if err != nil { return nil, err } @@ -855,12 +859,14 @@ func (ent *TestEntity) NewTestNodes(nCompute, nStorage int, idNonce []byte, runt } nod.Node = &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nod.Signer.Public(), - EntityID: ent.Entity.ID, - Expiration: uint64(expiration), - Runtimes: runtimes, - Roles: role, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nod.Signer.Public(), + EntityAddress: ent.Entity.AccountAddress, + Expiration: uint64(expiration), + Runtimes: runtimes, + Roles: role, } addr := node.Address{ TCPAddr: net.TCPAddr{ @@ -1139,12 +1145,14 @@ func (ent *TestEntity) NewTestNodes(nCompute, nStorage int, idNonce []byte, runt panic("should find one additional compute runtime") } nod.UpdatedNode = &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nod.Signer.Public(), - EntityID: ent.Entity.ID, - Expiration: uint64(expiration), - Runtimes: moreRuntimes, - Roles: role, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nod.Signer.Public(), + EntityAddress: ent.Entity.AccountAddress, + Expiration: uint64(expiration), + Runtimes: moreRuntimes, + Roles: role, } addr = node.Address{ TCPAddr: net.TCPAddr{ @@ -1171,14 +1179,16 @@ func (ent *TestEntity) NewTestNodes(nCompute, nStorage int, idNonce []byte, runt {ID: publicKeyToNamespace(testRuntimeSigner.Public(), false)}, } newNode := &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: nod.Signer.Public(), - EntityID: ent.Entity.ID, - Expiration: uint64(expiration), - Runtimes: newRuntimes, - Roles: role, - P2P: nod.Node.P2P, - TLS: nod.Node.TLS, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: nod.Signer.Public(), + EntityAddress: ent.Entity.AccountAddress, + Expiration: uint64(expiration), + Runtimes: newRuntimes, + Roles: role, + P2P: nod.Node.P2P, + TLS: nod.Node.TLS, } newNode.P2P.ID = invalidIdentity.P2PSigner.Public() newNode.Consensus.ID = invalidIdentity.ConsensusSigner.Public() @@ -1204,7 +1214,7 @@ func (ent *TestEntity) NewTestNodes(nCompute, nStorage int, idNonce []byte, runt descr: "Registering with an old descriptor should fail", } invNode15 := *nod.Node - invNode15.DescriptorVersion = 0 + invNode15.Versioned.V = 0 invalid15.signed, err = node.MultiSignNode( []signature.Signer{ nodeIdentity.NodeSigner, @@ -1241,13 +1251,16 @@ func NewTestEntities(seed []byte, n int) ([]*TestEntity, error) { if ent.Signer, err = memorySigner.NewSigner(rng); err != nil { return nil, err } + ent.Account = multisig.NewAccountFromPublicKey(ent.Signer.Public()) ent.Entity = &entity.Entity{ - DescriptorVersion: entity.LatestEntityDescriptorVersion, - ID: ent.Signer.Public(), + Versioned: cbor.Versioned{ + V: entity.LatestEntityDescriptorVersion, + }, + AccountAddress: staking.NewAddress(ent.Account), AllowEntitySignedNodes: true, } - ent.SignedRegistration, err = entity.SignEntity(ent.Signer, api.RegisterEntitySignatureContext, ent.Entity) + ent.SignedRegistration, err = entity.SingleSignEntity(ent.Signer, ent.Account, api.RegisterEntitySignatureContext, ent.Entity) if err != nil { return nil, err } @@ -1257,8 +1270,8 @@ func NewTestEntities(seed []byte, n int) ([]*TestEntity, error) { descr: "Registering with an old descriptor should fail", } invEnt1 := *ent.Entity - invEnt1.DescriptorVersion = 0 - invalid1.signed, err = entity.SignEntity(ent.Signer, api.RegisterEntitySignatureContext, &invEnt1) + invEnt1.Versioned.V = 0 + invalid1.signed, err = entity.SingleSignEntity(ent.Signer, ent.Account, api.RegisterEntitySignatureContext, &invEnt1) if err != nil { return nil, err } @@ -1275,6 +1288,7 @@ func NewTestEntities(seed []byte, n int) ([]*TestEntity, error) { type TestRuntime struct { Runtime *api.Runtime Signer signature.Signer + Account *multisig.Account entity *TestEntity nodes []*TestNode @@ -1290,10 +1304,10 @@ func (rt *TestRuntime) MustRegister(t *testing.T, backend api.Backend, consensus require.NoError(err, "WatchRuntimes") defer sub.Close() - signed, err := signature.SignSigned(rt.Signer, api.RegisterRuntimeSignatureContext, rt.Runtime) + signed, err := api.SingleSignRuntime(rt.Signer, rt.Account, api.RegisterRuntimeSignatureContext, rt.Runtime) require.NoError(err, "signed runtime descriptor") - tx := api.NewRegisterRuntimeTx(0, nil, &api.SignedRuntime{Signed: *signed}) + tx := api.NewRegisterRuntimeTx(0, nil, signed) err = consensusAPI.SignAndSubmitTx(context.Background(), consensus, rt.Signer, tx) require.NoError(err, "RegisterRuntime") @@ -1341,10 +1355,10 @@ func (rt *TestRuntime) MustRegister(t *testing.T, backend api.Backend, consensus func (rt *TestRuntime) MustNotRegister(t *testing.T, backend api.Backend, consensus consensusAPI.Backend) { require := require.New(t) - signed, err := signature.SignSigned(rt.Signer, api.RegisterRuntimeSignatureContext, rt.Runtime) + signed, err := api.SingleSignRuntime(rt.Signer, rt.Account, api.RegisterRuntimeSignatureContext, rt.Runtime) require.NoError(err, "signed runtime descriptor") - tx := api.NewRegisterRuntimeTx(0, nil, &api.SignedRuntime{Signed: *signed}) + tx := api.NewRegisterRuntimeTx(0, nil, signed) err = consensusAPI.SignAndSubmitTx(context.Background(), consensus, rt.Signer, tx) require.Error(err, "RegisterRuntime failure") } @@ -1387,7 +1401,7 @@ func BulkPopulate(t *testing.T, backend api.Backend, consensus consensusAPI.Back var gotIt bool for _, evt := range evts { if evt.EntityEvent != nil { - if evt.EntityEvent.Entity.ID.Equal(ev.Entity.ID) && evt.EntityEvent.IsRegistration { + if evt.EntityEvent.Entity.AccountAddress.Equal(ev.Entity.AccountAddress) && evt.EntityEvent.IsRegistration { require.False(evt.TxHash.IsEmpty(), "Event transaction hash should not be empty") require.Greater(evt.Height, int64(0), "Event height should be greater than zero") gotIt = true @@ -1517,7 +1531,7 @@ func (rt *TestRuntime) Cleanup(t *testing.T, backend api.Backend, consensus cons func NewTestRuntime(seed []byte, ent *TestEntity, isKeyManager bool) (*TestRuntime, error) { if ent == nil { ent = new(TestEntity) - ent.Entity, ent.Signer, _ = entity.TestEntity() + ent.Entity, ent.Signer, ent.Account, _ = entity.TestEntity() } flags := common.NamespaceTest @@ -1528,11 +1542,14 @@ func NewTestRuntime(seed []byte, ent *TestEntity, isKeyManager bool) (*TestRunti var rt TestRuntime rt.Signer = ent.Signer + rt.Account = ent.Account rt.Runtime = &api.Runtime{ - DescriptorVersion: api.LatestRuntimeDescriptorVersion, - ID: id, - EntityID: ent.Entity.ID, - Kind: api.KindCompute, + Versioned: cbor.Versioned{ + V: api.LatestRuntimeDescriptorVersion, + }, + ID: id, + EntityAddress: ent.Entity.AccountAddress, + Kind: api.KindCompute, Executor: api.ExecutorParameters{ GroupSize: 3, GroupBackupSize: 5, diff --git a/go/roothash/api/commitment/pool_test.go b/go/roothash/api/commitment/pool_test.go index da0fcb1528d..41b69d1eb05 100644 --- a/go/roothash/api/commitment/pool_test.go +++ b/go/roothash/api/commitment/pool_test.go @@ -61,9 +61,11 @@ type staticNodeLookup struct { func (n *staticNodeLookup) Node(ctx context.Context, id signature.PublicKey) (*node.Node, error) { return &node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: id, - Runtimes: []*node.Runtime{n.runtime}, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: id, + Runtimes: []*node.Runtime{n.runtime}, }, nil } @@ -110,10 +112,12 @@ func TestPoolSingleCommitment(t *testing.T) { _ = rtID.UnmarshalHex("0000000000000000000000000000000000000000000000000000000000000000") rt := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: rtID, - Kind: registry.KindCompute, - TEEHardware: node.TEEHardwareInvalid, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: rtID, + Kind: registry.KindCompute, + TEEHardware: node.TEEHardwareInvalid, Storage: registry.StorageParameters{ GroupSize: 1, MinWriteReplication: 1, @@ -226,10 +230,12 @@ func TestPoolSingleCommitmentTEE(t *testing.T) { _ = rtID.UnmarshalHex("0000000000000000000000000000000000000000000000000000000000000000") rt := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: rtID, - Kind: registry.KindCompute, - TEEHardware: node.TEEHardwareIntelSGX, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: rtID, + Kind: registry.KindCompute, + TEEHardware: node.TEEHardwareIntelSGX, } // Generate a commitment signing key. @@ -458,10 +464,12 @@ func TestPoolSerialization(t *testing.T) { _ = rtID.UnmarshalHex("0000000000000000000000000000000000000000000000000000000000000000") rt := ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: rtID, - Kind: registry.KindCompute, - TEEHardware: node.TEEHardwareInvalid, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: rtID, + Kind: registry.KindCompute, + TEEHardware: node.TEEHardwareInvalid, } // Generate a commitment signing key. @@ -1114,10 +1122,12 @@ func generateMockCommittee(t *testing.T) ( _ = rtID.UnmarshalHex("0000000000000000000000000000000000000000000000000000000000000000") rt = ®istry.Runtime{ - DescriptorVersion: registry.LatestRuntimeDescriptorVersion, - ID: rtID, - Kind: registry.KindCompute, - TEEHardware: node.TEEHardwareInvalid, + Versioned: cbor.Versioned{ + V: registry.LatestRuntimeDescriptorVersion, + }, + ID: rtID, + Kind: registry.KindCompute, + TEEHardware: node.TEEHardwareInvalid, Storage: registry.StorageParameters{ GroupSize: 1, MinWriteReplication: 1, diff --git a/go/staking/api/address.go b/go/staking/api/address.go index 1b177075857..7e430490b6d 100644 --- a/go/staking/api/address.go +++ b/go/staking/api/address.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/oasisprotocol/oasis-core/go/common/crypto/address" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/encoding/bech32" ) @@ -83,14 +84,16 @@ func (a Address) IsValid() bool { return address.Address(a).IsValid() && !a.IsReserved() } -// NewAddress creates a new address from the given public key, i.e. entity ID. -func NewAddress(pk signature.PublicKey) (a Address) { - pkData, _ := pk.MarshalBinary() - return (Address)(address.NewAddress(AddressV0Context, pkData)) +// NewAddress creates a new address from the given multisig account +// descriptor. +func NewAddress(account *multisig.Account) (a Address) { + addrData := account.Hash() + return (Address)(address.NewAddress(AddressV0Context, addrData)) } -// NewReservedAddress creates a new reserved address from the given public key -// or panics. +// NewReservedAddress creates a new reserved address from the given +// public key or panics. +// // NOTE: The given public key is also blacklisted. func NewReservedAddress(pk signature.PublicKey) (a Address) { // Blacklist the public key. @@ -98,8 +101,9 @@ func NewReservedAddress(pk signature.PublicKey) (a Address) { panic(err) } - // Add the address to the reserved addresses list. - addr := NewAddress(pk) + // Add the account address to the reserved addresses list. + account := multisig.NewAccountFromPublicKey(pk) + addr := NewAddress(account) if err := addr.Reserve(); err != nil { panic(err) } diff --git a/go/staking/api/address_test.go b/go/staking/api/address_test.go index 4c97d1ea6e6..256991fb95f 100644 --- a/go/staking/api/address_test.go +++ b/go/staking/api/address_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" ) @@ -16,7 +17,7 @@ func TestReserved(t *testing.T) { var addr, addr2 Address - addr = NewAddress(pk) + addr = NewAddress(multisig.NewAccountFromPublicKey(pk)) require.True(addr.IsValid(), "test address should initially be valid") require.False(addr.IsReserved(), "test address should not initially be reserved") diff --git a/go/staking/gen_vectors/main.go b/go/staking/gen_vectors/main.go index 79baeb4b550..a56a4647ad8 100644 --- a/go/staking/gen_vectors/main.go +++ b/go/staking/gen_vectors/main.go @@ -7,6 +7,7 @@ import ( "math" "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -35,7 +36,8 @@ func main() { for _, nonce := range []uint64{0, 1, 10, 42, 1000, 1_000_000, 10_000_000, math.MaxUint64} { // Valid transfer transactions. transferDst := memorySigner.NewTestSigner("oasis-core staking test vectors: Transfer dst") - transferDstAddr := staking.NewAddress(transferDst.Public()) + transferDstAccount := multisig.NewAccountFromPublicKey(transferDst.Public()) + transferDstAddr := staking.NewAddress(transferDstAccount) for _, amt := range []uint64{0, 1000, 10_000_000} { for _, tx := range []*transaction.Transaction{ staking.NewTransferTx(nonce, fee, &staking.Transfer{ @@ -60,7 +62,8 @@ func main() { // Valid escrow transactions. escrowDst := memorySigner.NewTestSigner("oasis-core staking test vectors: Escrow dst") - escrowDstAddr := staking.NewAddress(escrowDst.Public()) + escrowDstAccount := multisig.NewAccountFromPublicKey(escrowDst.Public()) + escrowDstAddr := staking.NewAddress(escrowDstAccount) for _, amt := range []uint64{0, 1000, 10_000_000} { for _, tx := range []*transaction.Transaction{ staking.NewAddEscrowTx(nonce, fee, &staking.Escrow{ @@ -74,7 +77,8 @@ func main() { // Valid reclaim escrow transactions. escrowSrc := memorySigner.NewTestSigner("oasis-core staking test vectors: ReclaimEscrow src") - escrowSrcAddr := staking.NewAddress(escrowSrc.Public()) + escrowSrcAccount := multisig.NewAccountFromPublicKey(escrowSrc.Public()) + escrowSrcAddr := staking.NewAddress(escrowSrcAccount) for _, amt := range []uint64{0, 1000, 10_000_000} { for _, tx := range []*transaction.Transaction{ staking.NewReclaimEscrowTx(nonce, fee, &staking.ReclaimEscrow{ diff --git a/go/staking/tests/debug/debug_stake.go b/go/staking/tests/debug/debug_stake.go index 8a873665371..4935b47c1c8 100644 --- a/go/staking/tests/debug/debug_stake.go +++ b/go/staking/tests/debug/debug_stake.go @@ -5,6 +5,7 @@ import ( "math" "math/big" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/quantity" @@ -65,9 +66,11 @@ var ( } DebugStateSrcSigner = mustGenerateSigner() - DebugStateSrcAddress = api.NewAddress(DebugStateSrcSigner.Public()) + DebugStateSrcAddress = api.NewAddress(DebugStateSrcAccount) + DebugStateSrcAccount = multisig.NewAccountFromPublicKey(DebugStateSrcSigner.Public()) destSigner = mustGenerateSigner() - DebugStateDestAddress = api.NewAddress(destSigner.Public()) + destAccount = multisig.NewAccountFromPublicKey(destSigner.Public()) + DebugStateDestAddress = api.NewAddress(destAccount) ) func QtyFromInt(n int) quantity.Quantity { diff --git a/go/staking/tests/tester.go b/go/staking/tests/tester.go index a2ecc4c0bcc..449fff7f5fd 100644 --- a/go/staking/tests/tester.go +++ b/go/staking/tests/tester.go @@ -712,10 +712,8 @@ func testSlashDoubleSigning( require.NoError(err, "WatchEscrows") defer escrowSub.Close() - entAddr := api.NewAddress(ent.ID) - escrow := &api.Escrow{ - Account: entAddr, + Account: ent.AccountAddress, Amount: debug.QtyFromInt(math.MaxUint32), } tx := api.NewAddEscrowTx(srcAcc.General.Nonce, nil, escrow) @@ -727,7 +725,7 @@ func testSlashDoubleSigning( ev := rawEv.Add require.NotNil(ev) require.Equal(SrcAddr, ev.Owner, "Event: owner") - require.Equal(entAddr, ev.Escrow, "Event: escrow") + require.Equal(ent.AccountAddress, ev.Escrow, "Event: escrow") require.Equal(escrow.Amount, ev.Amount, "Event: amount") case <-time.After(recvTimeout): t.Fatalf("failed to receive escrow event") @@ -754,7 +752,7 @@ WaitLoop: select { case ev := <-slashCh: if e := ev.Take; e != nil { - require.Equal(entAddr, e.Owner, "TakeEscrowEvent - owner must be entity's address") + require.Equal(ent.AccountAddress, e.Owner, "TakeEscrowEvent - owner must be entity's address") // All stake must be slashed as defined in debugGenesisState. require.Equal(escrow.Amount, e.Amount, "TakeEscrowEvent - all stake slashed") break WaitLoop diff --git a/go/upgrade/migrations/dummy.go b/go/upgrade/migrations/dummy.go index 903ecc010d2..990aaad9fa9 100644 --- a/go/upgrade/migrations/dummy.go +++ b/go/upgrade/migrations/dummy.go @@ -3,6 +3,7 @@ package migrations import ( "fmt" + "github.com/oasisprotocol/oasis-core/go/common/crypto/multisig" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -25,13 +26,18 @@ var ( TestEntity entity.Entity - entitySigner signature.Signer + entityAccount *multisig.Account + entityAddress staking.Address + entitySigner signature.Signer ) func init() { entitySigner = memory.NewTestSigner(testSigningSeed) - TestEntity.DescriptorVersion = entity.LatestEntityDescriptorVersion - TestEntity.ID = entitySigner.Public() + entityAccount = multisig.NewAccountFromPublicKey(entitySigner.Public()) + entityAddress = staking.NewAddress(entityAccount) + + TestEntity.Versioned.V = entity.LatestEntityDescriptorVersion + TestEntity.AccountAddress = entityAddress } type dummyMigrationHandler struct { @@ -46,7 +52,7 @@ func (th *dummyMigrationHandler) ConsensusUpgrade(ctx *Context, privateCtx inter regState := registryState.NewMutableState(abciCtx.State()) stakeState := stakingState.NewMutableState(abciCtx.State()) - sigEntity, err := entity.SignEntity(entitySigner, registry.RegisterEntitySignatureContext, &TestEntity) + sigEntity, err := entity.SingleSignEntity(entitySigner, entityAccount, registry.RegisterEntitySignatureContext, &TestEntity) if err != nil { return fmt.Errorf("failed to sign entity: %w", err) } @@ -58,8 +64,7 @@ func (th *dummyMigrationHandler) ConsensusUpgrade(ctx *Context, privateCtx inter } // Set this entity's staking properly. - testEntityAddr := staking.NewAddress(TestEntity.ID) - err = stakeState.SetAccount(abciCtx, testEntityAddr, &staking.Account{ + err = stakeState.SetAccount(abciCtx, entityAddress, &staking.Account{ Escrow: staking.EscrowAccount{ StakeAccumulator: staking.StakeAccumulator{ Claims: map[staking.StakeClaim][]staking.StakeThreshold{ diff --git a/go/worker/registration/worker.go b/go/worker/registration/worker.go index 9e50461ecf9..4d23e85c517 100644 --- a/go/worker/registration/worker.go +++ b/go/worker/registration/worker.go @@ -14,6 +14,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/accessctl" + "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" fileSigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/file" "github.com/oasisprotocol/oasis-core/go/common/entity" @@ -28,6 +29,7 @@ import ( registry "github.com/oasisprotocol/oasis-core/go/registry/api" runtimeRegistry "github.com/oasisprotocol/oasis-core/go/runtime/registry" sentryClient "github.com/oasisprotocol/oasis-core/go/sentry/client" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" workerCommon "github.com/oasisprotocol/oasis-core/go/worker/common" "github.com/oasisprotocol/oasis-core/go/worker/common/p2p" ) @@ -146,7 +148,7 @@ type Worker struct { // nolint: maligned deregRequested uint32 delegate Delegate - entityID signature.PublicKey + entityAddress staking.Address registrationSigner signature.Signer sentryAddresses []node.TLSAddress @@ -630,10 +632,12 @@ func (w *Worker) registerNode(epoch epochtime.EpochTime, hook RegisterNodeHook) } nodeDesc := node.Node{ - DescriptorVersion: node.LatestNodeDescriptorVersion, - ID: identityPublic, - EntityID: w.entityID, - Expiration: uint64(epoch) + 2, + Versioned: cbor.Versioned{ + V: node.LatestNodeDescriptorVersion, + }, + ID: identityPublic, + EntityAddress: w.entityAddress, + Expiration: uint64(epoch) + 2, TLS: node.TLSInfo{ PubKey: w.identity.GetTLSSigner().Public(), NextPubKey: nextPubKey, @@ -803,14 +807,15 @@ func (w *Worker) RequestDeregistration() error { } // GetRegistrationSigner loads the signing credentials as configured by this package's flags. -func GetRegistrationSigner(logger *logging.Logger, dataDir string, identity *identity.Identity) (signature.PublicKey, signature.Signer, error) { - var defaultPk signature.PublicKey +func GetRegistrationSigner(logger *logging.Logger, dataDir string, identity *identity.Identity) (staking.Address, signature.Signer, error) { + var defaultAddr staking.Address // If the test entity is enabled, use the entity signing key for signing // registrations. if flags.DebugTestEntity() { - testEntity, testSigner, _ := entity.TestEntity() - return testEntity.ID, testSigner, nil + _, testSigner, testAccount, _ := entity.TestEntity() + testAccountAddr := staking.NewAddress(testAccount) + return testAccountAddr, testSigner, nil } // Load the registration entity descriptor. @@ -820,29 +825,29 @@ func GetRegistrationSigner(logger *logging.Logger, dataDir string, identity *ide // spin up workers, which require a registration worker, but don't // need it, and do not have an owning entity. The registration worker // should not be initialized in this case. - return defaultPk, nil, nil + return defaultAddr, nil, nil } // Attempt to load the entity descriptor. entity, err := entity.LoadDescriptor(f) if err != nil { - return defaultPk, nil, fmt.Errorf("worker/registration: failed to load entity descriptor: %w", err) + return defaultAddr, nil, fmt.Errorf("worker/registration: failed to load entity descriptor: %w", err) } if !entity.AllowEntitySignedNodes { // If the entity does not allow any entity-signed nodes, then // registrations will always be node-signed. - return entity.ID, identity.NodeSigner, nil + return entity.AccountAddress, identity.NodeSigner, nil } for _, v := range entity.Nodes { if v.Equal(identity.NodeSigner.Public()) { // If the node is in the entity's list of allowed nodes // then registrations MUST be node-signed. - return entity.ID, identity.NodeSigner, nil + return entity.AccountAddress, identity.NodeSigner, nil } } if !flags.DebugDontBlameOasis() { - return defaultPk, nil, fmt.Errorf("worker/registration: entity signed nodes disallowed by node config") + return defaultAddr, nil, fmt.Errorf("worker/registration: entity signed nodes disallowed by node config") } // At this point, the entity allows entity-signed registrations, @@ -862,22 +867,22 @@ func GetRegistrationSigner(logger *logging.Logger, dataDir string, identity *ide // just be stale. logger.Warn("no entity signing key provided, falling back to the node identity key") - return entity.ID, identity.NodeSigner, nil + return entity.AccountAddress, identity.NodeSigner, nil } logger.Warn("using the entity signing key for node registration") factory, err := fileSigner.NewFactory(dataDir, signature.SignerEntity) if err != nil { - return defaultPk, nil, fmt.Errorf("worker/registration: failed to create entity signer factory: %w", err) + return defaultAddr, nil, fmt.Errorf("worker/registration: failed to create entity signer factory: %w", err) } fileFactory := factory.(*fileSigner.Factory) entitySigner, err := fileFactory.ForceLoad(f) if err != nil { - return defaultPk, nil, fmt.Errorf("worker/registration: failed to load entity signing key: %w", err) + return defaultAddr, nil, fmt.Errorf("worker/registration: failed to load entity signing key: %w", err) } - return entity.ID, entitySigner, nil + return entity.AccountAddress, entitySigner, nil } // New constructs a new worker node registration service. @@ -903,7 +908,7 @@ func New( return nil, err } - entityID, registrationSigner, err := GetRegistrationSigner(logger, dataDir, identity) + entityAddr, registrationSigner, err := GetRegistrationSigner(logger, dataDir, identity) if err != nil { return nil, err } @@ -931,7 +936,7 @@ func New( store: serviceStore, storedDeregister: storedDeregister, delegate: delegate, - entityID: entityID, + entityAddress: entityAddr, sentryAddresses: workerCommonCfg.SentryAddresses, registrationSigner: registrationSigner, runtimeRegistry: runtimeRegistry, @@ -972,7 +977,7 @@ func (w *Worker) Start() error { w.logger.Info("starting node registration service") // HACK: This can be ok in certain configurations. - if !w.entityID.IsValid() || w.registrationSigner == nil { + if !w.entityAddress.IsValid() || w.registrationSigner == nil { w.logger.Warn("no entity/signer for this node, registration will NEVER succeed") // Make sure the node is stopped on quit. go func() { diff --git a/tests/fixture-data/consim/genesis.json b/tests/fixture-data/consim/genesis.json index 5b0259745cc..57ae2b568a1 100644 --- a/tests/fixture-data/consim/genesis.json +++ b/tests/fixture-data/consim/genesis.json @@ -1,328 +1 @@ -{ - "height": 0, - "genesis_time": "2020-04-01T11:27:11.906400344Z", - "chain_id": "test", - "epochtime": { - "params": { - "interval": 86400, - "debug_mock_backend": false - }, - "base": 0 - }, - "registry": { - "params": { - "gas_costs": { - "deregister_entity": 1000, - "register_entity": 1000, - "register_node": 1000, - "register_runtime": 1000, - "runtime_epoch_maintenance": 1000, - "unfreeze_node": 1000, - "update_keymanager": 1000 - }, - "max_node_expiration": 5 - }, - "entities": [ - { - "untrusted_raw_value": "o2F2AWJpZFggTqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TV4GWFsbG93X2VudGl0eV9zaWduZWRfbm9kZXP1", - "signature": { - "public_key": "TqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TU=", - "signature": "CHTyRfJoYWxIbX6gmXvCrHg10tZHS6pwKuPdyhrw/5zrA5cnlYAzZwuhtL0j9a9le1mA23v9hRbKSai8XjqAAA==" - } - } - ] - }, - "roothash": { - "params": { - "gas_costs": { - "compute_commit": 1000, - "merge_commit": 1000 - } - } - }, - "staking": { - "params": { - "thresholds": { - "entity": "0", - "node-validator": "0", - "node-compute": "0", - "node-storage": "0", - "node-keymanager": "0", - "runtime-compute": "0", - "runtime-keymanager": "0" - }, - "debonding_interval": 2, - "commission_schedule_rules": { - "rate_change_interval": 10, - "rate_bound_lead": 30, - "max_rate_steps": 12, - "max_bound_steps": 12 - }, - "gas_costs": { - "add_escrow": 10, - "burn": 10, - "reclaim_escrow": 10, - "transfer": 10 - }, - "min_delegation": "0", - "fee_split_weight_propose": "2", - "fee_split_weight_vote": "1", - "fee_split_weight_next_propose": "1", - "reward_factor_epoch_signed": "0", - "reward_factor_block_proposed": "0" - }, - "token_symbol": "TEST", - "total_supply": "290000000000", - "common_pool": "0", - "last_block_fees": "0", - "ledger": { - "oasis1qrhp36j49ncpaac0aufwyuvtk04nfxcj2yq7y4my": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qp6tl30ljsrrqnw2awxxu2mtxk0qxyy2nymtsy90": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qryg8qf3ydzcphr328l8psz007fms9dxeuy8lgzq": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qpt202cf6t0s5ugkk34p83yf0c30gpjkny92u7dh": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qzzd6khm3acqskpxlk9vd5044cmmcce78y5l6000": { - "general": { - "balance": "100000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "100000000000", - "total_shares": "1" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qzyw75ds6nw0af98xfmmpl3z8sgf3mdslvtzzcn6": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qzc2fexm30puzq2cmlm832fvpnyaxrq33cx4zukj": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qpx0k28va6n0r25qd2j4jdh9f42n5vex6s9lp780": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qr77y0cqdzcqgz2wqkv59yz0j4vfvyryfv8vxllt": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - }, - "oasis1qz74khszg55gfnmpxut3t3gdymn76hfchu9nhtd0": { - "general": { - "balance": "10000000000", - "nonce": 0 - }, - "escrow": { - "active": { - "balance": "0", - "total_shares": "0" - }, - "debonding": { - "balance": "0", - "total_shares": "0" - }, - "commission_schedule": { - "rates": null, - "bounds": null - }, - "stake_accumulator": {} - } - } - }, - "delegations": { - "oasis1qzzd6khm3acqskpxlk9vd5044cmmcce78y5l6000": { - "oasis1qzzd6khm3acqskpxlk9vd5044cmmcce78y5l6000": { - "shares": "1" - } - } - } - }, - "keymanager": {}, - "scheduler": { - "params": { - "min_validators": 1, - "max_validators": 100, - "max_validators_per_entity": 1, - "debug_bypass_stake": false, - "debug_static_validators": false, - "reward_factor_epoch_election_any": "0" - } - }, - "beacon": { - "params": { - "debug_deterministic": false - } - }, - "consensus": { - "backend": "tendermint", - "params": { - "timeout_commit": 1000000000, - "skip_timeout_commit": false, - "empty_block_interval": 0, - "max_tx_size": 32768, - "max_block_size": 22020096, - "max_block_gas": 0, - "max_evidence_age": 100000 - } - }, - "halt_epoch": 18446744073709551615, - "extra_data": null -} +{"height":0,"genesis_time":"2020-07-16T12:21:45.321530685Z","chain_id":"test","epochtime":{"params":{"interval":86400,"debug_mock_backend":false},"base":0},"registry":{"params":{"gas_costs":{"deregister_entity":1000,"register_entity":1000,"register_node":1000,"register_runtime":1000,"runtime_epoch_maintenance":1000,"unfreeze_node":1000,"update_keymanager":1000},"max_node_expiration":5},"entities":[{"account":{"v":0,"signers":[{"public_key":"TqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TU=","weight":1}],"threshold":1},"signatures":["ZkMI7Pbn/ZsdLzH1F/d1mMz1XY4O1KWU/AyYRvgbJ8O90FLuYyvV+CvxakhCcnzwXg+bTgxdZt59hxu50Z1lBw=="],"payload":"o2F2AW9hY2NvdW50X2FkZHJlc3NVAKn/rA3/YzKBP4g3oB1YQ063Z7w3eBlhbGxvd19lbnRpdHlfc2lnbmVkX25vZGVz9Q=="}]},"roothash":{"params":{"gas_costs":{"compute_commit":1000,"merge_commit":1000}}},"staking":{"params":{"thresholds":{"entity":"0","node-compute":"0","node-keymanager":"0","node-storage":"0","node-validator":"0","runtime-compute":"0","runtime-keymanager":"0"},"commission_schedule_rules":{},"min_delegation":"0","fee_split_weight_propose":"0","fee_split_weight_vote":"1","fee_split_weight_next_propose":"0","reward_factor_epoch_signed":"0","reward_factor_block_proposed":"0"},"token_symbol":"TEST","token_value_exponent":0,"total_supply":"200000000000","common_pool":"0","last_block_fees":"0","ledger":{"oasis1qz5lltqdla3n9qfl3qm6q82cgd8tweauxucvwlzp":{"general":{"balance":"100000000000"},"escrow":{"active":{"balance":"100000000000","total_shares":"1"},"debonding":{"balance":"0","total_shares":"0"},"commission_schedule":{},"stake_accumulator":{}}}},"delegations":{"oasis1qz5lltqdla3n9qfl3qm6q82cgd8tweauxucvwlzp":{"oasis1qz5lltqdla3n9qfl3qm6q82cgd8tweauxucvwlzp":{"shares":"1"}}}},"keymanager":{},"scheduler":{"params":{"min_validators":1,"max_validators":100,"max_validators_per_entity":1,"debug_bypass_stake":false,"debug_static_validators":false,"reward_factor_epoch_election_any":"0"}},"beacon":{"params":{"debug_deterministic":false}},"consensus":{"backend":"tendermint","params":{"timeout_commit":1000000000,"skip_timeout_commit":false,"empty_block_interval":0,"max_tx_size":32768,"max_block_size":22020096,"max_block_gas":0,"max_evidence_age_blocks":100000,"max_evidence_age_time":172800000000000,"state_checkpoint_interval":10000,"state_checkpoint_num_kept":2,"state_checkpoint_chunk_size":8388608,"gas_costs":{"tx_byte":1}}},"halt_epoch":18446744073709551615,"extra_data":null} \ No newline at end of file diff --git a/tests/fixture-data/debond/staking-genesis.json b/tests/fixture-data/debond/staking-genesis.json index cd187f9d582..0609f1f1010 100644 --- a/tests/fixture-data/debond/staking-genesis.json +++ b/tests/fixture-data/debond/staking-genesis.json @@ -18,7 +18,7 @@ }, "total_supply": "1000", "ledger": { - "oasis1qq7us2p22udg2t24u6ry4m29wzql005pjsske8gt": { + "oasis1qrl72f9zn2huxejvn9f2tahf96scanllqgdlzqmf": { "escrow": { "debonding": { "balance": "1000", @@ -28,8 +28,8 @@ } }, "debonding_delegations": { - "oasis1qq7us2p22udg2t24u6ry4m29wzql005pjsske8gt": { - "oasis1qpt202cf6t0s5ugkk34p83yf0c30gpjkny92u7dh": [ + "oasis1qrl72f9zn2huxejvn9f2tahf96scanllqgdlzqmf": { + "oasis1qz92ljfknlc2nqzwdwhk0lqqe4rxzuthfcvj49hs": [ { "shares": "500", "debond_end": 1 diff --git a/tests/fixture-data/gas-fees-runtimes/staking-genesis.json b/tests/fixture-data/gas-fees-runtimes/staking-genesis.json index dec677efb5e..4c98a19a979 100644 --- a/tests/fixture-data/gas-fees-runtimes/staking-genesis.json +++ b/tests/fixture-data/gas-fees-runtimes/staking-genesis.json @@ -6,17 +6,17 @@ }, "total_supply": "90000000", "ledger": { - "oasis1qpt202cf6t0s5ugkk34p83yf0c30gpjkny92u7dh": {"general": {"balance": "10000000"}}, - "oasis1qryg8qf3ydzcphr328l8psz007fms9dxeuy8lgzq": {"general": {"balance": "10000000"}}, - "oasis1qz74khszg55gfnmpxut3t3gdymn76hfchu9nhtd0": {"general": {"balance": "10000000"}}, + "oasis1qz92ljfknlc2nqzwdwhk0lqqe4rxzuthfcvj49hs": {"general": {"balance": "10000000"}}, + "oasis1qphhqrp0gafutys4uafmqpp30v9x4pqtwur5e4h9": {"general": {"balance": "10000000"}}, + "oasis1qqae9l2pqawz3tqlqnvlhr0uj0e6x84lgylrxqhn": {"general": {"balance": "10000000"}}, - "oasis1qp6tl30ljsrrqnw2awxxu2mtxk0qxyy2nymtsy90": {"general": {"balance": "10000000"}}, - "oasis1qr77y0cqdzcqgz2wqkv59yz0j4vfvyryfv8vxllt": {"general": {"balance": "10000000"}}, - "oasis1qzyw75ds6nw0af98xfmmpl3z8sgf3mdslvtzzcn6": {"general": {"balance": "10000000"}}, + "oasis1qpqhgt3jfp782dtt7lt3hj3nf87vjlcnm5h055fd": {"general": {"balance": "10000000"}}, + "oasis1qzm9nscealzaercv8xq6a9g5cn48g2xcavt8v2ax": {"general": {"balance": "10000000"}}, + "oasis1qp4q7depyuke0exm0c847pun04v2d5hg45m73edz": {"general": {"balance": "10000000"}}, - "oasis1qrhp36j49ncpaac0aufwyuvtk04nfxcj2yq7y4my": {"general": {"balance": "10000000"}}, - "oasis1qzc2fexm30puzq2cmlm832fvpnyaxrq33cx4zukj": {"general": {"balance": "10000000"}}, + "oasis1qz4ayq5q5hzrsphuaam0j4qat80w0eaq95ny8spu": {"general": {"balance": "10000000"}}, + "oasis1qrfv2hle58n6yuarka99kpp0l6g6lms8fsewuvym": {"general": {"balance": "10000000"}}, - "oasis1qpx0k28va6n0r25qd2j4jdh9f42n5vex6s9lp780": {"general": {"balance": "10000000"}} + "oasis1qpr2z463zr0fcnpn5lvyssg0k2hkl3f6yszvu9ju": {"general": {"balance": "10000000"}} } } diff --git a/tests/fixture-data/gas-fees/staking-genesis.json b/tests/fixture-data/gas-fees/staking-genesis.json index 9faf6a96928..bb2fb0fd08c 100644 --- a/tests/fixture-data/gas-fees/staking-genesis.json +++ b/tests/fixture-data/gas-fees/staking-genesis.json @@ -15,7 +15,7 @@ "common_pool": "150", "last_block_fees": "50", "ledger": { - "oasis1qq7us2p22udg2t24u6ry4m29wzql005pjsske8gt": { + "oasis1qrl72f9zn2huxejvn9f2tahf96scanllqgdlzqmf": { "general": { "balance": "1000" } diff --git a/tests/fixture-data/txsource/staking-genesis.json b/tests/fixture-data/txsource/staking-genesis.json index 3ef770e6046..c68bb542215 100644 --- a/tests/fixture-data/txsource/staking-genesis.json +++ b/tests/fixture-data/txsource/staking-genesis.json @@ -19,18 +19,18 @@ }, "total_supply": "100000000000", "ledger": { - "oasis1qpt202cf6t0s5ugkk34p83yf0c30gpjkny92u7dh": {"general": {"balance": "10000000000"}}, - "oasis1qryg8qf3ydzcphr328l8psz007fms9dxeuy8lgzq": {"general": {"balance": "10000000000"}}, - "oasis1qz74khszg55gfnmpxut3t3gdymn76hfchu9nhtd0": {"general": {"balance": "10000000000"}}, - "oasis1qqkspsglt3quhpkghr837trfwm048srjuv8g92jj": {"general": {"balance": "10000000000"}}, + "oasis1qz92ljfknlc2nqzwdwhk0lqqe4rxzuthfcvj49hs": {"general": {"balance": "10000000000"}}, + "oasis1qphhqrp0gafutys4uafmqpp30v9x4pqtwur5e4h9": {"general": {"balance": "10000000000"}}, + "oasis1qqae9l2pqawz3tqlqnvlhr0uj0e6x84lgylrxqhn": {"general": {"balance": "10000000000"}}, + "oasis1qrpgfhtv63trcpfrw4evmx0z8mg6cd6vrshvkgwe": {"general": {"balance": "10000000000"}}, - "oasis1qp6tl30ljsrrqnw2awxxu2mtxk0qxyy2nymtsy90": {"general": {"balance": "10000000000"}}, - "oasis1qr77y0cqdzcqgz2wqkv59yz0j4vfvyryfv8vxllt": {"general": {"balance": "10000000000"}}, - "oasis1qzyw75ds6nw0af98xfmmpl3z8sgf3mdslvtzzcn6": {"general": {"balance": "10000000000"}}, + "oasis1qpqhgt3jfp782dtt7lt3hj3nf87vjlcnm5h055fd": {"general": {"balance": "10000000000"}}, + "oasis1qzm9nscealzaercv8xq6a9g5cn48g2xcavt8v2ax": {"general": {"balance": "10000000000"}}, + "oasis1qp4q7depyuke0exm0c847pun04v2d5hg45m73edz": {"general": {"balance": "10000000000"}}, - "oasis1qrhp36j49ncpaac0aufwyuvtk04nfxcj2yq7y4my": {"general": {"balance": "10000000000"}}, - "oasis1qzc2fexm30puzq2cmlm832fvpnyaxrq33cx4zukj": {"general": {"balance": "10000000000"}}, + "oasis1qz4ayq5q5hzrsphuaam0j4qat80w0eaq95ny8spu": {"general": {"balance": "10000000000"}}, + "oasis1qrfv2hle58n6yuarka99kpp0l6g6lms8fsewuvym": {"general": {"balance": "10000000000"}}, - "oasis1qpx0k28va6n0r25qd2j4jdh9f42n5vex6s9lp780": {"general": {"balance": "10000000000"}} + "oasis1qpr2z463zr0fcnpn5lvyssg0k2hkl3f6yszvu9ju": {"general": {"balance": "10000000000"}} } }