Finished first pass of APIs

This commit is contained in:
StephenButtolph 2020-03-27 02:42:16 -04:00
parent 9da52e70c0
commit 65393729d2
22 changed files with 758 additions and 263 deletions

View File

@ -54,12 +54,9 @@ func (t *txJob) Execute() {
case choices.Unknown, choices.Rejected: case choices.Unknown, choices.Rejected:
t.numDropped.Inc() t.numDropped.Inc()
case choices.Processing: case choices.Processing:
if err := t.tx.Verify(); err == nil { t.tx.Verify()
t.tx.Accept() t.tx.Accept()
t.numAccepted.Inc() t.numAccepted.Inc()
} else {
t.numDropped.Inc()
}
} }
} }
func (t *txJob) Bytes() []byte { return t.tx.Bytes() } func (t *txJob) Bytes() []byte { return t.tx.Bytes() }

View File

@ -51,12 +51,9 @@ func (b *blockJob) Execute() {
case choices.Unknown, choices.Rejected: case choices.Unknown, choices.Rejected:
b.numDropped.Inc() b.numDropped.Inc()
case choices.Processing: case choices.Processing:
if err := b.blk.Verify(); err == nil { b.blk.Verify()
b.blk.Accept() b.blk.Accept()
b.numAccepted.Inc() b.numAccepted.Inc()
} else {
b.numDropped.Inc()
}
} }
} }
func (b *blockJob) Bytes() []byte { return b.blk.Bytes() } func (b *blockJob) Bytes() []byte { return b.blk.Bytes() }

View File

@ -37,20 +37,6 @@ type BaseTx struct {
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
// NetworkID is the ID of the network on which this transaction exists
func (t *BaseTx) NetworkID() uint32 { return t.NetID }
// ChainID is the ID of the chain on which this transaction exists
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() []*ava.TransferableOutput { return t.Outs }
// Inputs track which UTXOs this transaction is consuming. The returned array
// should not be modified.
func (t *BaseTx) Inputs() []*ava.TransferableInput { return t.Ins }
// InputUTXOs track which UTXOs this transaction is consuming. // InputUTXOs track which UTXOs this transaction is consuming.
func (t *BaseTx) InputUTXOs() []*ava.UTXOID { func (t *BaseTx) InputUTXOs() []*ava.UTXOID {
utxos := []*ava.UTXOID(nil) utxos := []*ava.UTXOID(nil)

View File

@ -167,19 +167,7 @@ func TestBaseTxGetters(t *testing.T) {
txID := tx.ID() txID := tx.ID()
if netID := tx.NetworkID(); netID != networkID { if assets := tx.AssetIDs(); assets.Len() != 1 {
t.Fatalf("Wrong network ID returned")
} else if bcID := tx.ChainID(); !bcID.Equals(chainID) {
t.Fatalf("Wrong chain ID returned")
} else if outs := tx.Outputs(); len(outs) != 1 {
t.Fatalf("Outputs returned wrong number of outs")
} else if out := outs[0]; out != tx.Outs[0] {
t.Fatalf("Outputs returned wrong output")
} else if ins := tx.Inputs(); len(ins) != 1 {
t.Fatalf("Inputs returned wrong number of ins")
} else if in := ins[0]; in != tx.Ins[0] {
t.Fatalf("Inputs returned wrong input")
} else if assets := tx.AssetIDs(); assets.Len() != 1 {
t.Fatalf("Wrong number of assets returned") t.Fatalf("Wrong number of assets returned")
} else if !assets.Contains(asset) { } else if !assets.Contains(asset) {
t.Fatalf("Wrong asset returned") t.Fatalf("Wrong asset returned")
@ -191,8 +179,6 @@ func TestBaseTxGetters(t *testing.T) {
t.Fatalf("Wrong output index returned") t.Fatalf("Wrong output index returned")
} else if assetID := utxo.AssetID(); !assetID.Equals(asset) { } else if assetID := utxo.AssetID(); !assetID.Equals(asset) {
t.Fatalf("Wrong asset ID returned") t.Fatalf("Wrong asset ID returned")
} else if utxoOut := utxo.Out; utxoOut != out.Out {
t.Fatalf("Wrong output returned")
} }
} }

View File

@ -17,7 +17,7 @@ import (
type ExportTx struct { type ExportTx struct {
BaseTx `serialize:"true"` BaseTx `serialize:"true"`
ExportOuts []*ava.TransferableOutput `serialize:"true"` // The outputs this transaction is sending to the other chain Outs []*ava.TransferableOutput `serialize:"true"` // The outputs this transaction is sending to the other chain
} }
// SyntacticVerify that this transaction is well-formed. // SyntacticVerify that this transaction is well-formed.
@ -32,6 +32,16 @@ func (t *ExportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) erro
} }
fc := ava.NewFlowChecker() fc := ava.NewFlowChecker()
for _, out := range t.BaseTx.Outs {
if err := out.Verify(); err != nil {
return err
}
fc.Produce(out.AssetID(), out.Output().Amount())
}
if !ava.IsSortedTransferableOutputs(t.BaseTx.Outs, c) {
return errOutputsNotSorted
}
for _, out := range t.Outs { for _, out := range t.Outs {
if err := out.Verify(); err != nil { if err := out.Verify(); err != nil {
return err return err
@ -42,16 +52,6 @@ func (t *ExportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, _ int) erro
return errOutputsNotSorted return errOutputsNotSorted
} }
for _, out := range t.ExportOuts {
if err := out.Verify(); err != nil {
return err
}
fc.Produce(out.AssetID(), out.Output().Amount())
}
if !ava.IsSortedTransferableOutputs(t.ExportOuts, c) {
return errOutputsNotSorted
}
for _, in := range t.Ins { for _, in := range t.Ins {
if err := in.Verify(); err != nil { if err := in.Verify(); err != nil {
return err return err
@ -102,7 +102,7 @@ func (t *ExportTx) SemanticVerify(vm *VM, uTx *UniqueTx, creds []verify.Verifiab
} }
} }
for _, out := range t.ExportOuts { for _, out := range t.Outs {
if !out.AssetID().Equals(vm.ava) { if !out.AssetID().Equals(vm.ava) {
return errWrongAssetID return errWrongAssetID
} }
@ -121,11 +121,11 @@ func (t *ExportTx) ExecuteWithSideEffects(vm *VM, batch database.Batch) error {
vsmDB := versiondb.New(smDB) vsmDB := versiondb.New(smDB)
state := ava.NewPrefixedState(vsmDB, vm.codec) state := ava.NewPrefixedState(vsmDB, vm.codec)
for i, out := range t.ExportOuts { for i, out := range t.Outs {
utxo := &ava.UTXO{ utxo := &ava.UTXO{
UTXOID: ava.UTXOID{ UTXOID: ava.UTXOID{
TxID: txID, TxID: txID,
OutputIndex: uint32(len(t.Outs) + i), OutputIndex: uint32(len(t.BaseTx.Outs) + i),
}, },
Asset: ava.Asset{ID: out.AssetID()}, Asset: ava.Asset{ID: out.AssetID()},
Out: out.Out, Out: out.Out,

View File

@ -168,7 +168,7 @@ func TestIssueExportTx(t *testing.T) {
}, },
}}, }},
}, },
ExportOuts: []*ava.TransferableOutput{&ava.TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: avaID}, Asset: ava.Asset{ID: avaID},
Out: &secp256k1fx.TransferOutput{ Out: &secp256k1fx.TransferOutput{
Amt: 50000, Amt: 50000,
@ -301,7 +301,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
}, },
}}, }},
}, },
ExportOuts: []*ava.TransferableOutput{&ava.TransferableOutput{ Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: avaID}, Asset: ava.Asset{ID: avaID},
Out: &secp256k1fx.TransferOutput{ Out: &secp256k1fx.TransferOutput{
Amt: 50000, Amt: 50000,

View File

@ -20,8 +20,7 @@ import (
type ImportTx struct { type ImportTx struct {
BaseTx `serialize:"true"` BaseTx `serialize:"true"`
Outs []*ava.TransferableOutput `serialize:"true"` // The outputs of this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
// InputUTXOs track which UTXOs this transaction is consuming. // InputUTXOs track which UTXOs this transaction is consuming.
@ -43,25 +42,6 @@ func (t *ImportTx) AssetIDs() ids.Set {
return assets return assets
} }
// UTXOs returns the UTXOs transaction is producing.
func (t *ImportTx) UTXOs() []*ava.UTXO {
txID := t.ID()
utxos := t.BaseTx.UTXOs()
for _, out := range t.Outs {
utxos = append(utxos, &ava.UTXO{
UTXOID: ava.UTXOID{
TxID: txID,
OutputIndex: uint32(len(utxos)),
},
Asset: ava.Asset{ID: out.AssetID()},
Out: out.Out,
})
}
return utxos
}
var ( var (
errNoImportInputs = errors.New("no import inputs") errNoImportInputs = errors.New("no import inputs")
) )
@ -71,14 +51,14 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
switch { switch {
case t == nil: case t == nil:
return errNilTx return errNilTx
case t.NetID != ctx.NetworkID:
return errWrongNetworkID
case !t.BCID.Equals(ctx.ChainID):
return errWrongChainID
case len(t.Ins) == 0: case len(t.Ins) == 0:
return errNoImportInputs return errNoImportInputs
} }
if err := t.BaseTx.SyntacticVerify(ctx, c, numFxs); err != nil {
return err
}
fc := ava.NewFlowChecker() fc := ava.NewFlowChecker()
for _, out := range t.Outs { for _, out := range t.Outs {
if err := out.Verify(); err != nil { if err := out.Verify(); err != nil {
@ -90,6 +70,16 @@ func (t *ImportTx) SyntacticVerify(ctx *snow.Context, c codec.Codec, numFxs int)
return errOutputsNotSorted return errOutputsNotSorted
} }
for _, in := range t.BaseTx.Ins {
if err := in.Verify(); err != nil {
return err
}
fc.Consume(in.AssetID(), in.Input().Amount())
}
if !ava.IsSortedAndUniqueTransferableInputs(t.BaseTx.Ins) {
return errInputsNotSortedUnique
}
for _, in := range t.Ins { for _, in := range t.Ins {
if err := in.Verify(); err != nil { if err := in.Verify(); err != nil {
return err return err

View File

@ -34,8 +34,6 @@ func TestImportTxSerialization(t *testing.T) {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// number of base inputs: // number of base inputs:
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// number of outs:
0x00, 0x00, 0x00, 0x00,
// number of inputs: // number of inputs:
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
// utxoID: // utxoID:

View File

@ -948,6 +948,147 @@ func (service *Service) SignMintTx(r *http.Request, args *SignMintTxArgs, reply
return nil return nil
} }
// SendImportArgs are arguments for passing into SendImport requests
type SendImportArgs struct {
Username string `json:"username"`
Password string `json:"password"`
To string `json:"to"`
}
// SendImportReply defines the SendImport replies returned from the API
type SendImportReply struct {
TxID ids.ID `json:"txID"`
}
// SendImport returns the ID of the newly created atomic transaction
func (service *Service) SendImport(_ *http.Request, args *SendImportArgs, reply *SendImportReply) error {
service.vm.ctx.Log.Verbo("SendExport called with username: %s", args.Username)
toBytes, err := service.vm.Parse(args.To)
if err != nil {
return fmt.Errorf("problem parsing to address: %w", err)
}
to, err := ids.ToShortID(toBytes)
if err != nil {
return fmt.Errorf("problem parsing to address: %w", err)
}
db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil {
return fmt.Errorf("problem retrieving user: %w", err)
}
user := userState{vm: service.vm}
addresses, _ := user.Addresses(db)
addrs := ids.Set{}
addrs.Add(addresses...)
utxos, err := service.vm.GetAtomicUTXOs(addrs)
if err != nil {
return fmt.Errorf("problem retrieving user's atomic UTXOs: %w", err)
}
kc := secp256k1fx.NewKeychain()
for _, addr := range addresses {
sk, err := user.Key(db, addr)
if err != nil {
return fmt.Errorf("problem retrieving private key: %w", err)
}
kc.Add(sk)
}
amount := uint64(0)
time := service.vm.clock.Unix()
ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range utxos {
if !utxo.AssetID().Equals(service.vm.ava) {
continue
}
inputIntf, signers, err := kc.Spend(utxo.Out, time)
if err != nil {
continue
}
input, ok := inputIntf.(ava.Transferable)
if !ok {
continue
}
spent, err := math.Add64(amount, input.Amount())
if err != nil {
return errSpendOverflow
}
amount = spent
in := &ava.TransferableInput{
UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: service.vm.ava},
In: input,
}
ins = append(ins, in)
keys = append(keys, signers)
}
ava.SortTransferableInputsWithSigners(ins, keys)
outs := []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: service.vm.ava},
Out: &secp256k1fx.TransferOutput{
Amt: amount,
Locktime: 0,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{to},
},
},
}}
tx := Tx{UnsignedTx: &ImportTx{
BaseTx: BaseTx{
NetID: service.vm.ctx.NetworkID,
BCID: service.vm.ctx.ChainID,
Outs: outs,
},
Ins: ins,
}}
unsignedBytes, err := service.vm.codec.Marshal(&tx.UnsignedTx)
if err != nil {
return fmt.Errorf("problem creating transaction: %w", err)
}
hash := hashing.ComputeHash256(unsignedBytes)
for _, credKeys := range keys {
cred := &secp256k1fx.Credential{}
for _, key := range credKeys {
sig, err := key.SignHash(hash)
if err != nil {
return fmt.Errorf("problem creating transaction: %w", err)
}
fixedSig := [crypto.SECP256K1RSigLen]byte{}
copy(fixedSig[:], sig)
cred.Sigs = append(cred.Sigs, fixedSig)
}
tx.Creds = append(tx.Creds, cred)
}
b, err := service.vm.codec.Marshal(tx)
if err != nil {
return fmt.Errorf("problem creating transaction: %w", err)
}
txID, err := service.vm.IssueTx(b, nil)
if err != nil {
return fmt.Errorf("problem issuing transaction: %w", err)
}
reply.TxID = txID
return nil
}
// SendExportArgs are arguments for passing into SendExport requests // SendExportArgs are arguments for passing into SendExport requests
type SendExportArgs struct { type SendExportArgs struct {
Username string `json:"username"` Username string `json:"username"`
@ -1083,7 +1224,7 @@ func (service *Service) SendExport(_ *http.Request, args *SendExportArgs, reply
Outs: outs, Outs: outs,
Ins: ins, Ins: ins,
}, },
ExportOuts: exportOuts, Outs: exportOuts,
}} }}
unsignedBytes, err := service.vm.codec.Marshal(&tx.UnsignedTx) unsignedBytes, err := service.vm.codec.Marshal(&tx.UnsignedTx)

View File

@ -24,11 +24,6 @@ type UnsignedTx interface {
ID() ids.ID ID() ids.ID
Bytes() []byte Bytes() []byte
NetworkID() uint32
ChainID() ids.ID
Outputs() []*ava.TransferableOutput
Inputs() []*ava.TransferableInput
AssetIDs() ids.Set AssetIDs() ids.Set
InputUTXOs() []*ava.UTXOID InputUTXOs() []*ava.UTXOID
UTXOs() []*ava.UTXO UTXOs() []*ava.UTXO

View File

@ -275,6 +275,31 @@ func (vm *VM) IssueTx(b []byte, onDecide func(choices.Status)) (ids.ID, error) {
return tx.ID(), nil return tx.ID(), nil
} }
// GetAtomicUTXOs returns the utxos that at least one of the provided addresses is
// referenced in.
func (vm *VM) GetAtomicUTXOs(addrs ids.Set) ([]*ava.UTXO, error) {
smDB := vm.ctx.SharedMemory.GetDatabase(vm.platform)
defer vm.ctx.SharedMemory.ReleaseDatabase(vm.platform)
state := ava.NewPrefixedState(smDB, vm.codec)
utxoIDs := ids.Set{}
for _, addr := range addrs.List() {
utxos, _ := state.PlatformFunds(addr)
utxoIDs.Add(utxos...)
}
utxos := []*ava.UTXO{}
for _, utxoID := range utxoIDs.List() {
utxo, err := state.PlatformUTXO(utxoID)
if err != nil {
return nil, err
}
utxos = append(utxos, utxo)
}
return utxos, nil
}
// GetUTXOs returns the utxos that at least one of the provided addresses is // GetUTXOs returns the utxos that at least one of the provided addresses is
// referenced in. // referenced in.
func (vm *VM) GetUTXOs(addrs ids.Set) ([]*ava.UTXO, error) { func (vm *VM) GetUTXOs(addrs ids.Set) ([]*ava.UTXO, error) {

View File

@ -162,7 +162,7 @@ func (tx *addNonDefaultSubnetValidatorTx) SemanticVerify(db database.Database) (
} }
var subnet *CreateSubnetTx var subnet *CreateSubnetTx
for _, sn := range subnets { for _, sn := range subnets {
if sn.ID.Equals(tx.SubnetID()) { if sn.id.Equals(tx.SubnetID()) {
subnet = sn subnet = sn
break break
} }

View File

@ -28,7 +28,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -48,7 +48,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID+1, testNetworkID+1,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -67,7 +67,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -87,7 +87,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -107,7 +107,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -126,7 +126,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix())-1, uint64(defaultValidateEndTime.Unix())-1,
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -147,7 +147,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateStartTime.Add(MinimumStakingDuration).Unix())-1, uint64(defaultValidateStartTime.Add(MinimumStakingDuration).Unix())-1,
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -167,7 +167,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateStartTime.Add(MaximumStakingDuration).Unix())+1, uint64(defaultValidateStartTime.Add(MaximumStakingDuration).Unix())+1,
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -187,7 +187,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -212,7 +212,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix())+1, uint64(defaultValidateEndTime.Unix())+1,
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -235,7 +235,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -245,7 +245,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
} }
_, _, _, _, err = tx.SemanticVerify(vm.DB) _, _, _, _, err = tx.SemanticVerify(vm.DB)
if err != nil { if err != nil {
t.Log(testSubnet1.ID) t.Log(testSubnet1.id)
subnets, err := vm.getSubnets(vm.DB) subnets, err := vm.getSubnets(vm.DB)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -253,7 +253,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
if len(subnets) == 0 { if len(subnets) == 0 {
t.Fatal("no subnets found") t.Fatal("no subnets found")
} }
t.Logf("subnets[0].ID: %v", subnets[0].ID) t.Logf("subnets[0].ID: %v", subnets[0].id)
t.Fatal(err) t.Fatal(err)
} }
@ -290,7 +290,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(DSStartTime.Unix()), // start validating non-default subnet before default subnet uint64(DSStartTime.Unix()), // start validating non-default subnet before default subnet
uint64(DSEndTime.Unix()), uint64(DSEndTime.Unix()),
pendingDSValidatorID, pendingDSValidatorID,
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -324,7 +324,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(DSStartTime.Unix())-1, // start validating non-default subnet before default subnet uint64(DSStartTime.Unix())-1, // start validating non-default subnet before default subnet
uint64(DSEndTime.Unix()), uint64(DSEndTime.Unix()),
pendingDSValidatorID, pendingDSValidatorID,
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -346,7 +346,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(DSStartTime.Unix()), uint64(DSStartTime.Unix()),
uint64(DSEndTime.Unix())+1, // stop validating non-default subnet after stopping validating default subnet uint64(DSEndTime.Unix())+1, // stop validating non-default subnet after stopping validating default subnet
pendingDSValidatorID, pendingDSValidatorID,
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -368,7 +368,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(DSStartTime.Unix()), // same start time as for default subnet uint64(DSStartTime.Unix()), // same start time as for default subnet
uint64(DSEndTime.Unix()), // same end time as for default subnet uint64(DSEndTime.Unix()), // same end time as for default subnet
pendingDSValidatorID, pendingDSValidatorID,
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,
@ -389,12 +389,12 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
} }
tx, err = vm.newAddNonDefaultSubnetValidatorTx( tx, err = vm.newAddNonDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultWeight, // weight defaultWeight, // weight
uint64(newTimestamp.Unix()), // start time uint64(newTimestamp.Unix()), // start time
uint64(newTimestamp.Add(MinimumStakingDuration).Unix()), // end time uint64(newTimestamp.Add(MinimumStakingDuration).Unix()), // end time
defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -429,7 +429,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), // start time uint64(defaultValidateStartTime.Unix()), // start time
uint64(defaultValidateEndTime.Unix()), // end time uint64(defaultValidateEndTime.Unix()), // end time
defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
newAcctKey.(*crypto.PrivateKeySECP256K1R), // tx fee payer newAcctKey.(*crypto.PrivateKeySECP256K1R), // tx fee payer
@ -451,7 +451,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), // start time uint64(defaultValidateStartTime.Unix()), // start time
uint64(defaultValidateEndTime.Unix()), // end time uint64(defaultValidateEndTime.Unix()), // end time
defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -465,7 +465,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
SortByStartTime: false, SortByStartTime: false,
Txs: []TimedTx{tx}, Txs: []TimedTx{tx},
}, },
testSubnet1.ID, testSubnet1.id,
) )
// Node with ID nodeIDKey.PublicKey().Address() now validating subnet with ID testSubnet1.ID // Node with ID nodeIDKey.PublicKey().Address() now validating subnet with ID testSubnet1.ID
@ -475,7 +475,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), // start time uint64(defaultValidateStartTime.Unix()), // start time
uint64(defaultValidateEndTime.Unix()), // end time uint64(defaultValidateEndTime.Unix()), // end time
defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -494,17 +494,17 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
&EventHeap{ &EventHeap{
SortByStartTime: false, SortByStartTime: false,
}, },
testSubnet1.ID, testSubnet1.id,
) )
// Case 9: Too many signatures // Case 9: Too many signatures
tx, err = vm.newAddNonDefaultSubnetValidatorTx( tx, err = vm.newAddNonDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultWeight, // weight defaultWeight, // weight
uint64(defaultGenesisTime.Unix()), // start time uint64(defaultGenesisTime.Unix()), // start time
uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix())+1, // end time uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix())+1, // end time
keys[0].PublicKey().Address(), // node ID keys[0].PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -520,12 +520,12 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
// Case 10: Too few signatures // Case 10: Too few signatures
tx, err = vm.newAddNonDefaultSubnetValidatorTx( tx, err = vm.newAddNonDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultWeight, // weight defaultWeight, // weight
uint64(defaultGenesisTime.Unix()), // start time uint64(defaultGenesisTime.Unix()), // start time
uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix()), // end time uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix()), // end time
keys[0].PublicKey().Address(), // node ID keys[0].PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[2]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[2]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -541,12 +541,12 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
// Case 10: Control Signature from invalid key // Case 10: Control Signature from invalid key
tx, err = vm.newAddNonDefaultSubnetValidatorTx( tx, err = vm.newAddNonDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultWeight, // weight defaultWeight, // weight
uint64(defaultGenesisTime.Unix()), // start time uint64(defaultGenesisTime.Unix()), // start time
uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix()), // end time uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix()), // end time
keys[0].PublicKey().Address(), // node ID keys[0].PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], keys[3]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], keys[3]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -563,12 +563,12 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
// Case 11: Proposed validator in pending validator set for subnet // Case 11: Proposed validator in pending validator set for subnet
// First, add validator to pending validator set of subnet // First, add validator to pending validator set of subnet
tx, err = vm.newAddNonDefaultSubnetValidatorTx( tx, err = vm.newAddNonDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultWeight, // weight defaultWeight, // weight
uint64(defaultGenesisTime.Unix())+1, // start time uint64(defaultGenesisTime.Unix())+1, // start time
uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix())+1, // end time uint64(defaultGenesisTime.Add(MinimumStakingDuration).Unix())+1, // end time
defaultKey.PublicKey().Address(), // node ID defaultKey.PublicKey().Address(), // node ID
testSubnet1.ID, // subnet ID testSubnet1.id, // subnet ID
testNetworkID, // network ID testNetworkID, // network ID
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, // tx fee payer defaultKey, // tx fee payer
@ -582,7 +582,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
SortByStartTime: true, SortByStartTime: true,
Txs: []TimedTx{tx}, Txs: []TimedTx{tx},
}, },
testSubnet1.ID, testSubnet1.id,
) )
// Node with ID nodeIDKey.PublicKey().Address() now pending validator for subnet with ID testSubnet1.ID // Node with ID nodeIDKey.PublicKey().Address() now pending validator for subnet with ID testSubnet1.ID
@ -604,7 +604,7 @@ func TestAddNonDefaultSubnetValidatorMarshal(t *testing.T) {
uint64(defaultValidateStartTime.Unix()), uint64(defaultValidateStartTime.Unix()),
uint64(defaultValidateEndTime.Unix()), uint64(defaultValidateEndTime.Unix()),
defaultKey.PublicKey().Address(), defaultKey.PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey, defaultKey,

View File

@ -105,15 +105,15 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
for _, subnet := range subnets { for _, subnet := range subnets {
current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.ID) current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.id)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
if err := tx.vm.putCurrentValidators(onCommitDB, current, subnet.ID); err != nil { if err := tx.vm.putCurrentValidators(onCommitDB, current, subnet.id); err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
if err := tx.vm.putPendingValidators(onCommitDB, pending, subnet.ID); err != nil { if err := tx.vm.putPendingValidators(onCommitDB, pending, subnet.id); err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
} }
@ -127,7 +127,7 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return return
} }
for _, subnet := range subnets { for _, subnet := range subnets {
if err := tx.vm.updateValidators(subnet.ID); err != nil { if err := tx.vm.updateValidators(subnet.id); err != nil {
tx.vm.Ctx.Log.Debug("failed to update validators on the default subnet: %s", err) tx.vm.Ctx.Log.Debug("failed to update validators on the default subnet: %s", err)
} }
} }

View File

@ -21,6 +21,8 @@ var (
type AtomicTx interface { type AtomicTx interface {
initialize(vm *VM) error initialize(vm *VM) error
ID() ids.ID
// UTXOs this tx consumes // UTXOs this tx consumes
InputUTXOs() ids.Set InputUTXOs() ids.Set

View File

@ -23,12 +23,6 @@ var (
// UnsignedCreateSubnetTx is an unsigned proposal to create a new subnet // UnsignedCreateSubnetTx is an unsigned proposal to create a new subnet
type UnsignedCreateSubnetTx struct { type UnsignedCreateSubnetTx struct {
// The VM this tx exists within
vm *VM
// ID is this transaction's ID
ID ids.ID
// NetworkID is the ID of the network this tx was issued on // NetworkID is the ID of the network this tx was issued on
NetworkID uint32 `serialize:"true"` NetworkID uint32 `serialize:"true"`
@ -47,6 +41,12 @@ type UnsignedCreateSubnetTx struct {
type CreateSubnetTx struct { type CreateSubnetTx struct {
UnsignedCreateSubnetTx `serialize:"true"` UnsignedCreateSubnetTx `serialize:"true"`
// The VM this tx exists within
vm *VM
// ID is this transaction's ID
id ids.ID
// The public key that signed this transaction // The public key that signed this transaction
// The transaction fee will be paid from the corresponding account // The transaction fee will be paid from the corresponding account
// (ie the account whose ID is [key].Address()) // (ie the account whose ID is [key].Address())
@ -60,6 +60,9 @@ type CreateSubnetTx struct {
bytes []byte bytes []byte
} }
// ID returns the ID of this tx
func (tx *CreateSubnetTx) ID() ids.ID { return tx.id }
// SyntacticVerify nil iff [tx] is syntactically valid. // SyntacticVerify nil iff [tx] is syntactically valid.
// If [tx] is valid, this method sets [tx.key] // If [tx] is valid, this method sets [tx.key]
func (tx *CreateSubnetTx) SyntacticVerify() error { func (tx *CreateSubnetTx) SyntacticVerify() error {
@ -68,7 +71,7 @@ func (tx *CreateSubnetTx) SyntacticVerify() error {
return errNilTx return errNilTx
case tx.key != nil: case tx.key != nil:
return nil // Only verify the transaction once return nil // Only verify the transaction once
case tx.ID.IsZero(): case tx.id.IsZero():
return errInvalidID return errInvalidID
case tx.NetworkID != tx.vm.Ctx.NetworkID: case tx.NetworkID != tx.vm.Ctx.NetworkID:
return errWrongNetworkID return errWrongNetworkID
@ -106,8 +109,8 @@ func (tx *CreateSubnetTx) SemanticVerify(db database.Database) (func(), error) {
} }
for _, subnet := range subnets { for _, subnet := range subnets {
if subnet.ID.Equals(tx.ID) { if subnet.id.Equals(tx.id) {
return nil, fmt.Errorf("there is already a subnet with ID %s", tx.ID) return nil, fmt.Errorf("there is already a subnet with ID %s", tx.id)
} }
} }
subnets = append(subnets, tx) // add new subnet subnets = append(subnets, tx) // add new subnet
@ -152,7 +155,7 @@ func (tx *CreateSubnetTx) initialize(vm *VM) error {
return err return err
} }
tx.bytes = txBytes tx.bytes = txBytes
tx.ID = ids.NewID(hashing.ComputeHash256Array(txBytes)) tx.id = ids.NewID(hashing.ComputeHash256Array(txBytes))
return nil return nil
} }
@ -160,15 +163,12 @@ func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []id
threshold uint16, payerKey *crypto.PrivateKeySECP256K1R, threshold uint16, payerKey *crypto.PrivateKeySECP256K1R,
) (*CreateSubnetTx, error) { ) (*CreateSubnetTx, error) {
tx := &CreateSubnetTx{ tx := &CreateSubnetTx{UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{ NetworkID: networkID,
vm: vm, Nonce: nonce,
NetworkID: networkID, ControlKeys: controlKeys,
Nonce: nonce, Threshold: threshold,
ControlKeys: controlKeys, }}
Threshold: threshold,
},
}
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx) unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) unsignedBytes, err := Codec.Marshal(&unsignedIntf)

View File

@ -38,7 +38,7 @@ type UnsignedImportTx struct {
Nonce uint64 `serialize:"true"` Nonce uint64 `serialize:"true"`
// Account that this transaction is being sent by. This is needed to ensure the Credentials are replay safe. // Account that this transaction is being sent by. This is needed to ensure the Credentials are replay safe.
Account [crypto.SECP256K1RPKLen]byte `serialize:"true"` Account ids.ShortID `serialize:"true"`
Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction Ins []*ava.TransferableInput `serialize:"true"` // The inputs to this transaction
} }
@ -129,17 +129,12 @@ func (tx *ImportTx) SyntacticVerify() error {
return err return err
} }
expectedPublicKey, err := tx.vm.factory.ToPublicKey(tx.Account[:])
if err != nil {
return err
}
key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:]) key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:])
if err != nil { if err != nil {
return err return err
} }
if !expectedPublicKey.Address().Equals(key.Address()) { if !tx.Account.Equals(key.Address()) {
return errPublicKeySignatureMismatch return errPublicKeySignatureMismatch
} }
@ -236,12 +231,10 @@ func (vm *VM) newImportTx(nonce uint64, networkID uint32, ins []*ava.Transferabl
tx := &ImportTx{UnsignedImportTx: UnsignedImportTx{ tx := &ImportTx{UnsignedImportTx: UnsignedImportTx{
NetworkID: networkID, NetworkID: networkID,
Nonce: nonce, Nonce: nonce,
Account: key.PublicKey().Address(),
Ins: ins, Ins: ins,
}} }}
pubkeyBytes := key.PublicKey().Bytes()
copy(tx.Account[:], pubkeyBytes)
unsignedIntf := interface{}(&tx.UnsignedImportTx) unsignedIntf := interface{}(&tx.UnsignedImportTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction
if err != nil { if err != nil {

View File

@ -16,7 +16,11 @@ import (
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto" "github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/math"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/secp256k1fx"
) )
var ( var (
@ -97,7 +101,7 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon
response.Subnets = make([]APISubnet, len(subnets)) response.Subnets = make([]APISubnet, len(subnets))
for i, subnet := range subnets { for i, subnet := range subnets {
response.Subnets[i] = APISubnet{ response.Subnets[i] = APISubnet{
ID: subnet.ID, ID: subnet.id,
ControlKeys: subnet.ControlKeys, ControlKeys: subnet.ControlKeys,
Threshold: json.Uint16(subnet.Threshold), Threshold: json.Uint16(subnet.Threshold),
} }
@ -108,10 +112,10 @@ func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, respon
idsSet := ids.Set{} idsSet := ids.Set{}
idsSet.Add(args.IDs...) idsSet.Add(args.IDs...)
for _, subnet := range subnets { for _, subnet := range subnets {
if idsSet.Contains(subnet.ID) { if idsSet.Contains(subnet.id) {
response.Subnets = append(response.Subnets, response.Subnets = append(response.Subnets,
APISubnet{ APISubnet{
ID: subnet.ID, ID: subnet.id,
ControlKeys: subnet.ControlKeys, ControlKeys: subnet.ControlKeys,
Threshold: json.Uint16(subnet.Threshold), Threshold: json.Uint16(subnet.Threshold),
}, },
@ -432,6 +436,11 @@ type genericTx struct {
****************************************************** ******************************************************
*/ */
// CreateTxResponse is the response from calls to create a transaction
type CreateTxResponse struct {
UnsignedTx formatting.CB58 `json:"unsignedTx"`
}
// AddDefaultSubnetValidatorArgs are the arguments to AddDefaultSubnetValidator // AddDefaultSubnetValidatorArgs are the arguments to AddDefaultSubnetValidator
type AddDefaultSubnetValidatorArgs struct { type AddDefaultSubnetValidatorArgs struct {
APIDefaultSubnetValidator APIDefaultSubnetValidator
@ -440,15 +449,9 @@ type AddDefaultSubnetValidatorArgs struct {
PayerNonce json.Uint64 `json:"payerNonce"` PayerNonce json.Uint64 `json:"payerNonce"`
} }
// AddDefaultSubnetValidatorResponse is the response from a call to AddDefaultSubnetValidator
type AddDefaultSubnetValidatorResponse struct {
// The unsigned transaction
UnsignedTx formatting.CB58 `json:"unsignedTx"`
}
// AddDefaultSubnetValidator returns an unsigned transaction to add a validator to the default subnet // AddDefaultSubnetValidator returns an unsigned transaction to add a validator to the default subnet
// The returned unsigned transaction should be signed using Sign() // The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *AddDefaultSubnetValidatorResponse) error { func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetValidator called") service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetValidator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
@ -490,16 +493,10 @@ type AddDefaultSubnetDelegatorArgs struct {
PayerNonce json.Uint64 `json:"payerNonce"` PayerNonce json.Uint64 `json:"payerNonce"`
} }
// AddDefaultSubnetDelegatorResponse is the response from a call to AddDefaultSubnetDelegator
type AddDefaultSubnetDelegatorResponse struct {
// The unsigned transaction
UnsignedTx formatting.CB58 `json:"unsignedTx"`
}
// AddDefaultSubnetDelegator returns an unsigned transaction to add a delegator // AddDefaultSubnetDelegator returns an unsigned transaction to add a delegator
// to the default subnet // to the default subnet
// The returned unsigned transaction should be signed using Sign() // The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *AddDefaultSubnetDelegatorResponse) error { func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetDelegator called") service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetDelegator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
@ -541,15 +538,9 @@ type AddNonDefaultSubnetValidatorArgs struct {
PayerNonce json.Uint64 `json:"payerNonce"` PayerNonce json.Uint64 `json:"payerNonce"`
} }
// AddNonDefaultSubnetValidatorResponse is the response from a call to AddNonDefaultSubnetValidator
type AddNonDefaultSubnetValidatorResponse struct {
// The unsigned transaction
UnsignedTx formatting.CB58 `json:"unsignedTx"`
}
// AddNonDefaultSubnetValidator adds a validator to a subnet other than the default subnet // AddNonDefaultSubnetValidator adds a validator to a subnet other than the default subnet
// Returns the unsigned transaction, which must be signed using Sign // Returns the unsigned transaction, which must be signed using Sign
func (service *Service) AddNonDefaultSubnetValidator(_ *http.Request, args *AddNonDefaultSubnetValidatorArgs, response *AddNonDefaultSubnetValidatorResponse) error { func (service *Service) AddNonDefaultSubnetValidator(_ *http.Request, args *AddNonDefaultSubnetValidatorArgs, response *CreateTxResponse) error {
tx := addNonDefaultSubnetValidatorTx{ tx := addNonDefaultSubnetValidatorTx{
UnsignedAddNonDefaultSubnetValidatorTx: UnsignedAddNonDefaultSubnetValidatorTx{ UnsignedAddNonDefaultSubnetValidatorTx: UnsignedAddNonDefaultSubnetValidatorTx{
SubnetValidator: SubnetValidator{ SubnetValidator: SubnetValidator{
@ -583,6 +574,83 @@ func (service *Service) AddNonDefaultSubnetValidator(_ *http.Request, args *AddN
return nil return nil
} }
// CreateSubnetArgs are the arguments to CreateSubnet
type CreateSubnetArgs struct {
// The ID member of APISubnet is ignored
APISubnet
// Nonce of the account that pays the transaction fee
PayerNonce json.Uint64 `json:"payerNonce"`
}
// CreateSubnet returns an unsigned transaction to create a new subnet.
// The unsigned transaction must be signed with the key of [args.Payer]
func (service *Service) CreateSubnet(_ *http.Request, args *CreateSubnetArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.createSubnet called")
// Create the transaction
tx := CreateSubnetTx{
UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
NetworkID: service.vm.Ctx.NetworkID,
Nonce: uint64(args.PayerNonce),
ControlKeys: args.ControlKeys,
Threshold: uint16(args.Threshold),
},
key: nil,
Sig: [65]byte{},
bytes: nil,
}
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil {
return errCreatingTransaction
}
response.UnsignedTx.Bytes = txBytes
return nil
}
// CreateExportTxArgs are the arguments to CreateExportTx
type CreateExportTxArgs struct {
// ID of the address that will receive the exported funds
To ids.ShortID `json:"to"`
// Nonce of the account that pays the transaction fee
PayerNonce json.Uint64 `json:"payerNonce"`
Amount json.Uint64 `json:"amount"`
}
// CreateExportTx returns an unsigned transaction to export funds.
// The unsigned transaction must be signed with the key of [args.Payer]
func (service *Service) CreateExportTx(_ *http.Request, args *CreateExportTxArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.createExportTx called")
// Create the transaction
tx := ExportTx{UnsignedExportTx: UnsignedExportTx{
NetworkID: service.vm.Ctx.NetworkID,
Nonce: uint64(args.PayerNonce),
Outs: []*ava.TransferableOutput{&ava.TransferableOutput{
Asset: ava.Asset{ID: service.vm.ava},
Out: &secp256k1fx.TransferOutput{
Amt: uint64(args.Amount),
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{args.To},
},
},
}},
}}
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil {
return errCreatingTransaction
}
response.UnsignedTx.Bytes = txBytes
return nil
}
/* /*
****************************************************** ******************************************************
**************** Sign/Issue Txs ********************** **************** Sign/Issue Txs **********************
@ -606,7 +674,7 @@ type SignArgs struct {
// SignResponse is the response from Sign // SignResponse is the response from Sign
type SignResponse struct { type SignResponse struct {
// The signed bytes // The signed bytes
Tx formatting.CB58 Tx formatting.CB58 `json:"tx"`
} }
// Sign [args.bytes] // Sign [args.bytes]
@ -642,6 +710,8 @@ func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignRespons
genTx.Tx, err = service.signAddNonDefaultSubnetValidatorTx(tx, key) genTx.Tx, err = service.signAddNonDefaultSubnetValidatorTx(tx, key)
case *CreateSubnetTx: case *CreateSubnetTx:
genTx.Tx, err = service.signCreateSubnetTx(tx, key) genTx.Tx, err = service.signCreateSubnetTx(tx, key)
case *ExportTx:
genTx.Tx, err = service.signExportTx(tx, key)
default: default:
err = errors.New("Could not parse given tx. Must be one of: addDefaultSubnetValidatorTx, addNonDefaultSubnetValidatorTx, createSubnetTx") err = errors.New("Could not parse given tx. Must be one of: addDefaultSubnetValidatorTx, addNonDefaultSubnetValidatorTx, createSubnetTx")
} }
@ -722,6 +792,29 @@ func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.Priva
return tx, nil return tx, nil
} }
// Sign [xt] with [key]
func (service *Service) signExportTx(tx *ExportTx, key *crypto.PrivateKeySECP256K1R) (*ExportTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedExportTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
}
sig, err := key.Sign(unsignedTxBytes)
if err != nil {
return nil, errors.New("error while signing")
}
if len(sig) != crypto.SECP256K1RSigLen {
return nil, fmt.Errorf("expected signature to be length %d but was length %d", crypto.SECP256K1RSigLen, len(sig))
}
copy(tx.Sig[:], sig)
return tx, nil
}
// Signs an unsigned or partially signed addNonDefaultSubnetValidatorTx with [key] // Signs an unsigned or partially signed addNonDefaultSubnetValidatorTx with [key]
// If [key] is a control key for the subnet and there is an empty spot in tx.ControlSigs, signs there // If [key] is a control key for the subnet and there is an empty spot in tx.ControlSigs, signs there
// If [key] is a control key for the subnet and there is no empty spot in tx.ControlSigs, signs as payer // If [key] is a control key for the subnet and there is no empty spot in tx.ControlSigs, signs as payer
@ -775,6 +868,141 @@ func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubn
return tx, nil return tx, nil
} }
// CreateImportTxArgs are the arguments to CreateImportTx
type CreateImportTxArgs struct {
// Addresses that can be used to sign the import
ImportAddresses []ids.ShortID `json:"importAddresses"`
// ID of the account that will receive the imported funds, and pay the
// import fee
AccountID ids.ShortID `json:"accountID"`
// Nonce of the account that pays the transaction fee
PayerNonce json.Uint64 `json:"payerNonce"`
// User that controls the Addresses
Username string `json:"username"`
Password string `json:"password"`
}
// CreateImportTx returns an unsigned transaction to import funds.
// The unsigned transaction must be signed with the key of [args.Payer]
func (service *Service) CreateImportTx(_ *http.Request, args *CreateImportTxArgs, response *SignResponse) error {
service.vm.Ctx.Log.Debug("platform.createImportTx called")
// Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil {
return fmt.Errorf("couldn't get data for user '%s'. Does user exist?", args.Username)
}
user := user{db: db}
kc := secp256k1fx.NewKeychain()
for _, addr := range args.ImportAddresses {
key, err := user.getKey(addr)
if err != nil {
return errDB
}
kc.Add(key)
}
key, err := user.getKey(args.AccountID)
if err != nil {
return errDB
}
kc.Add(key)
addrs := ids.Set{}
for _, addr := range args.ImportAddresses {
addrs.Add(ids.NewID(hashing.ComputeHash256Array(addr.Bytes())))
}
utxos, err := service.vm.GetAtomicUTXOs(addrs)
if err != nil {
return fmt.Errorf("problem retrieving user's atomic UTXOs: %w", err)
}
amount := uint64(0)
time := service.vm.clock.Unix()
ins := []*ava.TransferableInput{}
keys := [][]*crypto.PrivateKeySECP256K1R{}
for _, utxo := range utxos {
if !utxo.AssetID().Equals(service.vm.ava) {
continue
}
inputIntf, signers, err := kc.Spend(utxo.Out, time)
if err != nil {
continue
}
input, ok := inputIntf.(ava.Transferable)
if !ok {
continue
}
spent, err := math.Add64(amount, input.Amount())
if err != nil {
return err
}
amount = spent
in := &ava.TransferableInput{
UTXOID: utxo.UTXOID,
Asset: ava.Asset{ID: service.vm.ava},
In: input,
}
ins = append(ins, in)
keys = append(keys, signers)
}
ava.SortTransferableInputsWithSigners(ins, keys)
// Create the transaction
tx := ImportTx{UnsignedImportTx: UnsignedImportTx{
NetworkID: service.vm.Ctx.NetworkID,
Nonce: uint64(args.PayerNonce),
Account: args.AccountID,
Ins: ins,
}}
// TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedImportTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return fmt.Errorf("error serializing unsigned tx: %w", err)
}
hash := hashing.ComputeHash256(unsignedTxBytes)
sig, err := key.SignHash(unsignedTxBytes)
if err != nil {
return errors.New("error while signing")
}
copy(tx.Sig[:], sig)
for _, credKeys := range keys {
cred := &secp256k1fx.Credential{}
for _, key := range credKeys {
sig, err := key.SignHash(hash)
if err != nil {
return fmt.Errorf("problem creating transaction: %w", err)
}
fixedSig := [crypto.SECP256K1RSigLen]byte{}
copy(fixedSig[:], sig)
cred.Sigs = append(cred.Sigs, fixedSig)
}
tx.Creds = append(tx.Creds, cred)
}
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil {
return errCreatingTransaction
}
response.Tx.Bytes = txBytes
return nil
}
// IssueTxArgs are the arguments to IssueTx // IssueTxArgs are the arguments to IssueTx
type IssueTxArgs struct { type IssueTxArgs struct {
// Tx being sent to the network // Tx being sent to the network
@ -800,69 +1028,25 @@ func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *Is
return fmt.Errorf("error initializing tx: %s", err) return fmt.Errorf("error initializing tx: %s", err)
} }
service.vm.unissuedEvents.Push(tx) service.vm.unissuedEvents.Push(tx)
defer service.vm.resetTimer()
response.TxID = tx.ID() response.TxID = tx.ID()
return nil case DecisionTx:
case *CreateSubnetTx:
if err := tx.initialize(service.vm); err != nil { if err := tx.initialize(service.vm); err != nil {
return fmt.Errorf("error initializing tx: %s", err) return fmt.Errorf("error initializing tx: %s", err)
} }
service.vm.unissuedDecisionTxs = append(service.vm.unissuedDecisionTxs, tx) service.vm.unissuedDecisionTxs = append(service.vm.unissuedDecisionTxs, tx)
defer service.vm.resetTimer() response.TxID = tx.ID()
response.TxID = tx.ID case AtomicTx:
return nil if err := tx.initialize(service.vm); err != nil {
return fmt.Errorf("error initializing tx: %s", err)
}
service.vm.unissuedAtomicTxs = append(service.vm.unissuedAtomicTxs, tx)
response.TxID = tx.ID()
default: default:
return errors.New("Could not parse given tx. Must be one of: addDefaultSubnetValidatorTx, addDefaultSubnetDelegatorTx, addNonDefaultSubnetValidatorTx, createSubnetTx") return errors.New("Could not parse given tx. Must be a TimedTx, DecisionTx, or AtomicTx")
}
}
/*
******************************************************
**************** Create a Subnet *********************
******************************************************
*/
// CreateSubnetArgs are the arguments to CreateSubnet
type CreateSubnetArgs struct {
// The ID member of APISubnet is ignored
APISubnet
// Nonce of the account that pays the transaction fee
PayerNonce json.Uint64 `json:"payerNonce"`
}
// CreateSubnetResponse is the response from a call to CreateSubnet
type CreateSubnetResponse struct {
// Byte representation of the unsigned transaction to create a new subnet
UnsignedTx formatting.CB58 `json:"unsignedTx"`
}
// CreateSubnet returns an unsigned transaction to create a new subnet.
// The unsigned transaction must be signed with the key of [args.Payer]
func (service *Service) CreateSubnet(_ *http.Request, args *CreateSubnetArgs, response *CreateSubnetResponse) error {
service.vm.Ctx.Log.Debug("platform.createSubnet called")
// Create the transaction
tx := CreateSubnetTx{
UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
NetworkID: service.vm.Ctx.NetworkID,
Nonce: uint64(args.PayerNonce),
ControlKeys: args.ControlKeys,
Threshold: uint16(args.Threshold),
},
key: nil,
Sig: [65]byte{},
bytes: nil,
} }
txBytes, err := Codec.Marshal(genericTx{Tx: &tx}) service.vm.resetTimer()
if err != nil {
return errCreatingTransaction
}
response.UnsignedTx.Bytes = txBytes
return nil return nil
} }
/* /*
@ -1028,3 +1212,176 @@ func (service *Service) chainExists(blockID ids.ID, chainID ids.ID) (bool, error
return false, nil return false, nil
} }
// // SendExportArgs are arguments for passing into SendExport requests
// type SendExportArgs struct {
// Username string `json:"username"`
// Password string `json:"password"`
// Amount json.Uint64 `json:"amount"`
// To string `json:"to"`
// }
// // SendExportReply defines the Send replies returned from the API
// type SendExportReply struct {
// TxID ids.ID `json:"txID"`
// }
// // SendExport returns the ID of the newly created atomic transaction
// func (service *Service) SendExport(_ *http.Request, args *SendExportArgs, reply *SendExportReply) error {
// service.vm.ctx.Log.Verbo("SendExport called with username: %s", args.Username)
// if args.Amount == 0 {
// return errInvalidAmount
// }
// toBytes, err := service.vm.Parse(args.To)
// if err != nil {
// return fmt.Errorf("problem parsing to address: %w", err)
// }
// to, err := ids.ToShortID(toBytes)
// if err != nil {
// return fmt.Errorf("problem parsing to address: %w", err)
// }
// db, err := service.vm.ctx.Keystore.GetDatabase(args.Username, args.Password)
// if err != nil {
// return fmt.Errorf("problem retrieving user: %w", err)
// }
// user := userState{vm: service.vm}
// addresses, _ := user.Addresses(db)
// addrs := ids.Set{}
// addrs.Add(addresses...)
// utxos, err := service.vm.GetUTXOs(addrs)
// if err != nil {
// return fmt.Errorf("problem retrieving user's UTXOs: %w", err)
// }
// kc := secp256k1fx.NewKeychain()
// for _, addr := range addresses {
// sk, err := user.Key(db, addr)
// if err != nil {
// return fmt.Errorf("problem retrieving private key: %w", err)
// }
// kc.Add(sk)
// }
// amountSpent := uint64(0)
// time := service.vm.clock.Unix()
// ins := []*ava.TransferableInput{}
// keys := [][]*crypto.PrivateKeySECP256K1R{}
// for _, utxo := range utxos {
// if !utxo.AssetID().Equals(service.vm.ava) {
// continue
// }
// inputIntf, signers, err := kc.Spend(utxo.Out, time)
// if err != nil {
// continue
// }
// input, ok := inputIntf.(ava.Transferable)
// if !ok {
// continue
// }
// spent, err := math.Add64(amountSpent, input.Amount())
// if err != nil {
// return errSpendOverflow
// }
// amountSpent = spent
// in := &ava.TransferableInput{
// UTXOID: utxo.UTXOID,
// Asset: ava.Asset{ID: service.vm.ava},
// In: input,
// }
// ins = append(ins, in)
// keys = append(keys, signers)
// if amountSpent >= uint64(args.Amount) {
// break
// }
// }
// if amountSpent < uint64(args.Amount) {
// return errInsufficientFunds
// }
// ava.SortTransferableInputsWithSigners(ins, keys)
// exportOuts := []*ava.TransferableOutput{&ava.TransferableOutput{
// Asset: ava.Asset{ID: service.vm.ava},
// Out: &secp256k1fx.TransferOutput{
// Amt: uint64(args.Amount),
// Locktime: 0,
// OutputOwners: secp256k1fx.OutputOwners{
// Threshold: 1,
// Addrs: []ids.ShortID{to},
// },
// },
// }}
// outs := []*ava.TransferableOutput{}
// if amountSpent > uint64(args.Amount) {
// changeAddr := kc.Keys[0].PublicKey().Address()
// outs = append(outs, &ava.TransferableOutput{
// Asset: ava.Asset{ID: service.vm.ava},
// Out: &secp256k1fx.TransferOutput{
// Amt: amountSpent - uint64(args.Amount),
// Locktime: 0,
// OutputOwners: secp256k1fx.OutputOwners{
// Threshold: 1,
// Addrs: []ids.ShortID{changeAddr},
// },
// },
// })
// }
// ava.SortTransferableOutputs(outs, service.vm.codec)
// tx := Tx{UnsignedTx: &ExportTx{
// BaseTx: BaseTx{
// NetID: service.vm.ctx.NetworkID,
// BCID: service.vm.ctx.ChainID,
// Outs: outs,
// Ins: ins,
// },
// Outs: exportOuts,
// }}
// unsignedBytes, err := service.vm.codec.Marshal(&tx.UnsignedTx)
// if err != nil {
// return fmt.Errorf("problem creating transaction: %w", err)
// }
// hash := hashing.ComputeHash256(unsignedBytes)
// for _, credKeys := range keys {
// cred := &secp256k1fx.Credential{}
// for _, key := range credKeys {
// sig, err := key.SignHash(hash)
// if err != nil {
// return fmt.Errorf("problem creating transaction: %w", err)
// }
// fixedSig := [crypto.SECP256K1RSigLen]byte{}
// copy(fixedSig[:], sig)
// cred.Sigs = append(cred.Sigs, fixedSig)
// }
// tx.Creds = append(tx.Creds, cred)
// }
// b, err := service.vm.codec.Marshal(tx)
// if err != nil {
// return fmt.Errorf("problem creating transaction: %w", err)
// }
// txID, err := service.vm.IssueTx(b, nil)
// if err != nil {
// return fmt.Errorf("problem issuing transaction: %w", err)
// }
// reply.TxID = txID
// return nil
// }

View File

@ -12,6 +12,8 @@ import (
// DecisionTx is an operation that can be decided without being proposed // DecisionTx is an operation that can be decided without being proposed
type DecisionTx interface { type DecisionTx interface {
ID() ids.ID
initialize(vm *VM) error initialize(vm *VM) error
// Attempt to verify this transaction with the provided state. The provided // Attempt to verify this transaction with the provided state. The provided

View File

@ -211,18 +211,18 @@ func (vm *VM) getSubnets(db database.Database) ([]*CreateSubnetTx, error) {
} }
// get the subnet with the specified ID // get the subnet with the specified ID
func (vm *VM) getSubnet(db database.Database, ID ids.ID) (*CreateSubnetTx, error) { func (vm *VM) getSubnet(db database.Database, id ids.ID) (*CreateSubnetTx, error) {
subnets, err := vm.getSubnets(db) subnets, err := vm.getSubnets(db)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, subnet := range subnets { for _, subnet := range subnets {
if subnet.ID.Equals(ID) { if subnet.id.Equals(id) {
return subnet, nil return subnet, nil
} }
} }
return nil, fmt.Errorf("couldn't find subnet with ID %s", ID) return nil, fmt.Errorf("couldn't find subnet with ID %s", id)
} }
// register each type that we'll be storing in the database // register each type that we'll be storing in the database

View File

@ -24,6 +24,7 @@ import (
"github.com/ava-labs/gecko/utils/timer" "github.com/ava-labs/gecko/utils/timer"
"github.com/ava-labs/gecko/utils/units" "github.com/ava-labs/gecko/utils/units"
"github.com/ava-labs/gecko/utils/wrappers" "github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/components/core" "github.com/ava-labs/gecko/vms/components/core"
"github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/gecko/vms/secp256k1fx"
@ -632,7 +633,7 @@ func (vm *VM) nextValidatorChangeTime(db database.Database, start bool) time.Tim
return earliest return earliest
} }
for _, subnet := range subnets { for _, subnet := range subnets {
t := vm.nextSubnetValidatorChangeTime(db, subnet.ID, start) t := vm.nextSubnetValidatorChangeTime(db, subnet.id, start)
if t.Before(earliest) { if t.Before(earliest) {
earliest = t earliest = t
} }
@ -740,3 +741,28 @@ func (vm *VM) Codec() codec.Codec { return vm.codec }
// Clock ... // Clock ...
func (vm *VM) Clock() *timer.Clock { return &vm.clock } func (vm *VM) Clock() *timer.Clock { return &vm.clock }
// GetAtomicUTXOs returns the utxos that at least one of the provided addresses is
// referenced in.
func (vm *VM) GetAtomicUTXOs(addrs ids.Set) ([]*ava.UTXO, error) {
smDB := vm.Ctx.SharedMemory.GetDatabase(vm.avm)
defer vm.Ctx.SharedMemory.ReleaseDatabase(vm.avm)
state := ava.NewPrefixedState(smDB, vm.codec)
utxoIDs := ids.Set{}
for _, addr := range addrs.List() {
utxos, _ := state.AVMFunds(addr)
utxoIDs.Add(utxos...)
}
utxos := []*ava.UTXO{}
for _, utxoID := range utxoIDs.List() {
utxo, err := state.AVMUTXO(utxoID)
if err != nil {
return nil, err
}
utxos = append(utxos, utxo)
}
return utxos, nil
}

View File

@ -153,7 +153,7 @@ func defaultVM() *VM {
&EventHeap{ &EventHeap{
SortByStartTime: false, SortByStartTime: false,
}, },
tx.ID, tx.id,
) )
if err != nil { if err != nil {
panic(err) panic(err)
@ -163,7 +163,7 @@ func defaultVM() *VM {
&EventHeap{ &EventHeap{
SortByStartTime: true, SortByStartTime: true,
}, },
tx.ID, tx.id,
) )
if err != nil { if err != nil {
panic(err) panic(err)
@ -437,7 +437,7 @@ func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
uint64(startTime.Unix()), uint64(startTime.Unix()),
uint64(endTime.Unix()), uint64(endTime.Unix()),
keys[0].PublicKey().Address(), keys[0].PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
keys[0], keys[0],
@ -482,7 +482,7 @@ func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
commit.Accept() // accept the proposal commit.Accept() // accept the proposal
// Verify that new validator is in pending validator set // Verify that new validator is in pending validator set
pendingValidators, err := vm.getPendingValidators(vm.DB, testSubnet1.ID) pendingValidators, err := vm.getPendingValidators(vm.DB, testSubnet1.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -510,7 +510,7 @@ func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
uint64(startTime.Unix()), uint64(startTime.Unix()),
uint64(endTime.Unix()), uint64(endTime.Unix()),
keys[0].PublicKey().Address(), keys[0].PublicKey().Address(),
testSubnet1.ID, testSubnet1.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]}, []*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[1], testSubnet1ControlKeys[2]},
keys[0], keys[0],
@ -555,7 +555,7 @@ func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
abort.Accept() // reject the proposal abort.Accept() // reject the proposal
// Verify that new validator NOT in pending validator set // Verify that new validator NOT in pending validator set
pendingValidators, err := vm.getPendingValidators(vm.DB, testSubnet1.ID) pendingValidators, err := vm.getPendingValidators(vm.DB, testSubnet1.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -885,7 +885,7 @@ func TestCreateSubnet(t *testing.T) {
uint64(startTime.Unix()), uint64(startTime.Unix()),
uint64(endTime.Unix()), uint64(endTime.Unix()),
keys[0].PublicKey().Address(), keys[0].PublicKey().Address(),
createSubnetTx.ID, createSubnetTx.id,
testNetworkID, testNetworkID,
[]*crypto.PrivateKeySECP256K1R{keys[0]}, []*crypto.PrivateKeySECP256K1R{keys[0]},
keys[0], keys[0],
@ -935,7 +935,7 @@ func TestCreateSubnet(t *testing.T) {
commit.Accept() // add the validator to pending validator set commit.Accept() // add the validator to pending validator set
// Verify validator is in pending validator set // Verify validator is in pending validator set
pendingValidators, err := vm.getPendingValidators(vm.DB, createSubnetTx.ID) pendingValidators, err := vm.getPendingValidators(vm.DB, createSubnetTx.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -989,7 +989,7 @@ func TestCreateSubnet(t *testing.T) {
// Verify validator no longer in pending validator set // Verify validator no longer in pending validator set
// Verify validator is in pending validator set // Verify validator is in pending validator set
pendingValidators, err = vm.getPendingValidators(vm.DB, createSubnetTx.ID) pendingValidators, err = vm.getPendingValidators(vm.DB, createSubnetTx.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -998,7 +998,7 @@ func TestCreateSubnet(t *testing.T) {
} }
// Verify validator is in current validator set // Verify validator is in current validator set
currentValidators, err := vm.getCurrentValidators(vm.DB, createSubnetTx.ID) currentValidators, err := vm.getCurrentValidators(vm.DB, createSubnetTx.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1048,14 +1048,14 @@ func TestCreateSubnet(t *testing.T) {
commit.Accept() // remove validator from current validator set commit.Accept() // remove validator from current validator set
// pending validators and current validator should be empty // pending validators and current validator should be empty
pendingValidators, err = vm.getPendingValidators(vm.DB, createSubnetTx.ID) pendingValidators, err = vm.getPendingValidators(vm.DB, createSubnetTx.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if pendingValidators.Len() != 0 { if pendingValidators.Len() != 0 {
t.Fatal("pending validator set should be empty") t.Fatal("pending validator set should be empty")
} }
currentValidators, err = vm.getCurrentValidators(vm.DB, createSubnetTx.ID) currentValidators, err = vm.getCurrentValidators(vm.DB, createSubnetTx.id)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }