Implemented Properties

This commit is contained in:
StephenButtolph 2020-03-30 17:46:18 -04:00
parent a2c0ad56f7
commit 00332620a6
13 changed files with 753 additions and 0 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/ava-labs/gecko/vms/evm"
"github.com/ava-labs/gecko/vms/nftfx"
"github.com/ava-labs/gecko/vms/platformvm"
"github.com/ava-labs/gecko/vms/propertyfx"
"github.com/ava-labs/gecko/vms/secp256k1fx"
"github.com/ava-labs/gecko/vms/spchainvm"
"github.com/ava-labs/gecko/vms/spdagvm"
@ -160,6 +161,7 @@ func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases
timestampvm.ID.Key(): []string{"timestamp"},
secp256k1fx.ID.Key(): []string{"secp256k1fx"},
nftfx.ID.Key(): []string{"nftfx"},
propertyfx.ID.Key(): []string{"propertyfx"},
}
genesisBytes := Genesis(networkID)
@ -353,6 +355,7 @@ func Genesis(networkID uint32) []byte {
FxIDs: []ids.ID{
secp256k1fx.ID,
nftfx.ID,
propertyfx.ID,
},
Name: "X-Chain",
},

View File

@ -41,6 +41,7 @@ import (
"github.com/ava-labs/gecko/vms/evm"
"github.com/ava-labs/gecko/vms/nftfx"
"github.com/ava-labs/gecko/vms/platformvm"
"github.com/ava-labs/gecko/vms/propertyfx"
"github.com/ava-labs/gecko/vms/secp256k1fx"
"github.com/ava-labs/gecko/vms/spchainvm"
"github.com/ava-labs/gecko/vms/spdagvm"
@ -337,6 +338,7 @@ func (n *Node) initVMManager() {
n.vmManager.RegisterVMFactory(timestampvm.ID, &timestampvm.Factory{})
n.vmManager.RegisterVMFactory(secp256k1fx.ID, &secp256k1fx.Factory{})
n.vmManager.RegisterVMFactory(nftfx.ID, &nftfx.Factory{})
n.vmManager.RegisterVMFactory(propertyfx.ID, &propertyfx.Factory{})
}
// Create the EventDispatcher used for hooking events

View File

@ -0,0 +1,14 @@
package propertyfx
import (
"github.com/ava-labs/gecko/vms/components/verify"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
// BurnOperation ...
type BurnOperation struct {
secp256k1fx.Input `serialize:"true"`
}
// Outs ...
func (op *BurnOperation) Outs() []verify.Verifiable { return nil }

View File

@ -0,0 +1,23 @@
package propertyfx
import (
"testing"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
func TestBurnOperationInvalid(t *testing.T) {
op := BurnOperation{Input: secp256k1fx.Input{
SigIndices: []uint32{1, 0},
}}
if err := op.Verify(); err == nil {
t.Fatalf("operation should have failed verification")
}
}
func TestBurnOperationNumberOfOutput(t *testing.T) {
op := BurnOperation{}
if outs := op.Outs(); len(outs) != 0 {
t.Fatalf("wrong number of outputs")
}
}

View File

@ -0,0 +1,10 @@
package propertyfx
import (
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
// Credential ...
type Credential struct {
secp256k1fx.Credential `serialize:"true"`
}

16
vms/propertyfx/factory.go Normal file
View File

@ -0,0 +1,16 @@
package propertyfx
import (
"github.com/ava-labs/gecko/ids"
)
// ID that this Fx uses when labeled
var (
ID = ids.NewID([32]byte{'p', 'r', 'o', 'p', 'e', 'r', 't', 'y', 'f', 'x'})
)
// Factory ...
type Factory struct{}
// New ...
func (f *Factory) New() interface{} { return &Fx{} }

View File

@ -0,0 +1,12 @@
package propertyfx
import (
"testing"
)
func TestFactory(t *testing.T) {
factory := Factory{}
if fx := factory.New(); fx == nil {
t.Fatalf("Factory.New returned nil")
}
}

109
vms/propertyfx/fx.go Normal file
View File

@ -0,0 +1,109 @@
package propertyfx
import (
"errors"
"github.com/ava-labs/gecko/utils/wrappers"
"github.com/ava-labs/gecko/vms/components/verify"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
var (
errWrongTxType = errors.New("wrong tx type")
errWrongUTXOType = errors.New("wrong utxo type")
errWrongOperationType = errors.New("wrong operation type")
errWrongCredentialType = errors.New("wrong credential type")
errNoUTXOs = errors.New("an operation must consume at least one UTXO")
errWrongNumberOfUTXOs = errors.New("wrong number of UTXOs for the operation")
errWrongNumberOfCreds = errors.New("wrong number of credentials for the operation")
errWrongMintOutput = errors.New("wrong mint output provided")
errCantTransfer = errors.New("cant transfer with this fx")
)
// Fx ...
type Fx struct{ secp256k1fx.Fx }
// Initialize ...
func (fx *Fx) Initialize(vmIntf interface{}) error {
if err := fx.InitializeVM(vmIntf); err != nil {
return err
}
log := fx.VM.Logger()
log.Debug("Initializing nft fx")
c := fx.VM.Codec()
errs := wrappers.Errs{}
errs.Add(
c.RegisterType(&MintOutput{}),
c.RegisterType(&OwnedOutput{}),
c.RegisterType(&MintOperation{}),
c.RegisterType(&BurnOperation{}),
c.RegisterType(&Credential{}),
)
return errs.Err
}
// VerifyOperation ...
func (fx *Fx) VerifyOperation(txIntf, opIntf, credIntf interface{}, utxosIntf []interface{}) error {
tx, ok := txIntf.(secp256k1fx.Tx)
switch {
case !ok:
return errWrongTxType
case len(utxosIntf) != 1:
return errWrongNumberOfUTXOs
}
cred, ok := credIntf.(*Credential)
if !ok {
return errWrongCredentialType
}
switch op := opIntf.(type) {
case *MintOperation:
return fx.VerifyMintOperation(tx, op, cred, utxosIntf[0])
case *BurnOperation:
return fx.VerifyTransferOperation(tx, op, cred, utxosIntf[0])
default:
return errWrongOperationType
}
}
// VerifyMintOperation ...
func (fx *Fx) VerifyMintOperation(tx secp256k1fx.Tx, op *MintOperation, cred *Credential, utxoIntf interface{}) error {
out, ok := utxoIntf.(*MintOutput)
if !ok {
return errWrongUTXOType
}
if err := verify.All(op, cred, out); err != nil {
return err
}
switch {
case !out.OutputOwners.Equals(&op.MintOutput.OutputOwners):
return errWrongMintOutput
default:
return fx.Fx.VerifyCredentials(tx, &op.MintInput, &cred.Credential, &out.OutputOwners)
}
}
// VerifyTransferOperation ...
func (fx *Fx) VerifyTransferOperation(tx secp256k1fx.Tx, op *BurnOperation, cred *Credential, utxoIntf interface{}) error {
out, ok := utxoIntf.(*OwnedOutput)
if !ok {
return errWrongUTXOType
}
if err := verify.All(op, cred, out); err != nil {
return err
}
return fx.VerifyCredentials(tx, &op.Input, &cred.Credential, &out.OutputOwners)
}
// VerifyTransfer ...
func (fx *Fx) VerifyTransfer(_, _, _, _ interface{}) error { return errCantTransfer }

473
vms/propertyfx/fx_test.go Normal file
View File

@ -0,0 +1,473 @@
package propertyfx
import (
"testing"
"time"
"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/utils/logging"
"github.com/ava-labs/gecko/utils/timer"
"github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
var (
txBytes = []byte{0, 1, 2, 3, 4, 5}
sigBytes = [crypto.SECP256K1RSigLen]byte{
0x0e, 0x33, 0x4e, 0xbc, 0x67, 0xa7, 0x3f, 0xe8,
0x24, 0x33, 0xac, 0xa3, 0x47, 0x88, 0xa6, 0x3d,
0x58, 0xe5, 0x8e, 0xf0, 0x3a, 0xd5, 0x84, 0xf1,
0xbc, 0xa3, 0xb2, 0xd2, 0x5d, 0x51, 0xd6, 0x9b,
0x0f, 0x28, 0x5d, 0xcd, 0x3f, 0x71, 0x17, 0x0a,
0xf9, 0xbf, 0x2d, 0xb1, 0x10, 0x26, 0x5c, 0xe9,
0xdc, 0xc3, 0x9d, 0x7a, 0x01, 0x50, 0x9d, 0xe8,
0x35, 0xbd, 0xcb, 0x29, 0x3a, 0xd1, 0x49, 0x32,
0x00,
}
addrBytes = [hashing.AddrLen]byte{
0x01, 0x5c, 0xce, 0x6c, 0x55, 0xd6, 0xb5, 0x09,
0x84, 0x5c, 0x8c, 0x4e, 0x30, 0xbe, 0xd9, 0x8d,
0x39, 0x1a, 0xe7, 0xf0,
}
)
func TestFxInitialize(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
fx := Fx{}
err := fx.Initialize(&vm)
if err != nil {
t.Fatal(err)
}
}
func TestFxInitializeInvalid(t *testing.T) {
fx := Fx{}
err := fx.Initialize(nil)
if err == nil {
t.Fatalf("Should have returned an error")
}
}
func TestFxVerifyMintOperation(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
MintOutput: MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}},
}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, cred, utxos); err != nil {
t.Fatal(err)
}
}
func TestFxVerifyMintOperationWrongTx(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(nil, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid tx")
}
}
func TestFxVerifyMintOperationWrongNumberUTXOs(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to not enough utxos")
}
}
func TestFxVerifyMintOperationWrongCredential(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
utxo := &MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, nil, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to a bad credential")
}
}
func TestFxVerifyMintOperationInvalidUTXO(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{nil}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid utxo")
}
}
func TestFxVerifyMintOperationFailingVerification(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
ids.ShortEmpty,
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid utxo output")
}
}
func TestFxVerifyMintOperationInvalidGroupID(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &MintOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &MintOperation{
MintInput: secp256k1fx.Input{
SigIndices: []uint32{0},
},
}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid mint output")
}
}
func TestFxVerifyTransferOperation(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &OwnedOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &BurnOperation{Input: secp256k1fx.Input{
SigIndices: []uint32{0},
}}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, cred, utxos); err != nil {
t.Fatal(err)
}
}
func TestFxVerifyTransferOperationWrongUTXO(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
op := &BurnOperation{Input: secp256k1fx.Input{
SigIndices: []uint32{0},
}}
utxos := []interface{}{nil}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid utxo")
}
}
func TestFxVerifyTransferOperationFailedVerify(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &OwnedOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
op := &BurnOperation{Input: secp256k1fx.Input{
SigIndices: []uint32{1, 0},
}}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, op, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an invalid utxo output")
}
}
func TestFxVerifyOperationUnknownOperation(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
tx := &secp256k1fx.TestTx{
Bytes: txBytes,
}
cred := &Credential{Credential: secp256k1fx.Credential{
Sigs: [][crypto.SECP256K1RSigLen]byte{
sigBytes,
},
}}
utxo := &OwnedOutput{OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{
ids.NewShortID(addrBytes),
},
}}
utxos := []interface{}{utxo}
if err := fx.VerifyOperation(tx, nil, cred, utxos); err == nil {
t.Fatalf("VerifyOperation should have errored due to an unknown operation")
}
}
func TestFxVerifyTransfer(t *testing.T) {
vm := secp256k1fx.TestVM{
CLK: new(timer.Clock),
Code: codec.NewDefault(),
Log: logging.NoLog{},
}
date := time.Date(2019, time.January, 19, 16, 25, 17, 3, time.UTC)
vm.CLK.Set(date)
fx := Fx{}
if err := fx.Initialize(&vm); err != nil {
t.Fatal(err)
}
if err := fx.VerifyTransfer(nil, nil, nil, nil); err == nil {
t.Fatalf("this Fx doesn't support transfers")
}
}

View File

@ -0,0 +1,37 @@
package propertyfx
import (
"errors"
"github.com/ava-labs/gecko/vms/components/verify"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
var (
errNilMintOperation = errors.New("nil mint operation")
)
// MintOperation ...
type MintOperation struct {
MintInput secp256k1fx.Input `serialize:"true"`
MintOutput MintOutput `serialize:"true"`
OwnedOutput OwnedOutput `serialize:"true"`
}
// Outs ...
func (op *MintOperation) Outs() []verify.Verifiable {
return []verify.Verifiable{
&op.MintOutput,
&op.OwnedOutput,
}
}
// Verify ...
func (op *MintOperation) Verify() error {
switch {
case op == nil:
return errNilMintOperation
default:
return verify.All(&op.MintInput, &op.MintOutput, &op.OwnedOutput)
}
}

View File

@ -0,0 +1,34 @@
package propertyfx
import (
"testing"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
func TestMintOperationVerifyNil(t *testing.T) {
op := (*MintOperation)(nil)
if err := op.Verify(); err == nil {
t.Fatalf("nil operation should have failed verification")
}
}
func TestMintOperationVerifyInvalidOutput(t *testing.T) {
op := MintOperation{
OwnedOutput: OwnedOutput{
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
},
},
}
if err := op.Verify(); err == nil {
t.Fatalf("operation should have failed verification")
}
}
func TestMintOperationOuts(t *testing.T) {
op := MintOperation{}
if outs := op.Outs(); len(outs) != 2 {
t.Fatalf("Wrong number of outputs returned")
}
}

View File

@ -0,0 +1,10 @@
package propertyfx
import (
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
// MintOutput ...
type MintOutput struct {
secp256k1fx.OutputOwners `serialize:"true"`
}

View File

@ -0,0 +1,10 @@
package propertyfx
import (
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
// OwnedOutput ...
type OwnedOutput struct {
secp256k1fx.OutputOwners `serialize:"true"`
}