Skip to content

Commit

Permalink
api: v2alpha1: Add vesting, vault and drain vault contents to Transac…
Browse files Browse the repository at this point in the history
…tion (#6112)
  • Loading branch information
kacpersaw committed Jul 10, 2024
1 parent c325a63 commit 23a0e15
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 12 deletions.
70 changes: 61 additions & 9 deletions api/grpcserver/v2alpha1/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,11 @@ func (s *TransactionService) ParseTransaction(
t.MaxGas = header.MaxGas
t.GasPrice = header.GasPrice
t.MaxSpend = header.MaxSpend
contents, err := toTxContents(raw.Raw)
contents, txType, err := toTxContents(raw.Raw)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
t.Type = txType
t.Contents = contents
}

Expand Down Expand Up @@ -329,11 +330,12 @@ func toTx(tx *types.MeshTransaction, result *types.TransactionResult,
t.GasPrice = tx.GasPrice
t.MaxSpend = tx.MaxSpend

contents, err := toTxContents(tx.Raw)
contents, txType, err := toTxContents(tx.Raw)
if err != nil {
return nil
}
t.Contents = contents
t.Type = txType
}

if includeResult && result != nil {
Expand Down Expand Up @@ -412,12 +414,15 @@ func decodeTxArgs(decoder *scale.Decoder) (uint8, *core.Address, scale.Encodable

var templateAddress *core.Address
var handler core.Handler
if method == core.MethodSpawn {
switch method {
case core.MethodSpawn:
templateAddress = &core.Address{}
if _, err := templateAddress.DecodeScale(decoder); err != nil {
return 0, nil, nil, fmt.Errorf("%w failed to decode template address %w", core.ErrMalformed, err)
}
} else {
case vesting.MethodDrainVault:
templateAddress = &vesting.TemplateAddress
default:
templateAddress = &wallet.TemplateAddress
}

Expand All @@ -442,12 +447,18 @@ func decodeTxArgs(decoder *scale.Decoder) (uint8, *core.Address, scale.Encodable
return method, templateAddress, args, nil
}

func toTxContents(rawTx []byte) (*spacemeshv2alpha1.TransactionContents, error) {
method, template, txArgs, err := decodeTxArgs(scale.NewDecoder(bytes.NewReader(rawTx)))
func toTxContents(rawTx []byte) (*spacemeshv2alpha1.TransactionContents,
spacemeshv2alpha1.Transaction_TransactionType, error,
) {
res := &spacemeshv2alpha1.TransactionContents{}
txType := spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_UNSPECIFIED

r := bytes.NewReader(rawTx)
method, template, txArgs, err := decodeTxArgs(scale.NewDecoder(r))
if err != nil {
return nil, err
return res, txType, err
}
res := &spacemeshv2alpha1.TransactionContents{}

switch method {
case core.MethodSpawn:
switch *template {
Expand All @@ -458,6 +469,7 @@ func toTxContents(rawTx []byte) (*spacemeshv2alpha1.TransactionContents, error)
Pubkey: args.PublicKey.String(),
},
}
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_SINGLE_SIG_SPAWN
case multisig.TemplateAddress:
args := txArgs.(*multisig.SpawnArguments)
contents := &spacemeshv2alpha1.TransactionContents_MultiSigSpawn{
Expand All @@ -470,6 +482,32 @@ func toTxContents(rawTx []byte) (*spacemeshv2alpha1.TransactionContents, error)
contents.MultiSigSpawn.Pubkey[i] = args.PublicKeys[i].String()
}
res.Contents = contents
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_MULTI_SIG_SPAWN
case vesting.TemplateAddress:
args := txArgs.(*multisig.SpawnArguments)
contents := &spacemeshv2alpha1.TransactionContents_VestingSpawn{
VestingSpawn: &spacemeshv2alpha1.ContentsMultiSigSpawn{
Required: uint32(args.Required),
},
}
contents.VestingSpawn.Pubkey = make([]string, len(args.PublicKeys))
for i := range args.PublicKeys {
contents.VestingSpawn.Pubkey[i] = args.PublicKeys[i].String()
}
res.Contents = contents
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_VESTING_SPAWN
case vault.TemplateAddress:
args := txArgs.(*vault.SpawnArguments)
res.Contents = &spacemeshv2alpha1.TransactionContents_VaultSpawn{
VaultSpawn: &spacemeshv2alpha1.ContentsVaultSpawn{
Owner: args.Owner.String(),
TotalAmount: args.TotalAmount,
InitialUnlockAmount: args.InitialUnlockAmount,
VestingStart: args.VestingStart.Uint32(),
VestingEnd: args.VestingEnd.Uint32(),
},
}
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_VAULT_SPAWN
}
case core.MethodSpend:
args := txArgs.(*wallet.SpendArguments)
Expand All @@ -479,7 +517,21 @@ func toTxContents(rawTx []byte) (*spacemeshv2alpha1.TransactionContents, error)
Amount: args.Amount,
},
}
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_SINGLE_SIG_SEND
if r.Len() > types.EdSignatureSize {
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_MULTI_SIG_SEND
}
case vesting.MethodDrainVault:
args := txArgs.(*vesting.DrainVaultArguments)
res.Contents = &spacemeshv2alpha1.TransactionContents_DrainVault{
DrainVault: &spacemeshv2alpha1.ContentsDrainVault{
Vault: args.Vault.String(),
Destination: args.Destination.String(),
Amount: args.Amount,
},
}
txType = spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_DRAIN_VAULT
}

return res, nil
return res, txType, nil
}
232 changes: 232 additions & 0 deletions api/grpcserver/v2alpha1/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import (
vm "github.com/spacemeshos/go-spacemesh/genvm"
"github.com/spacemeshos/go-spacemesh/genvm/core"
"github.com/spacemeshos/go-spacemesh/genvm/sdk"
multisig2 "github.com/spacemeshos/go-spacemesh/genvm/sdk/multisig"
"github.com/spacemeshos/go-spacemesh/genvm/sdk/vesting"
"github.com/spacemeshos/go-spacemesh/genvm/sdk/wallet"
"github.com/spacemeshos/go-spacemesh/genvm/templates/multisig"
"github.com/spacemeshos/go-spacemesh/genvm/templates/vault"
vesting2 "github.com/spacemeshos/go-spacemesh/genvm/templates/vesting"
pubsubmocks "github.com/spacemeshos/go-spacemesh/p2p/pubsub/mocks"
"github.com/spacemeshos/go-spacemesh/signing"
"github.com/spacemeshos/go-spacemesh/sql"
Expand Down Expand Up @@ -474,3 +479,230 @@ func newTx(nonce uint64, recipient types.Address, signer *signing.EdSigner) *typ
}
return &tx
}

func TestToTxContents(t *testing.T) {
t.Parallel()

t.Run("singlesig spawn", func(t *testing.T) {
t.Parallel()

signer, err := signing.NewEdSigner()
require.NoError(t, err)
tx := newTx(0, types.Address{}, signer)

contents, txType, err := toTxContents(tx.Raw)
require.NoError(t, err)
require.NotNil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetSend())
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_SINGLE_SIG_SPAWN, txType)
})

t.Run("singlesig send", func(t *testing.T) {
t.Parallel()

signer, err := signing.NewEdSigner()
require.NoError(t, err)
tx := newTx(1, types.Address{}, signer)

contents, txType, err := toTxContents(tx.Raw)
require.NoError(t, err)
require.NotNil(t, contents.GetSend())
require.Nil(t, contents.GetSingleSigSpawn())
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_SINGLE_SIG_SEND, txType)
})

t.Run("multisig spawn", func(t *testing.T) {
t.Parallel()

var pubs []ed25519.PublicKey
pks := make([]ed25519.PrivateKey, 0, 3)
for i := 0; i < 3; i++ {
pub, pk, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
pubs = append(pubs, pub)
pks = append(pks, pk)
}

var agg *multisig2.Aggregator
for i := 0; i < len(pks); i++ {
part := multisig2.SelfSpawn(uint8(i), pks[i], multisig.TemplateAddress, 1, pubs, types.Nonce(1))
if agg == nil {
agg = part
} else {
agg.Add(*part.Part(uint8(i)))
}
}
rawTx := agg.Raw()

contents, txType, err := toTxContents(rawTx)
require.NoError(t, err)
require.NotNil(t, contents.GetMultiSigSpawn())
require.Nil(t, contents.GetSend())
require.Nil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetVestingSpawn())
require.Nil(t, contents.GetVaultSpawn())
require.Nil(t, contents.GetDrainVault())
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_MULTI_SIG_SPAWN, txType)
})

t.Run("multisig send", func(t *testing.T) {
t.Parallel()

var pubs []ed25519.PublicKey
pks := make([]ed25519.PrivateKey, 0, 3)
for i := 0; i < 3; i++ {
pub, pk, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
pubs = append(pubs, pub)
pks = append(pks, pk)
}

to := wallet.Address(pubs[0])

var agg *multisig2.Aggregator
for i := 0; i < len(pks); i++ {
part := multisig2.Spend(uint8(i), pks[i], multisig.TemplateAddress, to, 100, types.Nonce(1))
if agg == nil {
agg = part
} else {
agg.Add(*part.Part(uint8(i)))
}
}
rawTx := agg.Raw()

contents, txType, err := toTxContents(rawTx)
require.NoError(t, err)
require.NotNil(t, contents.GetSend())
require.Nil(t, contents.GetMultiSigSpawn())
require.Nil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetVestingSpawn())
require.Nil(t, contents.GetVaultSpawn())
require.Nil(t, contents.GetDrainVault())
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_MULTI_SIG_SEND, txType)
})

t.Run("vault spawn", func(t *testing.T) {
t.Parallel()

var pubs []ed25519.PublicKey
pks := make([]ed25519.PrivateKey, 0, 3)
for i := 0; i < 3; i++ {
pub, pk, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
pubs = append(pubs, pub)
pks = append(pks, pk)
}

owner := wallet.Address(pubs[0])
vaultArgs := &vault.SpawnArguments{
Owner: owner,
InitialUnlockAmount: uint64(1000),
TotalAmount: uint64(1001),
VestingStart: 105120,
VestingEnd: 4 * 105120,
}
vaultAddr := core.ComputePrincipal(vault.TemplateAddress, vaultArgs)

var agg *multisig2.Aggregator
for i := 0; i < len(pks); i++ {
part := multisig2.Spawn(uint8(i), pks[i], vaultAddr, vault.TemplateAddress, vaultArgs, types.Nonce(0))
if agg == nil {
agg = part
} else {
agg.Add(*part.Part(uint8(i)))
}
}
rawTx := agg.Raw()

contents, txType, err := toTxContents(rawTx)
require.NoError(t, err)
require.NotNil(t, contents.GetVaultSpawn())
require.Nil(t, contents.GetMultiSigSpawn())
require.Nil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetVestingSpawn())
require.Nil(t, contents.GetSend())
require.Nil(t, contents.GetDrainVault())
require.Equal(t, vaultArgs.Owner.String(), contents.GetVaultSpawn().Owner)
require.Equal(t, vaultArgs.InitialUnlockAmount, contents.GetVaultSpawn().InitialUnlockAmount)
require.Equal(t, vaultArgs.TotalAmount, contents.GetVaultSpawn().TotalAmount)
require.Equal(t, vaultArgs.VestingStart.Uint32(), contents.GetVaultSpawn().VestingStart)
require.Equal(t, vaultArgs.VestingEnd.Uint32(), contents.GetVaultSpawn().VestingEnd)
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_VAULT_SPAWN, txType)
})

t.Run("drain vault", func(t *testing.T) {
t.Parallel()

var pubs [][]byte
pks := make([]ed25519.PrivateKey, 0, 3)
for i := 0; i < 3; i++ {
pub, pk, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
pubs = append(pubs, pub)
pks = append(pks, pk)
}

principal := multisig2.Address(multisig.TemplateAddress, 3, pubs...)
to := wallet.Address(pubs[1])
vaultAddr := wallet.Address(pubs[2])

agg := vesting.DrainVault(
0,
pks[0],
principal,
vaultAddr,
to,
100,
types.Nonce(1))
for i := 1; i < len(pks); i++ {
part := vesting.DrainVault(uint8(i), pks[i], principal, vaultAddr, to, 100, types.Nonce(1))
agg.Add(*part.Part(uint8(i)))
}
rawTx := agg.Raw()

contents, txType, err := toTxContents(rawTx)
require.NoError(t, err)
require.NotNil(t, contents.GetDrainVault())
require.Nil(t, contents.GetMultiSigSpawn())
require.Nil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetVestingSpawn())
require.Nil(t, contents.GetSend())
require.Nil(t, contents.GetVaultSpawn())
require.Equal(t, vaultAddr.String(), contents.GetDrainVault().Vault)
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_DRAIN_VAULT, txType)
})

t.Run("multisig vesting spawn", func(t *testing.T) {
t.Parallel()

var pubs []ed25519.PublicKey
pks := make([]ed25519.PrivateKey, 0, 3)
for i := 0; i < 3; i++ {
pub, pk, err := ed25519.GenerateKey(nil)
require.NoError(t, err)
pubs = append(pubs, pub)
pks = append(pks, pk)
}

var agg *multisig2.Aggregator
for i := 0; i < len(pks); i++ {
part := multisig2.SelfSpawn(uint8(i), pks[i], vesting2.TemplateAddress, 1, pubs, types.Nonce(1))
if agg == nil {
agg = part
} else {
agg.Add(*part.Part(uint8(i)))
}
}
rawTx := agg.Raw()

contents, txType, err := toTxContents(rawTx)
require.NoError(t, err)
require.NotNil(t, contents.GetVestingSpawn())
require.Nil(t, contents.GetSend())
require.Nil(t, contents.GetSingleSigSpawn())
require.Nil(t, contents.GetMultiSigSpawn())
require.Nil(t, contents.GetVaultSpawn())
require.Nil(t, contents.GetDrainVault())
require.Equal(t, spacemeshv2alpha1.Transaction_TRANSACTION_TYPE_VESTING_SPAWN, txType)
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ require (
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/seehuhn/mt19937 v1.0.0
github.com/slok/go-http-metrics v0.12.0
github.com/spacemeshos/api/release/go v1.49.0
github.com/spacemeshos/api/release/go v1.50.0
github.com/spacemeshos/economics v0.1.3
github.com/spacemeshos/fixed v0.1.1
github.com/spacemeshos/go-scale v1.2.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -604,8 +604,8 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spacemeshos/api/release/go v1.49.0 h1:1hRPztB5aTX1ebnjS8Fh86Q2JQPDzM4+MbU40rrbK5A=
github.com/spacemeshos/api/release/go v1.49.0/go.mod h1:8pxGN6/di8iBpQReiOgY+Cppi7bhJ+qJ3QiRQtJfoag=
github.com/spacemeshos/api/release/go v1.50.0 h1:M7Usg/LxymscwqYO7/Doyb+sU4lS1e+JIsSgqTDGk/0=
github.com/spacemeshos/api/release/go v1.50.0/go.mod h1:PvgDpjfwkZLVVNExYG7wDNzgMqT3p+ppfTU2UESSF9U=
github.com/spacemeshos/economics v0.1.3 h1:ACkq3mTebIky4Zwbs9SeSSRZrUCjU/Zk0wq9Z0BTh2A=
github.com/spacemeshos/economics v0.1.3/go.mod h1:FH7u0FzTIm6Kpk+X5HOZDvpkgNYBKclmH86rVwYaDAo=
github.com/spacemeshos/fixed v0.1.1 h1:N1y4SUpq1EV+IdJrWJwUCt1oBFzeru/VKVcBsvPc2Fk=
Expand Down

0 comments on commit 23a0e15

Please sign in to comment.