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 = 32
// SECP256K1PKLen is the number of bytes in a secp2561k public key
SECP256K1PKLen = 33
)
// FactorySECP256K1 ...

View File

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

View File

@ -31,10 +31,10 @@ var (
type BaseTx struct {
ava.Metadata
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)
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
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)
Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
}
// 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
// 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
// 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.
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())
}
if !IsSortedTransferableOutputs(t.Outs, c) {
if !ava.IsSortedTransferableOutputs(t.Outs, c) {
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())
}
if !isSortedAndUniqueTransferableInputs(t.Ins) {
if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
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,
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}),
Outs: []*TransferableOutput{
&TransferableOutput{
Asset: ava.Asset{
ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}),
},
Out: &secp256k1fx.TransferOutput{
Amt: 12345,
Locktime: 54321,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID([20]byte{
0x51, 0x02, 0x5c, 0x61, 0xfb, 0xcf, 0xc0, 0x78,
0xf6, 0x93, 0x34, 0xf8, 0x34, 0xbe, 0x6d, 0xd2,
0x6d, 0x55, 0xa9, 0x55,
}),
ids.NewShortID([20]byte{
0xc3, 0x34, 0x41, 0x28, 0xe0, 0x60, 0x12, 0x8e,
0xde, 0x35, 0x23, 0xa2, 0x4a, 0x46, 0x1c, 0x89,
0x43, 0xab, 0x08, 0x59,
}),
},
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{
ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}),
},
Out: &secp256k1fx.TransferOutput{
Amt: 12345,
Locktime: 54321,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID([20]byte{
0x51, 0x02, 0x5c, 0x61, 0xfb, 0xcf, 0xc0, 0x78,
0xf6, 0x93, 0x34, 0xf8, 0x34, 0xbe, 0x6d, 0xd2,
0x6d, 0x55, 0xa9, 0x55,
}),
ids.NewShortID([20]byte{
0xc3, 0x34, 0x41, 0x28, 0xe0, 0x60, 0x12, 0x8e,
0xde, 0x35, 0x23, 0xa2, 0x4a, 0x46, 0x1c, 0x89,
0x43, 0xab, 0x08, 0x59,
}),
},
},
},
},
Ins: []*TransferableInput{
&TransferableInput{
UTXOID: ava.UTXOID{
TxID: ids.NewID([32]byte{
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80,
0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00,
}),
OutputIndex: 5,
},
Asset: ava.Asset{
ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}),
},
In: &secp256k1fx.TransferInput{
Amt: 123456789,
Input: secp256k1fx.Input{
SigIndices: []uint32{3, 7},
},
}},
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{
TxID: ids.NewID([32]byte{
0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81,
0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80,
0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00,
}),
OutputIndex: 5,
},
Asset: ava.Asset{
ID: ids.NewID([32]byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
}),
},
In: &secp256k1fx.TransferInput{
Amt: 123456789,
Input: secp256k1fx.Input{
SigIndices: []uint32{3, 7},
},
},
},
}},
},
Name: "Volatility Index",
Symbol: "VIX",

View File

@ -21,8 +21,8 @@ import (
type ExportTx struct {
BaseTx `serialize:"true"`
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
}
// 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())
}
if !IsSortedTransferableOutputs(t.Outs, c) {
if !ava.IsSortedTransferableOutputs(t.Outs, c) {
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())
}
if !isSortedAndUniqueTransferableInputs(t.Ins) {
if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
return errInputsNotSortedUnique
}

View File

@ -72,7 +72,7 @@ func TestExportTxSerialization(t *testing.T) {
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}),
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.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,
@ -156,7 +156,7 @@ func TestIssueExportTx(t *testing.T) {
NetID: networkID,
BCID: chainID,
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{
TxID: genesisTx.ID(),
OutputIndex: 1,
@ -167,7 +167,7 @@ func TestIssueExportTx(t *testing.T) {
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
},
}},
Outs: []*TransferableOutput{&TransferableOutput{
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: genesisTx.ID()},
Out: &secp256k1fx.TransferOutput{
Amt: 50000,
@ -287,7 +287,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
NetID: networkID,
BCID: chainID,
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{
TxID: genesisTx.ID(),
OutputIndex: 1,
@ -298,7 +298,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
Input: secp256k1fx.Input{SigIndices: []uint32{0}},
},
}},
Outs: []*TransferableOutput{&TransferableOutput{
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: genesisTx.ID()},
Out: &secp256k1fx.TransferOutput{
Amt: 50000,

View File

@ -5,7 +5,6 @@ package avm
import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/vms/components/verify"
)
type parsedFx struct {
@ -32,16 +31,6 @@ type Fx interface {
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
// be tracked as a part of the utxo set for a set of addresses
type FxAddressable interface {

View File

@ -17,16 +17,12 @@ import (
"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.
type ImportTx struct {
BaseTx `serialize:"true"`
Outs []*TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*TransferableInput `serialize:"true"` // The inputs to this transaction
Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
}
// 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())
}
if !IsSortedTransferableOutputs(t.Outs, c) {
if !ava.IsSortedTransferableOutputs(t.Outs, c) {
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())
}
if !isSortedAndUniqueTransferableInputs(t.Ins) {
if !ava.IsSortedAndUniqueTransferableInputs(t.Ins) {
return errInputsNotSortedUnique
}

View File

@ -72,7 +72,7 @@ func TestImportTxSerialization(t *testing.T) {
0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88,
}),
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.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,
@ -165,7 +165,7 @@ func TestIssueImportTx(t *testing.T) {
NetID: networkID,
BCID: chainID,
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: utxoID,
Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{
@ -306,7 +306,7 @@ func TestForceAcceptImportTx(t *testing.T) {
NetID: networkID,
BCID: chainID,
},
Ins: []*TransferableInput{&TransferableInput{
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: utxoID,
Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ func TestPrefixedSetsAndGets(t *testing.T) {
vm := GenesisVM(t)
state := vm.state
vm.codec.RegisterType(&testVerifiable{})
vm.codec.RegisterType(&ava.TestVerifiable{})
utxo := &ava.UTXO{
UTXOID: ava.UTXOID{
@ -27,29 +27,27 @@ func TestPrefixedSetsAndGets(t *testing.T) {
OutputIndex: 1,
},
Asset: ava.Asset{ID: ids.Empty},
Out: &testVerifiable{},
Out: &ava.TestVerifiable{},
}
tx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID,
BCID: chainID,
Ins: []*TransferableInput{
&TransferableInput{
UTXOID: ava.UTXOID{
TxID: ids.Empty,
OutputIndex: 0,
},
Asset: ava.Asset{ID: asset},
In: &secp256k1fx.TransferInput{
Amt: 20 * units.KiloAva,
Input: secp256k1fx.Input{
SigIndices: []uint32{
0,
},
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{
TxID: ids.Empty,
OutputIndex: 0,
},
Asset: ava.Asset{ID: asset},
In: &secp256k1fx.TransferInput{
Amt: 20 * units.KiloAva,
Input: secp256k1fx.Input{
SigIndices: []uint32{
0,
},
},
},
},
}},
}}
unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx)
@ -115,7 +113,7 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
vm := GenesisVM(t)
state := vm.state
vm.codec.RegisterType(&testVerifiable{})
vm.codec.RegisterType(&ava.TestVerifiable{})
utxo := &ava.UTXO{
UTXOID: ava.UTXOID{
@ -123,7 +121,7 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
OutputIndex: 1,
},
Asset: ava.Asset{ID: ids.Empty},
Out: &testVerifiable{},
Out: &ava.TestVerifiable{},
}
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 {
if utxo.AssetID().Equals(assetID) {
transferable, ok := utxo.Out.(FxTransferable)
transferable, ok := utxo.Out.(ava.Transferable)
if !ok {
continue
}
@ -601,7 +601,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
amountSpent := uint64(0)
time := service.vm.clock.Unix()
ins := []*TransferableInput{}
ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range utxos {
if !utxo.AssetID().Equals(assetID) {
@ -611,7 +611,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
if err != nil {
continue
}
input, ok := inputIntf.(FxTransferable)
input, ok := inputIntf.(ava.Transferable)
if !ok {
continue
}
@ -621,7 +621,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
}
amountSpent = spent
in := &TransferableInput{
in := &ava.TransferableInput{
UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: assetID},
In: input,
@ -641,38 +641,34 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
SortTransferableInputsWithSigners(ins, keys)
outs := []*TransferableOutput{
&TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: uint64(args.Amount),
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{to},
},
outs := []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: uint64(args.Amount),
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{to},
},
},
}
}}
if amountSpent > uint64(args.Amount) {
changeAddr := kc.Keys[0].PublicKey().Address()
outs = append(outs,
&TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amountSpent - uint64(args.Amount),
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{changeAddr},
},
outs = append(outs, &ava.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amountSpent - uint64(args.Amount),
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{changeAddr},
},
},
)
})
}
SortTransferableOutputs(outs, service.vm.codec)
ava.SortTransferableOutputs(outs, service.vm.codec)
tx := Tx{
UnsignedTx: &BaseTx{
@ -719,7 +715,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
}
type innerSortTransferableInputsWithSigners struct {
ins []*TransferableInput
ins []*ava.TransferableInput
signers [][]*crypto.PrivateKeySECP256K1R
}
@ -744,13 +740,13 @@ func (ins *innerSortTransferableInputsWithSigners) Swap(i, j int) {
// SortTransferableInputsWithSigners sorts the inputs and signers based on the
// 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})
}
// IsSortedAndUniqueTransferableInputsWithSigners returns true if the inputs are
// 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})
}

View File

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

View File

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

View File

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

View File

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

View File

@ -317,7 +317,7 @@ func TestTxSerialization(t *testing.T) {
for _, key := range keys {
addr := key.PublicKey().Address()
unsignedTx.Outs = append(unsignedTx.Outs, &TransferableOutput{
unsignedTx.Outs = append(unsignedTx.Outs, &ava.TransferableOutput{
Asset: ava.Asset{ID: asset},
Out: &secp256k1fx.TransferOutput{
Amt: 20 * units.KiloAva,
@ -444,23 +444,21 @@ func TestIssueTx(t *testing.T) {
newTx := &Tx{UnsignedTx: &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,
},
Ins: []*ava.TransferableInput{&ava.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,
},
},
},
},
}},
}}
unsignedBytes, err := vm.codec.Marshal(&newTx.UnsignedTx)
@ -575,35 +573,31 @@ func TestIssueDependentTx(t *testing.T) {
firstTx := &Tx{UnsignedTx: &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,
},
Ins: []*ava.TransferableInput{&ava.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()},
},
}},
Outs: []*ava.TransferableOutput{&ava.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(&firstTx.UnsignedTx)
@ -638,23 +632,21 @@ func TestIssueDependentTx(t *testing.T) {
secondTx := &Tx{UnsignedTx: &BaseTx{
NetID: networkID,
BCID: chainID,
Ins: []*TransferableInput{
&TransferableInput{
UTXOID: ava.UTXOID{
TxID: firstTx.ID(),
OutputIndex: 0,
},
Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{
Amt: 50000,
Input: secp256k1fx.Input{
SigIndices: []uint32{
0,
},
Ins: []*ava.TransferableInput{&ava.TransferableInput{
UTXOID: ava.UTXOID{
TxID: firstTx.ID(),
OutputIndex: 0,
},
Asset: ava.Asset{ID: genesisTx.ID()},
In: &secp256k1fx.TransferInput{
Amt: 50000,
Input: secp256k1fx.Input{
SigIndices: []uint32{
0,
},
},
},
},
}},
}}
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.
// See the file LICENSE for licensing terms.
package avm
package ava
import (
"bytes"
@ -9,7 +9,6 @@ import (
"sort"
"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/verify"
)
@ -22,15 +21,25 @@ var (
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 ...
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.
func (out *TransferableOutput) Output() FxTransferable { return out.Out }
func (out *TransferableOutput) Output() Transferable { return out.Out }
// Verify implements the verify.Verifiable interface
func (out *TransferableOutput) Verify() error {
@ -88,14 +97,14 @@ func IsSortedTransferableOutputs(outs []*TransferableOutput, c codec.Codec) bool
// TransferableInput ...
type TransferableInput struct {
ava.UTXOID `serialize:"true"`
ava.Asset `serialize:"true"`
UTXOID `serialize:"true"`
Asset `serialize:"true"`
In FxTransferable `serialize:"true"`
In Transferable `serialize:"true"`
}
// 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
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) Swap(i, j int) { ins[j], ins[i] = ins[i], ins[j] }
func sortTransferableInputs(ins []*TransferableInput) { sort.Sort(innerSortTransferableInputs(ins)) }
func isSortedAndUniqueTransferableInputs(ins []*TransferableInput) bool {
// SortTransferableInputs ...
func SortTransferableInputs(ins []*TransferableInput) { sort.Sort(innerSortTransferableInputs(ins)) }
// IsSortedAndUniqueTransferableInputs ...
func IsSortedAndUniqueTransferableInputs(ins []*TransferableInput) bool {
return utils.IsSortedAndUnique(innerSortTransferableInputs(ins))
}

View File

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

View File

@ -144,9 +144,6 @@ func (tx *CreateSubnetTx) Bytes() []byte {
return tx.bytes
}
// InputUTXOs returns an empty set
func (tx *CreateSubnetTx) InputUTXOs() ids.Set { return ids.Set{} }
// initialize sets [tx.vm] to [vm]
func (tx *CreateSubnetTx) initialize(vm *VM) error {
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
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")
errConflictingTxs = errors.New("block contains conflicting transactions")
)
// DecisionTx is an operation that can be decided without being proposed
type DecisionTx interface {
initialize(vm *VM) error
InputUTXOs() ids.Set
// Attempt to verify this transaction with the provided state. The provided
// database can be modified arbitrarily. If a nil error is returned, it is
// assumped onAccept is non-nil.
@ -36,8 +26,6 @@ type StandardBlock struct {
CommonDecisionBlock `serialize:"true"`
Txs []DecisionTx `serialize:"true"`
inputs ids.Set
}
// initialize this block
@ -53,17 +41,6 @@ func (sb *StandardBlock) initialize(vm *VM, bytes []byte) error {
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.
//
// The parent block must be a proposal
@ -87,20 +64,11 @@ func (sb *StandardBlock) Verify() error {
if err != nil {
return err
}
inputs := tx.InputUTXOs()
if inputs.Overlaps(sb.inputs) {
return errConflictingTxs
}
sb.inputs.Union(inputs)
if onAccept != nil {
funcs = append(funcs, onAccept)
}
}
if parentBlock.conflicts(sb.inputs) {
return errConflictingParentTxs
}
if numFuncs := len(funcs); numFuncs == 1 {
sb.onAcceptFunc = funcs[0]
} else if numFuncs > 1 {

View File

@ -26,6 +26,7 @@ import (
"github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/core"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
const (
@ -108,6 +109,13 @@ func init() {
Codec.RegisterType(&Abort{}),
Codec.RegisterType(&Commit{}),
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(&addDefaultSubnetValidatorTx{}),
@ -124,6 +132,12 @@ func init() {
Codec.RegisterType(&UnsignedCreateSubnetTx{}),
Codec.RegisterType(&CreateSubnetTx{}),
Codec.RegisterType(&UnsignedImportTx{}),
Codec.RegisterType(&ImportTx{}),
// Codec.RegisterType(&UnsignedExportTx{}),
// Codec.RegisterType(&ExportTx{}),
Codec.RegisterType(&advanceTimeTx{}),
Codec.RegisterType(&rewardValidatorTx{}),
)
@ -141,6 +155,11 @@ type VM struct {
// The node's chain manager
ChainManager chains.Manager
// AVA asset ID
AVA ids.ID
fx secp256k1fx.Fx
// Used to create and use keys.
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
// The UTXO's output must be an OutputPayment
func (w *Wallet) AddUTXO(utxo *ava.UTXO) {
out, ok := utxo.Out.(avm.FxTransferable)
out, ok := utxo.Out.(ava.Transferable)
if !ok {
return
}
@ -116,7 +116,7 @@ func (w *Wallet) RemoveUTXO(utxoID ids.ID) {
assetID := utxo.AssetID()
assetKey := assetID.Key()
newBalance := w.balance[assetKey] - utxo.Out.(avm.FxTransferable).Amount()
newBalance := w.balance[assetKey] - utxo.Out.(ava.Transferable).Amount()
if newBalance == 0 {
delete(w.balance, assetKey)
} else {
@ -138,7 +138,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
amountSpent := uint64(0)
time := w.clock.Unix()
ins := []*avm.TransferableInput{}
ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range w.utxoSet.UTXOs {
if !utxo.AssetID().Equals(assetID) {
@ -148,7 +148,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
if err != nil {
continue
}
input, ok := inputIntf.(avm.FxTransferable)
input, ok := inputIntf.(ava.Transferable)
if !ok {
continue
}
@ -158,7 +158,7 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
}
amountSpent = spent
in := &avm.TransferableInput{
in := &ava.TransferableInput{
UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: assetID},
In: input,
@ -178,41 +178,37 @@ func (w *Wallet) CreateTx(assetID ids.ID, amount uint64, destAddr ids.ShortID) (
avm.SortTransferableInputsWithSigners(ins, keys)
outs := []*avm.TransferableOutput{
&avm.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amount,
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{destAddr},
},
outs := []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amount,
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{destAddr},
},
},
}
}}
if amountSpent > amount {
changeAddr, err := w.GetAddress()
if err != nil {
return nil, err
}
outs = append(outs,
&avm.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amountSpent - amount,
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{changeAddr},
},
outs = append(outs, &ava.TransferableOutput{
Asset: ava.Asset{ID: assetID},
Out: &secp256k1fx.TransferOutput{
Amt: amountSpent - amount,
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{changeAddr},
},
},
)
})
}
avm.SortTransferableOutputs(outs, w.codec)
ava.SortTransferableOutputs(outs, w.codec)
tx := &avm.Tx{
UnsignedTx: &avm.BaseTx{