mirror of https://github.com/poanetwork/gecko.git
Added export Tx to the AVM
This commit is contained in:
parent
01fe74ec6b
commit
552e63f2eb
|
@ -0,0 +1,164 @@
|
|||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package avm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ava-labs/gecko/chains/atomic"
|
||||
"github.com/ava-labs/gecko/database"
|
||||
"github.com/ava-labs/gecko/database/versiondb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
"github.com/ava-labs/gecko/vms/components/ava"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
)
|
||||
|
||||
// ExportTx is a transaction that exports an asset to another blockchain.
|
||||
type ExportTx struct {
|
||||
BaseTx `serialize:"true"`
|
||||
|
||||
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
|
||||
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
|
||||
}
|
||||
|
||||
// InputUTXOs track which UTXOs this transaction is consuming.
|
||||
func (t *ExportTx) InputUTXOs() []*ava.UTXOID {
|
||||
utxos := t.BaseTx.InputUTXOs()
|
||||
for _, in := range t.Ins {
|
||||
utxos = append(utxos, &in.UTXOID)
|
||||
}
|
||||
return utxos
|
||||
}
|
||||
|
||||
// AssetIDs returns the IDs of the assets this transaction depends on
|
||||
func (t *ExportTx) AssetIDs() ids.Set {
|
||||
assets := t.BaseTx.AssetIDs()
|
||||
for _, in := range t.Ins {
|
||||
assets.Add(in.AssetID())
|
||||
}
|
||||
return assets
|
||||
}
|
||||
|
||||
var (
|
||||
errNoExportInputs = errors.New("no export inputs")
|
||||
)
|
||||
|
||||
// SyntacticVerify that this transaction is well-formed.
|
||||
func (t *ExportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int) error {
|
||||
switch {
|
||||
case t == nil:
|
||||
return errNilTx
|
||||
case len(t.Ins) == 0:
|
||||
return errNoExportInputs
|
||||
}
|
||||
|
||||
if err := t.BaseTx.SyntacticVerify(ctx, c, numFxs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fc := ava.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
|
||||
}
|
||||
|
||||
for _, in := range t.Ins {
|
||||
if err := in.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
fc.Consume(in.AssetID(), in.Input().Amount())
|
||||
}
|
||||
if !isSortedAndUniqueTransferableInputs(t.Ins) {
|
||||
return errInputsNotSortedUnique
|
||||
}
|
||||
|
||||
// TODO: Add the Tx fee to the produced side
|
||||
|
||||
return fc.Verify()
|
||||
}
|
||||
|
||||
// SemanticVerify that this transaction is well-formed.
|
||||
func (t *ExportTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiable) error {
|
||||
if err := t.BaseTx.SemanticVerify(vm, uTx, creds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := len(t.BaseTx.Ins)
|
||||
for i, in := range t.Ins {
|
||||
cred := creds[i+offset]
|
||||
|
||||
fxIndex, err := vm.getFx(cred)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fx := vm.fxs[fxIndex].Fx
|
||||
|
||||
utxo, err := vm.getUTXO(&in.UTXOID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utxoAssetID := utxo.AssetID()
|
||||
inAssetID := in.AssetID()
|
||||
if !utxoAssetID.Equals(inAssetID) {
|
||||
return errAssetIDMismatch
|
||||
}
|
||||
|
||||
if !vm.verifyFxUsage(fxIndex, inAssetID) {
|
||||
return errIncompatibleFx
|
||||
}
|
||||
|
||||
if err := fx.VerifyTransfer(uTx, utxo.Out, in.In, cred); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteWithSideEffects writes the batch with any additional side effects
|
||||
func (t *ExportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error {
|
||||
txID := t.ID()
|
||||
|
||||
bID := ids.Empty // TODO: Needs to be set to the platform chain
|
||||
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
vsmDB := versiondb.New(smDB)
|
||||
|
||||
state := ava.NewPrefixedState(vsmDB, vm.codec)
|
||||
for i, out := range t.Outs {
|
||||
utxo := &ava.UTXO{
|
||||
UTXOID: ava.UTXOID{
|
||||
TxID: txID,
|
||||
OutputIndex: uint32(len(t.BaseTx.Outs) + i),
|
||||
},
|
||||
Asset: ava.Asset{ID: out.AssetID()},
|
||||
Out: out.Out,
|
||||
}
|
||||
|
||||
utxoID := utxo.InputID()
|
||||
if _, err := state.AVMStatus(utxoID); err == nil {
|
||||
if err := state.SetAVMStatus(utxoID, choices.Unknown); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := state.SetAVMUTXO(utxoID, utxo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sharedBatch, err := vsmDB.CommitBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return atomic.WriteAll(batch, sharedBatch)
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package avm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/gecko/chains/atomic"
|
||||
"github.com/ava-labs/gecko/database/memdb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
"github.com/ava-labs/gecko/snow/engine/common"
|
||||
"github.com/ava-labs/gecko/utils/crypto"
|
||||
"github.com/ava-labs/gecko/utils/logging"
|
||||
"github.com/ava-labs/gecko/vms/components/ava"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||
)
|
||||
|
||||
func TestExportTxSerialization(t *testing.T) {
|
||||
expected := []byte{
|
||||
// txID:
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
// networkID:
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
// blockchainID:
|
||||
0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||
// number of base outs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of base inputs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of outs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of inputs:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// utxoID:
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
// output index
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// assetID:
|
||||
0x1f, 0x3f, 0x5f, 0x7f, 0x9e, 0xbe, 0xde, 0xfe,
|
||||
0x1d, 0x3d, 0x5d, 0x7d, 0x9c, 0xbc, 0xdc, 0xfc,
|
||||
0x1b, 0x3b, 0x5b, 0x7b, 0x9a, 0xba, 0xda, 0xfa,
|
||||
0x19, 0x39, 0x59, 0x79, 0x98, 0xb8, 0xd8, 0xf8,
|
||||
// input:
|
||||
// input ID:
|
||||
0x00, 0x00, 0x00, 0x08,
|
||||
// amount:
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
|
||||
// num sig indices:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// sig index[0]:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
tx := &Tx{UnsignedTx: &ExportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: 2,
|
||||
BCID: ids.NewID([32]byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||
}),
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
})},
|
||||
Asset: ava.Asset{ID: ids.NewID([32]byte{
|
||||
0x1f, 0x3f, 0x5f, 0x7f, 0x9e, 0xbe, 0xde, 0xfe,
|
||||
0x1d, 0x3d, 0x5d, 0x7d, 0x9c, 0xbc, 0xdc, 0xfc,
|
||||
0x1b, 0x3b, 0x5b, 0x7b, 0x9a, 0xba, 0xda, 0xfa,
|
||||
0x19, 0x39, 0x59, 0x79, 0x98, 0xb8, 0xd8, 0xf8,
|
||||
})},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 1000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
c := codec.NewDefault()
|
||||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||
c.RegisterType(&secp256k1fx.MintInput{})
|
||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
||||
c.RegisterType(&secp256k1fx.Credential{})
|
||||
|
||||
b, err := c.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
result := tx.Bytes()
|
||||
if !bytes.Equal(expected, result) {
|
||||
t.Fatalf("\nExpected: 0x%x\nResult: 0x%x", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
// Test issuing an import transaction.
|
||||
func TestIssueExportTx(t *testing.T) {
|
||||
genesisBytes := BuildGenesisTest(t)
|
||||
|
||||
issuer := make(chan common.Message, 1)
|
||||
|
||||
sm := &atomic.SharedMemory{}
|
||||
sm.Initialize(logging.NoLog{}, memdb.New())
|
||||
|
||||
ctx := snow.DefaultContextTest()
|
||||
ctx.NetworkID = networkID
|
||||
ctx.ChainID = chainID
|
||||
ctx.SharedMemory = sm.NewBlockchainSharedMemory(chainID)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
vm := &VM{}
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
genesisBytes,
|
||||
issuer,
|
||||
[]*common.Fx{&common.Fx{
|
||||
ID: ids.Empty,
|
||||
Fx: &secp256k1fx.Fx{},
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm.batchTimeout = 0
|
||||
|
||||
key := keys[0]
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
tx := &Tx{UnsignedTx: &ExportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: networkID,
|
||||
BCID: chainID,
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: ava.UTXOID{
|
||||
TxID: genesisTx.ID(),
|
||||
OutputIndex: 1,
|
||||
},
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 50000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
Outs: []*TransferableOutput{&TransferableOutput{
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
Out: &secp256k1fx.TransferOutput{
|
||||
Amt: 50000,
|
||||
OutputOwners: secp256k1fx.OutputOwners{
|
||||
Threshold: 1,
|
||||
Addrs: []ids.ShortID{key.PublicKey().Address()},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
unsignedBytes, err := vm.codec.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := key.Sign(unsignedBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
||||
copy(fixedSig[:], sig)
|
||||
|
||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
||||
fixedSig,
|
||||
},
|
||||
})
|
||||
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
if _, err := vm.IssueTx(tx.Bytes(), nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
msg := <-issuer
|
||||
if msg != common.PendingTxs {
|
||||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 1)
|
||||
}
|
||||
|
||||
parsedTx := txs[0]
|
||||
if err := parsedTx.Verify(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parsedTx.Accept()
|
||||
|
||||
bID := ids.Empty // TODO: Needs to be set to the platform chain
|
||||
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
state := ava.NewPrefixedState(smDB, vm.codec)
|
||||
|
||||
utxo := ava.UTXOID{
|
||||
TxID: tx.ID(),
|
||||
OutputIndex: 0,
|
||||
}
|
||||
utxoID := utxo.InputID()
|
||||
if _, err := state.AVMUTXO(utxoID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := state.AVMStatus(utxoID); err == nil {
|
||||
t.Fatalf("should have failed to read the status")
|
||||
}
|
||||
}
|
||||
|
||||
// Test force accepting an import transaction.
|
||||
func TestClearForceAcceptedExportTx(t *testing.T) {
|
||||
genesisBytes := BuildGenesisTest(t)
|
||||
|
||||
issuer := make(chan common.Message, 1)
|
||||
|
||||
sm := &atomic.SharedMemory{}
|
||||
sm.Initialize(logging.NoLog{}, memdb.New())
|
||||
|
||||
ctx := snow.DefaultContextTest()
|
||||
ctx.NetworkID = networkID
|
||||
ctx.ChainID = chainID
|
||||
ctx.SharedMemory = sm.NewBlockchainSharedMemory(chainID)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
vm := &VM{}
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
genesisBytes,
|
||||
issuer,
|
||||
[]*common.Fx{&common.Fx{
|
||||
ID: ids.Empty,
|
||||
Fx: &secp256k1fx.Fx{},
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm.batchTimeout = 0
|
||||
|
||||
key := keys[0]
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
tx := &Tx{UnsignedTx: &ExportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: networkID,
|
||||
BCID: chainID,
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: ava.UTXOID{
|
||||
TxID: genesisTx.ID(),
|
||||
OutputIndex: 1,
|
||||
},
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 50000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
Outs: []*TransferableOutput{&TransferableOutput{
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
Out: &secp256k1fx.TransferOutput{
|
||||
Amt: 50000,
|
||||
OutputOwners: secp256k1fx.OutputOwners{
|
||||
Threshold: 1,
|
||||
Addrs: []ids.ShortID{key.PublicKey().Address()},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
unsignedBytes, err := vm.codec.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := key.Sign(unsignedBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
||||
copy(fixedSig[:], sig)
|
||||
|
||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
||||
fixedSig,
|
||||
},
|
||||
})
|
||||
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
if _, err := vm.IssueTx(tx.Bytes(), nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
msg := <-issuer
|
||||
if msg != common.PendingTxs {
|
||||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 1)
|
||||
}
|
||||
|
||||
parsedTx := txs[0]
|
||||
if err := parsedTx.Verify(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bID := ids.Empty // TODO: Needs to be set to the platform chain
|
||||
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
|
||||
state := ava.NewPrefixedState(smDB, vm.codec)
|
||||
|
||||
utxo := ava.UTXOID{
|
||||
TxID: tx.ID(),
|
||||
OutputIndex: 0,
|
||||
}
|
||||
utxoID := utxo.InputID()
|
||||
if err := state.SetAVMStatus(utxoID, choices.Accepted); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
parsedTx.Accept()
|
||||
|
||||
smDB = vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
state = ava.NewPrefixedState(smDB, vm.codec)
|
||||
|
||||
if _, err := state.AVMUTXO(utxoID); err == nil {
|
||||
t.Fatalf("should have failed to read the utxo")
|
||||
}
|
||||
if _, err := state.AVMStatus(utxoID); err == nil {
|
||||
t.Fatalf("should have failed to read the status")
|
||||
}
|
||||
}
|
|
@ -4,17 +4,14 @@
|
|||
package avm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/ava-labs/gecko/chains/atomic"
|
||||
"github.com/ava-labs/gecko/database/versiondb"
|
||||
|
||||
"github.com/ava-labs/gecko/database"
|
||||
"github.com/ava-labs/gecko/database/versiondb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/utils"
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
"github.com/ava-labs/gecko/vms/components/ava"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
|
@ -24,60 +21,20 @@ var (
|
|||
errNilUTXOID = errors.New("nil utxo ID is not valid")
|
||||
)
|
||||
|
||||
// ImportInput ...
|
||||
type ImportInput struct {
|
||||
UTXOID ids.ID `serialize:"true"`
|
||||
ava.Asset `serialize:"true"`
|
||||
|
||||
In FxTransferable `serialize:"true"`
|
||||
}
|
||||
|
||||
// Input returns the feature extension input that this Input is using.
|
||||
func (in *ImportInput) Input() FxTransferable { return in.In }
|
||||
|
||||
// Verify implements the verify.Verifiable interface
|
||||
func (in *ImportInput) Verify() error {
|
||||
switch {
|
||||
case in == nil:
|
||||
return errNilTransferableInput
|
||||
case in.UTXOID.IsZero():
|
||||
return errNilUTXOID
|
||||
case in.In == nil:
|
||||
return errNilTransferableFxInput
|
||||
default:
|
||||
return verify.All(&in.Asset, in.In)
|
||||
}
|
||||
}
|
||||
|
||||
type innerSortImportInputs []*ImportInput
|
||||
|
||||
func (ins innerSortImportInputs) Less(i, j int) bool {
|
||||
return bytes.Compare(ins[i].AssetID().Bytes(), ins[j].AssetID().Bytes()) == -1
|
||||
}
|
||||
func (ins innerSortImportInputs) Len() int { return len(ins) }
|
||||
func (ins innerSortImportInputs) Swap(i, j int) { ins[j], ins[i] = ins[i], ins[j] }
|
||||
|
||||
func sortImportInputs(ins []*ImportInput) { sort.Sort(innerSortImportInputs(ins)) }
|
||||
func isSortedAndUniqueImportInputs(ins []*ImportInput) bool {
|
||||
return utils.IsSortedAndUnique(innerSortImportInputs(ins))
|
||||
}
|
||||
|
||||
// ImportTx is a transaction that imports an asset from another blockchain.
|
||||
type ImportTx struct {
|
||||
BaseTx `serialize:"true"`
|
||||
|
||||
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
|
||||
Ins []*ImportInput `serialize:"true"` // The inputs to this transaction
|
||||
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
|
||||
}
|
||||
|
||||
// InputUTXOs track which UTXOs this transaction is consuming.
|
||||
func (t *ImportTx) InputUTXOs() []*ava.UTXOID {
|
||||
utxos := t.BaseTx.InputUTXOs()
|
||||
for _, in := range t.Ins {
|
||||
utxos = append(utxos, &ava.UTXOID{
|
||||
TxID: in.UTXOID,
|
||||
Symbol: true,
|
||||
})
|
||||
in.Symbol = true
|
||||
utxos = append(utxos, &in.UTXOID)
|
||||
}
|
||||
return utxos
|
||||
}
|
||||
|
@ -111,7 +68,7 @@ func (t *ImportTx) UTXOs() []*ava.UTXO {
|
|||
}
|
||||
|
||||
var (
|
||||
errNoInputs = errors.New("no import inputs")
|
||||
errNoImportInputs = errors.New("no import inputs")
|
||||
)
|
||||
|
||||
// SyntacticVerify that this transaction is well-formed.
|
||||
|
@ -120,7 +77,7 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
|
|||
case t == nil:
|
||||
return errNilTx
|
||||
case len(t.Ins) == 0:
|
||||
return errNoInputs
|
||||
return errNoImportInputs
|
||||
}
|
||||
|
||||
if err := t.BaseTx.SyntacticVerify(ctx, c, numFxs); err != nil {
|
||||
|
@ -144,17 +101,13 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
|
|||
}
|
||||
fc.Consume(in.AssetID(), in.Input().Amount())
|
||||
}
|
||||
if !isSortedAndUniqueImportInputs(t.Ins) {
|
||||
if !isSortedAndUniqueTransferableInputs(t.Ins) {
|
||||
return errInputsNotSortedUnique
|
||||
}
|
||||
|
||||
// TODO: Add the Tx fee to the produced side
|
||||
|
||||
if err := fc.Verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return fc.Verify()
|
||||
}
|
||||
|
||||
// SemanticVerify that this transaction is well-formed.
|
||||
|
@ -179,7 +132,7 @@ func (t *ImportTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiab
|
|||
}
|
||||
fx := vm.fxs[fxIndex].Fx
|
||||
|
||||
utxoID := in.UTXOID
|
||||
utxoID := in.UTXOID.InputID()
|
||||
utxo, err := state.PlatformUTXO(utxoID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -211,8 +164,12 @@ func (t *ImportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error {
|
|||
|
||||
state := ava.NewPrefixedState(vsmDB, vm.codec)
|
||||
for _, in := range t.Ins {
|
||||
utxoID := in.UTXOID
|
||||
if err := state.SetPlatformUTXO(utxoID, nil); err != nil {
|
||||
utxoID := in.UTXOID.InputID()
|
||||
if _, err := state.PlatformUTXO(utxoID); err == nil {
|
||||
if err := state.SetPlatformUTXO(utxoID, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := state.SetPlatformStatus(utxoID, choices.Accepted); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package avm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/gecko/chains/atomic"
|
||||
"github.com/ava-labs/gecko/database/memdb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow"
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
"github.com/ava-labs/gecko/snow/engine/common"
|
||||
"github.com/ava-labs/gecko/utils/crypto"
|
||||
"github.com/ava-labs/gecko/utils/logging"
|
||||
"github.com/ava-labs/gecko/vms/components/ava"
|
||||
"github.com/ava-labs/gecko/vms/components/codec"
|
||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||
)
|
||||
|
||||
func TestImportTxSerialization(t *testing.T) {
|
||||
expected := []byte{
|
||||
// txID:
|
||||
0x00, 0x00, 0x00, 0x03,
|
||||
// networkID:
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
// blockchainID:
|
||||
0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||
// number of base outs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of base inputs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of outs:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// number of inputs:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// utxoID:
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
// output index
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// assetID:
|
||||
0x1f, 0x3f, 0x5f, 0x7f, 0x9e, 0xbe, 0xde, 0xfe,
|
||||
0x1d, 0x3d, 0x5d, 0x7d, 0x9c, 0xbc, 0xdc, 0xfc,
|
||||
0x1b, 0x3b, 0x5b, 0x7b, 0x9a, 0xba, 0xda, 0xfa,
|
||||
0x19, 0x39, 0x59, 0x79, 0x98, 0xb8, 0xd8, 0xf8,
|
||||
// input:
|
||||
// input ID:
|
||||
0x00, 0x00, 0x00, 0x08,
|
||||
// amount:
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
|
||||
// num sig indices:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// sig index[0]:
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
tx := &Tx{UnsignedTx: &ImportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: 2,
|
||||
BCID: ids.NewID([32]byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
|
||||
}),
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
})},
|
||||
Asset: ava.Asset{ID: ids.NewID([32]byte{
|
||||
0x1f, 0x3f, 0x5f, 0x7f, 0x9e, 0xbe, 0xde, 0xfe,
|
||||
0x1d, 0x3d, 0x5d, 0x7d, 0x9c, 0xbc, 0xdc, 0xfc,
|
||||
0x1b, 0x3b, 0x5b, 0x7b, 0x9a, 0xba, 0xda, 0xfa,
|
||||
0x19, 0x39, 0x59, 0x79, 0x98, 0xb8, 0xd8, 0xf8,
|
||||
})},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 1000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
c := codec.NewDefault()
|
||||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||
c.RegisterType(&secp256k1fx.MintInput{})
|
||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
||||
c.RegisterType(&secp256k1fx.Credential{})
|
||||
|
||||
b, err := c.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
result := tx.Bytes()
|
||||
if !bytes.Equal(expected, result) {
|
||||
t.Fatalf("\nExpected: 0x%x\nResult: 0x%x", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
// Test issuing an import transaction.
|
||||
func TestIssueImportTx(t *testing.T) {
|
||||
genesisBytes := BuildGenesisTest(t)
|
||||
|
||||
issuer := make(chan common.Message, 1)
|
||||
|
||||
sm := &atomic.SharedMemory{}
|
||||
sm.Initialize(logging.NoLog{}, memdb.New())
|
||||
|
||||
ctx := snow.DefaultContextTest()
|
||||
ctx.NetworkID = networkID
|
||||
ctx.ChainID = chainID
|
||||
ctx.SharedMemory = sm.NewBlockchainSharedMemory(chainID)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
vm := &VM{}
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
genesisBytes,
|
||||
issuer,
|
||||
[]*common.Fx{&common.Fx{
|
||||
ID: ids.Empty,
|
||||
Fx: &secp256k1fx.Fx{},
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm.batchTimeout = 0
|
||||
|
||||
key := keys[0]
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
utxoID := ava.UTXOID{
|
||||
TxID: ids.NewID([32]byte{
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
}),
|
||||
}
|
||||
|
||||
tx := &Tx{UnsignedTx: &ImportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: networkID,
|
||||
BCID: chainID,
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: utxoID,
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 1000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
unsignedBytes, err := vm.codec.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := key.Sign(unsignedBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
||||
copy(fixedSig[:], sig)
|
||||
|
||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
||||
fixedSig,
|
||||
},
|
||||
})
|
||||
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
if _, err := vm.IssueTx(tx.Bytes(), nil); err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Provide the platform UTXO:
|
||||
|
||||
bID := ids.Empty // TODO: Needs to be set to the platform chain
|
||||
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
|
||||
utxo := &ava.UTXO{
|
||||
UTXOID: utxoID,
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
Out: &secp256k1fx.TransferOutput{
|
||||
Amt: 1000,
|
||||
OutputOwners: secp256k1fx.OutputOwners{
|
||||
Threshold: 1,
|
||||
Addrs: []ids.ShortID{key.PublicKey().Address()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
state := ava.NewPrefixedState(smDB, vm.codec)
|
||||
if err := state.SetPlatformUTXO(utxoID.InputID(), utxo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
if _, err := vm.IssueTx(tx.Bytes(), nil); err != nil {
|
||||
t.Fatalf("should have issued the transaction correctly but errored: %s", err)
|
||||
}
|
||||
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
msg := <-issuer
|
||||
if msg != common.PendingTxs {
|
||||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 1)
|
||||
}
|
||||
|
||||
parsedTx := txs[0]
|
||||
parsedTx.Accept()
|
||||
|
||||
smDB = vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
state = ava.NewPrefixedState(smDB, vm.codec)
|
||||
if _, err := state.PlatformUTXO(utxoID.InputID()); err == nil {
|
||||
t.Fatalf("shouldn't have been able to read the utxo")
|
||||
}
|
||||
}
|
||||
|
||||
// Test force accepting an import transaction.
|
||||
func TestForceAcceptImportTx(t *testing.T) {
|
||||
genesisBytes := BuildGenesisTest(t)
|
||||
|
||||
issuer := make(chan common.Message, 1)
|
||||
|
||||
sm := &atomic.SharedMemory{}
|
||||
sm.Initialize(logging.NoLog{}, memdb.New())
|
||||
|
||||
ctx := snow.DefaultContextTest()
|
||||
ctx.NetworkID = networkID
|
||||
ctx.ChainID = chainID
|
||||
ctx.SharedMemory = sm.NewBlockchainSharedMemory(chainID)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
genesisBytes,
|
||||
issuer,
|
||||
[]*common.Fx{&common.Fx{
|
||||
ID: ids.Empty,
|
||||
Fx: &secp256k1fx.Fx{},
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vm.batchTimeout = 0
|
||||
|
||||
key := keys[0]
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
utxoID := ava.UTXOID{
|
||||
TxID: ids.NewID([32]byte{
|
||||
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
|
||||
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
|
||||
0x0b, 0x2b, 0x4b, 0x6b, 0x8a, 0xaa, 0xca, 0xea,
|
||||
0x09, 0x29, 0x49, 0x69, 0x88, 0xa8, 0xc8, 0xe8,
|
||||
}),
|
||||
}
|
||||
|
||||
tx := &Tx{UnsignedTx: &ImportTx{
|
||||
BaseTx: BaseTx{
|
||||
NetID: networkID,
|
||||
BCID: chainID,
|
||||
},
|
||||
Ins: []*TransferableInput{&TransferableInput{
|
||||
UTXOID: utxoID,
|
||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||
In: &secp256k1fx.TransferInput{
|
||||
Amt: 1000,
|
||||
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
unsignedBytes, err := vm.codec.Marshal(&tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := key.Sign(unsignedBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
||||
copy(fixedSig[:], sig)
|
||||
|
||||
tx.Creds = append(tx.Creds, &secp256k1fx.Credential{
|
||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
||||
fixedSig,
|
||||
},
|
||||
})
|
||||
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx.Initialize(b)
|
||||
|
||||
parsedTx, err := vm.ParseTx(tx.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := parsedTx.Verify(); err == nil {
|
||||
t.Fatalf("Should have failed verification")
|
||||
}
|
||||
|
||||
parsedTx.Accept()
|
||||
|
||||
bID := ids.Empty // TODO: Needs to be set to the platform chain
|
||||
smDB := vm.ctx.SharedMemory.GetDatabase(bID)
|
||||
defer vm.ctx.SharedMemory.ReleaseDatabase(bID)
|
||||
|
||||
state := ava.NewPrefixedState(smDB, vm.codec)
|
||||
utxoSource := utxoID.InputID()
|
||||
if _, err := state.PlatformUTXO(utxoSource); err == nil {
|
||||
t.Fatalf("shouldn't have been able to read the utxo")
|
||||
} else if status, err := state.PlatformStatus(utxoSource); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if status != choices.Accepted {
|
||||
t.Fatalf("should have marked the utxo as consumed")
|
||||
}
|
||||
}
|
|
@ -136,7 +136,7 @@ func TestCreateFixedCapAsset(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reply.AssetID.String() != "27ySRc5CE4obYwkS6kyvj5S8eGxGkr994157Hdo82mKVHTWpUT" {
|
||||
if reply.AssetID.String() != "2PEdmaGjKsSd14xPHZkVjhDdxH1VBsCATW8gnmqfMYfp68EwU5" {
|
||||
t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ func TestCreateVariableCapAsset(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if reply.AssetID.String() != "2vnRkWvRN3G9JJ7pixBmNdq4pfwRFkpew4kccf27WokYLH9VYY" {
|
||||
if reply.AssetID.String() != "p58dzpQikQmKVp3QtXMrg4e9AcppDc7MgqjpQf18CiNrpr2ug" {
|
||||
t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, repl
|
|||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||
c.RegisterType(&secp256k1fx.MintInput{})
|
||||
|
|
|
@ -80,19 +80,88 @@ func TestBuildGenesis(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := "1112YAVd1YsJ7JBDMQssciuuu9ySgebznWfmfT8JSw5vUKERtP4WGyitE7z38J8tExNmvK2kuwHsUP3erfcncXBWmJkdnd9nDJoj9tCiQHJmW1pstNQn3zXHdTnw6KJcG8Ro36ahknQkuy9ZSXgnZtpFhqUuwSd7mPj8vzZcqJMXLXorCBfvhwypTbZKogM9tUshyUfngfkg256ZsoU2ufMjhTG14PBBrgJkXD2F38uVSXWvYbubMVWDZbDnUzbyD3Azrs2Hydf8Paio6aNjwfwc1py61oXS5ehC55wiYbKpfzwE4px3bfYBu9yV6rvhivksB56vop9LEo8Pdo71tFAMkhR5toZmYcqRKyLXAnYqonUgmPsyxNwU22as8oscT5dj3Qxy1jsg6bEp6GwQepNqsWufGYx6Hiby2r5hyRZeYdk6xsXMPGBSBWUXhKX3ReTxBnjcrVE2Zc3G9eMvRho1tKzt7ppkutpcQemdDy2dxGryMqaFmPJaTaqcH2vB197KgVFbPgmHZY3ufUdfpVzzHax365pwCmzQD2PQh8hCqEP7rfV5e8uXKQiSynngoNDM4ak145zTpcUaX8htMGinfs45aKQvo5WHcD6ccRnHzc7dyXN8xJRnMznsuRN7D6k66DdbfDYhc2NbVUgXRAF4wSNTtsuZGxCGTEjQyYaoUoJowGXvnxmXAWHvLyMJswNizBeYgw1agRg5qB4AEKX96BFXhJq3MbsBRiypLR6nSuZgPFhCrLdBtstxEC2SPQNuUVWW9Qy68dDWQ3Fxx95n1pnjVru9wDJFoemg2imXRR"
|
||||
expected := formatting.CB58{Bytes: []byte{
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x61, 0x73,
|
||||
0x73, 0x65, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0f, 0x6d, 0x79, 0x46, 0x69, 0x78, 0x65,
|
||||
0x64, 0x43, 0x61, 0x70, 0x41, 0x73, 0x73, 0x65,
|
||||
0x74, 0x00, 0x04, 0x4d, 0x46, 0x43, 0x41, 0x08,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x50,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0x3f, 0x78, 0xe5, 0x10, 0xdf, 0x62, 0xbc, 0x48,
|
||||
0xb0, 0x82, 0x9e, 0xc0, 0x6d, 0x6a, 0x6b, 0x98,
|
||||
0x06, 0x2d, 0x69, 0x53, 0x00, 0x00, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x50,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0xc5, 0x49, 0x03, 0xde, 0x51, 0x77, 0xa1, 0x6f,
|
||||
0x78, 0x11, 0x77, 0x1e, 0xf2, 0xf4, 0x65, 0x9d,
|
||||
0x9e, 0x86, 0x46, 0x71, 0x00, 0x00, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xa0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0x3f, 0x58, 0xfd, 0xa2, 0xe9, 0xea, 0x8d, 0x9e,
|
||||
0x4b, 0x18, 0x18, 0x32, 0xa0, 0x7b, 0x26, 0xda,
|
||||
0xe2, 0x86, 0xf2, 0xcb, 0x00, 0x00, 0x00, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xa0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
||||
0x64, 0x59, 0x38, 0xbb, 0x7a, 0xe2, 0x19, 0x32,
|
||||
0x70, 0xe6, 0xff, 0xef, 0x00, 0x9e, 0x36, 0x64,
|
||||
0xd1, 0x1e, 0x07, 0xc1, 0x00, 0x06, 0x61, 0x73,
|
||||
0x73, 0x65, 0x74, 0x32, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0d, 0x6d, 0x79, 0x56, 0x61, 0x72, 0x43,
|
||||
0x61, 0x70, 0x41, 0x73, 0x73, 0x65, 0x74, 0x00,
|
||||
0x04, 0x4d, 0x56, 0x43, 0x41, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x3f, 0x58,
|
||||
0xfd, 0xa2, 0xe9, 0xea, 0x8d, 0x9e, 0x4b, 0x18,
|
||||
0x18, 0x32, 0xa0, 0x7b, 0x26, 0xda, 0xe2, 0x86,
|
||||
0xf2, 0xcb, 0x64, 0x59, 0x38, 0xbb, 0x7a, 0xe2,
|
||||
0x19, 0x32, 0x70, 0xe6, 0xff, 0xef, 0x00, 0x9e,
|
||||
0x36, 0x64, 0xd1, 0x1e, 0x07, 0xc1, 0x00, 0x00,
|
||||
0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x02, 0x3f, 0x78, 0xe5, 0x10, 0xdf, 0x62,
|
||||
0xbc, 0x48, 0xb0, 0x82, 0x9e, 0xc0, 0x6d, 0x6a,
|
||||
0x6b, 0x98, 0x06, 0x2d, 0x69, 0x53, 0xc5, 0x49,
|
||||
0x03, 0xde, 0x51, 0x77, 0xa1, 0x6f, 0x78, 0x11,
|
||||
0x77, 0x1e, 0xf2, 0xf4, 0x65, 0x9d, 0x9e, 0x86,
|
||||
0x46, 0x71, 0x00, 0x06, 0x61, 0x73, 0x73, 0x65,
|
||||
0x74, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x6d, 0x79, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x56,
|
||||
0x61, 0x72, 0x43, 0x61, 0x70, 0x41, 0x73, 0x73,
|
||||
0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x64, 0x59, 0x38,
|
||||
0xbb, 0x7a, 0xe2, 0x19, 0x32, 0x70, 0xe6, 0xff,
|
||||
0xef, 0x00, 0x9e, 0x36, 0x64, 0xd1, 0x1e, 0x07,
|
||||
0xc1,
|
||||
}}
|
||||
|
||||
cb58 := formatting.CB58{}
|
||||
if err := cb58.FromString(expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedBytes := cb58.Bytes
|
||||
|
||||
if result := reply.Bytes.String(); result != expected {
|
||||
t.Fatalf("Create genesis returned unexpected bytes:\n\n%s\n\n%s\n\n%s",
|
||||
reply.Bytes,
|
||||
if result := reply.Bytes.String(); result != expected.String() {
|
||||
t.Fatalf("Create genesis returned:\n%s\nExpected:\n%s",
|
||||
formatting.DumpBytes{Bytes: reply.Bytes.Bytes},
|
||||
formatting.DumpBytes{Bytes: expectedBytes},
|
||||
formatting.DumpBytes{Bytes: expected.Bytes},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,13 +289,14 @@ func (tx *UniqueTx) SemanticVerify() error {
|
|||
return tx.validity
|
||||
}
|
||||
|
||||
tx.verifiedState = true
|
||||
tx.validity = tx.Tx.SemanticVerify(tx.vm, tx)
|
||||
|
||||
if tx.validity == nil {
|
||||
tx.vm.pubsub.Publish("verified", tx.ID())
|
||||
err := tx.Tx.SemanticVerify(tx.vm, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.validity
|
||||
|
||||
tx.verifiedState = true
|
||||
tx.vm.pubsub.Publish("verified", tx.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsignedBytes returns the unsigned bytes of the transaction
|
||||
|
|
|
@ -123,25 +123,12 @@ func (vm *VM) Initialize(
|
|||
return errs.Err
|
||||
}
|
||||
|
||||
vm.state = &prefixedState{
|
||||
state: &state{State: ava.State{
|
||||
Cache: &cache.LRU{Size: stateCacheSize},
|
||||
DB: vm.db,
|
||||
Codec: vm.codec,
|
||||
}},
|
||||
|
||||
tx: &cache.LRU{Size: idCacheSize},
|
||||
utxo: &cache.LRU{Size: idCacheSize},
|
||||
txStatus: &cache.LRU{Size: idCacheSize},
|
||||
funds: &cache.LRU{Size: idCacheSize},
|
||||
|
||||
uniqueTx: &cache.EvictableLRU{Size: txCacheSize},
|
||||
}
|
||||
|
||||
c := codec.NewDefault()
|
||||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
|
||||
vm.fxs = make([]*parsedFx, len(fxs))
|
||||
for i, fxContainer := range fxs {
|
||||
|
@ -168,6 +155,21 @@ func (vm *VM) Initialize(
|
|||
|
||||
vm.codec = c
|
||||
|
||||
vm.state = &prefixedState{
|
||||
state: &state{State: ava.State{
|
||||
Cache: &cache.LRU{Size: stateCacheSize},
|
||||
DB: vm.db,
|
||||
Codec: vm.codec,
|
||||
}},
|
||||
|
||||
tx: &cache.LRU{Size: idCacheSize},
|
||||
utxo: &cache.LRU{Size: idCacheSize},
|
||||
txStatus: &cache.LRU{Size: idCacheSize},
|
||||
funds: &cache.LRU{Size: idCacheSize},
|
||||
|
||||
uniqueTx: &cache.EvictableLRU{Size: txCacheSize},
|
||||
}
|
||||
|
||||
if err := vm.initAliases(genesisBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ func GetFirstTxFromGenesisTest(genesisBytes []byte, t *testing.T) *Tx {
|
|||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||
c.RegisterType(&secp256k1fx.MintInput{})
|
||||
|
@ -207,7 +209,7 @@ func TestTxSerialization(t *testing.T) {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// fxID:
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
// secp256k1 Transferable Output:
|
||||
// amount:
|
||||
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
|
||||
|
@ -228,7 +230,7 @@ func TestTxSerialization(t *testing.T) {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// fxID:
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
// secp256k1 Transferable Output:
|
||||
// amount:
|
||||
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
|
||||
|
@ -249,7 +251,7 @@ func TestTxSerialization(t *testing.T) {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// fxID:
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
// secp256k1 Transferable Output:
|
||||
// amount:
|
||||
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
|
||||
|
@ -278,7 +280,7 @@ func TestTxSerialization(t *testing.T) {
|
|||
// number of outputs:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// fxID:
|
||||
0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x05,
|
||||
// secp256k1 Mint Output:
|
||||
// threshold:
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
|
@ -331,6 +333,8 @@ func TestTxSerialization(t *testing.T) {
|
|||
c.RegisterType(&BaseTx{})
|
||||
c.RegisterType(&CreateAssetTx{})
|
||||
c.RegisterType(&OperationTx{})
|
||||
c.RegisterType(&ImportTx{})
|
||||
c.RegisterType(&ExportTx{})
|
||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||
c.RegisterType(&secp256k1fx.MintInput{})
|
||||
|
|
|
@ -65,3 +65,23 @@ func (s *PrefixedState) PlatformStatus(id ids.ID) (choices.Status, error) {
|
|||
func (s *PrefixedState) SetPlatformStatus(id ids.ID, status choices.Status) error {
|
||||
return s.SetStatus(UniqueID(id, platformStatusID, s.platformStatus), status)
|
||||
}
|
||||
|
||||
// AVMUTXO attempts to load a utxo from AVM's storage.
|
||||
func (s *PrefixedState) AVMUTXO(id ids.ID) (*UTXO, error) {
|
||||
return s.UTXO(UniqueID(id, avmUTXOID, s.platformUTXO))
|
||||
}
|
||||
|
||||
// SetAVMUTXO saves the provided utxo to AVM's storage.
|
||||
func (s *PrefixedState) SetAVMUTXO(id ids.ID, utxo *UTXO) error {
|
||||
return s.SetUTXO(UniqueID(id, avmUTXOID, s.platformUTXO), utxo)
|
||||
}
|
||||
|
||||
// AVMStatus returns the AVM status from storage.
|
||||
func (s *PrefixedState) AVMStatus(id ids.ID) (choices.Status, error) {
|
||||
return s.Status(UniqueID(id, avmStatusID, s.platformStatus))
|
||||
}
|
||||
|
||||
// SetAVMStatus saves the provided platform status to storage.
|
||||
func (s *PrefixedState) SetAVMStatus(id ids.ID, status choices.Status) error {
|
||||
return s.SetStatus(UniqueID(id, avmStatusID, s.platformStatus), status)
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ func NewWallet(log logging.Logger, networkID uint32, chainID ids.ID, txFee uint6
|
|||
c.RegisterType(&avm.BaseTx{}),
|
||||
c.RegisterType(&avm.CreateAssetTx{}),
|
||||
c.RegisterType(&avm.OperationTx{}),
|
||||
c.RegisterType(&avm.ImportTx{}),
|
||||
c.RegisterType(&avm.ExportTx{}),
|
||||
c.RegisterType(&secp256k1fx.MintOutput{}),
|
||||
c.RegisterType(&secp256k1fx.TransferOutput{}),
|
||||
c.RegisterType(&secp256k1fx.MintInput{}),
|
||||
|
|
Loading…
Reference in New Issue