This commit is contained in:
StephenButtolph 2020-03-25 23:48:21 -04:00
parent a6a92510af
commit 93ed25f878
31 changed files with 1372 additions and 919 deletions

View File

@ -21,6 +21,9 @@ const (
// SECP256K1SKLen is the number of bytes in a secp2561k private key // SECP256K1SKLen is the number of bytes in a secp2561k private key
SECP256K1SKLen = 32 SECP256K1SKLen = 32
// SECP256K1PKLen is the number of bytes in a secp2561k public key
SECP256K1PKLen = 33
) )
// FactorySECP256K1 ... // FactorySECP256K1 ...

View File

@ -27,6 +27,10 @@ const (
// SECP256K1RSKLen is the number of bytes in a secp2561k recoverable private // SECP256K1RSKLen is the number of bytes in a secp2561k recoverable private
// key // key
SECP256K1RSKLen = 32 SECP256K1RSKLen = 32
// SECP256K1RPKLen is the number of bytes in a secp2561k recoverable public
// key
SECP256K1RPKLen = 33
) )
// FactorySECP256K1R ... // FactorySECP256K1R ...

View File

@ -31,10 +31,10 @@ var (
type BaseTx struct { type BaseTx struct {
ava.Metadata ava.Metadata
NetID uint32 `serialize:"true"` // ID of the network this chain lives on NetID uint32 `serialize:"true"` // ID of the network this chain lives on
BCID ids.ID `serialize:"true"` // ID of the chain on which this transaction exists (prevents replay attacks) BCID ids.ID `serialize:"true"` // ID of the chain on which this transaction exists (prevents replay attacks)
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
// NetworkID is the ID of the network on which this transaction exists // NetworkID is the ID of the network on which this transaction exists
@ -45,11 +45,11 @@ func (t *BaseTx) ChainID() ids.ID { return t.BCID }
// Outputs track which outputs this transaction is producing. The returned array // Outputs track which outputs this transaction is producing. The returned array
// should not be modified. // should not be modified.
func (t *BaseTx) Outputs() []*TransferableOutput { return t.Outs } func (t *BaseTx) Outputs() []*ava.TransferableOutput { return t.Outs }
// Inputs track which UTXOs this transaction is consuming. The returned array // Inputs track which UTXOs this transaction is consuming. The returned array
// should not be modified. // should not be modified.
func (t *BaseTx) Inputs() []*TransferableInput { return t.Ins } func (t *BaseTx) Inputs() []*ava.TransferableInput { return t.Ins }
// InputUTXOs track which UTXOs this transaction is consuming. // InputUTXOs track which UTXOs this transaction is consuming.
func (t *BaseTx) InputUTXOs() []*ava.UTXOID { func (t *BaseTx) InputUTXOs() []*ava.UTXOID {
@ -104,7 +104,7 @@ func (t *BaseTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) error
} }
fc.Produce(out.AssetID(), out.Output().Amount()) fc.Produce(out.AssetID(), out.Output().Amount())
} }
if !IsSortedTransferableOutputs(t.Outs, c) { if !ava.IsSortedTransferableOutputs(t.Outs, c) {
return errOutputsNotSorted return errOutputsNotSorted
} }
@ -114,7 +114,7 @@ func (t *BaseTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) error
} }
fc.Consume(in.AssetID(), in.Input().Amount()) fc.Consume(in.AssetID(), in.Input().Amount())
} }
if !isSortedAndUniqueTransferableInputs(t.Ins) { if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
return errInputsNotSortedUnique return errInputsNotSortedUnique
} }

File diff suppressed because it is too large Load Diff

View File

@ -93,64 +93,60 @@ func TestCreateAssetTxSerialization(t *testing.T) {
0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0xaa, 0xaa, 0xaa,
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}), }),
Outs: []*TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
&TransferableOutput{ Asset: ava.Asset{
Asset: ava.Asset{ ID: ids.NewID([32]byte{
ID: ids.NewID([32]byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }),
}), },
}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: 12345,
Amt: 12345, Locktime: 54321,
Locktime: 54321, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{
Addrs: []ids.ShortID{ ids.NewShortID([20]byte{
ids.NewShortID([20]byte{ 0x51, 0x02, 0x5c, 0x61, 0xfb, 0xcf, 0xc0, 0x78,
0x51, 0x02, 0x5c, 0x61, 0xfb, 0xcf, 0xc0, 0x78, 0xf6, 0x93, 0x34, 0xf8, 0x34, 0xbe, 0x6d, 0xd2,
0xf6, 0x93, 0x34, 0xf8, 0x34, 0xbe, 0x6d, 0xd2, 0x6d, 0x55, 0xa9, 0x55,
0x6d, 0x55, 0xa9, 0x55, }),
}), ids.NewShortID([20]byte{
ids.NewShortID([20]byte{ 0xc3, 0x34, 0x41, 0x28, 0xe0, 0x60, 0x12, 0x8e,
0xc3, 0x34, 0x41, 0x28, 0xe0, 0x60, 0x12, 0x8e, 0xde, 0x35, 0x23, 0xa2, 0x4a, 0x46, 0x1c, 0x89,
0xde, 0x35, 0x23, 0xa2, 0x4a, 0x46, 0x1c, 0x89, 0x43, 0xab, 0x08, 0x59,
0x43, 0xab, 0x08, 0x59, }),
}),
},
}, },
}, },
}, },
}, }},
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: ids.NewID([32]byte{
TxID: ids.NewID([32]byte{ 0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81, 0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01, 0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80,
0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00,
0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00, }),
}), OutputIndex: 5,
OutputIndex: 5, },
}, Asset: ava.Asset{
Asset: ava.Asset{ ID: ids.NewID([32]byte{
ID: ids.NewID([32]byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }),
}), },
}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 123456789,
Amt: 123456789, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{3, 7},
SigIndices: []uint32{3, 7},
},
}, },
}, },
}, }},
}, },
Name: "Volatility Index", Name: "Volatility Index",
Symbol: "VIX", Symbol: "VIX",

View File

@ -21,8 +21,8 @@ import (
type ExportTx struct { type ExportTx struct {
BaseTx `serialize:"true"` BaseTx `serialize:"true"`
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
// InputUTXOs track which UTXOs this transaction is consuming. // InputUTXOs track which UTXOs this transaction is consuming.
@ -67,7 +67,7 @@ func (t *ExportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
} }
fc.Produce(out.AssetID(), out.Output().Amount()) fc.Produce(out.AssetID(), out.Output().Amount())
} }
if !IsSortedTransferableOutputs(t.Outs, c) { if !ava.IsSortedTransferableOutputs(t.Outs, c) {
return errOutputsNotSorted return errOutputsNotSorted
} }
@ -77,7 +77,7 @@ func (t *ExportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
} }
fc.Consume(in.AssetID(), in.Input().Amount()) fc.Consume(in.AssetID(), in.Input().Amount())
} }
if !isSortedAndUniqueTransferableInputs(t.Ins) { if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
return errInputsNotSortedUnique return errInputsNotSortedUnique
} }

View File

@ -72,7 +72,7 @@ func TestExportTxSerialization(t *testing.T) {
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}), }),
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{ UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
@ -156,7 +156,7 @@ func TestIssueExportTx(t *testing.T) {
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: genesisTx.ID(), TxID: genesisTx.ID(),
OutputIndex: 1, OutputIndex: 1,
@ -167,7 +167,7 @@ func TestIssueExportTx(t *testing.T) {
Input: secp256k1fx.Input{SigIndices: []uint32{0}}, Input: secp256k1fx.Input{SigIndices: []uint32{0}},
}, },
}}, }},
Outs: []*TransferableOutput{&TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: genesisTx.ID()}, Asset: ava.Asset{ID: genesisTx.ID()},
Out: &secp256k1fx.TransferOutput{ Out: &secp256k1fx.TransferOutput{
Amt: 50000, Amt: 50000,
@ -287,7 +287,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: genesisTx.ID(), TxID: genesisTx.ID(),
OutputIndex: 1, OutputIndex: 1,
@ -298,7 +298,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
Input: secp256k1fx.Input{SigIndices: []uint32{0}}, Input: secp256k1fx.Input{SigIndices: []uint32{0}},
}, },
}}, }},
Outs: []*TransferableOutput{&TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: genesisTx.ID()}, Asset: ava.Asset{ID: genesisTx.ID()},
Out: &secp256k1fx.TransferOutput{ Out: &secp256k1fx.TransferOutput{
Amt: 50000, Amt: 50000,

View File

@ -5,7 +5,6 @@ package avm
import ( import (
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/vms/components/verify"
) )
type parsedFx struct { type parsedFx struct {
@ -32,16 +31,6 @@ type Fx interface {
VerifyOperation(tx interface{}, utxos, ins, creds, outs []interface{}) error VerifyOperation(tx interface{}, utxos, ins, creds, outs []interface{}) error
} }
// FxTransferable is the interface a feature extension must provide to transfer
// value between features extensions.
type FxTransferable interface {
verify.Verifiable
// Amount returns how much value this output consumes of the asset in its
// transaction.
Amount() uint64
}
// FxAddressable is the interface a feature extension must provide to be able to // FxAddressable is the interface a feature extension must provide to be able to
// be tracked as a part of the utxo set for a set of addresses // be tracked as a part of the utxo set for a set of addresses
type FxAddressable interface { type FxAddressable interface {

View File

@ -17,16 +17,12 @@ import (
"github.com/ava-labs/gecko/vms/components/verify" "github.com/ava-labs/gecko/vms/components/verify"
) )
var (
errNilUTXOID = errors.New("nil utxo ID is not valid")
)
// ImportTx is a transaction that imports an asset from another blockchain. // ImportTx is a transaction that imports an asset from another blockchain.
type ImportTx struct { type ImportTx struct {
BaseTx `serialize:"true"` BaseTx `serialize:"true"`
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
// InputUTXOs track which UTXOs this transaction is consuming. // InputUTXOs track which UTXOs this transaction is consuming.
@ -91,7 +87,7 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
} }
fc.Produce(out.AssetID(), out.Output().Amount()) fc.Produce(out.AssetID(), out.Output().Amount())
} }
if !IsSortedTransferableOutputs(t.Outs, c) { if !ava.IsSortedTransferableOutputs(t.Outs, c) {
return errOutputsNotSorted return errOutputsNotSorted
} }
@ -101,7 +97,7 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
} }
fc.Consume(in.AssetID(), in.Input().Amount()) fc.Consume(in.AssetID(), in.Input().Amount())
} }
if !isSortedAndUniqueTransferableInputs(t.Ins) { if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
return errInputsNotSortedUnique return errInputsNotSortedUnique
} }

View File

@ -72,7 +72,7 @@ func TestImportTxSerialization(t *testing.T) {
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}), }),
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{ UTXOID: ava.UTXOID{TxID: ids.NewID([32]byte{
0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee, 0x0f, 0x2f, 0x4f, 0x6f, 0x8e, 0xae, 0xce, 0xee,
0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec, 0x0d, 0x2d, 0x4d, 0x6d, 0x8c, 0xac, 0xcc, 0xec,
@ -165,7 +165,7 @@ func TestIssueImportTx(t *testing.T) {
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: utxoID, UTXOID: utxoID,
Asset: ava.Asset{ID: genesisTx.ID()}, Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{ In: &secp256k1fx.TransferInput{
@ -306,7 +306,7 @@ func TestForceAcceptImportTx(t *testing.T) {
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
}, },
Ins: []*TransferableInput{&TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: utxoID, UTXOID: utxoID,
Asset: ava.Asset{ID: genesisTx.ID()}, Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{ In: &secp256k1fx.TransferInput{

View File

@ -10,6 +10,7 @@ import (
"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/formatting"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/verify" "github.com/ava-labs/gecko/vms/components/verify"
"github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/gecko/vms/secp256k1fx"
@ -52,14 +53,12 @@ func TestInitialStateVerifyNilOutput(t *testing.T) {
func TestInitialStateVerifyInvalidOutput(t *testing.T) { func TestInitialStateVerifyInvalidOutput(t *testing.T) {
c := codec.NewDefault() c := codec.NewDefault()
c.RegisterType(&testVerifiable{}) c.RegisterType(&ava.TestVerifiable{})
numFxs := 1 numFxs := 1
is := InitialState{ is := InitialState{
FxID: 0, FxID: 0,
Outs: []verify.Verifiable{ Outs: []verify.Verifiable{&ava.TestVerifiable{Err: errors.New("")}},
&testVerifiable{err: errors.New("")},
},
} }
if err := is.Verify(c, numFxs); err == nil { if err := is.Verify(c, numFxs); err == nil {
t.Fatalf("Should have errored due to an invalid output") t.Fatalf("Should have errored due to an invalid output")
@ -68,14 +67,14 @@ func TestInitialStateVerifyInvalidOutput(t *testing.T) {
func TestInitialStateVerifyUnsortedOutputs(t *testing.T) { func TestInitialStateVerifyUnsortedOutputs(t *testing.T) {
c := codec.NewDefault() c := codec.NewDefault()
c.RegisterType(&TestTransferable{}) c.RegisterType(&ava.TestTransferable{})
numFxs := 1 numFxs := 1
is := InitialState{ is := InitialState{
FxID: 0, FxID: 0,
Outs: []verify.Verifiable{ Outs: []verify.Verifiable{
&TestTransferable{Val: 1}, &ava.TestTransferable{Val: 1},
&TestTransferable{Val: 0}, &ava.TestTransferable{Val: 0},
}, },
} }
if err := is.Verify(c, numFxs); err == nil { if err := is.Verify(c, numFxs); err == nil {

View File

@ -27,7 +27,7 @@ func TestOperableInputVerifyNilFx(t *testing.T) {
func TestOperableInputVerify(t *testing.T) { func TestOperableInputVerify(t *testing.T) {
oi := &OperableInput{ oi := &OperableInput{
UTXOID: ava.UTXOID{TxID: ids.Empty}, UTXOID: ava.UTXOID{TxID: ids.Empty},
In: &testVerifiable{}, In: &ava.TestVerifiable{},
} }
if err := oi.Verify(); err != nil { if err := oi.Verify(); err != nil {
t.Fatal(err) t.Fatal(err)
@ -44,28 +44,28 @@ func TestOperableInputSorting(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
&OperableInput{ &OperableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.NewID([32]byte{1}), TxID: ids.NewID([32]byte{1}),
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
&OperableInput{ &OperableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
&OperableInput{ &OperableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.NewID([32]byte{1}), TxID: ids.NewID([32]byte{1}),
OutputIndex: 0, OutputIndex: 0,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
} }
if isSortedAndUniqueOperableInputs(ins) { if isSortedAndUniqueOperableInputs(ins) {
@ -98,7 +98,7 @@ func TestOperableInputSorting(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}) })
if isSortedAndUniqueOperableInputs(ins) { if isSortedAndUniqueOperableInputs(ins) {
t.Fatalf("Shouldn't be unique") t.Fatalf("Shouldn't be unique")

View File

@ -53,14 +53,14 @@ func TestOperationVerifyInputsNotSorted(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
&OperableInput{ &OperableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
}, },
} }
@ -71,13 +71,13 @@ func TestOperationVerifyInputsNotSorted(t *testing.T) {
func TestOperationVerifyOutputsNotSorted(t *testing.T) { func TestOperationVerifyOutputsNotSorted(t *testing.T) {
c := codec.NewDefault() c := codec.NewDefault()
c.RegisterType(&TestTransferable{}) c.RegisterType(&ava.TestTransferable{})
op := &Operation{ op := &Operation{
Asset: ava.Asset{ID: ids.Empty}, Asset: ava.Asset{ID: ids.Empty},
Outs: []verify.Verifiable{ Outs: []verify.Verifiable{
&TestTransferable{Val: 1}, &ava.TestTransferable{Val: 1},
&TestTransferable{Val: 0}, &ava.TestTransferable{Val: 0},
}, },
} }
if err := op.Verify(c); err == nil { if err := op.Verify(c); err == nil {
@ -90,7 +90,7 @@ func TestOperationVerify(t *testing.T) {
op := &Operation{ op := &Operation{
Asset: ava.Asset{ID: ids.Empty}, Asset: ava.Asset{ID: ids.Empty},
Outs: []verify.Verifiable{ Outs: []verify.Verifiable{
&testVerifiable{}, &ava.TestVerifiable{},
}, },
} }
if err := op.Verify(c); err != nil { if err := op.Verify(c); err != nil {
@ -100,7 +100,7 @@ func TestOperationVerify(t *testing.T) {
func TestOperationSorting(t *testing.T) { func TestOperationSorting(t *testing.T) {
c := codec.NewDefault() c := codec.NewDefault()
c.RegisterType(&testVerifiable{}) c.RegisterType(&ava.TestVerifiable{})
ops := []*Operation{ ops := []*Operation{
&Operation{ &Operation{
@ -111,7 +111,7 @@ func TestOperationSorting(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
}, },
}, },
@ -123,7 +123,7 @@ func TestOperationSorting(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
}, },
}, },
@ -143,7 +143,7 @@ func TestOperationSorting(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
}, },
}) })

View File

@ -19,7 +19,7 @@ func TestPrefixedSetsAndGets(t *testing.T) {
vm := GenesisVM(t) vm := GenesisVM(t)
state := vm.state state := vm.state
vm.codec.RegisterType(&testVerifiable{}) vm.codec.RegisterType(&ava.TestVerifiable{})
utxo := &ava.UTXO{ utxo := &ava.UTXO{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
@ -27,29 +27,27 @@ func TestPrefixedSetsAndGets(t *testing.T) {
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: ava.Asset{ID: ids.Empty},
Out: &testVerifiable{}, Out: &ava.TestVerifiable{},
} }
tx := &Tx{UnsignedTx: &BaseTx{ tx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: ids.Empty,
TxID: ids.Empty, OutputIndex: 0,
OutputIndex: 0, },
}, Asset: ava.Asset{ID: asset},
Asset: ava.Asset{ID: asset}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 20 * units.KiloAva,
Amt: 20 * units.KiloAva, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
}} }}
unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx) unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx)
@ -115,7 +113,7 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
vm := GenesisVM(t) vm := GenesisVM(t)
state := vm.state state := vm.state
vm.codec.RegisterType(&testVerifiable{}) vm.codec.RegisterType(&ava.TestVerifiable{})
utxo := &ava.UTXO{ utxo := &ava.UTXO{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
@ -123,7 +121,7 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: ava.Asset{ID: ids.Empty},
Out: &testVerifiable{}, Out: &ava.TestVerifiable{},
} }
if err := state.FundUTXO(utxo); err != nil { if err := state.FundUTXO(utxo); err != nil {

View File

@ -219,7 +219,7 @@ func (service *Service) GetBalance(r *http.Request, args *GetBalanceArgs, reply
for _, utxo := range utxos { for _, utxo := range utxos {
if utxo.AssetID().Equals(assetID) { if utxo.AssetID().Equals(assetID) {
transferable, ok := utxo.Out.(FxTransferable) transferable, ok := utxo.Out.(ava.Transferable)
if !ok { if !ok {
continue continue
} }
@ -601,7 +601,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
amountSpent := uint64(0) amountSpent := uint64(0)
time := service.vm.clock.Unix() time := service.vm.clock.Unix()
ins := []*TransferableInput{} ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{} keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range utxos { for _, utxo := range utxos {
if !utxo.AssetID().Equals(assetID) { if !utxo.AssetID().Equals(assetID) {
@ -611,7 +611,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
if err != nil { if err != nil {
continue continue
} }
input, ok := inputIntf.(FxTransferable) input, ok := inputIntf.(ava.Transferable)
if !ok { if !ok {
continue continue
} }
@ -621,7 +621,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
} }
amountSpent = spent amountSpent = spent
in := &TransferableInput{ in := &ava.TransferableInput{
UTXOID: utxo.UTXOID, UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: assetID}, Asset: ava.Asset{ID: assetID},
In: input, In: input,
@ -641,38 +641,34 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
SortTransferableInputsWithSigners(ins, keys) SortTransferableInputsWithSigners(ins, keys)
outs := []*TransferableOutput{ outs := []*ava.TransferableOutput{&ava.TransferableOutput{
&TransferableOutput{ Asset: ava.Asset{ID: assetID},
Asset: ava.Asset{ID: assetID}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: uint64(args.Amount),
Amt: uint64(args.Amount), Locktime: 0,
Locktime: 0, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{to},
Addrs: []ids.ShortID{to},
},
}, },
}, },
} }}
if amountSpent > uint64(args.Amount) { if amountSpent > uint64(args.Amount) {
changeAddr := kc.Keys[0].PublicKey().Address() changeAddr := kc.Keys[0].PublicKey().Address()
outs = append(outs, outs = append(outs, &ava.TransferableOutput{
&TransferableOutput{ Asset: ava.Asset{ID: assetID},
Asset: ava.Asset{ID: assetID}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: amountSpent - uint64(args.Amount),
Amt: amountSpent - uint64(args.Amount), Locktime: 0,
Locktime: 0, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{changeAddr},
Addrs: []ids.ShortID{changeAddr},
},
}, },
}, },
) })
} }
SortTransferableOutputs(outs, service.vm.codec) ava.SortTransferableOutputs(outs, service.vm.codec)
tx := Tx{ tx := Tx{
UnsignedTx: &BaseTx{ UnsignedTx: &BaseTx{
@ -719,7 +715,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
} }
type innerSortTransferableInputsWithSigners struct { type innerSortTransferableInputsWithSigners struct {
ins []*TransferableInput ins []*ava.TransferableInput
signers [][]*crypto.PrivateKeySECP256K1R signers [][]*crypto.PrivateKeySECP256K1R
} }
@ -744,13 +740,13 @@ func (ins *innerSortTransferableInputsWithSigners) Swap(i, j int) {
// SortTransferableInputsWithSigners sorts the inputs and signers based on the // SortTransferableInputsWithSigners sorts the inputs and signers based on the
// input's utxo ID // input's utxo ID
func SortTransferableInputsWithSigners(ins []*TransferableInput, signers [][]*crypto.PrivateKeySECP256K1R) { func SortTransferableInputsWithSigners(ins []*ava.TransferableInput, signers [][]*crypto.PrivateKeySECP256K1R) {
sort.Sort(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers}) sort.Sort(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers})
} }
// IsSortedAndUniqueTransferableInputsWithSigners returns true if the inputs are // IsSortedAndUniqueTransferableInputsWithSigners returns true if the inputs are
// sorted and unique // sorted and unique
func IsSortedAndUniqueTransferableInputsWithSigners(ins []*TransferableInput, signers [][]*crypto.PrivateKeySECP256K1R) bool { func IsSortedAndUniqueTransferableInputsWithSigners(ins []*ava.TransferableInput, signers [][]*crypto.PrivateKeySECP256K1R) bool {
return utils.IsSortedAndUnique(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers}) return utils.IsSortedAndUnique(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers})
} }

View File

@ -175,7 +175,7 @@ func TestStateUTXOs(t *testing.T) {
vm := GenesisVM(t) vm := GenesisVM(t)
state := vm.state.state state := vm.state.state
vm.codec.RegisterType(&testVerifiable{}) vm.codec.RegisterType(&ava.TestVerifiable{})
if _, err := state.UTXO(ids.Empty); err == nil { if _, err := state.UTXO(ids.Empty); err == nil {
t.Fatalf("Should have errored when reading utxo") t.Fatalf("Should have errored when reading utxo")
@ -187,7 +187,7 @@ func TestStateUTXOs(t *testing.T) {
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: ava.Asset{ID: ids.Empty},
Out: &testVerifiable{}, Out: &ava.TestVerifiable{},
} }
if err := state.SetUTXO(ids.Empty, utxo); err != nil { if err := state.SetUTXO(ids.Empty, utxo); err != nil {
@ -245,7 +245,7 @@ func TestStateTXs(t *testing.T) {
vm := GenesisVM(t) vm := GenesisVM(t)
state := vm.state.state state := vm.state.state
vm.codec.RegisterType(&TestTransferable{}) vm.codec.RegisterType(&ava.TestTransferable{})
if _, err := state.Tx(ids.Empty); err == nil { if _, err := state.Tx(ids.Empty); err == nil {
t.Fatalf("Should have errored when reading tx") t.Fatalf("Should have errored when reading tx")
@ -254,23 +254,21 @@ func TestStateTXs(t *testing.T) {
tx := &Tx{UnsignedTx: &BaseTx{ tx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: ids.Empty,
TxID: ids.Empty, OutputIndex: 0,
OutputIndex: 0, },
}, Asset: ava.Asset{ID: asset},
Asset: ava.Asset{ID: asset}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 20 * units.KiloAva,
Amt: 20 * units.KiloAva, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
}} }}
unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx) unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx)

View File

@ -26,8 +26,8 @@ type UnsignedTx interface {
NetworkID() uint32 NetworkID() uint32
ChainID() ids.ID ChainID() ids.ID
Outputs() []*TransferableOutput Outputs() []*ava.TransferableOutput
Inputs() []*TransferableInput Inputs() []*ava.TransferableInput
AssetIDs() ids.Set AssetIDs() ids.Set
InputUTXOs() []*ava.UTXOID InputUTXOs() []*ava.UTXOID

View File

@ -44,33 +44,29 @@ func TestTxInvalidCredential(t *testing.T) {
c.RegisterType(&secp256k1fx.MintInput{}) c.RegisterType(&secp256k1fx.MintInput{})
c.RegisterType(&secp256k1fx.TransferInput{}) c.RegisterType(&secp256k1fx.TransferInput{})
c.RegisterType(&secp256k1fx.Credential{}) c.RegisterType(&secp256k1fx.Credential{})
c.RegisterType(&testVerifiable{}) c.RegisterType(&ava.TestVerifiable{})
tx := &Tx{ tx := &Tx{
UnsignedTx: &BaseTx{ UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: ids.Empty,
TxID: ids.Empty, OutputIndex: 0,
OutputIndex: 0, },
}, Asset: ava.Asset{ID: asset},
Asset: ava.Asset{ID: asset}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 20 * units.KiloAva,
Amt: 20 * units.KiloAva, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
},
Creds: []verify.Verifiable{
&testVerifiable{err: errUnneededAddress},
}, },
Creds: []verify.Verifiable{&ava.TestVerifiable{Err: errUnneededAddress}},
} }
b, err := c.Marshal(tx) b, err := c.Marshal(tx)
@ -94,14 +90,14 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
c.RegisterType(&secp256k1fx.MintInput{}) c.RegisterType(&secp256k1fx.MintInput{})
c.RegisterType(&secp256k1fx.TransferInput{}) c.RegisterType(&secp256k1fx.TransferInput{})
c.RegisterType(&secp256k1fx.Credential{}) c.RegisterType(&secp256k1fx.Credential{})
c.RegisterType(&testVerifiable{}) c.RegisterType(&ava.TestVerifiable{})
tx := &Tx{ tx := &Tx{
UnsignedTx: &BaseTx{ UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{
&TransferableInput{ &ava.TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
@ -116,7 +112,7 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
}, },
}, },
}, },
&TransferableInput{ &ava.TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
@ -134,8 +130,8 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
}, },
}, },
Creds: []verify.Verifiable{ Creds: []verify.Verifiable{
&testVerifiable{}, &ava.TestVerifiable{},
&testVerifiable{}, &ava.TestVerifiable{},
}, },
} }
@ -160,27 +156,25 @@ func TestTxInvalidNumberOfCredentials(t *testing.T) {
c.RegisterType(&secp256k1fx.MintInput{}) c.RegisterType(&secp256k1fx.MintInput{})
c.RegisterType(&secp256k1fx.TransferInput{}) c.RegisterType(&secp256k1fx.TransferInput{})
c.RegisterType(&secp256k1fx.Credential{}) c.RegisterType(&secp256k1fx.Credential{})
c.RegisterType(&testVerifiable{}) c.RegisterType(&ava.TestVerifiable{})
tx := &Tx{ tx := &Tx{
UnsignedTx: &OperationTx{ UnsignedTx: &OperationTx{
BaseTx: BaseTx{ BaseTx: BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 0},
UTXOID: ava.UTXOID{TxID: ids.Empty, OutputIndex: 0}, Asset: ava.Asset{ID: asset},
Asset: ava.Asset{ID: asset}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 20 * units.KiloAva,
Amt: 20 * units.KiloAva, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
}, },
Ops: []*Operation{ Ops: []*Operation{
&Operation{ &Operation{
@ -191,15 +185,13 @@ func TestTxInvalidNumberOfCredentials(t *testing.T) {
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
In: &testVerifiable{}, In: &ava.TestVerifiable{},
}, },
}, },
}, },
}, },
}, },
Creds: []verify.Verifiable{ Creds: []verify.Verifiable{&ava.TestVerifiable{}},
&testVerifiable{},
},
} }
b, err := c.Marshal(tx) b, err := c.Marshal(tx)

View File

@ -3,20 +3,10 @@
package avm package avm
type testVerifiable struct{ err error } import "github.com/ava-labs/gecko/vms/components/ava"
func (v *testVerifiable) Verify() error { return v.err }
type TestTransferable struct {
testVerifiable
Val uint64 `serialize:"true"`
}
func (t *TestTransferable) Amount() uint64 { return t.Val }
type testAddressable struct { type testAddressable struct {
TestTransferable `serialize:"true"` ava.TestTransferable `serialize:"true"`
Addrs [][]byte `serialize:"true"` Addrs [][]byte `serialize:"true"`
} }

View File

@ -317,7 +317,7 @@ func TestTxSerialization(t *testing.T) {
for _, key := range keys { for _, key := range keys {
addr := key.PublicKey().Address() addr := key.PublicKey().Address()
unsignedTx.Outs = append(unsignedTx.Outs, &TransferableOutput{ unsignedTx.Outs = append(unsignedTx.Outs, &ava.TransferableOutput{
Asset: ava.Asset{ID: asset}, Asset: ava.Asset{ID: asset},
Out: &secp256k1fx.TransferOutput{ Out: &secp256k1fx.TransferOutput{
Amt: 20 * units.KiloAva, Amt: 20 * units.KiloAva,
@ -444,23 +444,21 @@ func TestIssueTx(t *testing.T) {
newTx := &Tx{UnsignedTx: &BaseTx{ newTx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: genesisTx.ID(),
TxID: genesisTx.ID(), OutputIndex: 1,
OutputIndex: 1, },
}, Asset: ava.Asset{ID: genesisTx.ID()},
Asset: ava.Asset{ID: genesisTx.ID()}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 50000,
Amt: 50000, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
}} }}
unsignedBytes, err := vm.codec.Marshal(&newTx.UnsignedTx) unsignedBytes, err := vm.codec.Marshal(&newTx.UnsignedTx)
@ -575,35 +573,31 @@ func TestIssueDependentTx(t *testing.T) {
firstTx := &Tx{UnsignedTx: &BaseTx{ firstTx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: genesisTx.ID(),
TxID: genesisTx.ID(), OutputIndex: 1,
OutputIndex: 1, },
}, Asset: ava.Asset{ID: genesisTx.ID()},
Asset: ava.Asset{ID: genesisTx.ID()}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 50000,
Amt: 50000, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
Outs: []*TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
&TransferableOutput{ Asset: ava.Asset{ID: genesisTx.ID()},
Asset: ava.Asset{ID: genesisTx.ID()}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: 50000,
Amt: 50000, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{key.PublicKey().Address()},
Addrs: []ids.ShortID{key.PublicKey().Address()},
},
}, },
}, },
}, }},
}} }}
unsignedBytes, err := vm.codec.Marshal(&firstTx.UnsignedTx) unsignedBytes, err := vm.codec.Marshal(&firstTx.UnsignedTx)
@ -638,23 +632,21 @@ func TestIssueDependentTx(t *testing.T) {
secondTx := &Tx{UnsignedTx: &BaseTx{ secondTx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID, NetID: networkID,
BCID: chainID, BCID: chainID,
Ins: []*TransferableInput{ Ins: []*ava.TransferableInput{&ava.TransferableInput{
&TransferableInput{ UTXOID: ava.UTXOID{
UTXOID: ava.UTXOID{ TxID: firstTx.ID(),
TxID: firstTx.ID(), OutputIndex: 0,
OutputIndex: 0, },
}, Asset: ava.Asset{ID: genesisTx.ID()},
Asset: ava.Asset{ID: genesisTx.ID()}, In: &secp256k1fx.TransferInput{
In: &secp256k1fx.TransferInput{ Amt: 50000,
Amt: 50000, Input: secp256k1fx.Input{
Input: secp256k1fx.Input{ SigIndices: []uint32{
SigIndices: []uint32{ 0,
0,
},
}, },
}, },
}, },
}, }},
}} }}
unsignedBytes, err = vm.codec.Marshal(&secondTx.UnsignedTx) unsignedBytes, err = vm.codec.Marshal(&secondTx.UnsignedTx)

View File

@ -0,0 +1,20 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ava
// TestVerifiable ...
type TestVerifiable struct{ Err error }
// Verify ...
func (v *TestVerifiable) Verify() error { return v.Err }
// TestTransferable ...
type TestTransferable struct {
TestVerifiable
Val uint64 `serialize:"true"`
}
// Amount ...
func (t *TestTransferable) Amount() uint64 { return t.Val }

View File

@ -1,7 +1,7 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved. // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
package avm package ava
import ( import (
"bytes" "bytes"
@ -9,7 +9,6 @@ import (
"sort" "sort"
"github.com/ava-labs/gecko/utils" "github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/verify" "github.com/ava-labs/gecko/vms/components/verify"
) )
@ -22,15 +21,25 @@ var (
errNilTransferableFxInput = errors.New("nil transferable feature extension input is not valid") errNilTransferableFxInput = errors.New("nil transferable feature extension input is not valid")
) )
// Transferable is the interface a feature extension must provide to transfer
// value between features extensions.
type Transferable interface {
verify.Verifiable
// Amount returns how much value this output consumes of the asset in its
// transaction.
Amount() uint64
}
// TransferableOutput ... // TransferableOutput ...
type TransferableOutput struct { type TransferableOutput struct {
ava.Asset `serialize:"true"` Asset `serialize:"true"`
Out FxTransferable `serialize:"true"` Out Transferable `serialize:"true"`
} }
// Output returns the feature extension output that this Output is using. // Output returns the feature extension output that this Output is using.
func (out *TransferableOutput) Output() FxTransferable { return out.Out } func (out *TransferableOutput) Output() Transferable { return out.Out }
// Verify implements the verify.Verifiable interface // Verify implements the verify.Verifiable interface
func (out *TransferableOutput) Verify() error { func (out *TransferableOutput) Verify() error {
@ -88,14 +97,14 @@ func IsSortedTransferableOutputs(outs []*TransferableOutput, c codec.Codec) bool
// TransferableInput ... // TransferableInput ...
type TransferableInput struct { type TransferableInput struct {
ava.UTXOID `serialize:"true"` UTXOID `serialize:"true"`
ava.Asset `serialize:"true"` Asset `serialize:"true"`
In FxTransferable `serialize:"true"` In Transferable `serialize:"true"`
} }
// Input returns the feature extension input that this Input is using. // Input returns the feature extension input that this Input is using.
func (in *TransferableInput) Input() FxTransferable { return in.In } func (in *TransferableInput) Input() Transferable { return in.In }
// Verify implements the verify.Verifiable interface // Verify implements the verify.Verifiable interface
func (in *TransferableInput) Verify() error { func (in *TransferableInput) Verify() error {
@ -127,7 +136,10 @@ func (ins innerSortTransferableInputs) Less(i, j int) bool {
func (ins innerSortTransferableInputs) Len() int { return len(ins) } func (ins innerSortTransferableInputs) Len() int { return len(ins) }
func (ins innerSortTransferableInputs) Swap(i, j int) { ins[j], ins[i] = ins[i], ins[j] } func (ins innerSortTransferableInputs) Swap(i, j int) { ins[j], ins[i] = ins[i], ins[j] }
func sortTransferableInputs(ins []*TransferableInput) { sort.Sort(innerSortTransferableInputs(ins)) } // SortTransferableInputs ...
func isSortedAndUniqueTransferableInputs(ins []*TransferableInput) bool { func SortTransferableInputs(ins []*TransferableInput) { sort.Sort(innerSortTransferableInputs(ins)) }
// IsSortedAndUniqueTransferableInputs ...
func IsSortedAndUniqueTransferableInputs(ins []*TransferableInput) bool {
return utils.IsSortedAndUnique(innerSortTransferableInputs(ins)) return utils.IsSortedAndUnique(innerSortTransferableInputs(ins))
} }

View File

@ -1,7 +1,7 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved. // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms. // See the file LICENSE for licensing terms.
package avm package ava
import ( import (
"bytes" "bytes"
@ -9,7 +9,6 @@ import (
"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/formatting"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/gecko/vms/secp256k1fx"
) )
@ -22,7 +21,7 @@ func TestTransferableOutputVerifyNil(t *testing.T) {
} }
func TestTransferableOutputVerifyNilFx(t *testing.T) { func TestTransferableOutputVerifyNilFx(t *testing.T) {
to := &TransferableOutput{Asset: ava.Asset{ID: ids.Empty}} to := &TransferableOutput{Asset: Asset{ID: ids.Empty}}
if err := to.Verify(); err == nil { if err := to.Verify(); err == nil {
t.Fatalf("Should have errored due to nil transferable fx output") t.Fatalf("Should have errored due to nil transferable fx output")
} }
@ -30,7 +29,7 @@ func TestTransferableOutputVerifyNilFx(t *testing.T) {
func TestTransferableOutputVerify(t *testing.T) { func TestTransferableOutputVerify(t *testing.T) {
to := &TransferableOutput{ to := &TransferableOutput{
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
Out: &TestTransferable{Val: 1}, Out: &TestTransferable{Val: 1},
} }
if err := to.Verify(); err != nil { if err := to.Verify(); err != nil {
@ -47,23 +46,23 @@ func TestTransferableOutputSorting(t *testing.T) {
outs := []*TransferableOutput{ outs := []*TransferableOutput{
&TransferableOutput{ &TransferableOutput{
Asset: ava.Asset{ID: ids.NewID([32]byte{1})}, Asset: Asset{ID: ids.NewID([32]byte{1})},
Out: &TestTransferable{Val: 1}, Out: &TestTransferable{Val: 1},
}, },
&TransferableOutput{ &TransferableOutput{
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
Out: &TestTransferable{Val: 1}, Out: &TestTransferable{Val: 1},
}, },
&TransferableOutput{ &TransferableOutput{
Asset: ava.Asset{ID: ids.NewID([32]byte{1})}, Asset: Asset{ID: ids.NewID([32]byte{1})},
Out: &TestTransferable{Val: 0}, Out: &TestTransferable{Val: 0},
}, },
&TransferableOutput{ &TransferableOutput{
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
Out: &TestTransferable{Val: 0}, Out: &TestTransferable{Val: 0},
}, },
&TransferableOutput{ &TransferableOutput{
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
Out: &TestTransferable{Val: 0}, Out: &TestTransferable{Val: 0},
}, },
} }
@ -115,7 +114,7 @@ func TestTransferableOutputSerialization(t *testing.T) {
} }
out := &TransferableOutput{ out := &TransferableOutput{
Asset: ava.Asset{ Asset: Asset{
ID: ids.NewID([32]byte{ ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
@ -165,8 +164,8 @@ func TestTransferableInputVerifyNil(t *testing.T) {
func TestTransferableInputVerifyNilFx(t *testing.T) { func TestTransferableInputVerifyNilFx(t *testing.T) {
ti := &TransferableInput{ ti := &TransferableInput{
UTXOID: ava.UTXOID{TxID: ids.Empty}, UTXOID: UTXOID{TxID: ids.Empty},
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
} }
if err := ti.Verify(); err == nil { if err := ti.Verify(); err == nil {
t.Fatalf("Should have errored due to nil transferable fx input") t.Fatalf("Should have errored due to nil transferable fx input")
@ -175,8 +174,8 @@ func TestTransferableInputVerifyNilFx(t *testing.T) {
func TestTransferableInputVerify(t *testing.T) { func TestTransferableInputVerify(t *testing.T) {
ti := &TransferableInput{ ti := &TransferableInput{
UTXOID: ava.UTXOID{TxID: ids.Empty}, UTXOID: UTXOID{TxID: ids.Empty},
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
} }
if err := ti.Verify(); err != nil { if err := ti.Verify(); err != nil {
@ -193,57 +192,57 @@ func TestTransferableInputSorting(t *testing.T) {
ins := []*TransferableInput{ ins := []*TransferableInput{
&TransferableInput{ &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.NewID([32]byte{1}), TxID: ids.NewID([32]byte{1}),
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
}, },
&TransferableInput{ &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.NewID([32]byte{1}), TxID: ids.NewID([32]byte{1}),
OutputIndex: 0, OutputIndex: 0,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
}, },
&TransferableInput{ &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
}, },
&TransferableInput{ &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 0, OutputIndex: 0,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
}, },
} }
if isSortedAndUniqueTransferableInputs(ins) { if IsSortedAndUniqueTransferableInputs(ins) {
t.Fatalf("Shouldn't be sorted") t.Fatalf("Shouldn't be sorted")
} }
sortTransferableInputs(ins) SortTransferableInputs(ins)
if !isSortedAndUniqueTransferableInputs(ins) { if !IsSortedAndUniqueTransferableInputs(ins) {
t.Fatalf("Should be sorted") t.Fatalf("Should be sorted")
} }
ins = append(ins, &TransferableInput{ ins = append(ins, &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.Empty, TxID: ids.Empty,
OutputIndex: 1, OutputIndex: 1,
}, },
Asset: ava.Asset{ID: ids.Empty}, Asset: Asset{ID: ids.Empty},
In: &TestTransferable{}, In: &TestTransferable{},
}) })
if isSortedAndUniqueTransferableInputs(ins) { if IsSortedAndUniqueTransferableInputs(ins) {
t.Fatalf("Shouldn't be unique") t.Fatalf("Shouldn't be unique")
} }
} }
@ -272,7 +271,7 @@ func TestTransferableInputSerialization(t *testing.T) {
} }
in := &TransferableInput{ in := &TransferableInput{
UTXOID: ava.UTXOID{ UTXOID: UTXOID{
TxID: ids.NewID([32]byte{ TxID: ids.NewID([32]byte{
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81, 0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01, 0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
@ -281,7 +280,7 @@ func TestTransferableInputSerialization(t *testing.T) {
}), }),
OutputIndex: 5, OutputIndex: 5,
}, },
Asset: ava.Asset{ Asset: Asset{
ID: ids.NewID([32]byte{ ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,

View File

@ -0,0 +1,150 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
import (
"errors"
"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/choices"
"github.com/ava-labs/gecko/vms/components/core"
)
var (
errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")
)
// AtomicTx is an operation that can be decided without being proposed, but must have special control over database commitment
type AtomicTx interface {
initialize(vm *VM) error
// UTXOs this tx consumes
InputUTXOs() ids.Set
// Attempt to verify this transaction with the provided state. The provided
// database can be modified arbitrarily.
SemanticVerify(database.Database) error
Accept(database.Batch) error
}
// AtomicBlock being accepted results in the transaction contained in the
// block to be accepted and committed to the chain.
type AtomicBlock struct {
CommonDecisionBlock `serialize:"true"`
Tx AtomicTx `serialize:"true"`
inputs ids.Set
}
// initialize this block
func (ab *AtomicBlock) initialize(vm *VM, bytes []byte) error {
if err := ab.CommonDecisionBlock.initialize(vm, bytes); err != nil {
return err
}
return ab.Tx.initialize(vm)
}
// Reject implements the snowman.Block interface
func (ab *AtomicBlock) conflicts(s ids.Set) bool {
if ab.Status() == choices.Accepted {
return false
}
if ab.inputs.Overlaps(s) {
return true
}
return ab.parentBlock().conflicts(s)
}
// Verify this block performs a valid state transition.
//
// The parent block must be a proposal
//
// This function also sets onAcceptDB database if the verification passes.
func (ab *AtomicBlock) Verify() error {
parentBlock := ab.parentBlock()
// AtomicBlock is not a modifier on a proposal block, so its parent must be
// a decision.
parent, ok := parentBlock.(decision)
if !ok {
return errInvalidBlockType
}
pdb := parent.onAccept()
ab.onAcceptDB = versiondb.New(pdb)
if err := ab.Tx.SemanticVerify(ab.onAcceptDB); err != nil {
return err
}
ab.inputs = ab.Tx.InputUTXOs()
if parentBlock.conflicts(ab.inputs) {
return errConflictingParentTxs
}
ab.vm.currentBlocks[ab.ID().Key()] = ab
ab.parentBlock().addChild(ab)
return nil
}
// Accept implements the snowman.Block interface
func (ab *AtomicBlock) Accept() {
ab.vm.Ctx.Log.Verbo("Accepting block with ID %s", ab.ID())
ab.CommonBlock.Accept()
// Update the state of the chain in the database
if err := ab.onAcceptDB.Commit(); err != nil {
ab.vm.Ctx.Log.Error("unable to commit onAcceptDB")
}
batch, err := ab.vm.DB.CommitBatch()
if err != nil {
ab.vm.Ctx.Log.Fatal("unable to commit vm's DB")
}
defer ab.vm.DB.Abort()
if err := ab.Tx.Accept(batch); err != nil {
ab.vm.Ctx.Log.Error("unable to atomically commit block")
}
for _, child := range ab.children {
child.setBaseDatabase(ab.vm.DB)
}
if ab.onAcceptFunc != nil {
ab.onAcceptFunc()
}
parent := ab.parentBlock()
// remove this block and its parent from memory
parent.free()
ab.free()
}
// newAtomicBlock returns a new *AtomicBlock where the block's parent, a
// decision block, has ID [parentID].
func (vm *VM) newAtomicBlock(parentID ids.ID, tx AtomicTx) (*AtomicBlock, error) {
ab := &AtomicBlock{
CommonDecisionBlock: CommonDecisionBlock{
CommonBlock: CommonBlock{
Block: core.NewBlock(parentID),
vm: vm,
},
},
Tx: tx,
}
// We serialize this block as a Block so that it can be deserialized into a
// Block
blk := Block(ab)
bytes, err := Codec.Marshal(&blk)
if err != nil {
return nil, err
}
ab.Block.Initialize(bytes, vm.SnowmanVM)
return ab, nil
}

View File

@ -71,9 +71,6 @@ 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 }
// InputUTXOs returns an empty set
func (tx *CreateChainTx) InputUTXOs() ids.Set { return ids.Set{} }
// SyntacticVerify this transaction is well-formed // SyntacticVerify this transaction is well-formed
// Also populates [tx.Key] with the public key that signed this transaction // Also populates [tx.Key] with the public key that signed this transaction
func (tx *CreateChainTx) SyntacticVerify() error { func (tx *CreateChainTx) SyntacticVerify() error {

View File

@ -144,9 +144,6 @@ func (tx *CreateSubnetTx) Bytes() []byte {
return tx.bytes return tx.bytes
} }
// InputUTXOs returns an empty set
func (tx *CreateSubnetTx) InputUTXOs() ids.Set { return ids.Set{} }
// initialize sets [tx.vm] to [vm] // initialize sets [tx.vm] to [vm]
func (tx *CreateSubnetTx) initialize(vm *VM) error { func (tx *CreateSubnetTx) initialize(vm *VM) error {
tx.vm = vm tx.vm = vm

168
vms/platformvm/export_tx.go Normal file
View File

@ -0,0 +1,168 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
// import (
// "fmt"
// "github.com/ava-labs/gecko/chains"
// "github.com/ava-labs/gecko/database"
// "github.com/ava-labs/gecko/ids"
// "github.com/ava-labs/gecko/utils/crypto"
// "github.com/ava-labs/gecko/utils/hashing"
// "github.com/ava-labs/gecko/vms/components/ava"
// )
// // UnsignedExportTx is an unsigned ExportTx
// type UnsignedExportTx struct {
// // ID of the network this blockchain exists on
// NetworkID uint32 `serialize:"true"`
// // Next unused nonce of account paying the transaction fee for this transaction.
// // Currently unused, as there are no tx fees.
// Nonce uint64 `serialize:"true"`
// Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
// }
// // ExportTx exports funds to the AVM
// type ExportTx struct {
// UnsignedExportTx `serialize:"true"`
// Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// vm *VM
// id ids.ID
// key crypto.PublicKey // public key of transaction signer
// bytes []byte
// }
// func (tx *ExportTx) initialize(vm *VM) error {
// tx.vm = vm
// txBytes, err := Codec.Marshal(tx) // byte repr. of the signed tx
// tx.bytes = txBytes
// tx.id = ids.NewID(hashing.ComputeHash256Array(txBytes))
// return err
// }
// // ID of this transaction
// func (tx *ExportTx) 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 *ExportTx) Key() crypto.PublicKey { return tx.key }
// // Bytes returns the byte representation of a CreateChainTx
// func (tx *ExportTx) Bytes() []byte { return tx.bytes }
// // InputUTXOs returns an empty set
// func (tx *ExportTx) InputUTXOs() ids.Set { return ids.Set{} }
// // SyntacticVerify this transaction is well-formed
// // Also populates [tx.Key] with the public key that signed this transaction
// func (tx *ExportTx) SyntacticVerify() error {
// switch {
// case tx == nil:
// return errNilTx
// case tx.key != nil:
// return nil // Only verify the transaction once
// case tx.NetworkID != tx.vm.Ctx.NetworkID: // verify the transaction is on this network
// return errWrongNetworkID
// case tx.id.IsZero():
// return errInvalidID
// }
// unsignedIntf := interface{}(&tx.UnsignedImportTx)
// unsignedBytes, err := Codec.Marshal(&unsignedIntf) // byte repr of unsigned tx
// if err != nil {
// return err
// }
// key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:])
// if err != nil {
// return err
// }
// tx.key = key
// return nil
// }
// // SemanticVerify this transaction is valid.
// func (tx *ExportTx) SemanticVerify(db database.Database) (func(), error) {
// if err := tx.SyntacticVerify(); err != nil {
// return nil, err
// }
// currentChains, err := tx.vm.getChains(db) // chains that currently exist
// if err != nil {
// return nil, errDBChains
// }
// for _, chain := range currentChains {
// if chain.ID().Equals(tx.ID()) {
// return nil, fmt.Errorf("chain with ID %s already exists", chain.ID())
// }
// }
// currentChains = append(currentChains, tx) // add this new chain
// if err := tx.vm.putChains(db, currentChains); err != nil {
// return nil, err
// }
// // Deduct tx fee from payer's account
// account, err := tx.vm.getAccount(db, tx.Key().Address())
// if err != nil {
// return nil, err
// }
// account, err = account.Remove(0, tx.Nonce)
// if err != nil {
// return nil, err
// }
// if err := tx.vm.putAccount(db, account); err != nil {
// return nil, err
// }
// // If this proposal is committed, create the new blockchain using the chain manager
// onAccept := func() {
// chainParams := chains.ChainParameters{
// 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
// }
// func (vm *VM) newExportTx(nonce uint64, genesisData []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32, key *crypto.PrivateKeySECP256K1R) (*ExportTx, error) {
// tx := &CreateChainTx{
// UnsignedCreateChainTx: UnsignedCreateChainTx{
// NetworkID: networkID,
// Nonce: nonce,
// GenesisData: genesisData,
// VMID: vmID,
// FxIDs: fxIDs,
// ChainName: chainName,
// },
// }
// unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
// unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction
// if err != nil {
// return nil, err
// }
// sig, err := key.Sign(unsignedBytes)
// if err != nil {
// return nil, err
// }
// copy(tx.Sig[:], sig)
// return tx, tx.initialize(vm)
// }

252
vms/platformvm/import_tx.go Normal file
View File

@ -0,0 +1,252 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
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/choices"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/math"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/components/verify"
)
var (
errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo")
errWrongNumberOfCredentials = errors.New("should have the same number of credentials as inputs")
errNoImportInputs = errors.New("no import inputs")
errInputsNotSortedUnique = errors.New("inputs not sorted and unique")
errPublicKeySignatureMismatch = errors.New("signature doesn't match public key")
errUnknownAsset = errors.New("unknown asset ID")
)
// UnsignedImportTx is an unsigned ImportTx
type UnsignedImportTx struct {
// ID of the network this blockchain exists on
NetworkID uint32 `serialize:"true"`
// Next unused nonce of account paying the transaction fee for this transaction.
// Currently unused, as there are no tx fees.
Nonce uint64 `serialize:"true"`
// Account that this transaction is being sent by. This is needed to ensure the Credentials are replay safe.
Account [crypto.SECP256K1RPKLen]byte `serialize:"true"`
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
}
// ImportTx imports funds from the AVM
type ImportTx struct {
UnsignedImportTx `serialize:"true"`
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
Creds []verify.Verifiable `serialize:"true"` // The credentials of this transaction
vm *VM
id ids.ID
key crypto.PublicKey // public key of transaction signer
bytes []byte
}
func (tx *ImportTx) initialize(vm *VM) error {
tx.vm = vm
txBytes, err := Codec.Marshal(tx) // byte repr. of the signed tx
tx.bytes = txBytes
tx.id = ids.NewID(hashing.ComputeHash256Array(txBytes))
return err
}
// ID of this transaction
func (tx *ImportTx) 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 *ImportTx) Key() crypto.PublicKey { return tx.key }
// Bytes returns the byte representation of a CreateChainTx
func (tx *ImportTx) Bytes() []byte { return tx.bytes }
// InputUTXOs returns an empty set
func (tx *ImportTx) InputUTXOs() ids.Set {
set := ids.Set{}
for _, in := range tx.Ins {
set.Add(in.InputID())
}
return set
}
// SyntacticVerify this transaction is well-formed
// Also populates [tx.Key] with the public key that signed this transaction
func (tx *ImportTx) SyntacticVerify() error {
switch {
case tx == nil:
return errNilTx
case tx.key != nil:
return nil // Only verify the transaction once
case tx.NetworkID != tx.vm.Ctx.NetworkID: // verify the transaction is on this network
return errWrongNetworkID
case tx.id.IsZero():
return errInvalidID
case len(tx.Ins) == 0:
return errNoImportInputs
case len(tx.Ins) != len(tx.Creds):
return errWrongNumberOfCredentials
}
for _, in := range tx.Ins {
if err := in.Verify(); err != nil {
return err
}
if !in.AssetID().Equals(tx.vm.AVA) {
return errUnknownAsset
}
}
if !ava.IsSortedAndUniqueTransferableInputs(tx.Ins) {
return errInputsNotSortedUnique
}
for _, cred := range tx.Creds {
if err := cred.Verify(); err != nil {
return err
}
}
unsignedIntf := interface{}(&tx.UnsignedImportTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // byte repr of unsigned tx
if err != nil {
return err
}
expectedPublicKey, err := tx.vm.factory.ToPublicKey(tx.Account[:])
if err != nil {
return err
}
key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:])
if err != nil {
return err
}
if !expectedPublicKey.Address().Equals(key.Address()) {
return errPublicKeySignatureMismatch
}
tx.key = key
return nil
}
// SemanticVerify this transaction is valid.
func (tx *ImportTx) SemanticVerify(db database.Database) error {
if err := tx.SyntacticVerify(); err != nil {
return err
}
bID := ids.Empty // TODO: Needs to be set to the platform chain
smDB := tx.vm.Ctx.SharedMemory.GetDatabase(bID)
defer tx.vm.Ctx.SharedMemory.ReleaseDatabase(bID)
state := ava.NewPrefixedState(smDB, Codec)
amount := uint64(0)
for i, in := range tx.Ins {
newAmount, err := math.Add64(in.In.Amount(), amount)
if err != nil {
return err
}
amount = newAmount
cred := tx.Creds[i]
utxoID := in.UTXOID.InputID()
utxo, err := state.AVMUTXO(utxoID)
if err != nil {
return err
}
utxoAssetID := utxo.AssetID()
inAssetID := in.AssetID()
if !utxoAssetID.Equals(inAssetID) {
return errAssetIDMismatch
}
if err := tx.vm.fx.VerifyTransfer(uTx, utxo.Out, in.In, cred); err != nil {
return err
}
}
// Deduct tx fee from payer's account
account, err := tx.vm.getAccount(db, tx.Key().Address())
if err != nil {
return err
}
account, err = account.Remove(0, tx.Nonce)
if err != nil {
return err
}
account, err = account.Add(amount)
if err != nil {
return err
}
return tx.vm.putAccount(db, account)
}
// Accept this transaction.
func (tx *ImportTx) Accept(batch database.Batch) error {
bID := ids.Empty // TODO: Needs to be set to the platform chain
smDB := tx.vm.Ctx.SharedMemory.GetDatabase(bID)
defer tx.vm.Ctx.SharedMemory.ReleaseDatabase(bID)
vsmDB := versiondb.New(smDB)
state := ava.NewPrefixedState(vsmDB, Codec)
for _, in := range tx.Ins {
utxoID := in.UTXOID.InputID()
if _, err := state.AVMUTXO(utxoID); err == nil {
if err := state.SetAVMUTXO(utxoID, nil); err != nil {
return err
}
} else if err := state.SetAVMStatus(utxoID, choices.Accepted); err != nil {
return err
}
}
sharedBatch, err := vsmDB.CommitBatch()
if err != nil {
return err
}
return atomic.WriteAll(batch, sharedBatch)
}
func (vm *VM) newImportTx(nonce uint64, genesisData []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32, key *crypto.PrivateKeySECP256K1R) (*ImportTx, error) {
tx := &CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: networkID,
Nonce: nonce,
GenesisData: genesisData,
VMID: vmID,
FxIDs: fxIDs,
ChainName: chainName,
},
}
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction
if err != nil {
return nil, err
}
sig, err := key.Sign(unsignedBytes)
if err != nil {
return nil, err
}
copy(tx.Sig[:], sig)
return tx, tx.initialize(vm)
}

View File

@ -4,26 +4,16 @@
package platformvm package platformvm
import ( import (
"errors"
"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"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/vms/components/core" "github.com/ava-labs/gecko/vms/components/core"
) )
var (
errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")
errConflictingTxs = errors.New("block contains conflicting transactions")
)
// DecisionTx is an operation that can be decided without being proposed // DecisionTx is an operation that can be decided without being proposed
type DecisionTx interface { type DecisionTx interface {
initialize(vm *VM) error initialize(vm *VM) error
InputUTXOs() ids.Set
// Attempt to verify this transaction with the provided state. The provided // Attempt to verify this transaction with the provided state. The provided
// database can be modified arbitrarily. If a nil error is returned, it is // database can be modified arbitrarily. If a nil error is returned, it is
// assumped onAccept is non-nil. // assumped onAccept is non-nil.
@ -36,8 +26,6 @@ type StandardBlock struct {
CommonDecisionBlock `serialize:"true"` CommonDecisionBlock `serialize:"true"`
Txs []DecisionTx `serialize:"true"` Txs []DecisionTx `serialize:"true"`
inputs ids.Set
} }
// initialize this block // initialize this block
@ -53,17 +41,6 @@ func (sb *StandardBlock) initialize(vm *VM, bytes []byte) error {
return nil return nil
} }
// Reject implements the snowman.Block interface
func (sb *StandardBlock) conflicts(s ids.Set) bool {
if sb.Status() == choices.Accepted {
return false
}
if sb.inputs.Overlaps(s) {
return true
}
return sb.parentBlock().conflicts(s)
}
// Verify this block performs a valid state transition. // Verify this block performs a valid state transition.
// //
// The parent block must be a proposal // The parent block must be a proposal
@ -87,20 +64,11 @@ func (sb *StandardBlock) Verify() error {
if err != nil { if err != nil {
return err return err
} }
inputs := tx.InputUTXOs()
if inputs.Overlaps(sb.inputs) {
return errConflictingTxs
}
sb.inputs.Union(inputs)
if onAccept != nil { if onAccept != nil {
funcs = append(funcs, onAccept) funcs = append(funcs, onAccept)
} }
} }
if parentBlock.conflicts(sb.inputs) {
return errConflictingParentTxs
}
if numFuncs := len(funcs); numFuncs == 1 { if numFuncs := len(funcs); numFuncs == 1 {
sb.onAcceptFunc = funcs[0] sb.onAcceptFunc = funcs[0]
} else if numFuncs > 1 { } else if numFuncs > 1 {

View File

@ -26,6 +26,7 @@ import (
"github.com/ava-labs/gecko/utils/wrappers" "github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/core" "github.com/ava-labs/gecko/vms/components/core"
"github.com/ava-labs/gecko/vms/secp256k1fx"
) )
const ( const (
@ -108,6 +109,13 @@ func init() {
Codec.RegisterType(&Abort{}), Codec.RegisterType(&Abort{}),
Codec.RegisterType(&Commit{}), Codec.RegisterType(&Commit{}),
Codec.RegisterType(&StandardBlock{}), Codec.RegisterType(&StandardBlock{}),
Codec.RegisterType(&AtomicBlock{}),
Codec.RegisterType(&secp256k1fx.MintOutput{}),
Codec.RegisterType(&secp256k1fx.TransferOutput{}),
Codec.RegisterType(&secp256k1fx.MintInput{}),
Codec.RegisterType(&secp256k1fx.TransferInput{}),
Codec.RegisterType(&secp256k1fx.Credential{}),
Codec.RegisterType(&UnsignedAddDefaultSubnetValidatorTx{}), Codec.RegisterType(&UnsignedAddDefaultSubnetValidatorTx{}),
Codec.RegisterType(&addDefaultSubnetValidatorTx{}), Codec.RegisterType(&addDefaultSubnetValidatorTx{}),
@ -124,6 +132,12 @@ func init() {
Codec.RegisterType(&UnsignedCreateSubnetTx{}), Codec.RegisterType(&UnsignedCreateSubnetTx{}),
Codec.RegisterType(&CreateSubnetTx{}), Codec.RegisterType(&CreateSubnetTx{}),
Codec.RegisterType(&UnsignedImportTx{}),
Codec.RegisterType(&ImportTx{}),
// Codec.RegisterType(&UnsignedExportTx{}),
// Codec.RegisterType(&ExportTx{}),
Codec.RegisterType(&advanceTimeTx{}), Codec.RegisterType(&advanceTimeTx{}),
Codec.RegisterType(&rewardValidatorTx{}), Codec.RegisterType(&rewardValidatorTx{}),
) )
@ -141,6 +155,11 @@ type VM struct {
// The node's chain manager // The node's chain manager
ChainManager chains.Manager ChainManager chains.Manager
// AVA asset ID
AVA ids.ID
fx secp256k1fx.Fx
// Used to create and use keys. // Used to create and use keys.
factory crypto.FactorySECP256K1R factory crypto.FactorySECP256K1R

View File

@ -96,7 +96,7 @@ func (w *Wallet) ImportKey(sk *crypto.PrivateKeySECP256K1R) { w.keychain.Add(sk)
// AddUTXO adds a new UTXO to this wallet if this wallet may spend it // AddUTXO adds a new UTXO to this wallet if this wallet may spend it
// The UTXO's output must be an OutputPayment // The UTXO's output must be an OutputPayment
func (w *Wallet) AddUTXO(utxo *ava.UTXO) { func (w *Wallet) AddUTXO(utxo *ava.UTXO) {
out, ok := utxo.Out.(avm.FxTransferable) out, ok := utxo.Out.(ava.Transferable)
if !ok { if !ok {
return return
} }
@ -116,7 +116,7 @@ func (w *Wallet) RemoveUTXO(utxoID ids.ID) {
assetID := utxo.AssetID() assetID := utxo.AssetID()
assetKey := assetID.Key() assetKey := assetID.Key()
newBalance := w.balance[assetKey] - utxo.Out.(avm.FxTransferable).Amount() newBalance := w.balance[assetKey] - utxo.Out.(ava.Transferable).Amount()
if newBalance == 0 { if newBalance == 0 {
delete(w.balance, assetKey) delete(w.balance, assetKey)
} else { } else {
@ -138,7 +138,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
amountSpent := uint64(0) amountSpent := uint64(0)
time := w.clock.Unix() time := w.clock.Unix()
ins := []*avm.TransferableInput{} ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{} keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range w.utxoSet.UTXOs { for _, utxo := range w.utxoSet.UTXOs {
if !utxo.AssetID().Equals(assetID) { if !utxo.AssetID().Equals(assetID) {
@ -148,7 +148,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
if err != nil { if err != nil {
continue continue
} }
input, ok := inputIntf.(avm.FxTransferable) input, ok := inputIntf.(ava.Transferable)
if !ok { if !ok {
continue continue
} }
@ -158,7 +158,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
} }
amountSpent = spent amountSpent = spent
in := &avm.TransferableInput{ in := &ava.TransferableInput{
UTXOID: utxo.UTXOID, UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: assetID}, Asset: ava.Asset{ID: assetID},
In: input, In: input,
@ -178,41 +178,37 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
avm.SortTransferableInputsWithSigners(ins, keys) avm.SortTransferableInputsWithSigners(ins, keys)
outs := []*avm.TransferableOutput{ outs := []*ava.TransferableOutput{&ava.TransferableOutput{
&avm.TransferableOutput{ Asset: ava.Asset{ID: assetID},
Asset: ava.Asset{ID: assetID}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: amount,
Amt: amount, Locktime: 0,
Locktime: 0, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{destAddr},
Addrs: []ids.ShortID{destAddr},
},
}, },
}, },
} }}
if amountSpent > amount { if amountSpent > amount {
changeAddr, err := w.GetAddress() changeAddr, err := w.GetAddress()
if err != nil { if err != nil {
return nil, err return nil, err
} }
outs = append(outs, outs = append(outs, &ava.TransferableOutput{
&avm.TransferableOutput{ Asset: ava.Asset{ID: assetID},
Asset: ava.Asset{ID: assetID}, Out: &secp256k1fx.TransferOutput{
Out: &secp256k1fx.TransferOutput{ Amt: amountSpent - amount,
Amt: amountSpent - amount, Locktime: 0,
Locktime: 0, OutputOwners: secp256k1fx.OutputOwners{
OutputOwners: secp256k1fx.OutputOwners{ Threshold: 1,
Threshold: 1, Addrs: []ids.ShortID{changeAddr},
Addrs: []ids.ShortID{changeAddr},
},
}, },
}, },
) })
} }
avm.SortTransferableOutputs(outs, w.codec) ava.SortTransferableOutputs(outs, w.codec)
tx := &avm.Tx{ tx := &avm.Tx{
UnsignedTx: &avm.BaseTx{ UnsignedTx: &avm.BaseTx{