mirror of https://github.com/poanetwork/gecko.git
wip
This commit is contained in:
parent
a6a92510af
commit
93ed25f878
|
@ -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 ...
|
||||
|
|
|
@ -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 ...
|
||||
|
|
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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{},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
|
@ -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))
|
||||
}
|
|
@ -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,
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
// }
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Reference in New Issue