Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Channel #3

Merged
merged 10 commits into from
Sep 11, 2023
54 changes: 54 additions & 0 deletions channel/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package channel

import (
"crypto/sha256"
"perun.network/go-perun/channel"
"perun.network/go-perun/wallet"
"perun.network/perun-stellar-backend/channel/types"
"perun.network/perun-stellar-backend/wire"
)

type backend struct{}

var Backend = backend{}

func init() {
channel.SetBackend(Backend)
}

func (b backend) CalcID(params *channel.Params) channel.ID {
wp := wire.MustMakeParams(*params)
bytes, err := wp.MarshalBinary()
if err != nil {
panic(err)
}
return sha256.Sum256(bytes)
}

func (b backend) Sign(account wallet.Account, state *channel.State) (wallet.Sig, error) {
bytes, err := EncodeState(state)
if err != nil {
return nil, err
}
return account.SignData(bytes)
}

func (b backend) Verify(addr wallet.Address, state *channel.State, sig wallet.Sig) (bool, error) {
bytes, err := EncodeState(state)
if err != nil {
return false, err
}
return wallet.VerifySignature(bytes, sig, addr)
}

func (b backend) NewAsset() channel.Asset {
return &types.StellarAsset{}
}

func EncodeState(state *channel.State) ([]byte, error) {
ws, err := wire.MakeState(*state)
if err != nil {
return nil, err
}
return ws.MarshalBinary()
}
7 changes: 7 additions & 0 deletions channel/test/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package test

import "perun.network/go-perun/channel/test"

func init() {
test.SetRandomizer(&Randomizer{})
}
23 changes: 23 additions & 0 deletions channel/test/randomizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package test

iljabvh marked this conversation as resolved.
Show resolved Hide resolved
import (
"github.com/stellar/go/xdr"
"math/rand"
"perun.network/go-perun/channel"
"perun.network/go-perun/channel/test"
"perun.network/perun-stellar-backend/channel/types"
)

type Randomizer struct{}

var _ test.Randomizer = (*Randomizer)(nil)

func (*Randomizer) NewRandomAsset(*rand.Rand) channel.Asset {
return NewRandomStellarAsset()
}

func NewRandomStellarAsset() *types.StellarAsset {
var contractID xdr.Hash
rand.Read(contractID[:])
return types.NewStellarAsset(contractID)
}
108 changes: 108 additions & 0 deletions channel/types/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package types

import (
"errors"
"github.com/stellar/go/keypair"
"github.com/stellar/go/xdr"
"perun.network/go-perun/channel"
)

const HashLenXdr = 32

type StellarAsset struct {
contractID xdr.Hash
}

func (s StellarAsset) ContractID() xdr.Hash {
return s.contractID
}

func NewStellarAsset(contractID xdr.Hash) *StellarAsset {
return &StellarAsset{contractID: contractID}
}

func (s StellarAsset) MarshalBinary() (data []byte, err error) {
return s.contractID.MarshalBinary()
}

func (s *StellarAsset) UnmarshalBinary(data []byte) error {
var addr [HashLenXdr]byte
copy(addr[:], data)
err := s.contractID.UnmarshalBinary(data)
if err != nil {
return errors.New("could not unmarshal contract id")
}
return nil
}

func (s StellarAsset) Equal(asset channel.Asset) bool {
_, ok := asset.(*StellarAsset)
return ok
}

func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) {
hash := s.contractID
scvAddr, err := MakeContractAddress(hash)
if err != nil {
return xdr.ScAddress{}, errors.New("could not generate contract address")
}
return scvAddr, nil
}

func (s *StellarAsset) FromScAddress(address xdr.ScAddress) error {
addrType := address.Type
if addrType != xdr.ScAddressTypeScAddressTypeContract {
return errors.New("invalid address type")
}

s.contractID = *address.ContractId
return nil
}

func NewStellarAssetFromScAddress(address xdr.ScAddress) (*StellarAsset, error) {
s := &StellarAsset{}
err := s.FromScAddress(address)
if err != nil {
return nil, err
}
return s, nil
}

func MustStellarAsset(asset channel.Asset) *StellarAsset {
p, ok := asset.(*StellarAsset)
if !ok {
panic("Asset has invalid type")
}
return p
}

func ToStellarAsset(asset channel.Asset) (*StellarAsset, error) {
p, ok := asset.(*StellarAsset)
if !ok {
return nil, errors.New("asset has invalid type")
}
return p, nil
}

func MakeAccountAddress(kp keypair.KP) (xdr.ScAddress, error) {
accountId, err := xdr.AddressToAccountId(kp.Address())
if err != nil {
return xdr.ScAddress{}, err
}
return xdr.NewScAddress(xdr.ScAddressTypeScAddressTypeAccount, accountId)
}

func MakeContractAddress(contractID xdr.Hash) (xdr.ScAddress, error) {
return xdr.NewScAddress(xdr.ScAddressTypeScAddressTypeContract, contractID)
}

func ToAccountAddress(address xdr.ScAddress) (keypair.FromAddress, error) {
if address.Type != xdr.ScAddressTypeScAddressTypeAccount {
return keypair.FromAddress{}, errors.New("invalid address type")
}
kp, err := keypair.ParseAddress(address.AccountId.Address())
if err != nil {
return keypair.FromAddress{}, err
}
return *kp, nil
}
35 changes: 35 additions & 0 deletions channel/types/asset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package types_test

iljabvh marked this conversation as resolved.
Show resolved Hide resolved
import (
"github.com/stellar/go/keypair"
"github.com/stellar/go/xdr"
"github.com/stretchr/testify/require"
"perun.network/perun-stellar-backend/channel/types"

"testing"
)

func TestMarshalAndUnmarshalBinary(t *testing.T) {
var hash xdr.Hash
copy(hash[:], []byte("testhashfortestingonly!testhash"))

asset := types.NewStellarAsset(hash)

data, err := asset.MarshalBinary()
require.NoError(t, err)

newAsset := &types.StellarAsset{}
err = newAsset.UnmarshalBinary(data)
require.NoError(t, err)

require.Equal(t, asset.ContractID().HexString(), newAsset.ContractID().HexString(), "Mismatched ContractID. Expected %x, got %x", asset.ContractID(), newAsset.ContractID())
}

func TestMakeAccountAddress(t *testing.T) {
kp, _ := keypair.Random()

address, err := types.MakeAccountAddress(kp)
require.NoError(t, err)

require.Equal(t, xdr.ScAddressTypeScAddressTypeAccount, address.Type, "Expected account address type, got %v", address.Type)
}
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ module perun.network/perun-stellar-backend

go 1.19

require github.com/stellar/go v0.0.0-20230718034032-95c2976a11f2
require github.com/stellar/go v0.0.0-20230905170723-6e18a2f2f583

require (
github.com/stretchr/testify v1.7.0
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee
github.com/stretchr/testify v1.8.1
perun.network/go-perun v0.10.6
polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
39 changes: 25 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stellar/go v0.0.0-20230718034032-95c2976a11f2 h1:puur8PRJFs+o3CFEUf5FF/w3kOVAd1bvK9PU9jr3ouA=
github.com/stellar/go v0.0.0-20230718034032-95c2976a11f2/go.mod h1:iTkyf5zUHlaIjZjyxaLLXLv+YHqg3etsqn8AOQ+DvG8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/stellar/go v0.0.0-20230905170723-6e18a2f2f583 h1:oC7naR8IBqtWV/b+G91gTOe+jR52+GpkXhCxEyYawYY=
github.com/stellar/go v0.0.0-20230905170723-6e18a2f2f583/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg=
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee h1:fbVs0xmXpBvVS4GBeiRmAE3Le70ofAqFMch1GTiq/e8=
github.com/stellar/go-xdr v0.0.0-20211103144802-8017fc4bdfee/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xdrpp/goxdr v0.1.1 h1:E1B2c6E8eYhOVyd7yEpOyopzTPirUeF6mVOfXfGyJyc=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
perun.network/go-perun v0.10.6 h1:uj1e33yfCSfE75DK/uwjNp+TwvGG85Qhi6HuYQ9EPrQ=
perun.network/go-perun v0.10.6/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8=
polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b h1:BJsSrLQ3kLRNYXNqly//IYeXlVmAhpI5wYbg2WD1wR0=
Expand Down
22 changes: 21 additions & 1 deletion wallet/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stellar/go/keypair"
"math/rand"
"perun.network/go-perun/wallet"
"perun.network/perun-stellar-backend/wallet/types"
)

// Account is used for signing channel state.
Expand Down Expand Up @@ -40,9 +41,28 @@ func NewRandomAccount(rng *rand.Rand) (*Account, *keypair.Full, error) {
return acc, kp, nil
}

func NewRandomAddress(rng *rand.Rand) wallet.Address {
kp, err := keypair.Random()
if err != nil {
panic(err)
}
acc, err := NewRandomAccountWithAddress(rng, kp.FromAddress())
if err != nil {
panic(err)
}

addr := acc.Address()

return addr
}

// Address returns the Participant this account belongs to.
func (a Account) Address() wallet.Address {
return NewParticipant(a.ParticipantAddress, a.privateKey.Public().(ed25519.PublicKey))
return types.NewParticipant(a.ParticipantAddress, a.privateKey.Public().(ed25519.PublicKey))
}

func (a Account) Participant() *types.Participant {
return types.NewParticipant(a.ParticipantAddress, a.privateKey.Public().(ed25519.PublicKey))
}

// SignData signs the given data with the account's private key.
Expand Down
5 changes: 3 additions & 2 deletions wallet/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"io"
"perun.network/go-perun/wallet"
"perun.network/perun-stellar-backend/wallet/types"
)

// SignatureLength is the length of a signature in bytes.
Expand All @@ -19,7 +20,7 @@ func init() {
}

func (b backend) NewAddress() wallet.Address {
return &Participant{}
return &types.Participant{}
}

// DecodeSig decodes a signature of length SignatureLength from the reader.
Expand All @@ -32,7 +33,7 @@ func (b backend) DecodeSig(reader io.Reader) (wallet.Sig, error) {
}

func (b backend) VerifySignature(msg []byte, sig wallet.Sig, a wallet.Address) (bool, error) {
p, ok := a.(*Participant)
p, ok := a.(*types.Participant)
if !ok {
return false, errors.New("Participant has invalid type")
}
Expand Down
10 changes: 10 additions & 0 deletions wallet/test/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package test

import (
"perun.network/go-perun/wallet/test"
_ "perun.network/perun-stellar-backend/channel/test"
)

func init() {
test.SetRandomizer(&Randomizer{})
}
Loading