mirror of https://github.com/poanetwork/gecko.git
Cleanup + introduction of side effects
This commit is contained in:
parent
638cce84ce
commit
aee9d03921
|
@ -6,9 +6,9 @@ package avm
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ava-labs/gecko/database"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/utils/math"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
)
|
||||
|
@ -78,10 +78,8 @@ func (t *BaseTx) UTXOs() []*UTXO {
|
|||
TxID: txID,
|
||||
OutputIndex: uint32(i),
|
||||
},
|
||||
Asset: Asset{
|
||||
ID: out.AssetID(),
|
||||
},
|
||||
Out: out.Out,
|
||||
Asset: Asset{ID: out.AssetID()},
|
||||
Out: out.Out,
|
||||
}
|
||||
}
|
||||
return utxos
|
||||
|
@ -98,10 +96,12 @@ func (t *BaseTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) error
|
|||
return errWrongChainID
|
||||
}
|
||||
|
||||
fc := NewFlowChecker()
|
||||
for _, out := range t.Outs {
|
||||
if err := out.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
fc.Produce(out.AssetID(), out.Output().Amount())
|
||||
}
|
||||
if !IsSortedTransferableOutputs(t.Outs, c) {
|
||||
return errOutputsNotSorted
|
||||
|
@ -111,45 +111,16 @@ func (t *BaseTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) error
|
|||
if err := in.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
fc.Consume(in.AssetID(), in.Input().Amount())
|
||||
}
|
||||
if !isSortedAndUniqueTransferableInputs(t.Ins) {
|
||||
return errInputsNotSortedUnique
|
||||
}
|
||||
|
||||
consumedFunds := map[[32]byte]uint64{}
|
||||
for _, in := range t.Ins {
|
||||
assetID := in.AssetID()
|
||||
amount := in.Input().Amount()
|
||||
// TODO: Add the Tx fee to the produced side
|
||||
|
||||
var err error
|
||||
assetIDKey := assetID.Key()
|
||||
consumedFunds[assetIDKey], err = math.Add64(consumedFunds[assetIDKey], amount)
|
||||
|
||||
if err != nil {
|
||||
return errInputOverflow
|
||||
}
|
||||
}
|
||||
producedFunds := map[[32]byte]uint64{}
|
||||
for _, out := range t.Outs {
|
||||
assetID := out.AssetID()
|
||||
amount := out.Output().Amount()
|
||||
|
||||
var err error
|
||||
assetIDKey := assetID.Key()
|
||||
producedFunds[assetIDKey], err = math.Add64(producedFunds[assetIDKey], amount)
|
||||
|
||||
if err != nil {
|
||||
return errOutputOverflow
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add the Tx fee to the producedFunds
|
||||
|
||||
for assetID, producedAssetAmount := range producedFunds {
|
||||
consumedAssetAmount := consumedFunds[assetID]
|
||||
if producedAssetAmount > consumedAssetAmount {
|
||||
return errInsufficientFunds
|
||||
}
|
||||
if err := fc.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.metadata.Verify()
|
||||
|
@ -166,46 +137,11 @@ func (t *BaseTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable
|
|||
}
|
||||
fx := vm.fxs[fxIndex].Fx
|
||||
|
||||
utxoID := in.InputID()
|
||||
utxo, err := vm.state.UTXO(utxoID)
|
||||
if err == nil {
|
||||
utxoAssetID := utxo.AssetID()
|
||||
inAssetID := in.AssetID()
|
||||
if !utxoAssetID.Equals(inAssetID) {
|
||||
return errAssetIDMismatch
|
||||
}
|
||||
|
||||
if !vm.verifyFxUsage(fxIndex, inAssetID) {
|
||||
return errIncompatibleFx
|
||||
}
|
||||
|
||||
err = fx.VerifyTransfer(uTx, utxo.Out, in.In, cred)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
utxo, err := vm.getUTXO(&in.UTXOID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputTx, inputIndex := in.InputSource()
|
||||
parent := UniqueTx{
|
||||
vm: vm,
|
||||
txID: inputTx,
|
||||
}
|
||||
|
||||
if err := parent.Verify(); err != nil {
|
||||
return errMissingUTXO
|
||||
} else if status := parent.Status(); status.Decided() {
|
||||
return errMissingUTXO
|
||||
}
|
||||
|
||||
utxos := parent.UTXOs()
|
||||
|
||||
if uint32(len(utxos)) <= inputIndex || int(inputIndex) < 0 {
|
||||
return errInvalidUTXO
|
||||
}
|
||||
|
||||
utxo = utxos[int(inputIndex)]
|
||||
|
||||
utxoAssetID := utxo.AssetID()
|
||||
inAssetID := in.AssetID()
|
||||
if !utxoAssetID.Equals(inAssetID) {
|
||||
|
@ -222,3 +158,6 @@ func (t *BaseTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SideEffects that this transaction has in addition to the UTXO operations.
|
||||
func (t *BaseTx) SideEffects(vm *VM) ([]database.Batch, error) { return nil, nil }
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -111,10 +110,5 @@ func (t *CreateAssetTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs
|
|||
return nil
|
||||
}
|
||||
|
||||
// SemanticVerify that this transaction is well-formed.
|
||||
func (t *CreateAssetTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable) error {
|
||||
return t.BaseTx.SemanticVerify(vm, uTx, creds)
|
||||
}
|
||||
|
||||
// Sort ...
|
||||
func (t *CreateAssetTx) Sort() { sortInitialStates(t.States) }
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
var (
|
||||
errOperationsNotSortedUnique = errors.New("operations not sorted and unique")
|
||||
errNoOperations = errors.New("an operationTx must have at least one operation")
|
||||
|
||||
errDoubleSpend = errors.New("inputs attempt to double spend an input")
|
||||
)
|
||||
|
@ -61,10 +62,8 @@ func (t *OperationTx) UTXOs() []*UTXO {
|
|||
TxID: txID,
|
||||
OutputIndex: uint32(len(utxos)),
|
||||
},
|
||||
Asset: Asset{
|
||||
ID: asset,
|
||||
},
|
||||
Out: out,
|
||||
Asset: Asset{ID: asset},
|
||||
Out: out,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +76,8 @@ func (t *OperationTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs i
|
|||
switch {
|
||||
case t == nil:
|
||||
return errNilTx
|
||||
case len(t.Ops) == 0:
|
||||
return errNoOperations
|
||||
}
|
||||
|
||||
if err := t.BaseTx.SyntacticVerify(ctx, c, numFxs); err != nil {
|
||||
|
@ -126,38 +127,11 @@ func (t *OperationTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verif
|
|||
cred := creds[i+offset]
|
||||
credIntfs = append(credIntfs, cred)
|
||||
|
||||
utxoID := in.InputID()
|
||||
utxo, err := vm.state.UTXO(utxoID)
|
||||
if err == nil {
|
||||
utxoAssetID := utxo.AssetID()
|
||||
if !utxoAssetID.Equals(opAssetID) {
|
||||
return errAssetIDMismatch
|
||||
}
|
||||
|
||||
utxos = append(utxos, utxo.Out)
|
||||
continue
|
||||
utxo, err := vm.getUTXO(&in.UTXOID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputTx, inputIndex := in.InputSource()
|
||||
parent := UniqueTx{
|
||||
vm: vm,
|
||||
txID: inputTx,
|
||||
}
|
||||
|
||||
if err := parent.Verify(); err != nil {
|
||||
return errMissingUTXO
|
||||
} else if status := parent.Status(); status.Decided() {
|
||||
return errMissingUTXO
|
||||
}
|
||||
|
||||
parentUTXOs := parent.UTXOs()
|
||||
|
||||
if uint32(len(parentUTXOs)) <= inputIndex || int(inputIndex) < 0 {
|
||||
return errInvalidUTXO
|
||||
}
|
||||
|
||||
utxo = parentUTXOs[int(inputIndex)]
|
||||
|
||||
utxoAssetID := utxo.AssetID()
|
||||
if !utxoAssetID.Equals(opAssetID) {
|
||||
return errAssetIDMismatch
|
||||
|
|
|
@ -6,8 +6,8 @@ package avm
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ava-labs/gecko/database"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
|
@ -31,8 +31,10 @@ type UnsignedTx interface {
|
|||
AssetIDs() ids.Set
|
||||
InputUTXOs() []*UTXOID
|
||||
UTXOs() []*UTXO
|
||||
|
||||
SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int) error
|
||||
SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable) error
|
||||
SideEffects(vm *VM) ([]database.Batch, error)
|
||||
}
|
||||
|
||||
// Tx is the core operation that can be performed. The tx uses the UTXO model.
|
||||
|
|
|
@ -6,6 +6,7 @@ package avm
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ava-labs/gecko/chains/atomic"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
||||
|
@ -98,13 +99,20 @@ func (tx *UniqueTx) ID() ids.ID { return tx.txID }
|
|||
|
||||
// Accept is called when the transaction was finalized as accepted by consensus
|
||||
func (tx *UniqueTx) Accept() {
|
||||
defer tx.vm.db.Abort()
|
||||
|
||||
if err := tx.setStatus(choices.Accepted); err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to accept tx %s due to %s", tx.txID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove spent utxos
|
||||
for _, utxoID := range tx.InputIDs().List() {
|
||||
for _, utxo := range tx.InputUTXOs() {
|
||||
if utxo.Symbolic() {
|
||||
// If the UTXO is symbolic, it can't be spent
|
||||
continue
|
||||
}
|
||||
utxoID := utxo.InputID()
|
||||
if err := tx.vm.state.SpendUTXO(utxoID); err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to spend utxo %s due to %s", utxoID, err)
|
||||
return
|
||||
|
@ -120,12 +128,23 @@ func (tx *UniqueTx) Accept() {
|
|||
}
|
||||
|
||||
txID := tx.ID()
|
||||
tx.vm.ctx.Log.Verbo("Accepting Tx: %s", txID)
|
||||
|
||||
if err := tx.vm.db.Commit(); err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", tx.txID, err)
|
||||
effects, err := tx.SideEffects(tx.vm)
|
||||
if err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to compute side effects from %s due to %s", txID, err)
|
||||
return
|
||||
}
|
||||
|
||||
commitBatch, err := tx.vm.db.CommitBatch()
|
||||
if err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to calculate CommitBatch for %s due to %s", txID, err)
|
||||
}
|
||||
|
||||
if err := atomic.WriteAll(commitBatch, effects...); err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", txID, err)
|
||||
}
|
||||
|
||||
tx.vm.ctx.Log.Verbo("Accepted Tx: %s", txID)
|
||||
|
||||
tx.vm.pubsub.Publish("accepted", txID)
|
||||
|
||||
tx.deps = nil // Needed to prevent a memory leak
|
||||
|
@ -137,6 +156,8 @@ func (tx *UniqueTx) Accept() {
|
|||
|
||||
// Reject is called when the transaction was finalized as rejected by consensus
|
||||
func (tx *UniqueTx) Reject() {
|
||||
defer tx.vm.db.Abort()
|
||||
|
||||
if err := tx.setStatus(choices.Rejected); err != nil {
|
||||
tx.vm.ctx.Log.Error("Failed to reject tx %s due to %s", tx.txID, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,9 @@ type UTXOID struct {
|
|||
TxID ids.ID `serialize:"true"`
|
||||
OutputIndex uint32 `serialize:"true"`
|
||||
|
||||
// Cached:
|
||||
// symbolic is false if the UTXO should be part of the DB
|
||||
symbolic bool
|
||||
// id is the unique ID of a UTXO, it is calculated from TxID and OutputIndex
|
||||
id ids.ID
|
||||
}
|
||||
|
||||
|
@ -35,6 +37,10 @@ func (utxo *UTXOID) InputID() ids.ID {
|
|||
return utxo.id
|
||||
}
|
||||
|
||||
// Symbolic returns if this is the ID of a UTXO in the DB, or if it is a
|
||||
// symbolic input
|
||||
func (utxo *UTXOID) Symbolic() bool { return utxo.symbolic }
|
||||
|
||||
// Verify implements the verify.Verifiable interface
|
||||
func (utxo *UTXOID) Verify() error {
|
||||
switch {
|
||||
|
|
|
@ -432,6 +432,32 @@ func (vm *VM) issueTx(tx snowstorm.Tx) {
|
|||
}
|
||||
}
|
||||
|
||||
func (vm *VM) getUTXO(utxoID *UTXOID) (*UTXO, error) {
|
||||
inputID := utxoID.InputID()
|
||||
utxo, err := vm.state.UTXO(inputID)
|
||||
if err == nil {
|
||||
return utxo, nil
|
||||
}
|
||||
|
||||
inputTx, inputIndex := utxoID.InputSource()
|
||||
parent := UniqueTx{
|
||||
vm: vm,
|
||||
txID: inputTx,
|
||||
}
|
||||
|
||||
if err := parent.Verify(); err != nil {
|
||||
return nil, errMissingUTXO
|
||||
} else if status := parent.Status(); status.Decided() {
|
||||
return nil, errMissingUTXO
|
||||
}
|
||||
|
||||
parentUTXOs := parent.UTXOs()
|
||||
if uint32(len(parentUTXOs)) <= inputIndex || int(inputIndex) < 0 {
|
||||
return nil, errInvalidUTXO
|
||||
}
|
||||
return parentUTXOs[int(inputIndex)], nil
|
||||
}
|
||||
|
||||
func (vm *VM) getFx(val interface{}) (int, error) {
|
||||
valType := reflect.TypeOf(val)
|
||||
fx, exists := vm.typeToFxIndex[valType]
|
||||
|
|
Loading…
Reference in New Issue