aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/evm/block.go15
-rw-r--r--plugin/evm/import_tx.go54
-rw-r--r--plugin/evm/vm.go78
3 files changed, 124 insertions, 23 deletions
diff --git a/plugin/evm/block.go b/plugin/evm/block.go
index d75ea1d..b3412a0 100644
--- a/plugin/evm/block.go
+++ b/plugin/evm/block.go
@@ -27,9 +27,18 @@ func (b *Block) ID() ids.ID { return b.id }
// Accept implements the snowman.Block interface
func (b *Block) Accept() error {
- b.vm.ctx.Log.Verbo("Block %s is accepted", b.ID())
- b.vm.updateStatus(b.ID(), choices.Accepted)
- return nil
+ vm := b.vm
+
+ vm.ctx.Log.Verbo("Block %s is accepted", b.ID())
+ vm.updateStatus(b.ID(), choices.Accepted)
+
+ tx := vm.getAtomicTx(b.ethBlock)
+ utx, ok := tx.UnsignedTx.(UnsignedAtomicTx)
+ if !ok {
+ return errors.New("unknown atomic tx type")
+ }
+
+ return utx.Accept(vm.ctx, nil)
}
// Reject implements the snowman.Block interface
diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go
index 2b4f995..35ba8cc 100644
--- a/plugin/evm/import_tx.go
+++ b/plugin/evm/import_tx.go
@@ -112,7 +112,49 @@ func (tx *UnsignedImportTx) SemanticVerify(
return permError{err}
}
- // TODO: verify UTXO inputs via gRPC (with creds)
+ if !vm.ctx.IsBootstrapped() {
+ // Allow for force committing during bootstrapping
+ return nil
+ }
+
+ utxoIDs := make([][]byte, len(tx.ImportedInputs))
+ for i, in := range tx.ImportedInputs {
+ utxoIDs[i] = in.UTXOID.InputID().Bytes()
+ }
+ allUTXOBytes, err := vm.ctx.SharedMemory.Get(tx.SourceChain, utxoIDs)
+ if err != nil {
+ return tempError{err}
+ }
+
+ utxos := make([]*avax.UTXO, len(tx.ImportedInputs))
+ for i, utxoBytes := range allUTXOBytes {
+ utxo := &avax.UTXO{}
+ if err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil {
+ return tempError{err}
+ }
+ utxos[i] = utxo
+ }
+
+ for i, in := range tx.ImportedInputs {
+ utxoBytes := allUTXOBytes[i]
+
+ utxo := &avax.UTXO{}
+ if err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil {
+ return tempError{err}
+ }
+
+ cred := stx.Creds[i]
+
+ utxoAssetID := utxo.AssetID()
+ inAssetID := in.AssetID()
+ if !utxoAssetID.Equals(inAssetID) {
+ return permError{errAssetIDMismatch}
+ }
+
+ if err := vm.fx.VerifyTransfer(tx, in.In, cred, utxo.Out); err != nil {
+ return tempError{err}
+ }
+ }
return nil
}
@@ -121,9 +163,13 @@ func (tx *UnsignedImportTx) SemanticVerify(
// we don't want to remove an imported UTXO in semanticVerify
// only to have the transaction not be Accepted. This would be inconsistent.
// Recall that imported UTXOs are not kept in a versionDB.
-func (tx *UnsignedImportTx) Accept(ctx *snow.Context, batch database.Batch) error {
- // TODO: finish this function via gRPC
- return nil
+func (tx *UnsignedImportTx) Accept(ctx *snow.Context, _ database.Batch) error {
+ // TODO: Is any batch passed in here?
+ utxoIDs := make([][]byte, len(tx.ImportedInputs))
+ for i, in := range tx.ImportedInputs {
+ utxoIDs[i] = in.InputID().Bytes()
+ }
+ return ctx.SharedMemory.Remove(tx.SourceChain, utxoIDs)
}
// Create a new transaction
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index 8823380..87487ff 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -39,6 +39,7 @@ import (
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
geckojson "github.com/ava-labs/gecko/utils/json"
+ "github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/timer"
"github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/avax"
@@ -63,6 +64,8 @@ const (
minBlockTime = 250 * time.Millisecond
maxBlockTime = 1000 * time.Millisecond
batchSize = 250
+
+ maxUTXOsToFetch = 1024
)
const (
@@ -117,13 +120,16 @@ func init() {
errs.Add(
Codec.RegisterType(&UnsignedImportTx{}),
Codec.RegisterType(&UnsignedExportTx{}),
+ )
+ Codec.Skip(3)
+ errs.Add(
Codec.RegisterType(&secp256k1fx.TransferInput{}),
- Codec.RegisterType(&secp256k1fx.Input{}),
- Codec.RegisterType(&secp256k1fx.Credential{}),
+ Codec.RegisterType(&secp256k1fx.MintOutput{}),
Codec.RegisterType(&secp256k1fx.TransferOutput{}),
- Codec.RegisterType(&secp256k1fx.OutputOwners{}),
Codec.RegisterType(&secp256k1fx.MintOperation{}),
- Codec.RegisterType(&secp256k1fx.MintOutput{}),
+ Codec.RegisterType(&secp256k1fx.Credential{}),
+ Codec.RegisterType(&secp256k1fx.Input{}),
+ Codec.RegisterType(&secp256k1fx.OutputOwners{}),
)
if errs.Errored() {
panic(errs.Err)
@@ -166,6 +172,8 @@ type VM struct {
txFee uint64
pendingAtomicTxs chan *Tx
blockAtomicInputCache cache.LRU
+
+ fx secp256k1fx.Fx
}
func (vm *VM) getAtomicTx(block *types.Block) *Tx {
@@ -178,6 +186,15 @@ func (vm *VM) getAtomicTx(block *types.Block) *Tx {
return atx
}
+// Codec implements the secp256k1fx interface
+func (vm *VM) Codec() codec.Codec { return codec.NewDefault() }
+
+// Clock implements the secp256k1fx interface
+func (vm *VM) Clock() *timer.Clock { return &vm.clock }
+
+// Logger implements the secp256k1fx interface
+func (vm *VM) Logger() logging.Logger { return vm.ctx.Log }
+
/*
******************************************************************************
********************************* Snowman API ********************************
@@ -355,16 +372,16 @@ func (vm *VM) Initialize(
})
vm.codec = Codec
- return nil
+ return vm.fx.Initialize(vm)
}
// Bootstrapping notifies this VM that the consensus engine is performing
// bootstrapping
-func (vm *VM) Bootstrapping() error { return nil }
+func (vm *VM) Bootstrapping() error { return vm.fx.Bootstrapping() }
// Bootstrapped notifies this VM that the consensus engine has finished
// bootstrapping
-func (vm *VM) Bootstrapped() error { return nil }
+func (vm *VM) Bootstrapped() error { return vm.fx.Bootstrapped() }
// Shutdown implements the snowman.ChainVM interface
func (vm *VM) Shutdown() error {
@@ -707,15 +724,44 @@ func (vm *VM) GetAtomicUTXOs(
startUTXOID ids.ID,
limit int,
) ([]*avax.UTXO, ids.ShortID, ids.ID, error) {
- // TODO: finish this function via gRPC
- utxos := []*avax.UTXO{{
- UTXOID: avax.UTXOID{TxID: ids.Empty},
- Asset: avax.Asset{ID: vm.ctx.AVAXAssetID},
- Out: &secp256k1fx.TransferOutput{
- Amt: 100,
- },
- }}
- return utxos, ids.ShortEmpty, ids.Empty, nil
+ if limit <= 0 || limit > maxUTXOsToFetch {
+ limit = maxUTXOsToFetch
+ }
+
+ addrsList := make([][]byte, addrs.Len())
+ for i, addr := range addrs.List() {
+ addrsList[i] = addr.Bytes()
+ }
+
+ allUTXOBytes, lastAddr, lastUTXO, err := vm.ctx.SharedMemory.Indexed(
+ chainID,
+ addrsList,
+ startAddr.Bytes(),
+ startUTXOID.Bytes(),
+ limit,
+ )
+ if err != nil {
+ return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error fetching atomic UTXOs: %w", err)
+ }
+
+ lastAddrID, err := ids.ToShortID(lastAddr)
+ if err != nil {
+ lastAddrID = ids.ShortEmpty
+ }
+ lastUTXOID, err := ids.ToID(lastUTXO)
+ if err != nil {
+ lastAddrID = ids.ShortEmpty
+ }
+
+ utxos := make([]*avax.UTXO, len(allUTXOBytes))
+ for i, utxoBytes := range allUTXOBytes {
+ utxo := &avax.UTXO{}
+ if err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil {
+ return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error parsing UTXO: %w", err)
+ }
+ utxos[i] = utxo
+ }
+ return utxos, lastAddrID, lastUTXOID, nil
}
func GetEthAddress(privKey *crypto.PrivateKeySECP256K1R) common.Address {