This commit is contained in:
StephenButtolph 2020-03-30 22:06:29 -04:00
commit 4d34dc042d
21 changed files with 973 additions and 664 deletions

View File

@ -93,6 +93,7 @@ type manager struct {
// That is, [chainID].String() is an alias for the chain, too // That is, [chainID].String() is an alias for the chain, too
ids.Aliaser ids.Aliaser
stakingEnabled bool // True iff the network has staking enabled
log logging.Logger log logging.Logger
logFactory logging.Factory logFactory logging.Factory
vmManager vms.Manager // Manage mappings from vm ID --> vm vmManager vms.Manager // Manage mappings from vm ID --> vm
@ -122,6 +123,7 @@ type manager struct {
// <validators> validate this chain // <validators> validate this chain
// TODO: Make this function take less arguments // TODO: Make this function take less arguments
func New( func New(
stakingEnabled bool,
log logging.Logger, log logging.Logger,
logFactory logging.Factory, logFactory logging.Factory,
vmManager vms.Manager, vmManager vms.Manager,
@ -146,6 +148,7 @@ func New(
router.Initialize(log, &timeoutManager) router.Initialize(log, &timeoutManager)
m := &manager{ m := &manager{
stakingEnabled: stakingEnabled,
log: log, log: log,
logFactory: logFactory, logFactory: logFactory,
vmManager: vmManager, vmManager: vmManager,
@ -261,7 +264,13 @@ func (m *manager) ForceCreateChain(chain ChainParameters) {
} }
// The validators of this blockchain // The validators of this blockchain
validators, ok := m.validators.GetValidatorSet(ids.Empty) // TODO: Change argument to chain.SubnetID var validators validators.Set // Validators validating this blockchain
var ok bool
if m.stakingEnabled {
validators, ok = m.validators.GetValidatorSet(chain.SubnetID)
} else { // Staking is disabled. Every peer validates every subnet.
validators, ok = m.validators.GetValidatorSet(ids.Empty) // ids.Empty is the default subnet ID. TODO: Move to const package so we can use it here.
}
if !ok { if !ok {
m.log.Error("couldn't get validator set of subnet with ID %s. The subnet may not exist", chain.SubnetID) m.log.Error("couldn't get validator set of subnet with ID %s. The subnet may not exist", chain.SubnetID)
return return
@ -358,7 +367,7 @@ func (m *manager) createAvalancheChain(
msgChan := make(chan common.Message, defaultChannelSize) msgChan := make(chan common.Message, defaultChannelSize)
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, fxs); err != nil { if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, fxs); err != nil {
return err return fmt.Errorf("error during vm's Initialize: %w", err)
} }
// Handles serialization/deserialization of vertices and also the // Handles serialization/deserialization of vertices and also the

37
chains/mock_manager.go Normal file
View File

@ -0,0 +1,37 @@
package chains
import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/networking/router"
)
// MockManager implements Manager but does nothing. Always returns nil error.
// To be used only in tests (namely in package platformvm)
type MockManager struct{}
// Router ...
func (mm MockManager) Router() router.Router { return nil }
// CreateChain ...
func (mm MockManager) CreateChain(ChainParameters) {}
// ForceCreateChain ...
func (mm MockManager) ForceCreateChain(ChainParameters) {}
// AddRegistrant ...
func (mm MockManager) AddRegistrant(Registrant) {}
// Lookup ...
func (mm MockManager) Lookup(string) (ids.ID, error) { return ids.ID{}, nil }
// LookupVM ...
func (mm MockManager) LookupVM(string) (ids.ID, error) { return ids.ID{}, nil }
// Aliases ...
func (mm MockManager) Aliases(ids.ID) []string { return nil }
// Alias ...
func (mm MockManager) Alias(ids.ID, string) error { return nil }
// Shutdown ...
func (mm MockManager) Shutdown() {}

View File

@ -3,16 +3,25 @@
package genesis package genesis
// TODO: Move this to a separate repo and leave only a byte array
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"math/big"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/params"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/units"
"github.com/ava-labs/gecko/vms/avm" "github.com/ava-labs/gecko/vms/avm"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/evm" "github.com/ava-labs/gecko/vms/evm"
@ -149,9 +158,10 @@ func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases
spdagvm.ID.Key(): []string{"spdag"}, spdagvm.ID.Key(): []string{"spdag"},
spchainvm.ID.Key(): []string{"spchain"}, spchainvm.ID.Key(): []string{"spchain"},
timestampvm.ID.Key(): []string{"timestamp"}, timestampvm.ID.Key(): []string{"timestamp"},
secp256k1fx.ID.Key(): []string{"secp256k1fx"},
} }
genesisBytes := Genesis(networkID) genesisBytes, _ := Genesis(networkID)
genesis := &platformvm.Genesis{} // TODO let's not re-create genesis to do aliasing genesis := &platformvm.Genesis{} // TODO let's not re-create genesis to do aliasing
platformvm.Codec.Unmarshal(genesisBytes, genesis) // TODO check for error platformvm.Codec.Unmarshal(genesisBytes, genesis) // TODO check for error
genesis.Initialize() genesis.Initialize()
@ -182,328 +192,205 @@ func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases
// Since the Platform Chain causes the creation of all other // Since the Platform Chain causes the creation of all other
// chains, this function returns the genesis data of the entire network. // chains, this function returns the genesis data of the entire network.
// The ID of the new network is [networkID]. // The ID of the new network is [networkID].
func Genesis(networkID uint32) []byte { func Genesis(networkID uint32) ([]byte, error) {
if networkID != LocalID { // Specify the genesis state of the AVM
panic("unknown network ID provided") avmArgs := avm.BuildGenesisArgs{}
{
holders := []interface{}(nil)
for _, addr := range Addresses {
holders = append(holders, avm.Holder{
Amount: json.Uint64(45 * units.MegaAva),
Address: addr,
})
}
avmArgs.GenesisData = map[string]avm.AssetDefinition{
// The AVM starts out with one asset, $AVA
"AVA": avm.AssetDefinition{
Name: "AVA",
Symbol: "AVA",
Denomination: 9,
InitialState: map[string][]interface{}{
"fixedCap": holders,
},
},
}
}
avmReply := avm.BuildGenesisReply{}
avmSS := avm.StaticService{}
err := avmSS.BuildGenesis(nil, &avmArgs, &avmReply)
if err != nil {
panic(err)
} }
return []byte{ // Specify the genesis state of Athereum (the built-in instance of the EVM)
0x00, 0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84, evmBalance, success := new(big.Int).SetString("33b2e3c9fd0804000000000", 16)
0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, if success != true {
0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, return nil, errors.New("problem creating evm genesis state")
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x0b, 0xde, 0x31, 0xb4, 0xd8, 0xb2, 0x29, 0x91,
0xd5, 0x1a, 0xa6, 0xaa, 0x1f, 0xc7, 0x33, 0xf2,
0x3a, 0x85, 0x1a, 0x8c, 0x94, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00,
0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30,
0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0x18,
0xd3, 0x99, 0x1c, 0xf6, 0x37, 0xaa, 0x6c, 0x16,
0x2f, 0x5e, 0x95, 0xcf, 0x16, 0x3f, 0x69, 0xcd,
0x82, 0x91, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb,
0x75, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c,
0xa9, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7,
0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd,
0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1,
0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b, 0xe9, 0x09, 0x4f, 0x73, 0x69,
0x80, 0x02, 0xfd, 0x52, 0xc9, 0x08, 0x19, 0xb4,
0x57, 0xb9, 0xfb, 0xc8, 0x66, 0xab, 0x80, 0x00,
0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00,
0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00,
0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
0x47, 0x9f, 0x66, 0xc8, 0xbe, 0x89, 0x58, 0x30,
0x54, 0x7e, 0x70, 0xb4, 0xb2, 0x98, 0xca, 0xfd,
0x43, 0x3d, 0xba, 0x6e, 0x00, 0x00, 0x12, 0x30,
0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00, 0x00,
0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30, 0x39,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a,
0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68,
0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0b, 0xf2, 0x9b, 0xce,
0x5f, 0x34, 0xa7, 0x43, 0x01, 0xeb, 0x0d, 0xe7,
0x16, 0xd5, 0x19, 0x4e, 0x4a, 0x4a, 0xea, 0x5d,
0x7a, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75,
0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9,
0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3,
0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09,
0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2,
0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x41, 0x56, 0x4d, 0x61, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x73,
0x65, 0x63, 0x70, 0x32, 0x35, 0x36, 0x6b, 0x31,
0x66, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x01, 0x00,
0x03, 0x41, 0x56, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x41, 0x56, 0x41, 0x00, 0x03, 0x41,
0x56, 0x41, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x06, 0x00, 0x9f, 0xdf, 0x42, 0xf6,
0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x41, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x65, 0x76,
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0xc9, 0x7b, 0x22,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x3a,
0x7b, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49,
0x64, 0x22, 0x3a, 0x34, 0x33, 0x31, 0x31, 0x30,
0x2c, 0x22, 0x68, 0x6f, 0x6d, 0x65, 0x73, 0x74,
0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61, 0x6f,
0x46, 0x6f, 0x72, 0x6b, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61,
0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x22, 0x3a, 0x74, 0x72,
0x75, 0x65, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22,
0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x48, 0x61, 0x73, 0x68, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x32, 0x30, 0x38, 0x36, 0x37,
0x39, 0x39, 0x61, 0x65, 0x65, 0x62, 0x65, 0x61,
0x65, 0x31, 0x33, 0x35, 0x63, 0x32, 0x34, 0x36,
0x63, 0x36, 0x35, 0x30, 0x32, 0x31, 0x63, 0x38,
0x32, 0x62, 0x34, 0x65, 0x31, 0x35, 0x61, 0x32,
0x63, 0x34, 0x35, 0x31, 0x33, 0x34, 0x30, 0x39,
0x39, 0x33, 0x61, 0x61, 0x63, 0x66, 0x64, 0x32,
0x37, 0x35, 0x31, 0x38, 0x38, 0x36, 0x35, 0x31,
0x34, 0x66, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x35, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x38, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x62, 0x79,
0x7a, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e,
0x74, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x65, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x70, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62,
0x75, 0x72, 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x6e, 0x6f,
0x6e, 0x63, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78,
0x30, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x78,
0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x22,
0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x22, 0x2c,
0x22, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69,
0x74, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x35, 0x66,
0x35, 0x65, 0x31, 0x30, 0x30, 0x22, 0x2c, 0x22,
0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c,
0x74, 0x79, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x22, 0x2c, 0x22, 0x6d, 0x69, 0x78, 0x48, 0x61,
0x73, 0x68, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61,
0x73, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x22,
0x3a, 0x7b, 0x22, 0x37, 0x35, 0x31, 0x61, 0x30,
0x62, 0x39, 0x36, 0x65, 0x31, 0x30, 0x34, 0x32,
0x62, 0x65, 0x65, 0x37, 0x38, 0x39, 0x34, 0x35,
0x32, 0x65, 0x63, 0x62, 0x32, 0x30, 0x32, 0x35,
0x33, 0x66, 0x62, 0x61, 0x34, 0x30, 0x64, 0x62,
0x65, 0x38, 0x35, 0x22, 0x3a, 0x7b, 0x22, 0x62,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x33, 0x33, 0x62, 0x32, 0x65,
0x33, 0x63, 0x39, 0x66, 0x64, 0x30, 0x38, 0x30,
0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x67, 0x61,
0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68,
0x22, 0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x53, 0x69,
0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x41, 0x47,
0x20, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
0x73, 0x73, 0x70, 0x64, 0x61, 0x67, 0x76, 0x6d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15,
0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43,
0x68, 0x61, 0x69, 0x6e, 0x20, 0x50, 0x61, 0x79,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x73, 0x70, 0x63,
0x68, 0x61, 0x69, 0x6e, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x17, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80,
} }
evmArgs := core.Genesis{
Config: &params.ChainConfig{
ChainID: big.NewInt(43110),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
},
Nonce: 0,
Timestamp: 0,
ExtraData: []byte{0},
GasLimit: 100000000,
Difficulty: big.NewInt(0),
Mixhash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
Alloc: core.GenesisAlloc{
common.HexToAddress(evm.GenesisTestAddr): core.GenesisAccount{
Balance: evmBalance,
},
},
Number: 0,
GasUsed: 0,
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
}
evmSS := evm.StaticService{}
evmReply, err := evmSS.BuildGenesis(nil, &evmArgs)
if err != nil {
return nil, err
}
// Specify the genesis state of the simple payments DAG
spdagvmArgs := spdagvm.BuildGenesisArgs{}
for _, addr := range ParsedAddresses {
spdagvmArgs.Outputs = append(spdagvmArgs.Outputs,
spdagvm.APIOutput{
Amount: json.Uint64(20 * units.KiloAva),
Threshold: 1,
Addresses: []ids.ShortID{addr},
},
)
}
spdagvmReply := spdagvm.BuildGenesisReply{}
spdagvmSS := spdagvm.StaticService{}
if err := spdagvmSS.BuildGenesis(nil, &spdagvmArgs, &spdagvmReply); err != nil {
return nil, fmt.Errorf("problem creating simple payments DAG: %w", err)
}
// Specify the genesis state of the simple payments chain
spchainvmArgs := spchainvm.BuildGenesisArgs{}
for _, addr := range ParsedAddresses {
spchainvmArgs.Accounts = append(spchainvmArgs.Accounts,
spchainvm.APIAccount{
Address: addr,
Balance: json.Uint64(20 * units.KiloAva),
},
)
}
spchainvmReply := spchainvm.BuildGenesisReply{}
spchainvmSS := spchainvm.StaticService{}
if err := spchainvmSS.BuildGenesis(nil, &spchainvmArgs, &spchainvmReply); err != nil {
return nil, fmt.Errorf("problem creating simple payments chain: %w", err)
}
// Specify the initial state of the Platform Chain
platformvmArgs := platformvm.BuildGenesisArgs{
NetworkID: json.Uint32(networkID),
}
for _, addr := range ParsedAddresses {
platformvmArgs.Accounts = append(platformvmArgs.Accounts,
platformvm.APIAccount{
Address: addr,
Balance: json.Uint64(20 * units.KiloAva),
},
)
}
genesisTime := time.Date(
/*year=*/ 2019,
/*month=*/ time.November,
/*day=*/ 1,
/*hour=*/ 0,
/*minute=*/ 0,
/*second=*/ 0,
/*nano-second=*/ 0,
/*location=*/ time.UTC,
)
stakingDuration := 365 * 24 * time.Hour // ~ 1 year
endStakingTime := genesisTime.Add(stakingDuration)
for i, validatorID := range ParsedStakerIDs {
weight := json.Uint64(20 * units.KiloAva)
platformvmArgs.Validators = append(platformvmArgs.Validators,
platformvm.APIDefaultSubnetValidator{
APIValidator: platformvm.APIValidator{
StartTime: json.Uint64(genesisTime.Unix()),
EndTime: json.Uint64(endStakingTime.Unix()),
Weight: &weight,
ID: validatorID,
},
Destination: ParsedAddresses[i%len(ParsedAddresses)],
},
)
}
// Specify the chains that exist upon this network's creation
platformvmArgs.Chains = []platformvm.APIChain{
platformvm.APIChain{
GenesisData: avmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: avm.ID,
FxIDs: []ids.ID{
secp256k1fx.ID,
},
Name: "X-Chain",
},
platformvm.APIChain{
GenesisData: evmReply,
SubnetID: platformvm.DefaultSubnetID,
VMID: evm.ID,
Name: "C-Chain",
},
platformvm.APIChain{
GenesisData: spdagvmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: spdagvm.ID,
Name: "Simple DAG Payments",
},
platformvm.APIChain{
GenesisData: spchainvmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: spchainvm.ID,
Name: "Simple Chain Payments",
},
platformvm.APIChain{
GenesisData: formatting.CB58{Bytes: []byte{}}, // There is no genesis data
SubnetID: platformvm.DefaultSubnetID,
VMID: timestampvm.ID,
Name: "Simple Timestamp Server",
},
}
platformvmArgs.Time = json.Uint64(genesisTime.Unix())
platformvmReply := platformvm.BuildGenesisReply{}
platformvmSS := platformvm.StaticService{}
if err := platformvmSS.BuildGenesis(nil, &platformvmArgs, &platformvmReply); err != nil {
return nil, fmt.Errorf("problem while building platform chain's genesis state: %w", err)
}
return platformvmReply.Bytes.Bytes, nil
} }
// VMGenesis ... // VMGenesis ...
func VMGenesis(networkID uint32, vmID ids.ID) *platformvm.CreateChainTx { func VMGenesis(networkID uint32, vmID ids.ID) *platformvm.CreateChainTx {
genesisBytes := Genesis(networkID) genesisBytes, _ := Genesis(networkID)
genesis := platformvm.Genesis{} genesis := platformvm.Genesis{}
platformvm.Codec.Unmarshal(genesisBytes, &genesis) platformvm.Codec.Unmarshal(genesisBytes, &genesis)
if err := genesis.Initialize(); err != nil { if err := genesis.Initialize(); err != nil {

View File

@ -106,7 +106,7 @@ func TestAliases(t *testing.T) {
} }
func TestGenesis(t *testing.T) { func TestGenesis(t *testing.T) {
genesisBytes := Genesis(LocalID) genesisBytes, _ := Genesis(LocalID)
genesis := platformvm.Genesis{} genesis := platformvm.Genesis{}
if err := platformvm.Codec.Unmarshal(genesisBytes, &genesis); err != nil { if err := platformvm.Codec.Unmarshal(genesisBytes, &genesis); err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -120,10 +120,6 @@ func init() {
networkID, err := genesis.NetworkID(*networkName) networkID, err := genesis.NetworkID(*networkName)
errs.Add(err) errs.Add(err)
if networkID != genesis.LocalID {
errs.Add(fmt.Errorf("the only supported networkID is: %s", genesis.LocalName))
}
Config.NetworkID = networkID Config.NetworkID = networkID
// DB: // DB:

View File

@ -354,8 +354,13 @@ func (n *Node) initChains() {
n.Log.Info("initializing chains") n.Log.Info("initializing chains")
vdrs := n.vdrs vdrs := n.vdrs
// If staking is disabled, ignore updates to Subnets' validator sets
// Instead of updating node's validator manager, platform chain makes changes
// to its own local validator manager (which isn't used for sampling)
if !n.Config.EnableStaking { if !n.Config.EnableStaking {
defaultSubnetValidators := validators.NewSet() defaultSubnetValidators := validators.NewSet()
defaultSubnetValidators.Add(validators.NewValidator(n.ID, 1))
vdrs = validators.NewManager() vdrs = validators.NewManager()
vdrs.PutValidatorSet(platformvm.DefaultSubnetID, defaultSubnetValidators) vdrs.PutValidatorSet(platformvm.DefaultSubnetID, defaultSubnetValidators)
} }
@ -363,10 +368,11 @@ func (n *Node) initChains() {
n.vmManager.RegisterVMFactory( n.vmManager.RegisterVMFactory(
/*vmID=*/ platformvm.ID, /*vmID=*/ platformvm.ID,
/*vmFactory=*/ &platformvm.Factory{ /*vmFactory=*/ &platformvm.Factory{
ChainManager: n.chainManager, ChainManager: n.chainManager,
Validators: vdrs, Validators: vdrs,
AVA: genesis.AVAAssetID(n.Config.NetworkID), StakingEnabled: n.Config.EnableStaking,
AVM: genesis.VMGenesis(n.Config.NetworkID, avm.ID).ID(), AVA: genesis.AVAAssetID(n.Config.NetworkID),
AVM: genesis.VMGenesis(n.Config.NetworkID, avm.ID).ID(),
}, },
) )
@ -375,11 +381,12 @@ func (n *Node) initChains() {
beacons.Add(validators.NewValidator(peer.ID, 1)) beacons.Add(validators.NewValidator(peer.ID, 1))
} }
genesisBytes := genesis.Genesis(n.Config.NetworkID) genesisBytes, _ := genesis.Genesis(n.Config.NetworkID)
// Create the Platform Chain // Create the Platform Chain
n.chainManager.ForceCreateChain(chains.ChainParameters{ n.chainManager.ForceCreateChain(chains.ChainParameters{
ID: ids.Empty, ID: ids.Empty,
SubnetID: platformvm.DefaultSubnetID,
GenesisData: genesisBytes, // Specifies other chains to create GenesisData: genesisBytes, // Specifies other chains to create
VMAlias: platformvm.ID.String(), VMAlias: platformvm.ID.String(),
CustomBeacons: beacons, CustomBeacons: beacons,
@ -409,6 +416,7 @@ func (n *Node) initAPIServer() {
// Assumes n.DB, n.vdrs all initialized (non-nil) // Assumes n.DB, n.vdrs all initialized (non-nil)
func (n *Node) initChainManager() { func (n *Node) initChainManager() {
n.chainManager = chains.New( n.chainManager = chains.New(
n.Config.EnableStaking,
n.Log, n.Log,
n.LogFactory, n.LogFactory,
n.vmManager, n.vmManager,

View File

@ -7,6 +7,8 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/versiondb" "github.com/ava-labs/gecko/database/versiondb"
) )
@ -86,7 +88,7 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), DefaultSubnetID) current, pending, _, _, err := tx.vm.calculateValidators(db, tx.Timestamp(), DefaultSubnetID)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
@ -98,29 +100,35 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
// For each subnet, calculate what current and pending validator sets should be // For each Subnet, calculate what current and pending validator sets should be
// given new timestamp // given new timestamp
// Key: Subnet ID
// Value: IDs of validators that will have started validating this Subnet when
// timestamp is advanced to tx.Timestamp()
startedValidating := make(map[ids.ID]ids.ShortSet, 0)
subnets, err := tx.vm.getSubnets(db) subnets, err := tx.vm.getSubnets(db)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
for _, subnet := range subnets { for _, subnet := range subnets {
current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.id) current, pending, started, _, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.id)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
if err := tx.vm.putCurrentValidators(onCommitDB, current, subnet.id); err != nil { if err := tx.vm.putCurrentValidators(onCommitDB, current, subnet.id); err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
if err := tx.vm.putPendingValidators(onCommitDB, pending, subnet.id); err != nil { if err := tx.vm.putPendingValidators(onCommitDB, pending, subnet.id); err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
startedValidating[subnet.ID()] = started
} }
// If this block is committed, update the validator sets // If this block is committed, update the validator sets
// onAbortDB or onCommitDB should commit (flush to vm.DB) before this is called // onAbortDB or onCommitDB should commit (flush to vm.DB) before this is called
updateValidators := func() { onCommitFunc := func() {
// For each Subnet, update the node's validator manager to reflect current Subnet membership
subnets, err := tx.vm.getSubnets(tx.vm.DB) subnets, err := tx.vm.getSubnets(tx.vm.DB)
if err != nil { if err != nil {
tx.vm.Ctx.Log.Error("failed to get subnets: %s", err) tx.vm.Ctx.Log.Error("failed to get subnets: %s", err)
@ -128,18 +136,35 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
} }
for _, subnet := range subnets { for _, subnet := range subnets {
if err := tx.vm.updateValidators(subnet.id); err != nil { if err := tx.vm.updateValidators(subnet.id); err != nil {
tx.vm.Ctx.Log.Debug("failed to update validators on the default subnet: %s", err) tx.vm.Ctx.Log.Debug("failed to update Subnet %s: %s", subnet.id, err)
} }
} }
if err := tx.vm.updateValidators(DefaultSubnetID); err != nil { if err := tx.vm.updateValidators(DefaultSubnetID); err != nil {
tx.vm.Ctx.Log.Fatal("failed to update validators on the default subnet: %s", err) tx.vm.Ctx.Log.Fatal("failed to update Default Subnet: %s", err)
}
// If this node started validating a Subnet, create the blockchains that the Subnet validates
chains, err := tx.vm.getChains(tx.vm.DB) // all blockchains
if err != nil {
tx.vm.Ctx.Log.Error("couldn't get blockchains: %s", err)
return
}
for subnetID, validatorIDs := range startedValidating {
if !validatorIDs.Contains(tx.vm.Ctx.NodeID) {
continue
}
for _, chain := range chains {
if chain.SubnetID.Equals(subnetID) {
tx.vm.createChain(chain)
}
}
} }
} }
// Specify what the state of the chain will be if this proposal is aborted // Specify what the state of the chain will be if this proposal is aborted
onAbortDB := versiondb.New(db) // state doesn't change onAbortDB := versiondb.New(db) // state doesn't change
return onCommitDB, onAbortDB, updateValidators, nil, nil return onCommitDB, onAbortDB, onCommitFunc, nil, nil
} }
// InitiallyPrefersCommit returns true if the proposed time isn't after the // InitiallyPrefersCommit returns true if the proposed time isn't after the

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/crypto"
@ -15,8 +14,9 @@ import (
) )
var ( var (
errInvalidVMID = errors.New("invalid VM ID") errInvalidVMID = errors.New("invalid VM ID")
errFxIDsNotSortedAndUnique = errors.New("feature extensions IDs must be sorted and unique") errFxIDsNotSortedAndUnique = errors.New("feature extensions IDs must be sorted and unique")
errControlSigsNotSortedAndUnique = errors.New("control signatures must be sorted and unique")
) )
// UnsignedCreateChainTx is an unsigned CreateChainTx // UnsignedCreateChainTx is an unsigned CreateChainTx
@ -24,6 +24,9 @@ type UnsignedCreateChainTx struct {
// ID of the network this blockchain exists on // ID of the network this blockchain exists on
NetworkID uint32 `serialize:"true"` NetworkID uint32 `serialize:"true"`
// ID of the Subnet that validates this blockchain
SubnetID ids.ID `serialize:"true"`
// Next unused nonce of account paying the transaction fee for this transaction. // Next unused nonce of account paying the transaction fee for this transaction.
// Currently unused, as there are no tx fees. // Currently unused, as there are no tx fees.
Nonce uint64 `serialize:"true"` Nonce uint64 `serialize:"true"`
@ -37,7 +40,7 @@ type UnsignedCreateChainTx struct {
// IDs of the feature extensions running on the new chain // IDs of the feature extensions running on the new chain
FxIDs []ids.ID `serialize:"true"` FxIDs []ids.ID `serialize:"true"`
// Byte representation of state of the new chain // Byte representation of genesis state of the new chain
GenesisData []byte `serialize:"true"` GenesisData []byte `serialize:"true"`
} }
@ -45,11 +48,19 @@ type UnsignedCreateChainTx struct {
type CreateChainTx struct { type CreateChainTx struct {
UnsignedCreateChainTx `serialize:"true"` UnsignedCreateChainTx `serialize:"true"`
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"` // Address of the account that provides the transaction fee
// Set in SemanticVerify
PayerAddress ids.ShortID
// Signature of key whose account provides the transaction fee
PayerSig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// Signatures from Subnet's control keys
// Should not empty slice, not nil, if there are no control sigs
ControlSigs [][crypto.SECP256K1RSigLen]byte `serialize:"true"`
vm *VM vm *VM
id ids.ID id ids.ID
key crypto.PublicKey // public key of transaction signer
bytes []byte bytes []byte
} }
@ -64,10 +75,6 @@ func (tx *CreateChainTx) initialize(vm *VM) error {
// ID of this transaction // ID of this transaction
func (tx *CreateChainTx) ID() ids.ID { return tx.id } func (tx *CreateChainTx) ID() ids.ID { return tx.id }
// Key returns the public key of the signer of this transaction
// Precondition: tx.Verify() has been called and returned nil
func (tx *CreateChainTx) Key() crypto.PublicKey { return tx.key }
// Bytes returns the byte representation of a CreateChainTx // Bytes returns the byte representation of a CreateChainTx
func (tx *CreateChainTx) Bytes() []byte { return tx.bytes } func (tx *CreateChainTx) Bytes() []byte { return tx.bytes }
@ -77,8 +84,8 @@ func (tx *CreateChainTx) SyntacticVerify() error {
switch { switch {
case tx == nil: case tx == nil:
return errNilTx return errNilTx
case tx.key != nil: case !tx.PayerAddress.IsZero(): // Only verify the transaction once
return nil // Only verify the transaction once return nil
case tx.NetworkID != tx.vm.Ctx.NetworkID: // verify the transaction is on this network case tx.NetworkID != tx.vm.Ctx.NetworkID: // verify the transaction is on this network
return errWrongNetworkID return errWrongNetworkID
case tx.id.IsZero(): case tx.id.IsZero():
@ -87,6 +94,8 @@ func (tx *CreateChainTx) SyntacticVerify() error {
return errInvalidVMID return errInvalidVMID
case !ids.IsSortedAndUniqueIDs(tx.FxIDs): case !ids.IsSortedAndUniqueIDs(tx.FxIDs):
return errFxIDsNotSortedAndUnique return errFxIDsNotSortedAndUnique
case !crypto.IsSortedAndUniqueSECP2561RSigs(tx.ControlSigs):
return errControlSigsNotSortedAndUnique
} }
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx) unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
@ -95,11 +104,11 @@ func (tx *CreateChainTx) SyntacticVerify() error {
return err return err
} }
key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:]) payerKey, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.PayerSig[:])
if err != nil { if err != nil {
return err return err
} }
tx.key = key tx.PayerAddress = payerKey.Address()
return nil return nil
} }
@ -125,10 +134,12 @@ func (tx *CreateChainTx) SemanticVerify(db database.Database) (func(), error) {
} }
// Deduct tx fee from payer's account // Deduct tx fee from payer's account
account, err := tx.vm.getAccount(db, tx.Key().Address()) account, err := tx.vm.getAccount(db, tx.PayerAddress)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// txFee is removed in account.Remove
// TODO: Consider changing Remove to be parameterized on total amount (inc. tx fee) to remove
account, err = account.Remove(0, tx.Nonce) account, err = account.Remove(0, tx.Nonce)
if err != nil { if err != nil {
return nil, err return nil, err
@ -137,20 +148,55 @@ func (tx *CreateChainTx) SemanticVerify(db database.Database) (func(), error) {
return nil, err return nil, err
} }
// If this proposal is committed, create the new blockchain using the chain manager // Verify that this transaction has sufficient control signatures
subnets, err := tx.vm.getSubnets(db) // all subnets that exist
if err != nil {
return nil, err
}
var subnet *CreateSubnetTx // the subnet that will validate the new chain
for _, sn := range subnets {
if sn.id.Equals(tx.SubnetID) {
subnet = sn
break
}
}
if subnet == nil {
return nil, fmt.Errorf("there is no subnet with ID %s", tx.SubnetID)
}
if len(tx.ControlSigs) != int(subnet.Threshold) {
return nil, fmt.Errorf("expected tx to have %d control sigs but has %d", subnet.Threshold, len(tx.ControlSigs))
}
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte representation of the unsigned transaction
if err != nil {
return nil, err
}
unsignedBytesHash := hashing.ComputeHash256(unsignedBytes)
// Each element is ID of key that signed this tx
controlIDs := make([]ids.ShortID, len(tx.ControlSigs))
for i, sig := range tx.ControlSigs {
key, err := tx.vm.factory.RecoverHashPublicKey(unsignedBytesHash, sig[:])
if err != nil {
return nil, err
}
controlIDs[i] = key.Address()
}
// Verify each control signature on this tx is from a control key
controlKeys := ids.ShortSet{}
controlKeys.Add(subnet.ControlKeys...)
for _, controlID := range controlIDs {
if !controlKeys.Contains(controlID) {
return nil, errors.New("tx has control signature from key not in subnet's ControlKeys")
}
}
// If this proposal is committed and this node is a member of the
// subnet that validates the blockchain, create the blockchain
onAccept := func() { onAccept := func() {
chainParams := chains.ChainParameters{ tx.vm.createChain(tx)
ID: tx.ID(),
GenesisData: tx.GenesisData,
VMAlias: tx.VMID.String(),
}
for _, fxID := range tx.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
// TODO: Not sure how else to make this not nil pointer error during tests
if tx.vm.chainManager != nil {
tx.vm.chainManager.CreateChain(chainParams)
}
} }
return onAccept, nil return onAccept, nil
@ -166,10 +212,14 @@ func (chains createChainList) Bytes() []byte {
return bytes return bytes
} }
func (vm *VM) newCreateChainTx(nonce uint64, genesisData []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32, key *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) { func (vm *VM) newCreateChainTx(nonce uint64, subnetID ids.ID, genesisData []byte,
vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32,
controlKeys []*crypto.PrivateKeySECP256K1R,
payerKey *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) {
tx := &CreateChainTx{ tx := &CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{ UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: networkID, NetworkID: networkID,
SubnetID: subnetID,
Nonce: nonce, Nonce: nonce,
GenesisData: genesisData, GenesisData: genesisData,
VMID: vmID, VMID: vmID,
@ -178,17 +228,33 @@ func (vm *VM) newCreateChainTx(nonce uint64, genesisData []byte, vmID ids.ID, fx
}, },
} }
// Generate byte repr. of unsigned transaction
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx) unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction unsignedBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
unsignedBytesHash := hashing.ComputeHash256(unsignedBytes)
// Sign the tx with control keys
tx.ControlSigs = make([][crypto.SECP256K1RSigLen]byte, len(controlKeys))
for i, key := range controlKeys {
sig, err := key.SignHash(unsignedBytesHash)
if err != nil {
return nil, err
}
copy(tx.ControlSigs[i][:], sig)
}
sig, err := key.Sign(unsignedBytes) // Sort the control signatures
crypto.SortSECP2561RSigs(tx.ControlSigs)
// Sign with the payer key
payerSig, err := payerKey.Sign(unsignedBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
copy(tx.Sig[:], sig) copy(tx.PayerSig[:], payerSig)
return tx, tx.initialize(vm) return tx, tx.initialize(vm)
} }

View File

@ -6,8 +6,8 @@ package platformvm
import ( import (
"testing" "testing"
"github.com/ava-labs/gecko/database/versiondb"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/vms/avm" "github.com/ava-labs/gecko/vms/avm"
) )
@ -24,18 +24,19 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// Case 2: network ID is wrong // Case 2: network ID is wrong
tx, err := vm.newCreateChainTx( tx, err := vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
avm.ID, avm.ID,
nil, nil,
"chain name", "chain name",
testNetworkID+1, testNetworkID+1,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = tx.SyntacticVerify() err = tx.SyntacticVerify()
t.Log(err)
if err == nil { if err == nil {
t.Fatal("should've errored because network ID is wrong") t.Fatal("should've errored because network ID is wrong")
} }
@ -43,11 +44,13 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// case 3: tx ID is empty // case 3: tx ID is empty
tx, err = vm.newCreateChainTx( tx, err = vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
avm.ID, avm.ID,
nil, nil,
"chain name", "chain name",
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
) )
if err != nil { if err != nil {
@ -61,11 +64,13 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// Case 4: vm ID is empty // Case 4: vm ID is empty
tx, err = vm.newCreateChainTx( tx, err = vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
avm.ID, avm.ID,
nil, nil,
"chain name", "chain name",
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
) )
if err != nil { if err != nil {
@ -75,62 +80,189 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
if err := tx.SyntacticVerify(); err == nil { if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because tx ID is empty") t.Fatal("should've errored because tx ID is empty")
} }
}
func TestSemanticVerify(t *testing.T) { // Case 5: Control sigs not sorted
vm := defaultVM() tx, err = vm.newCreateChainTx(
// create a tx
tx, err := vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
avm.ID, avm.ID,
nil, nil,
"chain name", "chain name",
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
// Reverse signature order
tx.ControlSigs[0], tx.ControlSigs[1] = tx.ControlSigs[1], tx.ControlSigs[0]
if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because control sigs not sorted")
}
// Case 6: Control sigs not unique
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
tx.ControlSigs[0] = tx.ControlSigs[1]
if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because control sigs not unique")
}
// Case 7: Valid tx passes syntactic verification
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatalf("should have passed verification but got %v", err)
}
}
// Ensure SemanticVerify fails when there are not enough control sigs
func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
vm := defaultVM()
// Case 1: No control sigs (2 are needed)
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
nil,
defaultKey, defaultKey,
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
newDB := versiondb.New(vm.DB) _, err = tx.SemanticVerify(vm.DB)
if err == nil {
_, err = tx.SemanticVerify(newDB) t.Fatal("should have errored because there are no control sigs")
if err != nil {
t.Fatal(err)
} }
chains, err := vm.getChains(newDB) // Case 2: 1 control sig (2 are needed)
if err != nil { tx, err = vm.newCreateChainTx(
t.Fatal(err)
}
for _, c := range chains {
if c.ID().Equals(tx.ID()) {
return
}
}
t.Fatalf("Should have added the chain to the set of chains")
}
func TestSemanticVerifyAlreadyExisting(t *testing.T) {
vm := defaultVM()
// create a tx
tx, err := vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
avm.ID, avm.ID,
nil, nil,
"chain name", "chain name",
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0]},
defaultKey, defaultKey,
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// put the chain in existing chain _, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because there are no control sigs")
}
}
// Ensure SemanticVerify fails when an incorrect control signature is given
func TestCreateChainTxWrongControlSig(t *testing.T) {
vm := defaultVM()
// Generate new, random key to sign tx with
factory := crypto.FactorySECP256K1R{}
key, err := factory.NewPrivateKey()
if err != nil {
t.Fatal(err)
}
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], key.(*crypto.PrivateKeySECP256K1R)},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because incorrect control sig given")
}
}
// Ensure SemanticVerify fails when the Subnet the blockchain specifies as
// its validator set doesn't exist
func TestCreateChainTxNoSuchSubnet(t *testing.T) {
vm := defaultVM()
tx, err := vm.newCreateChainTx(
defaultNonce+1,
ids.NewID([32]byte{1, 9, 124, 11, 20}), // pick some random ID for subnet
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because Subnet doesn't exist")
}
}
func TestCreateChainTxAlreadyExists(t *testing.T) {
vm := defaultVM()
// create a tx
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
// put the chain in existing chain list
if err := vm.putChains(vm.DB, []*CreateChainTx{tx}); err != nil { if err := vm.putChains(vm.DB, []*CreateChainTx{tx}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -140,3 +272,29 @@ func TestSemanticVerifyAlreadyExisting(t *testing.T) {
t.Fatalf("should have failed because there is already a chain with ID %s", tx.id) t.Fatalf("should have failed because there is already a chain with ID %s", tx.id)
} }
} }
// Ensure valid tx passes semanticVerify
func TestCreateChainTxValid(t *testing.T) {
vm := defaultVM()
// create a valid tx
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err != nil {
t.Fatalf("expected tx to pass verification but got error: %v", err)
}
}

View File

@ -8,8 +8,8 @@ import (
"fmt" "fmt"
"github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/hashing"
) )
@ -17,8 +17,10 @@ import (
const maxThreshold = 25 const maxThreshold = 25
var ( var (
errThresholdExceedsKeysLen = errors.New("threshold must be no more than number of control keys") errThresholdExceedsKeysLen = errors.New("threshold must be no more than number of control keys")
errThresholdTooHigh = fmt.Errorf("threshold can't be greater than %d", maxThreshold) errThresholdTooHigh = fmt.Errorf("threshold can't be greater than %d", maxThreshold)
errControlKeysNotSortedAndUnique = errors.New("control keys must be sorted and unique")
errUnneededKeys = errors.New("subnets shouldn't have keys if the threshold is 0")
) )
// UnsignedCreateSubnetTx is an unsigned proposal to create a new subnet // UnsignedCreateSubnetTx is an unsigned proposal to create a new subnet
@ -41,11 +43,8 @@ type UnsignedCreateSubnetTx struct {
type CreateSubnetTx struct { type CreateSubnetTx struct {
UnsignedCreateSubnetTx `serialize:"true"` UnsignedCreateSubnetTx `serialize:"true"`
// The VM this tx exists within // Signature on the UnsignedCreateSubnetTx's byte repr
vm *VM Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// ID is this transaction's ID
id ids.ID
// The public key that signed this transaction // The public key that signed this transaction
// The transaction fee will be paid from the corresponding account // The transaction fee will be paid from the corresponding account
@ -53,14 +52,17 @@ type CreateSubnetTx struct {
// [key] is non-nil iff this tx is valid // [key] is non-nil iff this tx is valid
key crypto.PublicKey key crypto.PublicKey
// Signature on the UnsignedCreateSubnetTx's byte repr // The VM this tx exists within
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"` vm *VM
// ID is this transaction's ID
id ids.ID
// Byte representation of this transaction (including signature) // Byte representation of this transaction (including signature)
bytes []byte bytes []byte
} }
// ID returns the ID of this tx // ID returns the ID of this transaction
func (tx *CreateSubnetTx) ID() ids.ID { return tx.id } func (tx *CreateSubnetTx) ID() ids.ID { return tx.id }
// SyntacticVerify nil iff [tx] is syntactically valid. // SyntacticVerify nil iff [tx] is syntactically valid.
@ -77,6 +79,12 @@ func (tx *CreateSubnetTx) SyntacticVerify() error {
return errWrongNetworkID return errWrongNetworkID
case tx.Threshold > uint16(len(tx.ControlKeys)): case tx.Threshold > uint16(len(tx.ControlKeys)):
return errThresholdExceedsKeysLen return errThresholdExceedsKeysLen
case tx.Threshold > maxThreshold:
return errThresholdTooHigh
case tx.Threshold == 0 && len(tx.ControlKeys) > 0:
return errUnneededKeys
case !ids.IsSortedAndUniqueShortIDs(tx.ControlKeys):
return errControlKeysNotSortedAndUnique
} }
// Byte representation of the unsigned transaction // Byte representation of the unsigned transaction
@ -107,12 +115,6 @@ func (tx *CreateSubnetTx) SemanticVerify(db database.Database) (func(), error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, subnet := range subnets {
if subnet.id.Equals(tx.id) {
return nil, fmt.Errorf("there is already a subnet with ID %s", tx.id)
}
}
subnets = append(subnets, tx) // add new subnet subnets = append(subnets, tx) // add new subnet
if err := tx.vm.putSubnets(db, subnets); err != nil { if err := tx.vm.putSubnets(db, subnets); err != nil {
return nil, err return nil, err
@ -131,7 +133,12 @@ func (tx *CreateSubnetTx) SemanticVerify(db database.Database) (func(), error) {
return nil, err return nil, err
} }
return nil, nil // Register new subnet in validator manager
onAccept := func() {
tx.vm.validators.PutValidatorSet(tx.id, validators.NewSet())
}
return onAccept, nil
} }
// Bytes returns the byte representation of [tx] // Bytes returns the byte representation of [tx]
@ -159,10 +166,11 @@ func (tx *CreateSubnetTx) initialize(vm *VM) error {
return nil return nil
} }
// [controlKeys] must be unique. They will be sorted by this method.
// If [controlKeys] is nil, [tx.Controlkeys] will be an empty list.
func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []ids.ShortID, func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []ids.ShortID,
threshold uint16, payerKey *crypto.PrivateKeySECP256K1R, threshold uint16, payerKey *crypto.PrivateKeySECP256K1R,
) (*CreateSubnetTx, error) { ) (*CreateSubnetTx, error) {
tx := &CreateSubnetTx{UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{ tx := &CreateSubnetTx{UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
NetworkID: networkID, NetworkID: networkID,
Nonce: nonce, Nonce: nonce,
@ -170,6 +178,17 @@ func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []id
Threshold: threshold, Threshold: threshold,
}} }}
if threshold == 0 && len(tx.ControlKeys) > 0 {
return nil, errUnneededKeys
}
// Sort control keys
ids.SortShortIDs(tx.ControlKeys)
// Ensure control keys are unique
if !ids.IsSortedAndUniqueShortIDs(tx.ControlKeys) {
return nil, errControlKeysNotSortedAndUnique
}
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx) unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) unsignedBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { if err != nil {

View File

@ -183,7 +183,7 @@ func (vm *VM) newExportTx(nonce uint64, networkID uint32, outs []*ava.Transferab
return nil, err return nil, err
} }
sig, err := key.Sign(unsignedBytes) sig, err := from.Sign(unsignedBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -16,18 +16,20 @@ var (
// Factory can create new instances of the Platform Chain // Factory can create new instances of the Platform Chain
type Factory struct { type Factory struct {
ChainManager chains.Manager ChainManager chains.Manager
Validators validators.Manager Validators validators.Manager
AVA ids.ID StakingEnabled bool
AVM ids.ID AVA ids.ID
AVM ids.ID
} }
// New returns a new instance of the Platform Chain // New returns a new instance of the Platform Chain
func (f *Factory) New() interface{} { func (f *Factory) New() interface{} {
return &VM{ return &VM{
chainManager: f.ChainManager, chainManager: f.ChainManager,
validators: f.Validators, validators: f.Validators,
ava: f.AVA, stakingEnabled: f.StakingEnabled,
avm: f.AVM, ava: f.AVA,
avm: f.AVM,
} }
} }

View File

@ -231,7 +231,7 @@ func (vm *VM) newImportTx(nonce uint64, networkID uint32, ins []*ava.Transferabl
tx := &ImportTx{UnsignedImportTx: UnsignedImportTx{ tx := &ImportTx{UnsignedImportTx: UnsignedImportTx{
NetworkID: networkID, NetworkID: networkID,
Nonce: nonce, Nonce: nonce,
Account: key.PublicKey().Address(), Account: to.PublicKey().Address(),
Ins: ins, Ins: ins,
}} }}
@ -258,7 +258,7 @@ func (vm *VM) newImportTx(nonce uint64, networkID uint32, ins []*ava.Transferabl
tx.Creds = append(tx.Creds, cred) tx.Creds = append(tx.Creds, cred)
} }
sig, err := key.SignHash(hash) sig, err := to.SignHash(hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -8,9 +8,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest"
"github.com/gorilla/rpc/v2/json2"
"github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
@ -19,39 +16,26 @@ import (
"github.com/ava-labs/gecko/utils/hashing" "github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/math" "github.com/ava-labs/gecko/utils/math"
"github.com/ava-labs/gecko/vms/avm"
"github.com/ava-labs/gecko/vms/components/ava" "github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/gecko/vms/secp256k1fx"
) )
var ( var (
errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks") errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks")
errParsingID = errors.New("error parsing ID") errParsingID = errors.New("error parsing ID")
errGetAccount = errors.New("error retrieving account information") errGetAccount = errors.New("error retrieving account information")
errGetAccounts = errors.New("error getting accounts controlled by specified user") errGetAccounts = errors.New("error getting accounts controlled by specified user")
errGetUser = errors.New("error while getting user. Does user exist?") errGetUser = errors.New("error while getting user. Does user exist?")
errNoMethodWithGenesis = errors.New("no method was provided but genesis data was provided") errNoMethodWithGenesis = errors.New("no method was provided but genesis data was provided")
errCreatingTransaction = errors.New("problem while creating transaction") errCreatingTransaction = errors.New("problem while creating transaction")
errNoDestination = errors.New("call is missing field 'stakeDestination'") errNoDestination = errors.New("call is missing field 'stakeDestination'")
errNoSource = errors.New("call is missing field 'stakeSource'") errNoSource = errors.New("call is missing field 'stakeSource'")
errGetStakeSource = errors.New("couldn't get account specified in 'stakeSource'") errGetStakeSource = errors.New("couldn't get account specified in 'stakeSource'")
errNoBlockchainWithAlias = errors.New("there is no blockchain with the specified alias")
errDSCantValidate = errors.New("new blockchain can't be validated by default Subnet")
) )
var key *crypto.PrivateKeySECP256K1R
func init() {
cb58 := formatting.CB58{}
err := cb58.FromString("24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5")
if err != nil {
panic(err)
}
factory := crypto.FactorySECP256K1R{}
pk, err := factory.ToPrivateKey(cb58.Bytes)
if err != nil {
panic(err)
}
key = pk.(*crypto.PrivateKeySECP256K1R)
}
// Service defines the API calls that can be made to the platform chain // Service defines the API calls that can be made to the platform chain
type Service struct{ vm *VM } type Service struct{ vm *VM }
@ -319,7 +303,7 @@ type ListAccountsReply struct {
// ListAccounts lists all of the accounts controlled by [args.Username] // ListAccounts lists all of the accounts controlled by [args.Username]
func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, reply *ListAccountsReply) error { func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, reply *ListAccountsReply) error {
service.vm.Ctx.Log.Debug("platform.listAccounts called for user '%s'", args.Username) service.vm.Ctx.Log.Debug("listAccounts called for user '%s'", args.Username)
// db holds the user's info that pertains to the Platform Chain // db holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -382,7 +366,7 @@ type CreateAccountReply struct {
// The account's ID is [privKey].PublicKey().Address(), where [privKey] is a // The account's ID is [privKey].PublicKey().Address(), where [privKey] is a
// private key controlled by the user. // private key controlled by the user.
func (service *Service) CreateAccount(_ *http.Request, args *CreateAccountArgs, reply *CreateAccountReply) error { func (service *Service) CreateAccount(_ *http.Request, args *CreateAccountArgs, reply *CreateAccountReply) error {
service.vm.Ctx.Log.Debug("platform.createAccount called for user '%s'", args.Username) service.vm.Ctx.Log.Debug("createAccount called for user '%s'", args.Username)
// userDB holds the user's info that pertains to the Platform Chain // userDB holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -452,7 +436,7 @@ type AddDefaultSubnetValidatorArgs struct {
// AddDefaultSubnetValidator returns an unsigned transaction to add a validator to the default subnet // AddDefaultSubnetValidator returns an unsigned transaction to add a validator to the default subnet
// The returned unsigned transaction should be signed using Sign() // The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error { func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetValidator called") service.vm.Ctx.Log.Debug("AddDefaultSubnetValidator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID args.ID = service.vm.Ctx.NodeID
@ -497,7 +481,7 @@ type AddDefaultSubnetDelegatorArgs struct {
// to the default subnet // to the default subnet
// The returned unsigned transaction should be signed using Sign() // The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error { func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetDelegator called") service.vm.Ctx.Log.Debug("AddDefaultSubnetDelegator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID args.ID = service.vm.Ctx.NodeID
@ -679,7 +663,7 @@ type SignResponse struct {
// Sign [args.bytes] // Sign [args.bytes]
func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignResponse) error { func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignResponse) error {
service.vm.Ctx.Log.Debug("platform.sign called") service.vm.Ctx.Log.Debug("sign called")
// Get the key of the Signer // Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -710,10 +694,12 @@ func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignRespons
genTx.Tx, err = service.signAddNonDefaultSubnetValidatorTx(tx, key) genTx.Tx, err = service.signAddNonDefaultSubnetValidatorTx(tx, key)
case *CreateSubnetTx: case *CreateSubnetTx:
genTx.Tx, err = service.signCreateSubnetTx(tx, key) genTx.Tx, err = service.signCreateSubnetTx(tx, key)
case *CreateChainTx:
genTx.Tx, err = service.signCreateChainTx(tx, key)
case *ExportTx: case *ExportTx:
genTx.Tx, err = service.signExportTx(tx, key) genTx.Tx, err = service.signExportTx(tx, key)
default: default:
err = errors.New("Could not parse given tx. Must be one of: addDefaultSubnetValidatorTx, addNonDefaultSubnetValidatorTx, createSubnetTx") err = errors.New("Could not parse given tx")
} }
if err != nil { if err != nil {
return err return err
@ -725,7 +711,7 @@ func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignRespons
// Sign [unsigned] with [key] // Sign [unsigned] with [key]
func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetValidatorTx, error) { func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetValidatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called") service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed? // TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx) unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx)
@ -748,7 +734,7 @@ func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetVali
// Sign [unsigned] with [key] // Sign [unsigned] with [key]
func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDelegatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetDelegatorTx, error) { func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDelegatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetDelegatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called") service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed? // TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx) unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx)
@ -771,7 +757,7 @@ func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDele
// Sign [xt] with [key] // Sign [xt] with [key]
func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.PrivateKeySECP256K1R) (*CreateSubnetTx, error) { func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.PrivateKeySECP256K1R) (*CreateSubnetTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called") service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed? // TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx) unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
@ -822,7 +808,7 @@ func (service *Service) signExportTx(tx *ExportTx, key *crypto.PrivateKeySECP256
// Sorts tx.ControlSigs before returning // Sorts tx.ControlSigs before returning
// Assumes each element of tx.ControlSigs is actually a signature, not just empty bytes // Assumes each element of tx.ControlSigs is actually a signature, not just empty bytes
func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addNonDefaultSubnetValidatorTx, error) { func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addNonDefaultSubnetValidatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddNonDefaultSubnetValidatorTx called") service.vm.Ctx.Log.Debug("signAddNonDefaultSubnetValidatorTx called")
// Compute the byte repr. of the unsigned tx and the signature of [key] over it // Compute the byte repr. of the unsigned tx and the signature of [key] over it
unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx) unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx)
@ -1003,6 +989,59 @@ func (service *Service) CreateImportTx(_ *http.Request, args *CreateImportTxArgs
return nil return nil
} }
// Signs an unsigned or partially signed CreateChainTx with [key]
// If [key] is a control key for the subnet and there is an empty spot in tx.ControlSigs, signs there
// If [key] is a control key for the subnet and there is no empty spot in tx.ControlSigs, signs as payer
// If [key] is not a control key, sign as payer (account controlled by [key] pays the tx fee)
// Sorts tx.ControlSigs before returning
// Assumes each element of tx.ControlSigs is actually a signature, not just empty bytes
func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) {
service.vm.Ctx.Log.Debug("signCreateChainTx called")
// Compute the byte repr. of the unsigned tx and the signature of [key] over it
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
}
sig, err := key.Sign(unsignedTxBytes)
if err != nil {
return nil, errors.New("error while signing")
}
if len(sig) != crypto.SECP256K1RSigLen {
return nil, fmt.Errorf("expected signature to be length %d but was length %d", crypto.SECP256K1RSigLen, len(sig))
}
// Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID)
if err != nil {
return nil, fmt.Errorf("problem getting subnet information: %v", err)
}
// Find the location at which [key] should put its signature.
// If [key] is a control key for this subnet and there is an empty spot in tx.ControlSigs, sign there
// If [key] is a control key for this subnet and there is no empty spot in tx.ControlSigs, sign as payer
// If [key] is not a control key, sign as payer (account controlled by [key] pays the tx fee)
controlKeySet := ids.ShortSet{}
controlKeySet.Add(subnet.ControlKeys...)
isControlKey := controlKeySet.Contains(key.PublicKey().Address())
payerSigEmpty := tx.PayerSig == [crypto.SECP256K1RSigLen]byte{} // true if no key has signed to pay the tx fee
if isControlKey && len(tx.ControlSigs) != int(subnet.Threshold) { // Sign as controlSig
tx.ControlSigs = append(tx.ControlSigs, [crypto.SECP256K1RSigLen]byte{})
copy(tx.ControlSigs[len(tx.ControlSigs)-1][:], sig)
} else if payerSigEmpty { // sign as payer
copy(tx.PayerSig[:], sig)
} else {
return nil, errors.New("no place for key to sign")
}
crypto.SortSECP2561RSigs(tx.ControlSigs)
return tx, nil
}
// IssueTxArgs are the arguments to IssueTx // IssueTxArgs are the arguments to IssueTx
type IssueTxArgs struct { type IssueTxArgs struct {
// Tx being sent to the network // Tx being sent to the network
@ -1017,6 +1056,8 @@ type IssueTxResponse struct {
// IssueTx issues the transaction [args.Tx] to the network // IssueTx issues the transaction [args.Tx] to the network
func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *IssueTxResponse) error { func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *IssueTxResponse) error {
service.vm.Ctx.Log.Debug("issueTx called")
genTx := genericTx{} genTx := genericTx{}
if err := Codec.Unmarshal(args.Tx.Bytes, &genTx); err != nil { if err := Codec.Unmarshal(args.Tx.Bytes, &genTx); err != nil {
return err return err
@ -1057,6 +1098,9 @@ func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *Is
// CreateBlockchainArgs is the arguments for calling CreateBlockchain // CreateBlockchainArgs is the arguments for calling CreateBlockchain
type CreateBlockchainArgs struct { type CreateBlockchainArgs struct {
// ID of Subnet that validates the new blockchain
SubnetID ids.ID `json:"subnetID"`
// ID of the VM the new blockchain is running // ID of the VM the new blockchain is running
VMID string `json:"vmID"` VMID string `json:"vmID"`
@ -1066,26 +1110,18 @@ type CreateBlockchainArgs struct {
// Human-readable name for the new blockchain, not necessarily unique // Human-readable name for the new blockchain, not necessarily unique
Name string `json:"name"` Name string `json:"name"`
// To generate the byte representation of the genesis data for this blockchain, // Next unused nonce of the account paying the transaction fee
// a POST request with body [GenesisData] is made to the API method whose name is [Method], whose PayerNonce json.Uint64 `json:"payerNonce"`
// endpoint is [Endpoint]. See Platform Chain documentation for more info and examples.
Method string `json:"method"` // Genesis state of the blockchain being created
Endpoint string `json:"endpoint"` GenesisData formatting.CB58 `json:"genesisData"`
GenesisData interface{} `json:"genesisData"`
} }
// CreateGenesisReply is the reply from a call to CreateGenesis // CreateBlockchain returns an unsigned transaction to create a new blockchain
type CreateGenesisReply struct { // Must be signed with the Subnet's control keys and with a key that pays the transaction fee before issuance
Bytes formatting.CB58 `json:"bytes"` func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, response *CreateTxResponse) error {
} service.vm.Ctx.Log.Debug("createBlockchain called")
// CreateBlockchainReply is the reply from calling CreateBlockchain
type CreateBlockchainReply struct {
BlockchainID ids.ID `json:"blockchainID"`
}
// CreateBlockchain issues a transaction to the network to create a new blockchain
func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, reply *CreateBlockchainReply) error {
vmID, err := service.vm.chainManager.LookupVM(args.VMID) vmID, err := service.vm.chainManager.LookupVM(args.VMID)
if err != nil { if err != nil {
return fmt.Errorf("no VM with ID '%s' found", args.VMID) return fmt.Errorf("no VM with ID '%s' found", args.VMID)
@ -1099,48 +1135,43 @@ func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchain
} }
fxIDs = append(fxIDs, fxID) fxIDs = append(fxIDs, fxID)
} }
// If creating AVM instance, use secp256k1fx
genesisBytes := []byte(nil) // TODO: Document FXs and have user specify them in API call
if args.Method != "" { fxIDsSet := ids.Set{}
buf, err := json2.EncodeClientRequest(args.Method, args.GenesisData) fxIDsSet.Add(fxIDs...)
if err != nil { if vmID.Equals(avm.ID) && !fxIDsSet.Contains(secp256k1fx.ID) {
return fmt.Errorf("problem building blockchain genesis state: %w", err) fxIDs = append(fxIDs, secp256k1fx.ID)
}
writer := httptest.NewRecorder()
service.vm.Ctx.HTTP.Call(
/*writer=*/ writer,
/*method=*/ "POST",
/*base=*/ args.VMID,
/*endpoint=*/ args.Endpoint,
/*body=*/ bytes.NewBuffer(buf),
/*headers=*/ map[string]string{
"Content-Type": "application/json",
},
)
result := CreateGenesisReply{}
if err := json2.DecodeClientResponse(writer.Body, &result); err != nil {
return fmt.Errorf("problem building blockchain genesis state: %w", err)
}
genesisBytes = result.Bytes.Bytes
} else if args.GenesisData != nil {
return errNoMethodWithGenesis
} }
// TODO: Should use the key store to sign this transaction. if args.SubnetID.Equals(DefaultSubnetID) {
// TODO: Nonce shouldn't always be 0 return errDSCantValidate
tx, err := service.vm.newCreateChainTx(0, genesisBytes, vmID, fxIDs, args.Name, service.vm.Ctx.NetworkID, key) }
tx := CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: service.vm.Ctx.NetworkID,
SubnetID: args.SubnetID,
Nonce: uint64(args.PayerNonce),
ChainName: args.Name,
VMID: vmID,
FxIDs: fxIDs,
GenesisData: args.GenesisData.Bytes,
},
PayerAddress: ids.ShortID{},
PayerSig: [crypto.SECP256K1RSigLen]byte{},
ControlSigs: nil,
vm: nil,
id: ids.ID{},
bytes: nil,
}
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil { if err != nil {
return fmt.Errorf("problem creating transaction: %w", err) service.vm.Ctx.Log.Error("problem marshaling createChainTx: %v", err)
return errCreatingTransaction
} }
// Add this tx to the set of unissued txs response.UnsignedTx.Bytes = txBytes
service.vm.unissuedDecisionTxs = append(service.vm.unissuedDecisionTxs, tx)
service.vm.resetTimer()
reply.BlockchainID = tx.ID()
return nil return nil
} }
@ -1158,6 +1189,8 @@ type GetBlockchainStatusReply struct {
// GetBlockchainStatus gets the status of a blockchain with the ID [args.BlockchainID]. // GetBlockchainStatus gets the status of a blockchain with the ID [args.BlockchainID].
func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error { func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error {
service.vm.Ctx.Log.Debug("getBlockchainStatus called")
_, err := service.vm.chainManager.Lookup(args.BlockchainID) _, err := service.vm.chainManager.Lookup(args.BlockchainID)
if err == nil { if err == nil {
reply.Status = Validating reply.Status = Validating
@ -1212,3 +1245,100 @@ func (service *Service) chainExists(blockID ids.ID, chainID ids.ID) (bool, error
return false, nil return false, nil
} }
// ValidatedByArgs is the arguments for calling ValidatedBy
type ValidatedByArgs struct {
// ValidatedBy returns the ID of the Subnet validating the blockchain with this ID
BlockchainID ids.ID `json:"blockchainID"`
}
// ValidatedByResponse is the reply from calling ValidatedBy
type ValidatedByResponse struct {
// ID of the Subnet validating the specified blockchain
SubnetID ids.ID `json:"subnetID"`
}
// ValidatedBy returns the ID of the Subnet that validates [args.BlockchainID]
func (service *Service) ValidatedBy(_ *http.Request, args *ValidatedByArgs, response *ValidatedByResponse) error {
service.vm.Ctx.Log.Debug("validatedBy called")
chain, err := service.vm.getChain(service.vm.DB, args.BlockchainID)
if err != nil {
return err
}
response.SubnetID = chain.SubnetID
return nil
}
// ValidatesArgs are the arguments to Validates
type ValidatesArgs struct {
SubnetID ids.ID `json:"subnetID"`
}
// ValidatesResponse is the response from calling Validates
type ValidatesResponse struct {
BlockchainIDs []ids.ID `json:"blockchainIDs"`
}
// Validates returns the IDs of the blockchains validated by [args.SubnetID]
func (service *Service) Validates(_ *http.Request, args *ValidatesArgs, response *ValidatesResponse) error {
service.vm.Ctx.Log.Debug("validates called")
// Verify that the Subnet exists
if _, err := service.vm.getSubnet(service.vm.DB, args.SubnetID); err != nil {
return err
}
// Get the chains that exist
chains, err := service.vm.getChains(service.vm.DB)
if err != nil {
return err
}
// Filter to get the chains validated by the specified Subnet
for _, chain := range chains {
if chain.SubnetID.Equals(args.SubnetID) {
response.BlockchainIDs = append(response.BlockchainIDs, chain.ID())
}
}
return nil
}
// APIBlockchain is the representation of a blockchain used in API calls
type APIBlockchain struct {
// Blockchain's ID
ID ids.ID `json:"id"`
// Blockchain's (non-unique) human-readable name
Name string `json:"name"`
// Subnet that validates the blockchain
SubnetID ids.ID `json:"subnetID"`
// Virtual Machine the blockchain runs
VMID ids.ID `json:"vmID"`
}
// GetBlockchainsResponse is the response from a call to GetBlockchains
type GetBlockchainsResponse struct {
// blockchains that exist
Blockchains []APIBlockchain `json:"blockchains"`
}
// GetBlockchains returns all of the blockchains that exist
func (service *Service) GetBlockchains(_ *http.Request, args *struct{}, response *GetBlockchainsResponse) error {
service.vm.Ctx.Log.Debug("getBlockchains called")
chains, err := service.vm.getChains(service.vm.DB)
if err != nil {
return fmt.Errorf("couldn't retrieve blockchains: %v", err)
}
for _, chain := range chains {
response.Blockchains = append(response.Blockchains, APIBlockchain{
ID: chain.ID(),
Name: chain.ChainName,
SubnetID: chain.SubnetID,
VMID: chain.VMID,
})
}
return nil
}

View File

@ -22,7 +22,7 @@ func TestAddDefaultSubnetValidator(t *testing.T) {
} }
func TestCreateBlockchainArgsParsing(t *testing.T) { func TestCreateBlockchainArgsParsing(t *testing.T) {
jsonString := `{"vmID":"lol","chainName":"awesome","genesisData":{"key":"value"}}` jsonString := `{"vmID":"lol","fxIDs":["secp256k1"], "name":"awesome", "payerNonce":5, "genesisData":"SkB92YpWm4Q2iPnLGCuDPZPgUQMxajqQQuz91oi3xD984f8r"}`
args := CreateBlockchainArgs{} args := CreateBlockchainArgs{}
err := json.Unmarshal([]byte(jsonString), &args) err := json.Unmarshal([]byte(jsonString), &args)
if err != nil { if err != nil {

View File

@ -17,6 +17,7 @@ import (
var ( var (
errEmptyAccountAddress = errors.New("account has empty address") errEmptyAccountAddress = errors.New("account has empty address")
errNoSuchBlockchain = errors.New("there is no blockchain with the specified ID")
) )
// TODO: Cache prefixed IDs or use different way of keying into database // TODO: Cache prefixed IDs or use different way of keying into database
@ -146,7 +147,7 @@ func (vm *VM) putAccount(db database.Database, account Account) error {
return nil return nil
} }
// get the blockchains that exist // get all the blockchains that exist
func (vm *VM) getChains(db database.Database) ([]*CreateChainTx, error) { func (vm *VM) getChains(db database.Database) ([]*CreateChainTx, error) {
chainsInterface, err := vm.State.Get(db, chainsTypeID, chainsKey) chainsInterface, err := vm.State.Get(db, chainsTypeID, chainsKey)
if err != nil { if err != nil {
@ -154,12 +155,26 @@ func (vm *VM) getChains(db database.Database) ([]*CreateChainTx, error) {
} }
chains, ok := chainsInterface.([]*CreateChainTx) chains, ok := chainsInterface.([]*CreateChainTx)
if !ok { if !ok {
vm.Ctx.Log.Warn("expected to retrieve []*CreateChainTx from database but got different type") vm.Ctx.Log.Error("expected to retrieve []*CreateChainTx from database but got different type")
return nil, errDBChains return nil, errDBChains
} }
return chains, nil return chains, nil
} }
// get a blockchain by its ID
func (vm *VM) getChain(db database.Database, ID ids.ID) (*CreateChainTx, error) {
chains, err := vm.getChains(db)
if err != nil {
return nil, err
}
for _, chain := range chains {
if chain.ID().Equals(ID) {
return chain, nil
}
}
return nil, errNoSuchBlockchain
}
// put the list of blockchains that exist to database // put the list of blockchains that exist to database
func (vm *VM) putChains(db database.Database, chains createChainList) error { func (vm *VM) putChains(db database.Database, chains createChainList) error {
if err := vm.State.Put(db, chainsTypeID, chainsKey, chains); err != nil { if err := vm.State.Put(db, chainsTypeID, chainsKey, chains); err != nil {

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/json"
) )
@ -74,11 +75,13 @@ type APIDefaultSubnetValidator struct {
// [VMID] is the ID of the VM this chain runs. // [VMID] is the ID of the VM this chain runs.
// [FxIDs] are the IDs of the Fxs the chain supports. // [FxIDs] are the IDs of the Fxs the chain supports.
// [Name] is a human-readable, non-unique name for the chain. // [Name] is a human-readable, non-unique name for the chain.
// [SubnetID] is the ID of the subnet that validates the chain
type APIChain struct { type APIChain struct {
GenesisData formatting.CB58 `json:"genesisData"` GenesisData formatting.CB58 `json:"genesisData"`
VMID ids.ID `json:"vmID"` VMID ids.ID `json:"vmID"`
FxIDs []ids.ID `json:"fxIDs"` FxIDs []ids.ID `json:"fxIDs"`
Name string `json:"name"` Name string `json:"name"`
SubnetID ids.ID `json:"subnetID"`
} }
// BuildGenesisArgs are the arguments used to create // BuildGenesisArgs are the arguments used to create
@ -134,8 +137,8 @@ func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, repl
return errAccountHasNoValue return errAccountHasNoValue
} }
accounts = append(accounts, newAccount( accounts = append(accounts, newAccount(
account.Address, // ID account.Address, // ID
0, // nonce 0, // nonce
uint64(account.Balance), // balance uint64(account.Balance), // balance
)) ))
} }
@ -182,12 +185,15 @@ func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, repl
tx := &CreateChainTx{ tx := &CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{ UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: uint32(args.NetworkID), NetworkID: uint32(args.NetworkID),
SubnetID: chain.SubnetID,
Nonce: 0, Nonce: 0,
ChainName: chain.Name, ChainName: chain.Name,
VMID: chain.VMID, VMID: chain.VMID,
FxIDs: chain.FxIDs, FxIDs: chain.FxIDs,
GenesisData: chain.GenesisData.Bytes, GenesisData: chain.GenesisData.Bytes,
}, },
ControlSigs: [][crypto.SECP256K1RSigLen]byte{},
PayerSig: [crypto.SECP256K1RSigLen]byte{},
} }
if err := tx.initialize(nil); err != nil { if err := tx.initialize(nil); err != nil {
return err return err

View File

@ -4,114 +4,12 @@
package platformvm package platformvm
import ( import (
"bytes"
"testing" "testing"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/json"
) )
func TestBuildGenesis(t *testing.T) {
expected := []byte{
0x00, 0x00, 0x00, 0x01, 0x01, 0x5c, 0xce, 0x6c,
0x55, 0xd6, 0xb5, 0x09, 0x84, 0x5c, 0x8c, 0x4e,
0x30, 0xbe, 0xd9, 0x8d, 0x39, 0x1a, 0xe7, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x5b, 0xcd, 0x15,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0b, 0x01, 0x5c, 0xce, 0x6c, 0x55, 0xd6, 0xb5,
0x09, 0x84, 0x5c, 0x8c, 0x4e, 0x30, 0xbe, 0xd9,
0x8d, 0x39, 0x1a, 0xe7, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x3a, 0xde, 0x68, 0xb1, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x5c, 0xce, 0x6c, 0x55, 0xd6, 0xb5,
0x09, 0x84, 0x5c, 0x8c, 0x4e, 0x30, 0xbe, 0xd9,
0x8d, 0x39, 0x1a, 0xe7, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x13, 0x4d, 0x79, 0x20, 0x46,
0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x20,
0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x53,
0x6f, 0x75, 0x74, 0x68, 0x20, 0x50, 0x61, 0x72,
0x6b, 0x20, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64,
0x65, 0x20, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x53,
0x63, 0x6f, 0x74, 0x74, 0x20, 0x54, 0x65, 0x6e,
0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x20, 0x6d, 0x75,
0x73, 0x74, 0x20, 0x64, 0x69, 0x65, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
}
addr, _ := ids.ShortFromString("8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")
genesisData := formatting.CB58{}
genesisData.FromString("CGgRrQ3nws7RRMGyDV59cetJBAwmsmDyCSgku")
vmID, _ := ids.FromString("dkFD29iYU9e9jah2nrnksTWJUy2VVpg5Lnqd7nQqvCJgR26H4")
account := APIAccount{
Address: addr,
Balance: 123456789,
}
weight := json.Uint64(987654321)
validator := APIDefaultSubnetValidator{
APIValidator: APIValidator{
EndTime: 15,
Weight: &weight,
ID: addr,
},
Destination: addr,
}
chains := APIChain{
GenesisData: genesisData,
VMID: vmID,
Name: "My Favorite Episode",
}
args := BuildGenesisArgs{
Accounts: []APIAccount{
account,
},
Validators: []APIDefaultSubnetValidator{
validator,
},
Chains: []APIChain{
chains,
},
Time: 5,
}
reply := BuildGenesisReply{}
ss := StaticService{}
if err := ss.BuildGenesis(nil, &args, &reply); err != nil {
t.Fatal(err)
}
if !bytes.Equal(reply.Bytes.Bytes, expected) {
t.Fatalf("StaticService.BuildGenesis:\nReturned:\n%s\nExpected:\n%s",
formatting.DumpBytes{Bytes: reply.Bytes.Bytes},
formatting.DumpBytes{Bytes: expected})
}
}
func TestBuildGenesisInvalidAccountBalance(t *testing.T) { func TestBuildGenesisInvalidAccountBalance(t *testing.T) {
id, _ := ids.ShortFromString("8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z") id, _ := ids.ShortFromString("8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")
account := APIAccount{ account := APIAccount{

View File

@ -151,8 +151,13 @@ func init() {
type VM struct { type VM struct {
*core.SnowmanVM *core.SnowmanVM
// Node's validator manager
// Maps Subnets --> nodes in the Subnet HEAD
validators validators.Manager validators validators.Manager
// true if the node is being run with staking enabled
stakingEnabled bool
// The node's chain manager // The node's chain manager
chainManager chains.Manager chainManager chains.Manager
@ -296,8 +301,8 @@ func (vm *VM) Initialize(
}) })
go ctx.Log.RecoverAndPanic(vm.timer.Dispatch) go ctx.Log.RecoverAndPanic(vm.timer.Dispatch)
if err := vm.updateValidators(DefaultSubnetID); err != nil { if err := vm.initSubnets(); err != nil {
ctx.Log.Error("failed to initialize the current validator set: %s", err) ctx.Log.Error("failed to initialize Subnets: %s", err)
return err return err
} }
@ -313,27 +318,67 @@ func (vm *VM) Initialize(
return nil return nil
} }
// Create all of the chains that the database says should exist // Create all chains that exist that this node validates
// Can only be called after initSubnets()
func (vm *VM) initBlockchains() error { func (vm *VM) initBlockchains() error {
vm.Ctx.Log.Verbo("platform chain initializing existing blockchains") vm.Ctx.Log.Info("initializing blockchains")
existingChains, err := vm.getChains(vm.DB) blockchains, err := vm.getChains(vm.DB) // get blockchains that exist
if err != nil { if err != nil {
return err return err
} }
for _, chain := range existingChains { // Create each blockchain
chainParams := chains.ChainParameters{ for _, chain := range blockchains {
ID: chain.ID(), vm.createChain(chain)
GenesisData: chain.GenesisData,
VMAlias: chain.VMID.String(),
}
for _, fxID := range chain.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
vm.chainManager.CreateChain(chainParams)
} }
return nil return nil
} }
// Set the node's validator manager to be up to date
func (vm *VM) initSubnets() error {
vm.Ctx.Log.Info("initializing Subnets")
subnets, err := vm.getSubnets(vm.DB)
if err != nil {
return err
}
if err := vm.updateValidators(DefaultSubnetID); err != nil {
return err
}
for _, subnet := range subnets {
if err := vm.updateValidators(subnet.id); err != nil {
return err
}
}
return nil
}
// Create the blockchain described in [tx], but only if this node is a member of
// the Subnet that validates the chain
func (vm *VM) createChain(tx *CreateChainTx) {
// The validators that compose the Subnet that validates this chain
validators, subnetExists := vm.validators.GetValidatorSet(tx.SubnetID)
if !subnetExists {
vm.Ctx.Log.Error("blockchain %s validated by Subnet %s but couldn't get that Subnet. Blockchain not created")
return
}
if !validators.Contains(vm.Ctx.NodeID) && vm.stakingEnabled { // This node doesn't validate this blockchain
return
}
chainParams := chains.ChainParameters{
ID: tx.ID(),
SubnetID: tx.SubnetID,
GenesisData: tx.GenesisData,
VMAlias: tx.VMID.String(),
}
for _, fxID := range tx.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
vm.chainManager.CreateChain(chainParams)
}
// Shutdown this blockchain // Shutdown this blockchain
func (vm *VM) Shutdown() { func (vm *VM) Shutdown() {
vm.timer.Stop() vm.timer.Stop()
@ -663,13 +708,16 @@ func (vm *VM) nextSubnetValidatorChangeTime(db database.Database, subnetID ids.I
// Returns: // Returns:
// 1) The validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp] // 1) The validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp]
// 2) The pending validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp] // 2) The pending validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp]
// 3) The IDs of the validators that start validating [subnetID] between now and [timestamp]
// 4) The IDs of the validators that stop validating [subnetID] between now and [timestamp]
// Note that this method will not remove validators from the current validator set of the default subnet. // Note that this method will not remove validators from the current validator set of the default subnet.
// That happens in reward blocks. // That happens in reward blocks.
func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, subnetID ids.ID) (current, pending *EventHeap, err error) { func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, subnetID ids.ID) (current,
pending *EventHeap, started, stopped ids.ShortSet, err error) {
// remove validators whose end time <= [timestamp] // remove validators whose end time <= [timestamp]
current, err = vm.getCurrentValidators(db, subnetID) current, err = vm.getCurrentValidators(db, subnetID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, nil, err
} }
if !subnetID.Equals(DefaultSubnetID) { // validators of default subnet removed in rewardValidatorTxs, not here if !subnetID.Equals(DefaultSubnetID) { // validators of default subnet removed in rewardValidatorTxs, not here
for current.Len() > 0 { for current.Len() > 0 {
@ -678,11 +726,12 @@ func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, sub
break break
} }
current.Remove() current.Remove()
stopped.Add(next.Vdr().ID())
} }
} }
pending, err = vm.getPendingValidators(db, subnetID) pending, err = vm.getPendingValidators(db, subnetID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, nil, err
} }
for pending.Len() > 0 { for pending.Len() > 0 {
nextTx := pending.Peek() // pending staker with earliest start time nextTx := pending.Peek() // pending staker with earliest start time
@ -691,8 +740,9 @@ func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, sub
} }
heap.Push(current, nextTx) heap.Push(current, nextTx)
heap.Pop(pending) heap.Pop(pending)
started.Add(nextTx.Vdr().ID())
} }
return current, pending, nil return current, pending, started, stopped, nil
} }
func (vm *VM) getValidators(validatorEvents *EventHeap) []validators.Validator { func (vm *VM) getValidators(validatorEvents *EventHeap) []validators.Validator {
@ -720,10 +770,12 @@ func (vm *VM) getValidators(validatorEvents *EventHeap) []validators.Validator {
return vdrList return vdrList
} }
// update the node's validator manager to contain the current validator set of the given Subnet
func (vm *VM) updateValidators(subnetID ids.ID) error { func (vm *VM) updateValidators(subnetID ids.ID) error {
validatorSet, ok := vm.validators.GetValidatorSet(subnetID) validatorSet, subnetInitialized := vm.validators.GetValidatorSet(subnetID)
if !ok { if !subnetInitialized { // validator manager doesn't know about this subnet yet
return fmt.Errorf("couldn't get the validator sampler of the %s subnet", subnetID) validatorSet = validators.NewSet()
vm.validators.PutValidatorSet(subnetID, validatorSet)
} }
currentValidators, err := vm.getCurrentValidators(vm.DB, subnetID) currentValidators, err := vm.getCurrentValidators(vm.DB, subnetID)

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/chains/atomic" "github.com/ava-labs/gecko/chains/atomic"
"github.com/ava-labs/gecko/database/memdb" "github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
@ -39,16 +40,17 @@ var (
// each key corresponds to an account that has $AVA and a genesis validator // each key corresponds to an account that has $AVA and a genesis validator
keys []*crypto.PrivateKeySECP256K1R keys []*crypto.PrivateKeySECP256K1R
// amount all genesis validators stake // amount all genesis validators stake in defaultVM
defaultStakeAmount uint64 defaultStakeAmount uint64
// balance of accounts that exist at genesis // balance of accounts that exist at genesis in defaultVM
defaultBalance = 100 * MinimumStakeAmount defaultBalance = 100 * MinimumStakeAmount
// At genesis this account has AVA and is validating the default subnet // At genesis this account has AVA and is validating the default subnet
defaultKey *crypto.PrivateKeySECP256K1R defaultKey *crypto.PrivateKeySECP256K1R
// non-default subnet that exists at genesis in defaultVM // non-default Subnet that exists at genesis in defaultVM
// Its controlKeys are keys[0], keys[1], keys[2]
testSubnet1 *CreateSubnetTx testSubnet1 *CreateSubnetTx
testSubnet1ControlKeys []*crypto.PrivateKeySECP256K1R testSubnet1ControlKeys []*crypto.PrivateKeySECP256K1R
) )
@ -116,7 +118,8 @@ func defaultVM() *VM {
} }
vm := &VM{ vm := &VM{
SnowmanVM: &core.SnowmanVM{}, SnowmanVM: &core.SnowmanVM{},
chainManager: chains.MockManager{},
} }
defaultSubnet := validators.NewSet() defaultSubnet := validators.NewSet()
@ -136,7 +139,7 @@ func defaultVM() *VM {
testNetworkID, testNetworkID,
0, 0,
[]ids.ShortID{keys[0].PublicKey().Address(), keys[1].PublicKey().Address(), keys[2].PublicKey().Address()}, // control keys are keys[0], keys[1], keys[2] []ids.ShortID{keys[0].PublicKey().Address(), keys[1].PublicKey().Address(), keys[2].PublicKey().Address()}, // control keys are keys[0], keys[1], keys[2]
2, // 2 sigs from keys[0], keys[1], keys[2] needed to add validator to this subnet 2, // threshold; 2 sigs from keys[0], keys[1], keys[2] needed to add validator to this subnet
keys[0], keys[0],
) )
if err != nil { if err != nil {
@ -765,11 +768,13 @@ func TestCreateChain(t *testing.T) {
tx, err := vm.newCreateChainTx( tx, err := vm.newCreateChainTx(
defaultNonce+1, defaultNonce+1,
testSubnet1.id,
nil, nil,
timestampvm.ID, timestampvm.ID,
nil, nil,
"name ", "name",
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
keys[0], keys[0],
) )
if err != nil { if err != nil {
@ -806,7 +811,7 @@ func TestCreateChain(t *testing.T) {
} }
// Verify tx fee was deducted // Verify tx fee was deducted
account, err := vm.getAccount(vm.DB, tx.Key().Address()) account, err := vm.getAccount(vm.DB, tx.PayerAddress)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -74,10 +74,6 @@ func init() {
networkID, err := genesis.NetworkID(*networkName) networkID, err := genesis.NetworkID(*networkName)
errs.Add(err) errs.Add(err)
if networkID != genesis.LocalID {
errs.Add(fmt.Errorf("the only supported networkID is: %s", genesis.LocalName))
}
config.NetworkID = networkID config.NetworkID = networkID
// Remote: // Remote: