diff --git a/vms/avm/base_tx_test.go b/vms/avm/base_tx_test.go index 197b7fe..163ef5c 100644 --- a/vms/avm/base_tx_test.go +++ b/vms/avm/base_tx_test.go @@ -840,6 +840,16 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + cr := codecRegistry{ index: 1, typeToFxIndex: vm.typeToFxIndex, @@ -1386,6 +1396,16 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + cr := codecRegistry{ index: 1, typeToFxIndex: vm.typeToFxIndex, @@ -1538,6 +1558,16 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + cr := codecRegistry{ index: 1, typeToFxIndex: vm.typeToFxIndex, diff --git a/vms/avm/export_tx_test.go b/vms/avm/export_tx_test.go index 67de4f2..4e9d064 100644 --- a/vms/avm/export_tx_test.go +++ b/vms/avm/export_tx_test.go @@ -151,6 +151,16 @@ func TestIssueExportTx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + key := keys[0] tx := &Tx{UnsignedTx: &ExportTx{ @@ -297,6 +307,16 @@ func TestClearForceAcceptedExportTx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + key := keys[0] tx := &Tx{UnsignedTx: &ExportTx{ diff --git a/vms/avm/fx.go b/vms/avm/fx.go index 432b177..1e16ad3 100644 --- a/vms/avm/fx.go +++ b/vms/avm/fx.go @@ -19,6 +19,12 @@ type Fx interface { // return an error if the VM is incompatible. Initialize(vm interface{}) error + // Notify this Fx that the VM is in bootstrapping + Bootstrapping() error + + // Notify this Fx that the VM is bootstrapped + Bootstrapped() error + // VerifyTransfer verifies that the specified transaction can spend the // provided utxo with no restrictions on the destination. If the transaction // can't spend the output based on the input and credential, a non-nil error diff --git a/vms/avm/fx_test.go b/vms/avm/fx_test.go index 59639e9..13469fa 100644 --- a/vms/avm/fx_test.go +++ b/vms/avm/fx_test.go @@ -4,10 +4,12 @@ package avm type testFx struct { - initialize, verifyTransfer, verifyOperation error + initialize, bootstrapping, bootstrapped, verifyTransfer, verifyOperation error } func (fx *testFx) Initialize(_ interface{}) error { return fx.initialize } +func (fx *testFx) Bootstrapping() error { return fx.bootstrapping } +func (fx *testFx) Bootstrapped() error { return fx.bootstrapped } func (fx *testFx) VerifyTransfer(_, _, _, _ interface{}) error { return fx.verifyTransfer } func (fx *testFx) VerifyOperation(_, _, _ interface{}, _ []interface{}) error { return fx.verifyOperation diff --git a/vms/avm/import_tx_test.go b/vms/avm/import_tx_test.go index f01be37..696e841 100644 --- a/vms/avm/import_tx_test.go +++ b/vms/avm/import_tx_test.go @@ -140,6 +140,16 @@ func TestIssueImportTx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + key := keys[0] utxoID := ava.UTXOID{ @@ -288,6 +298,16 @@ func TestForceAcceptImportTx(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + key := keys[0] genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t) diff --git a/vms/avm/vm.go b/vms/avm/vm.go index 6c6525c..7ec3944 100644 --- a/vms/avm/vm.go +++ b/vms/avm/vm.go @@ -45,6 +45,7 @@ var ( errGenesisAssetMustHaveState = errors.New("genesis asset must have non-empty state") errInvalidAddress = errors.New("invalid address") errWrongBlockchainID = errors.New("wrong blockchain ID") + errBootstrapping = errors.New("chain is currently bootstrapping") ) // VM implements the avalanche.DAGVM interface @@ -67,6 +68,8 @@ type VM struct { // State management state *prefixedState + bootstrapped bool + // Transaction issuing timer *timer.Timer batchTimeout time.Duration @@ -198,10 +201,25 @@ func (vm *VM) Initialize( } // Bootstrapping marks this VM as bootstrapping -func (vm *VM) Bootstrapping() error { return nil } +func (vm *VM) Bootstrapping() error { + for _, fx := range vm.fxs { + if err := fx.Fx.Bootstrapping(); err != nil { + return err + } + } + return nil +} // Bootstrapped marks this VM as bootstrapped -func (vm *VM) Bootstrapped() error { return nil } +func (vm *VM) Bootstrapped() error { + for _, fx := range vm.fxs { + if err := fx.Fx.Bootstrapped(); err != nil { + return err + } + } + vm.bootstrapped = true + return nil +} // Shutdown implements the avalanche.DAGVM interface func (vm *VM) Shutdown() error { @@ -278,6 +296,9 @@ func (vm *VM) GetTx(txID ids.ID) (snowstorm.Tx, error) { // either accepted or rejected with the appropriate status. This function will // go out of scope when the transaction is removed from memory. func (vm *VM) IssueTx(b []byte, onDecide func(choices.Status)) (ids.ID, error) { + if !vm.bootstrapped { + return ids.ID{}, errBootstrapping + } tx, err := vm.parseTx(b) if err != nil { return ids.ID{}, err diff --git a/vms/avm/vm_test.go b/vms/avm/vm_test.go index 397d4eb..d3a2d73 100644 --- a/vms/avm/vm_test.go +++ b/vms/avm/vm_test.go @@ -178,6 +178,14 @@ func GenesisVM(t *testing.T) ([]byte, chan common.Message, *VM) { } vm.batchTimeout = 0 + if err := vm.Bootstrapping(); err != nil { + t.Fatal(err) + } + + if err := vm.Bootstrapped(); err != nil { + t.Fatal(err) + } + return genesisBytes, issuer, vm } @@ -678,6 +686,16 @@ func TestIssueNFT(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + createAssetTx := &Tx{UnsignedTx: &CreateAssetTx{ BaseTx: BaseTx{ NetID: networkID, @@ -841,6 +859,16 @@ func TestIssueProperty(t *testing.T) { } vm.batchTimeout = 0 + err = vm.Bootstrapping() + if err != nil { + t.Fatal(err) + } + + err = vm.Bootstrapped() + if err != nil { + t.Fatal(err) + } + createAssetTx := &Tx{UnsignedTx: &CreateAssetTx{ BaseTx: BaseTx{ NetID: networkID, diff --git a/vms/secp256k1fx/fx.go b/vms/secp256k1fx/fx.go index e7571b5..608982b 100644 --- a/vms/secp256k1fx/fx.go +++ b/vms/secp256k1fx/fx.go @@ -34,8 +34,9 @@ var ( // Fx describes the secp256k1 feature extension type Fx struct { - VM VM - SECPFactory crypto.FactorySECP256K1R + VM VM + SECPFactory crypto.FactorySECP256K1R + bootstrapped bool } // Initialize ... @@ -69,6 +70,12 @@ func (fx *Fx) InitializeVM(vmIntf interface{}) error { return nil } +// Bootstrapping ... +func (fx *Fx) Bootstrapping() error { return nil } + +// Bootstrapped ... +func (fx *Fx) Bootstrapped() error { fx.bootstrapped = true; return nil } + // VerifyOperation ... func (fx *Fx) VerifyOperation(txIntf, opIntf, credIntf interface{}, utxosIntf []interface{}) error { tx, ok := txIntf.(Tx) @@ -156,6 +163,11 @@ func (fx *Fx) VerifyCredentials(tx Tx, in *Input, cred *Credential, out *OutputO return errInputCredentialSignersMismatch } + // disable signature verification during bootstrapping + if !fx.bootstrapped { + return nil + } + txBytes := tx.UnsignedBytes() txHash := hashing.ComputeHash256(txBytes) diff --git a/vms/secp256k1fx/fx_test.go b/vms/secp256k1fx/fx_test.go index 18cd7aa..79e6c89 100644 --- a/vms/secp256k1fx/fx_test.go +++ b/vms/secp256k1fx/fx_test.go @@ -76,6 +76,12 @@ func TestFxVerifyTransfer(t *testing.T) { if err := fx.Initialize(&vm); err != nil { t.Fatal(err) } + if err := fx.Bootstrapping(); err != nil { + t.Fatal(err) + } + if err := fx.Bootstrapped(); err != nil { + t.Fatal(err) + } tx := &testTx{ bytes: txBytes, } @@ -470,6 +476,9 @@ func TestFxVerifyTransferInvalidSignature(t *testing.T) { if err := fx.Initialize(&vm); err != nil { t.Fatal(err) } + if err := fx.Bootstrapping(); err != nil { + t.Fatal(err) + } tx := &testTx{ bytes: txBytes, } @@ -495,6 +504,14 @@ func TestFxVerifyTransferInvalidSignature(t *testing.T) { }, } + if err := fx.VerifyTransfer(tx, in, cred, out); err != nil { + t.Fatal(err) + } + + if err := fx.Bootstrapped(); err != nil { + t.Fatal(err) + } + if err := fx.VerifyTransfer(tx, in, cred, out); err == nil { t.Fatalf("Should have errored due to an invalid signature") } @@ -508,6 +525,9 @@ func TestFxVerifyTransferWrongSigner(t *testing.T) { if err := fx.Initialize(&vm); err != nil { t.Fatal(err) } + if err := fx.Bootstrapping(); err != nil { + t.Fatal(err) + } tx := &testTx{ bytes: txBytes, } @@ -533,6 +553,14 @@ func TestFxVerifyTransferWrongSigner(t *testing.T) { }, } + if err := fx.VerifyTransfer(tx, in, cred, out); err != nil { + t.Fatal(err) + } + + if err := fx.Bootstrapped(); err != nil { + t.Fatal(err) + } + if err := fx.VerifyTransfer(tx, in, cred, out); err == nil { t.Fatalf("Should have errored due to a wrong signer") }