From e5d7b8210d6ab2f13707a48b4d9bebaef6a81ed3 Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Thu, 3 Aug 2023 15:11:30 +0200 Subject: [PATCH 01/10] wire: Implement XDR encoding and Decoding of contract types --- go.mod | 2 +- go.sum | 2 + wire/balances.go | 125 ++++++++++++++++++++++++++ wire/balances_test.go | 20 +++++ wire/channel.go | 119 +++++++++++++++++++++++++ wire/channel_test.go | 17 ++++ wire/control.go | 184 +++++++++++++++++++++++++++++++++++++++ wire/control_test.go | 17 ++++ wire/params.go | 142 ++++++++++++++++++++++++++++++ wire/params_test.go | 17 ++++ wire/participant.go | 104 ++++++++++++++++++++++ wire/participant_test.go | 18 ++++ wire/scmap.go | 54 ++++++++++++ wire/scval/bool.go | 43 +++++++++ wire/scval/scval.go | 75 ++++++++++++++++ wire/state.go | 143 ++++++++++++++++++++++++++++++ wire/state_test.go | 20 +++++ 17 files changed, 1101 insertions(+), 1 deletion(-) create mode 100644 wire/balances.go create mode 100644 wire/balances_test.go create mode 100644 wire/channel.go create mode 100644 wire/channel_test.go create mode 100644 wire/control.go create mode 100644 wire/control_test.go create mode 100644 wire/params.go create mode 100644 wire/params_test.go create mode 100644 wire/participant.go create mode 100644 wire/participant_test.go create mode 100644 wire/scmap.go create mode 100644 wire/scval/bool.go create mode 100644 wire/scval/scval.go create mode 100644 wire/state.go create mode 100644 wire/state_test.go diff --git a/go.mod b/go.mod index 94fb5af..f3f6a03 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ 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-20230801100123-5ded27e97f53 require ( github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index ac77331..862d07d 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb 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/stellar/go v0.0.0-20230801100123-5ded27e97f53 h1:U/W5fqYM9zgmLpLT3jGOY9LbkinXaaqkW+P8bzBTO+0= +github.com/stellar/go v0.0.0-20230801100123-5ded27e97f53/go.mod h1:m9ABBUAExq/TfafyQrLie0owyyAR6IYCRhujttELLHU= 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= diff --git a/wire/balances.go b/wire/balances.go new file mode 100644 index 0000000..9844ae5 --- /dev/null +++ b/wire/balances.go @@ -0,0 +1,125 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +type Balances struct { + BalA xdr.Int128Parts + BalB xdr.Int128Parts + Token xdr.ScAddress +} + +const ( + SymbolBalancesBalA xdr.ScSymbol = "bal_a" + SymbolBalancesBalB xdr.ScSymbol = "bal_b" + SymbolBalancesToken xdr.ScSymbol = "token" +) + +func (b Balances) ToScVal() (xdr.ScVal, error) { + var err error + balA, err := scval.WrapInt128Parts(b.BalA) + if err != nil { + return xdr.ScVal{}, err + } + balB, err := scval.WrapInt128Parts(b.BalB) + if err != nil { + return xdr.ScVal{}, err + } + token, err := scval.WrapScAddress(b.Token) + if err != nil { + return xdr.ScVal{}, err + } + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolBalancesBalA, + SymbolBalancesBalB, + SymbolBalancesToken, + }, + []xdr.ScVal{balA, balB, token}, + ) + if err != nil { + return xdr.ScVal{}, err + } + return scval.WrapScMap(m) +} + +func (b *Balances) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 3 { + return errors.New("expected map of length 3") + } + balAVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesBalA), *m) + if err != nil { + return err + } + balA, ok := balAVal.GetI128() + if !ok { + return errors.New("expected i128") + } + balBVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesBalB), *m) + if err != nil { + return err + } + balB, ok := balBVal.GetI128() + if !ok { + return errors.New("expected i128") + } + tokenVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolBalancesToken), *m) + if err != nil { + return err + } + token, ok := tokenVal.GetAddress() + if !ok { + return errors.New("expected address") + } + b.BalA = balA + b.BalB = balB + b.Token = token + return nil +} + +func BalancesFromScVal(v xdr.ScVal) (Balances, error) { + var b Balances + err := (&b).FromScVal(v) + return b, err +} + +func (b Balances) EncodeTo(e *xdr3.Encoder) error { + v, err := b.ToScVal() + if err != nil { + return err + } + _, err = e.Encode(v) + return err +} + +func (b *Balances) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + n, err := d.Decode(&v) + if err != nil { + return n, err + } + return n, b.FromScVal(v) +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (b Balances) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := b.EncodeTo(e) + return buf.Bytes(), err +} + +func (b *Balances) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := b.DecodeFrom(d) + return err +} diff --git a/wire/balances_test.go b/wire/balances_test.go new file mode 100644 index 0000000..3e6f5ee --- /dev/null +++ b/wire/balances_test.go @@ -0,0 +1,20 @@ +package wire_test + +import ( + "fmt" + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestBalances(t *testing.T) { + // Balances XDR generated by soroban contract + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 206, 189, 184, 3, 222, 31, 3, 119, 148, 96, 26, 56, 67, 233, 206, 144, 53, 117, 109, 200, 172, 60, 79, 190, 69, 198, 194, 72, 127, 57, 239, 18} + balances := &wire.Balances{} + err := balances.UnmarshalBinary(x) + require.NoError(t, err) + fmt.Println(balances) + res, err := balances.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} diff --git a/wire/channel.go b/wire/channel.go new file mode 100644 index 0000000..6cbca7c --- /dev/null +++ b/wire/channel.go @@ -0,0 +1,119 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +const ( + SymbolChannelParams = "params" + SymbolChannelState = "state" + SymbolChannelControl = "control" +) + +type Channel struct { + Params Params + State State + Control Control +} + +func (c Channel) ToScVal() (xdr.ScVal, error) { + params, err := c.Params.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + state, err := c.State.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + control, err := c.Control.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolChannelParams, + SymbolChannelState, + SymbolChannelControl, + }, + []xdr.ScVal{params, state, control}, + ) + return scval.WrapScMap(m) +} + +func (c *Channel) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 3 { + return errors.New("expected map of length 3") + } + paramsVal, err := GetScMapValueFromSymbol(SymbolChannelParams, *m) + if err != nil { + return err + } + params, err := ParamsFromScVal(paramsVal) + if err != nil { + return err + } + stateVal, err := GetScMapValueFromSymbol(SymbolChannelState, *m) + if err != nil { + return err + } + state, err := StateFromScVal(stateVal) + if err != nil { + return err + } + controlVal, err := GetScMapValueFromSymbol(SymbolChannelControl, *m) + if err != nil { + return err + } + control, err := ControlFromScVal(controlVal) + if err != nil { + return err + } + c.Params = params + c.State = state + c.Control = control + return nil +} + +func (c Channel) EncodeTo(e *xdr3.Encoder) error { + v, err := c.ToScVal() + if err != nil { + return err + } + return v.EncodeTo(e) +} + +func (c *Channel) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + i, err := d.Decode(&v) + if err != nil { + return i, err + } + return i, c.FromScVal(v) +} + +func (c Channel) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := c.EncodeTo(e) + return buf.Bytes(), err +} + +func (c *Channel) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := c.DecodeFrom(d) + return err +} + +func ChannelFromScVal(v xdr.ScVal) (Channel, error) { + var p Channel + err := (&p).FromScVal(v) + return p, err +} diff --git a/wire/channel_test.go b/wire/channel_test.go new file mode 100644 index 0000000..dd356ad --- /dev/null +++ b/wire/channel_test.go @@ -0,0 +1,17 @@ +package wire_test + +import ( + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestChannel(t *testing.T) { + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 7, 99, 111, 110, 116, 114, 111, 108, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 15, 0, 0, 0, 6, 99, 108, 111, 115, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 100, 105, 115, 112, 117, 116, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 6, 112, 97, 114, 97, 109, 115, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 33, 51, 38, 216, 94, 104, 60, 95, 201, 68, 34, 38, 19, 154, 177, 126, 166, 239, 35, 70, 87, 92, 111, 155, 136, 232, 97, 200, 2, 113, 142, 45, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 138, 168, 57, 43, 69, 220, 86, 65, 46, 142, 9, 41, 77, 209, 98, 227, 110, 250, 188, 94, 141, 146, 8, 84, 176, 10, 181, 208, 111, 23, 195, 128, 0, 0, 0, 15, 0, 0, 0, 1, 98, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 54, 126, 124, 170, 14, 66, 237, 32, 130, 122, 190, 185, 235, 5, 11, 7, 174, 52, 43, 111, 248, 178, 245, 217, 196, 79, 72, 69, 30, 92, 173, 135, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 198, 110, 153, 155, 207, 65, 232, 251, 170, 116, 250, 218, 34, 78, 186, 124, 12, 123, 14, 67, 225, 189, 2, 217, 179, 38, 247, 170, 250, 88, 172, 100, 0, 0, 0, 15, 0, 0, 0, 18, 99, 104, 97, 108, 108, 101, 110, 103, 101, 95, 100, 117, 114, 97, 116, 105, 111, 110, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 5, 110, 111, 110, 99, 101, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 167, 227, 246, 89, 1, 11, 117, 248, 64, 115, 191, 29, 226, 14, 42, 64, 180, 8, 5, 38, 96, 118, 210, 229, 2, 82, 96, 187, 137, 133, 56, 90, 0, 0, 0, 15, 0, 0, 0, 5, 115, 116, 97, 116, 101, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 115, 100, 94, 175, 122, 86, 241, 234, 76, 123, 122, 25, 44, 31, 17, 97, 248, 88, 65, 165, 45, 214, 88, 65, 124, 10, 171, 36, 105, 116, 213, 227, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 24, 255, 93, 22, 90, 12, 183, 31, 139, 73, 106, 35, 75, 37, 233, 72, 131, 174, 135, 56, 56, 175, 189, 129, 239, 63, 190, 175, 7, 186, 165, 24, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + channel := &wire.Channel{} + err := channel.UnmarshalBinary(x) + require.NoError(t, err) + res, err := channel.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} diff --git a/wire/control.go b/wire/control.go new file mode 100644 index 0000000..78dd7d5 --- /dev/null +++ b/wire/control.go @@ -0,0 +1,184 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +const ( + SymbolControlFundedA = "funded_a" + SymbolControlFundedB = "funded_b" + SymbolControlClosed = "closed" + SymbolControlWithdrawnA = "withdrawn_a" + SymbolControlWithdrawnB = "withdrawn_b" + SymbolControlDisputed = "disputed" + SymbolControlTimestamp = "timestamp" +) + +type Control struct { + FundedA bool + FundedB bool + Closed bool + WithdrawnA bool + WithdrawnB bool + Disputed bool + Timestamp xdr.Uint64 +} + +func (c Control) ToScVal() (xdr.ScVal, error) { + fundedA, err := scval.WrapBool(c.FundedA) + if err != nil { + return xdr.ScVal{}, err + } + fundedB, err := scval.WrapBool(c.FundedB) + if err != nil { + return xdr.ScVal{}, err + } + closed, err := scval.WrapBool(c.Closed) + if err != nil { + return xdr.ScVal{}, err + } + withdrawnA, err := scval.WrapBool(c.WithdrawnA) + if err != nil { + return xdr.ScVal{}, err + } + withdrawnB, err := scval.WrapBool(c.WithdrawnB) + if err != nil { + return xdr.ScVal{}, err + } + disputed, err := scval.WrapBool(c.Disputed) + if err != nil { + return xdr.ScVal{}, err + } + timestamp, err := scval.WrapUint64(c.Timestamp) + if err != nil { + return xdr.ScVal{}, err + } + + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolControlFundedA, + SymbolControlFundedB, + SymbolControlClosed, + SymbolControlWithdrawnA, + SymbolControlWithdrawnB, + SymbolControlDisputed, + SymbolControlTimestamp, + }, + []xdr.ScVal{fundedA, fundedB, closed, withdrawnA, withdrawnB, disputed, timestamp}, + ) + return scval.WrapScMap(m) +} + +func (c *Control) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 7 { + return errors.New("expected map of length 7") + } + fundedAVal, err := GetScMapValueFromSymbol(SymbolControlFundedA, *m) + if err != nil { + return err + } + fundedA, ok := fundedAVal.GetB() + if !ok { + return errors.New("expected bool") + } + fundedBVal, err := GetScMapValueFromSymbol(SymbolControlFundedB, *m) + if err != nil { + return err + } + fundedB, ok := fundedBVal.GetB() + if !ok { + return errors.New("expected bool") + } + closedVal, err := GetScMapValueFromSymbol(SymbolControlClosed, *m) + if err != nil { + return err + } + closed, ok := closedVal.GetB() + if !ok { + return errors.New("expected bool") + } + withdrawnAVal, err := GetScMapValueFromSymbol(SymbolControlWithdrawnA, *m) + if err != nil { + return err + } + withdrawnA, ok := withdrawnAVal.GetB() + if !ok { + return errors.New("expected bool") + } + withdrawnBVal, err := GetScMapValueFromSymbol(SymbolControlWithdrawnB, *m) + if err != nil { + return err + } + withdrawnB, ok := withdrawnBVal.GetB() + if !ok { + return errors.New("expected bool") + } + disputedVal, err := GetScMapValueFromSymbol(SymbolControlDisputed, *m) + if err != nil { + return err + } + disputed, ok := disputedVal.GetB() + if !ok { + return errors.New("expected bool") + } + timestampVal, err := GetScMapValueFromSymbol(SymbolControlTimestamp, *m) + if err != nil { + return err + } + timestamp, ok := timestampVal.GetU64() + if !ok { + return errors.New("expected uint64") + } + c.FundedA = fundedA + c.FundedB = fundedB + c.Closed = closed + c.WithdrawnA = withdrawnA + c.WithdrawnB = withdrawnB + c.Disputed = disputed + c.Timestamp = timestamp + return nil +} + +func (c Control) EncodeTo(e *xdr3.Encoder) error { + v, err := c.ToScVal() + if err != nil { + return err + } + return v.EncodeTo(e) +} + +func (c *Control) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + i, err := d.Decode(&v) + if err != nil { + return i, err + } + return i, c.FromScVal(v) +} + +func (c Control) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := c.EncodeTo(e) + return buf.Bytes(), err +} + +func (c *Control) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := c.DecodeFrom(d) + return err +} + +func ControlFromScVal(v xdr.ScVal) (Control, error) { + var p Control + err := (&p).FromScVal(v) + return p, err +} diff --git a/wire/control_test.go b/wire/control_test.go new file mode 100644 index 0000000..149f085 --- /dev/null +++ b/wire/control_test.go @@ -0,0 +1,17 @@ +package wire_test + +import ( + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestControl(t *testing.T) { + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 15, 0, 0, 0, 6, 99, 108, 111, 115, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 100, 105, 115, 112, 117, 116, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 102, 117, 110, 100, 101, 100, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 11, 119, 105, 116, 104, 100, 114, 97, 119, 110, 95, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0} + control := &wire.Control{} + err := control.UnmarshalBinary(x) + require.NoError(t, err) + res, err := control.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} diff --git a/wire/params.go b/wire/params.go new file mode 100644 index 0000000..2ce5fcc --- /dev/null +++ b/wire/params.go @@ -0,0 +1,142 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +const NonceLength = 32 +const ( + SymbolParamsA = "a" + SymbolParamsB = "b" + SymbolParamsNonce = "nonce" + SymbolParamsChallengeDuration = "challenge_duration" +) + +type Params struct { + A Participant + B Participant + Nonce xdr.ScBytes + ChallengeDuration xdr.Uint64 +} + +func (p Params) ToScVal() (xdr.ScVal, error) { + if len(p.Nonce) != NonceLength { + return xdr.ScVal{}, errors.New("invalid nonce length") + } + a, err := p.A.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + b, err := p.B.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + nonce, err := scval.WrapScBytes(p.Nonce) + if err != nil { + return xdr.ScVal{}, err + } + challengeDuration, err := scval.WrapUint64(p.ChallengeDuration) + if err != nil { + return xdr.ScVal{}, err + } + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolParamsA, + SymbolParamsB, + SymbolParamsNonce, + SymbolParamsChallengeDuration, + }, + []xdr.ScVal{a, b, nonce, challengeDuration}, + ) + return scval.WrapScMap(m) +} + +func (p *Params) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 4 { + return errors.New("expected map of length 4") + } + aVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParamsA), *m) + if err != nil { + return err + } + a, err := ParticipantFromScVal(aVal) + if err != nil { + return err + } + bVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParamsB), *m) + if err != nil { + return err + } + b, err := ParticipantFromScVal(bVal) + if err != nil { + return err + } + nonceVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParamsNonce), *m) + if err != nil { + return err + } + nonce, ok := nonceVal.GetBytes() + if !ok { + return errors.New("expected bytes") + } + if len(nonce) != NonceLength { + return errors.New("invalid nonce length") + } + challengeDurationVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParamsChallengeDuration), *m) + if err != nil { + return err + } + challengeDuration, ok := challengeDurationVal.GetU64() + if !ok { + return errors.New("expected uint64") + } + p.A = a + p.B = b + p.Nonce = nonce + p.ChallengeDuration = challengeDuration + return nil +} + +func (p Params) EncodeTo(e *xdr3.Encoder) error { + v, err := p.ToScVal() + if err != nil { + return err + } + return v.EncodeTo(e) +} + +func (p *Params) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + i, err := d.Decode(&v) + if err != nil { + return i, err + } + return i, p.FromScVal(v) +} + +func (p Params) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := p.EncodeTo(e) + return buf.Bytes(), err +} + +func (p *Params) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := p.DecodeFrom(d) + return err +} + +func ParamsFromScVal(v xdr.ScVal) (Params, error) { + var p Params + err := (&p).FromScVal(v) + return p, err +} diff --git a/wire/params_test.go b/wire/params_test.go new file mode 100644 index 0000000..e294a13 --- /dev/null +++ b/wire/params_test.go @@ -0,0 +1,17 @@ +package wire_test + +import ( + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestParams(t *testing.T) { + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 100, 102, 1, 62, 144, 25, 203, 254, 77, 51, 254, 154, 48, 112, 147, 73, 240, 64, 244, 179, 161, 243, 111, 26, 76, 81, 122, 190, 16, 11, 6, 86, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 161, 254, 118, 204, 233, 36, 234, 44, 212, 63, 191, 30, 184, 43, 254, 124, 172, 122, 80, 159, 130, 160, 115, 195, 41, 12, 49, 89, 190, 53, 92, 50, 0, 0, 0, 15, 0, 0, 0, 1, 98, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 150, 139, 245, 155, 89, 98, 50, 60, 35, 101, 150, 241, 125, 46, 207, 112, 191, 2, 195, 252, 181, 164, 244, 5, 38, 183, 40, 224, 96, 159, 6, 162, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 182, 104, 55, 79, 130, 121, 159, 228, 251, 195, 211, 137, 145, 92, 106, 55, 206, 137, 219, 47, 233, 99, 114, 77, 172, 109, 60, 143, 113, 160, 141, 192, 0, 0, 0, 15, 0, 0, 0, 18, 99, 104, 97, 108, 108, 101, 110, 103, 101, 95, 100, 117, 114, 97, 116, 105, 111, 110, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 5, 110, 111, 110, 99, 101, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 64, 194, 171, 150, 181, 52, 147, 0, 150, 145, 33, 44, 252, 12, 67, 66, 219, 135, 26, 235, 252, 58, 248, 99, 218, 239, 18, 73, 247, 124, 196, 67} + p := &wire.Params{} + err := p.UnmarshalBinary(x) + require.NoError(t, err) + res, err := p.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} diff --git a/wire/participant.go b/wire/participant.go new file mode 100644 index 0000000..560515b --- /dev/null +++ b/wire/participant.go @@ -0,0 +1,104 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +const ( + PubKeyLength = 32 + SymbolParticipantAddr xdr.ScSymbol = "addr" + SymbolParticipantPubKey xdr.ScSymbol = "pubkey" +) + +type Participant struct { + Addr xdr.ScAddress + PubKey xdr.ScBytes +} + +func (p Participant) ToScVal() (xdr.ScVal, error) { + addr, err := scval.WrapScAddress(p.Addr) + if err != nil { + return xdr.ScVal{}, err + } + if len(p.PubKey) != PubKeyLength { + return xdr.ScVal{}, errors.New("invalid public key length") + } + pubKey, err := scval.WrapScBytes(p.PubKey) + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolParticipantAddr, + SymbolParticipantPubKey, + }, + []xdr.ScVal{addr, pubKey}, + ) + return scval.WrapScMap(m) +} + +func (p *Participant) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 2 { + return errors.New("expected map of length 2") + } + addrVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParticipantAddr), *m) + if err != nil { + return err + } + addr, ok := addrVal.GetAddress() + if !ok { + return errors.New("expected address") + } + pubKeyVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolParticipantPubKey), *m) + if err != nil { + return err + } + pubKey, ok := pubKeyVal.GetBytes() + if len(pubKey) != PubKeyLength { + return errors.New("invalid public key length") + } + p.Addr = addr + p.PubKey = pubKey + return nil +} + +func (p Participant) EncodeTo(e *xdr3.Encoder) error { + v, err := p.ToScVal() + if err != nil { + return err + } + return v.EncodeTo(e) +} + +func (p *Participant) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + i, err := d.Decode(&v) + if err != nil { + return i, err + } + return i, p.FromScVal(v) +} + +func (p Participant) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := p.EncodeTo(e) + return buf.Bytes(), err +} + +func (p *Participant) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := p.DecodeFrom(d) + return err +} + +func ParticipantFromScVal(v xdr.ScVal) (Participant, error) { + var p Participant + err := (&p).FromScVal(v) + return p, err +} diff --git a/wire/participant_test.go b/wire/participant_test.go new file mode 100644 index 0000000..6257c19 --- /dev/null +++ b/wire/participant_test.go @@ -0,0 +1,18 @@ +package wire_test + +import ( + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestParticipant(t *testing.T) { + // Participant XDR generated by soroban contract + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 15, 0, 0, 0, 4, 97, 100, 100, 114, 0, 0, 0, 18, 0, 0, 0, 1, 111, 69, 81, 199, 239, 4, 247, 14, 49, 177, 211, 74, 68, 253, 8, 232, 85, 220, 39, 55, 21, 152, 51, 165, 106, 167, 168, 9, 185, 195, 55, 133, 0, 0, 0, 15, 0, 0, 0, 6, 112, 117, 98, 107, 101, 121, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} + p := &wire.Participant{} + err := p.UnmarshalBinary(x) + require.NoError(t, err) + res, err := p.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} diff --git a/wire/scmap.go b/wire/scmap.go new file mode 100644 index 0000000..93e6321 --- /dev/null +++ b/wire/scmap.go @@ -0,0 +1,54 @@ +package wire + +import ( + "errors" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" + "sort" + "strings" +) + +// MakeSymbolScMap creates a xdr.ScMap from a slice of symbols and a slice of values. +// The entries are sorted lexicographically by symbol. We expect that keys does not contain duplicates. +func MakeSymbolScMap(keys []xdr.ScSymbol, values []xdr.ScVal) (xdr.ScMap, error) { + if len(keys) != len(values) { + return xdr.ScMap{}, errors.New("keys and values must have the same length") + } + m := make(xdr.ScMap, len(keys)) + for i, k := range keys { + m[i] = xdr.ScMapEntry{ + Key: scval.MustWrapScSymbol(k), + Val: values[i], + } + } + sort.Slice(m, func(i, j int) bool { + return strings.Compare(string(m[i].Key.MustSym()), string(m[j].Key.MustSym())) < 0 + }) + return m, nil +} + +func GetScMapEntry(key xdr.ScVal, m xdr.ScMap) (xdr.ScMapEntry, error) { + for _, v := range m { + if v.Key.Equals(key) { + return v, nil + } + } + + return xdr.ScMapEntry{}, errors.New("key not found") +} + +func GetMapValue(key xdr.ScVal, m xdr.ScMap) (xdr.ScVal, error) { + entry, err := GetScMapEntry(key, m) + if err != nil { + return xdr.ScVal{}, err + } + return entry.Val, nil +} + +func GetScMapValueFromSymbol(key xdr.ScSymbol, m xdr.ScMap) (xdr.ScVal, error) { + keyVal, err := scval.WrapScSymbol(key) + if err != nil { + return xdr.ScVal{}, err + } + return GetMapValue(keyVal, m) +} diff --git a/wire/scval/bool.go b/wire/scval/bool.go new file mode 100644 index 0000000..cec5fc4 --- /dev/null +++ b/wire/scval/bool.go @@ -0,0 +1,43 @@ +package scval + +import "github.com/stellar/go/xdr" + +func WrapTrue() (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvBool, true) +} + +func MustWrapTrue() xdr.ScVal { + v, err := WrapTrue() + if err != nil { + panic(err) + } + return v +} + +func WrapFalse() (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvBool, false) +} + +func MustWrapFalse() xdr.ScVal { + v, err := WrapFalse() + if err != nil { + panic(err) + } + return v +} + +func WrapBool(b bool) (xdr.ScVal, error) { + if b { + return WrapTrue() + } else { + return WrapFalse() + } +} + +func MustWrapBool(b bool) xdr.ScVal { + if b { + return MustWrapTrue() + } else { + return MustWrapFalse() + } +} diff --git a/wire/scval/scval.go b/wire/scval/scval.go new file mode 100644 index 0000000..8f1f2cd --- /dev/null +++ b/wire/scval/scval.go @@ -0,0 +1,75 @@ +package scval + +import "github.com/stellar/go/xdr" + +func WrapScAddress(address xdr.ScAddress) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvAddress, address) +} + +func MustWrapScAddress(address xdr.ScAddress) xdr.ScVal { + v, err := WrapScAddress(address) + if err != nil { + panic(err) + } + return v +} + +func WrapInt128Parts(parts xdr.Int128Parts) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvI128, parts) +} + +func MustWrapInt128Parts(parts xdr.Int128Parts) xdr.ScVal { + v, err := WrapInt128Parts(parts) + if err != nil { + panic(err) + } + return v +} + +func WrapScMap(m xdr.ScMap) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvMap, &m) +} + +func MustWrapScMap(m xdr.ScMap) xdr.ScVal { + v, err := WrapScMap(m) + if err != nil { + panic(err) + } + return v +} + +func WrapScSymbol(symbol xdr.ScSymbol) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvSymbol, symbol) +} + +func MustWrapScSymbol(symbol xdr.ScSymbol) xdr.ScVal { + v, err := WrapScSymbol(symbol) + if err != nil { + panic(err) + } + return v +} + +func WrapScBytes(b xdr.ScBytes) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvBytes, b) +} + +func MustWrapScBytes(b xdr.ScBytes) xdr.ScVal { + v, err := WrapScBytes(b) + if err != nil { + panic(err) + } + return v +} + +func WrapUint64(i xdr.Uint64) (xdr.ScVal, error) { + return xdr.NewScVal(xdr.ScValTypeScvU64, i) +} + +func MustWrapUint64(i xdr.Uint64) xdr.ScVal { + v, err := WrapUint64(i) + if err != nil { + panic(err) + } + return v +} diff --git a/wire/state.go b/wire/state.go new file mode 100644 index 0000000..8a0d555 --- /dev/null +++ b/wire/state.go @@ -0,0 +1,143 @@ +package wire + +import ( + "bytes" + "errors" + xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wire/scval" +) + +const ChannelIDLength = 32 + +const ( + SymbolStateChannelID xdr.ScSymbol = "channel_id" + SymbolStateBalances xdr.ScSymbol = "balances" + SymbolStateVersion xdr.ScSymbol = "version" + SymbolStateFinalized xdr.ScSymbol = "finalized" +) + +type State struct { + ChannelID xdr.ScBytes + Balances Balances + Version xdr.Uint64 + Finalized bool // Bool +} + +func (s State) ToScVal() (xdr.ScVal, error) { + if len(s.ChannelID) != ChannelIDLength { + return xdr.ScVal{}, errors.New("invalid channel id length") + } + channelID, err := scval.WrapScBytes(s.ChannelID) + if err != nil { + return xdr.ScVal{}, err + } + balances, err := s.Balances.ToScVal() + if err != nil { + return xdr.ScVal{}, err + } + version, err := scval.WrapUint64(s.Version) + if err != nil { + return xdr.ScVal{}, err + } + finalized, err := scval.WrapBool(s.Finalized) + if err != nil { + return xdr.ScVal{}, err + } + m, err := MakeSymbolScMap( + []xdr.ScSymbol{ + SymbolStateChannelID, + SymbolStateBalances, + SymbolStateVersion, + SymbolStateFinalized, + }, + []xdr.ScVal{channelID, balances, version, finalized}, + ) + return scval.WrapScMap(m) +} + +func (s *State) FromScVal(v xdr.ScVal) error { + m, ok := v.GetMap() + if !ok { + return errors.New("expected map") + } + if len(*m) != 4 { + return errors.New("expected map of length 4") + } + channelIDVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolStateChannelID), *m) + if err != nil { + return err + } + channelID, ok := channelIDVal.GetBytes() + if !ok { + return errors.New("expected bytes") + } + if len(channelID) != ChannelIDLength { + return errors.New("invalid channel id length") + } + balancesVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolStateBalances), *m) + if err != nil { + return err + } + balances, err := BalancesFromScVal(balancesVal) + if err != nil { + return err + } + versionVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolStateVersion), *m) + if err != nil { + return err + } + version, ok := versionVal.GetU64() + if !ok { + return errors.New("expected uint64") + } + finalizedVal, err := GetMapValue(scval.MustWrapScSymbol(SymbolStateFinalized), *m) + if err != nil { + return err + } + finalized, ok := finalizedVal.GetB() + if !ok { + return errors.New("expected bool") + } + s.ChannelID = channelID + s.Balances = balances + s.Version = version + s.Finalized = finalized + return nil +} + +func (s State) EncodeTo(e *xdr3.Encoder) error { + v, err := s.ToScVal() + if err != nil { + return err + } + return v.EncodeTo(e) +} + +func (s *State) DecodeFrom(d *xdr3.Decoder) (int, error) { + var v xdr.ScVal + i, err := d.Decode(&v) + if err != nil { + return i, err + } + return i, s.FromScVal(v) +} + +func (s State) MarshalBinary() ([]byte, error) { + buf := bytes.Buffer{} + e := xdr3.NewEncoder(&buf) + err := s.EncodeTo(e) + return buf.Bytes(), err +} + +func (s *State) UnmarshalBinary(data []byte) error { + d := xdr3.NewDecoder(bytes.NewReader(data)) + _, err := s.DecodeFrom(d) + return err +} + +func StateFromScVal(v xdr.ScVal) (State, error) { + var s State + err := (&s).FromScVal(v) + return s, err +} diff --git a/wire/state_test.go b/wire/state_test.go new file mode 100644 index 0000000..625ee98 --- /dev/null +++ b/wire/state_test.go @@ -0,0 +1,20 @@ +package wire_test + +import ( + "fmt" + "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wire" + "testing" +) + +func TestState(t *testing.T) { + // State XDR generated by soroban contract + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 48, 207, 108, 223, 126, 81, 182, 30, 80, 205, 206, 164, 29, 186, 161, 49, 19, 220, 169, 154, 82, 230, 27, 46, 112, 182, 98, 53, 61, 128, 204, 23, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 135, 93, 16, 248, 119, 202, 15, 58, 94, 69, 71, 246, 71, 210, 225, 253, 36, 204, 170, 27, 91, 210, 4, 152, 129, 94, 12, 28, 183, 59, 230, 206, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + state := &wire.State{} + err := state.UnmarshalBinary(x) + require.NoError(t, err) + fmt.Println(state) + res, err := state.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} From aa6aff9ce43ca648ccea50207bf956e96a82c38a Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Tue, 8 Aug 2023 16:51:06 +0200 Subject: [PATCH 02/10] wallet: Put Participant into types subpackage to avoid cyclic dependencies --- wallet/account.go | 3 ++- wallet/backend.go | 5 +++-- wallet/{ => types}/address.go | 10 +++++++++- wallet/wallet.go | 5 +++-- wallet/wallet_test.go | 3 ++- 5 files changed, 19 insertions(+), 7 deletions(-) rename wallet/{ => types}/address.go (93%) diff --git a/wallet/account.go b/wallet/account.go index 6fbfbb7..9712829 100644 --- a/wallet/account.go +++ b/wallet/account.go @@ -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. @@ -42,7 +43,7 @@ func NewRandomAccount(rng *rand.Rand) (*Account, *keypair.Full, error) { // 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)) } // SignData signs the given data with the account's private key. diff --git a/wallet/backend.go b/wallet/backend.go index 125512a..4a6a040 100644 --- a/wallet/backend.go +++ b/wallet/backend.go @@ -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. @@ -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. @@ -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") } diff --git a/wallet/address.go b/wallet/types/address.go similarity index 93% rename from wallet/address.go rename to wallet/types/address.go index 92d85c1..d311e5c 100644 --- a/wallet/address.go +++ b/wallet/types/address.go @@ -1,4 +1,4 @@ -package wallet +package types import ( "crypto/ed25519" @@ -91,6 +91,14 @@ func AsParticipant(address wallet.Address) *Participant { return p } +func ToParticipant(address wallet.Address) (*Participant, error) { + p, ok := address.(*Participant) + if !ok { + return nil, fmt.Errorf("address has invalid type") + } + return p, nil +} + func PublicKeyFromKeyPair(kp keypair.KP) (ed25519.PublicKey, error) { return strkey.Decode(strkey.VersionByteAccountID, kp.Address()) } diff --git a/wallet/wallet.go b/wallet/wallet.go index 1345f72..8e502ea 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -5,6 +5,7 @@ import ( "github.com/stellar/go/keypair" "math/rand" "perun.network/go-perun/wallet" + "perun.network/perun-stellar-backend/wallet/types" "polycry.pt/poly-go/sync" ) @@ -14,7 +15,7 @@ type EphemeralWallet struct { } func (e *EphemeralWallet) Unlock(a wallet.Address) (wallet.Account, error) { - addr, ok := a.(*Participant) + addr, ok := a.(*types.Participant) if !ok { return nil, errors.New("incorrect Participant type") } @@ -44,7 +45,7 @@ func (e *EphemeralWallet) AddNewAccount(rng *rand.Rand) (*Account, *keypair.Full func (e *EphemeralWallet) AddAccount(acc *Account) error { e.lock.Lock() defer e.lock.Unlock() - k := AsParticipant(acc.Address()).String() + k := types.AsParticipant(acc.Address()).String() _, ok := e.accounts[k] if ok { return errors.New("account already exists") diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index 0b98cda..8ee5525 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -5,6 +5,7 @@ import ( "math/rand" gptest "perun.network/go-perun/wallet/test" "perun.network/perun-stellar-backend/wallet" + "perun.network/perun-stellar-backend/wallet/types" pkgtest "polycry.pt/poly-go/test" "testing" ) @@ -43,7 +44,7 @@ func setup(rng *rand.Rand) *gptest.Setup { if err != nil { panic(err) } - z, err := wallet.ZeroAddress() + z, err := types.ZeroAddress() if err != nil { panic(err) } From 1f21d8a7a7497b5f77f7025d8d622404b5761afa Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Tue, 8 Aug 2023 16:52:20 +0200 Subject: [PATCH 03/10] wallet: Add function to obtain types.Participant from wallet.Account directly --- wallet/account.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wallet/account.go b/wallet/account.go index 9712829..5676c76 100644 --- a/wallet/account.go +++ b/wallet/account.go @@ -46,6 +46,10 @@ func (a Account) Address() wallet.Address { 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. func (a Account) SignData(data []byte) ([]byte, error) { if len(a.privateKey) != ed25519.PrivateKeySize { From 266f6687376c8155adb89d1dbed740ec9fb81cc8 Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Tue, 8 Aug 2023 16:53:42 +0200 Subject: [PATCH 04/10] channel/types: Add preliminary StellarAsset type --- channel/types/asset.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 channel/types/asset.go diff --git a/channel/types/asset.go b/channel/types/asset.go new file mode 100644 index 0000000..f36d73e --- /dev/null +++ b/channel/types/asset.go @@ -0,0 +1,60 @@ +package types + +import ( + "errors" + "github.com/stellar/go/xdr" + "perun.network/go-perun/channel" +) + +type StellarAsset struct { +} + +func (s StellarAsset) MarshalBinary() (data []byte, err error) { + //TODO implement me + panic("implement me") +} + +func (s StellarAsset) UnmarshalBinary(data []byte) error { + //TODO implement me + panic("implement me") +} + +func (s StellarAsset) Equal(asset channel.Asset) bool { + //TODO implement me + panic("implement me") +} + +func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) { + //TODO implement me + panic("implement me") +} + +func (s *StellarAsset) FromScAddress(xdr.ScAddress) error { + //TODO implement me + panic("implement me") +} + +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 +} From 88640af5078da2f64fc92a2a9284e34d22954163 Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Tue, 8 Aug 2023 16:54:33 +0200 Subject: [PATCH 05/10] wire: Add conversion between go-perun types and wire types This implements conversion between the types from the backend/go-perun (types.Participant, channel.State, channel.Params) and our wire representation types of the contract-types. --- wire/balances.go | 103 +++++++++++++++++++++++++++++++++++++++ wire/channel.go | 8 +++ wire/params.go | 78 +++++++++++++++++++++++++++++ wire/participant.go | 48 ++++++++++++++++++ wire/participant_test.go | 14 ++++++ wire/state.go | 26 ++++++++++ 6 files changed, 277 insertions(+) diff --git a/wire/balances.go b/wire/balances.go index 9844ae5..9c8c225 100644 --- a/wire/balances.go +++ b/wire/balances.go @@ -2,12 +2,18 @@ package wire import ( "bytes" + "encoding/binary" "errors" xdr3 "github.com/stellar/go-xdr/xdr3" "github.com/stellar/go/xdr" + "math/big" + "perun.network/go-perun/channel" + "perun.network/perun-stellar-backend/channel/types" "perun.network/perun-stellar-backend/wire/scval" ) +var MaxBalance = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 127), big.NewInt(1)) + type Balances struct { BalA xdr.Int128Parts BalB xdr.Int128Parts @@ -123,3 +129,100 @@ func (b *Balances) UnmarshalBinary(data []byte) error { _, err := b.DecodeFrom(d) return err } + +func MakeBalances(alloc channel.Allocation) (Balances, error) { + // TODO: Move all these checks into a compatibility layer + if err := alloc.Valid(); err != nil { + return Balances{}, err + } + // single asset + if len(alloc.Assets) != 1 { + return Balances{}, errors.New("expected exactly one asset") + } + + // No sub-channels + if len(alloc.Locked) != 0 { + return Balances{}, errors.New("expected no locked funds") + } + asset := alloc.Assets[0] + sa, err := types.ToStellarAsset(asset) + if err != nil { + return Balances{}, err + } + token, err := sa.MakeScAddress() + if err != nil { + return Balances{}, err + } + + if alloc.NumParts() != 2 { + return Balances{}, errors.New("expected exactly two parts") + } + + balA := alloc.Balance(0, asset) + xdrBalA, err := MakeInt128Parts(balA) + if err != nil { + return Balances{}, err + } + + balB := alloc.Balance(1, asset) + xdrBalB, err := MakeInt128Parts(balB) + if err != nil { + return Balances{}, err + } + + return Balances{ + BalA: xdrBalA, + BalB: xdrBalB, + Token: token, + }, nil +} + +func ToAllocation(b Balances) (*channel.Allocation, error) { + asset, err := types.NewStellarAssetFromScAddress(b.Token) + if err != nil { + return nil, err + } + alloc := channel.NewAllocation(2, asset) + + balA, err := ToBigInt(b.BalA) + if err != nil { + return nil, err + } + alloc.SetBalance(0, asset, balA) + + balB, err := ToBigInt(b.BalB) + if err != nil { + return nil, err + } + alloc.SetBalance(1, asset, balB) + if err = alloc.Valid(); err != nil { + return nil, err + } + return alloc, nil +} + +// MakeInt128Parts converts a big.Int to xdr.Int128Parts. +// It returns an error if the big.Int is negative or too large. +func MakeInt128Parts(i *big.Int) (xdr.Int128Parts, error) { + if i.Sign() < 0 { + return xdr.Int128Parts{}, errors.New("expected non-negative balance") + } + if i.Cmp(MaxBalance) > 0 { + return xdr.Int128Parts{}, errors.New("balance too large") + } + b := make([]byte, 16) + b = i.FillBytes(b) + hi := binary.BigEndian.Uint64(b[:8]) + lo := binary.BigEndian.Uint64(b[8:]) + return xdr.Int128Parts{ + Hi: xdr.Int64(hi), + Lo: xdr.Uint64(lo), + }, nil +} + +func ToBigInt(i xdr.Int128Parts) (*big.Int, error) { + b := make([]byte, 16) + binary.BigEndian.PutUint64(b[:8], uint64(i.Hi)) + binary.BigEndian.PutUint64(b[8:], uint64(i.Lo)) + return new(big.Int).SetBytes(b), nil +} diff --git a/wire/channel.go b/wire/channel.go index 6cbca7c..cd97b59 100644 --- a/wire/channel.go +++ b/wire/channel.go @@ -117,3 +117,11 @@ func ChannelFromScVal(v xdr.ScVal) (Channel, error) { err := (&p).FromScVal(v) return p, err } + +func MakeChannel(p Params, s State, c Control) Channel { + return Channel{ + Params: p, + State: s, + Control: c, + } +} diff --git a/wire/params.go b/wire/params.go index 2ce5fcc..e6c3a98 100644 --- a/wire/params.go +++ b/wire/params.go @@ -5,6 +5,9 @@ import ( "errors" xdr3 "github.com/stellar/go-xdr/xdr3" "github.com/stellar/go/xdr" + "perun.network/go-perun/channel" + "perun.network/go-perun/wallet" + "perun.network/perun-stellar-backend/wallet/types" "perun.network/perun-stellar-backend/wire/scval" ) @@ -140,3 +143,78 @@ func ParamsFromScVal(v xdr.ScVal) (Params, error) { err := (&p).FromScVal(v) return p, err } + +func MakeParams(params channel.Params) (Params, error) { + if !params.LedgerChannel { + return Params{}, errors.New("expected ledger channel") + } + if params.VirtualChannel { + return Params{}, errors.New("expected non-virtual channel") + } + if !channel.IsNoApp(params.App) { + return Params{}, errors.New("expected no app") + } + + if len(params.Parts) != 2 { + return Params{}, errors.New("expected exactly two participants") + } + + participantA, err := types.ToParticipant(params.Parts[0]) + if err != nil { + return Params{}, err + } + a, err := MakeParticipant(*participantA) + if err != nil { + return Params{}, err + } + participantB, err := types.ToParticipant(params.Parts[1]) + if err != nil { + return Params{}, err + } + b, err := MakeParticipant(*participantB) + if err != nil { + return Params{}, err + } + nonce := MakeNonce(params.Nonce) + return Params{ + A: a, + B: b, + Nonce: nonce, + ChallengeDuration: xdr.Uint64(params.ChallengeDuration), + }, nil +} + +func MustMakeParams(params channel.Params) Params { + p, err := MakeParams(params) + if err != nil { + panic(err) + } + return p +} + +func ToParams(params Params) (channel.Params, error) { + participantA, err := ToParticipant(params.A) + if err != nil { + return channel.Params{}, err + } + participantB, err := ToParticipant(params.B) + if err != nil { + return channel.Params{}, err + } + return channel.Params{ + LedgerChannel: true, + VirtualChannel: false, + App: channel.NoApp(), + Parts: []wallet.Address{&participantA, &participantB}, + Nonce: ToNonce(params.Nonce), + ChallengeDuration: uint64(params.ChallengeDuration), + }, nil +} + +func MakeNonce(nonce channel.Nonce) xdr.ScBytes { + return nonce.FillBytes(make([]byte, NonceLength)) +} + +func ToNonce(bytes xdr.ScBytes) channel.Nonce { + return channel.NonceFromBytes(bytes[:]) +} diff --git a/wire/participant.go b/wire/participant.go index 560515b..95bcc81 100644 --- a/wire/participant.go +++ b/wire/participant.go @@ -2,9 +2,12 @@ package wire import ( "bytes" + "crypto/ed25519" "errors" xdr3 "github.com/stellar/go-xdr/xdr3" + "github.com/stellar/go/keypair" "github.com/stellar/go/xdr" + "perun.network/perun-stellar-backend/wallet/types" "perun.network/perun-stellar-backend/wire/scval" ) @@ -102,3 +105,48 @@ func ParticipantFromScVal(v xdr.ScVal) (Participant, error) { err := (&p).FromScVal(v) return p, err } + +func MakeParticipant(participant types.Participant) (Participant, error) { + addr, err := MakeAccountAddress(&participant.Address) + if err != nil { + return Participant{}, err + } + if len(participant.PublicKey) != PubKeyLength { + return Participant{}, errors.New("invalid public key length") + } + pubKey := xdr.ScBytes(participant.PublicKey) + return Participant{ + Addr: addr, + PubKey: pubKey, + }, 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 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 +} + +func ToParticipant(participant Participant) (types.Participant, error) { + kp, err := ToAccountAddress(participant.Addr) + if err != nil { + return types.Participant{}, err + } + if len(participant.PubKey) != ed25519.PublicKeySize { + return types.Participant{}, errors.New("invalid public key length") + } + return *types.NewParticipant(kp, ed25519.PublicKey(participant.PubKey)), nil +} diff --git a/wire/participant_test.go b/wire/participant_test.go index 6257c19..ef82384 100644 --- a/wire/participant_test.go +++ b/wire/participant_test.go @@ -2,7 +2,9 @@ package wire_test import ( "github.com/stretchr/testify/require" + "perun.network/perun-stellar-backend/wallet" "perun.network/perun-stellar-backend/wire" + pkgtest "polycry.pt/poly-go/test" "testing" ) @@ -16,3 +18,15 @@ func TestParticipant(t *testing.T) { require.NoError(t, err) require.Equal(t, x, res) } + +func TestParticipantConversion(t *testing.T) { + rng := pkgtest.Prng(t) + acc, _, err := wallet.NewRandomAccount(rng) + require.NoError(t, err) + p := *acc.Participant() + wp, err := wire.MakeParticipant(p) + require.NoError(t, err) + res, err := wire.ToParticipant(wp) + require.NoError(t, err) + require.True(t, p.Equal(&res)) +} diff --git a/wire/state.go b/wire/state.go index 8a0d555..6973814 100644 --- a/wire/state.go +++ b/wire/state.go @@ -5,6 +5,7 @@ import ( "errors" xdr3 "github.com/stellar/go-xdr/xdr3" "github.com/stellar/go/xdr" + "perun.network/go-perun/channel" "perun.network/perun-stellar-backend/wire/scval" ) @@ -141,3 +142,28 @@ func StateFromScVal(v xdr.ScVal) (State, error) { err := (&s).FromScVal(v) return s, err } + +func MakeState(state channel.State) (State, error) { + // TODO: Put these checks into a compatibility layer + if err := state.Valid(); err != nil { + return State{}, err + } + if !channel.IsNoApp(state.App) { + return State{}, errors.New("expected NoApp") + } + if !channel.IsNoData(state.Data) { + return State{}, errors.New("expected NoData") + } + balances, err := MakeBalances(state.Allocation) + if err != nil { + return State{}, err + } + return State{ + ChannelID: state.ID[:], + Balances: balances, + Version: xdr.Uint64(state.Version), + Finalized: state.IsFinal, + }, nil +} + +// TODO: Check if ToState is needed From 83093e681b4bb5f5aeb9900e18ef3f020bf5d8f3 Mon Sep 17 00:00:00 2001 From: Jan Bormet Date: Tue, 8 Aug 2023 17:00:48 +0200 Subject: [PATCH 06/10] channel: Implement channel.Backend interface --- channel/backend.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 channel/backend.go diff --git a/channel/backend.go b/channel/backend.go new file mode 100644 index 0000000..59ec995 --- /dev/null +++ b/channel/backend.go @@ -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() +} From fb1e9512553f8d7d9edd3e7657b82ad34fa78c16 Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Tue, 22 Aug 2023 12:02:15 +0200 Subject: [PATCH 07/10] channel/types: Implement Asset interface from go-perun in asset.go. Add MakeAccountAddress and ToAccountAddress functions. wire: Move MakeAccountAddress and ToAccountAddress to channel/types to avoid cyclic imports. --- channel/types/asset.go | 56 +++++++++++++++++++++++++++++++++--------- wire/participant.go | 26 +++----------------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/channel/types/asset.go b/channel/types/asset.go index f36d73e..7c08989 100644 --- a/channel/types/asset.go +++ b/channel/types/asset.go @@ -2,36 +2,51 @@ package types import ( "errors" + "github.com/stellar/go/keypair" "github.com/stellar/go/xdr" "perun.network/go-perun/channel" ) +const AddressLen = 32 + type StellarAsset struct { + address keypair.FromAddress } func (s StellarAsset) MarshalBinary() (data []byte, err error) { - //TODO implement me - panic("implement me") + return s.address.MarshalBinary() } func (s StellarAsset) UnmarshalBinary(data []byte) error { - //TODO implement me - panic("implement me") + var addr [AddressLen]byte + copy(addr[:], data) + err := s.address.UnmarshalBinary(data) + if err != nil { + panic(err) + } + return nil } func (s StellarAsset) Equal(asset channel.Asset) bool { - //TODO implement me - panic("implement me") + _, ok := asset.(*StellarAsset) + return ok } func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) { - //TODO implement me - panic("implement me") + scvAddr, err := MakeAccountAddress(&s.address) + if err != nil { + panic(err) + } + return scvAddr, nil } -func (s *StellarAsset) FromScAddress(xdr.ScAddress) error { - //TODO implement me - panic("implement me") +func (s *StellarAsset) FromScAddress(address xdr.ScAddress) error { + addr, err := ToAccountAddress(address) + if err != nil { + panic(err) + } + s.address = addr + return nil } func NewStellarAssetFromScAddress(address xdr.ScAddress) (*StellarAsset, error) { @@ -58,3 +73,22 @@ func ToStellarAsset(asset channel.Asset) (*StellarAsset, error) { } 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 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 +} diff --git a/wire/participant.go b/wire/participant.go index 95bcc81..7b0e848 100644 --- a/wire/participant.go +++ b/wire/participant.go @@ -5,9 +5,10 @@ import ( "crypto/ed25519" "errors" xdr3 "github.com/stellar/go-xdr/xdr3" - "github.com/stellar/go/keypair" "github.com/stellar/go/xdr" + assettypes "perun.network/perun-stellar-backend/channel/types" "perun.network/perun-stellar-backend/wallet/types" + "perun.network/perun-stellar-backend/wire/scval" ) @@ -107,7 +108,7 @@ func ParticipantFromScVal(v xdr.ScVal) (Participant, error) { } func MakeParticipant(participant types.Participant) (Participant, error) { - addr, err := MakeAccountAddress(&participant.Address) + addr, err := assettypes.MakeAccountAddress(&participant.Address) if err != nil { return Participant{}, err } @@ -121,27 +122,8 @@ func MakeParticipant(participant types.Participant) (Participant, error) { }, 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 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 -} - func ToParticipant(participant Participant) (types.Participant, error) { - kp, err := ToAccountAddress(participant.Addr) + kp, err := assettypes.ToAccountAddress(participant.Addr) if err != nil { return types.Participant{}, err } From 1ab8a6e89ccc03f1374b339754378ae49a9e0a70 Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Fri, 8 Sep 2023 15:58:32 +0200 Subject: [PATCH 08/10] channel: Change type of StellarAsset * Adapt tests to change of type wire: Include initialization of backend and randomizer for go-perun tests * Add tests for ToState, MakeState, ToParams, MakeParams. * Modify error handling wallet: Include NewRandomAddress for go-perun tests go.mod, go.sum: Update stellar/go library commit hash --- channel/test/init.go | 8 +++ channel/test/randomizer.go | 17 ++++++ channel/types/asset.go | 40 ++++++++++---- channel/types/asset_test.go | 44 ++++++++++++++++ go.mod | 17 ++++-- go.sum | 79 +++++++++++++++++++++------ wallet/account.go | 15 ++++++ wallet/test/randomizer.go | 29 ++++++++++ wire/balances.go | 17 ++++++ wire/channel.go | 3 ++ wire/control.go | 3 ++ wire/params.go | 3 ++ wire/participant.go | 9 ++++ wire/state.go | 64 +++++++++++++++++++++- wire/state_test.go | 20 ------- wire/{ => test}/balances_test.go | 2 +- wire/{ => test}/channel_test.go | 2 +- wire/{ => test}/control_test.go | 2 +- wire/test/init.go | 11 ++++ wire/{ => test}/params_test.go | 58 +++++++++++++++++++- wire/{ => test}/participant_test.go | 2 +- wire/test/state_test.go | 82 +++++++++++++++++++++++++++++ 22 files changed, 469 insertions(+), 58 deletions(-) create mode 100644 channel/test/init.go create mode 100644 channel/test/randomizer.go create mode 100644 channel/types/asset_test.go create mode 100644 wallet/test/randomizer.go delete mode 100644 wire/state_test.go rename wire/{ => test}/balances_test.go (98%) rename wire/{ => test}/channel_test.go (99%) rename wire/{ => test}/control_test.go (98%) create mode 100644 wire/test/init.go rename wire/{ => test}/params_test.go (50%) rename wire/{ => test}/participant_test.go (98%) create mode 100644 wire/test/state_test.go diff --git a/channel/test/init.go b/channel/test/init.go new file mode 100644 index 0000000..7283b97 --- /dev/null +++ b/channel/test/init.go @@ -0,0 +1,8 @@ +package test + +import "perun.network/go-perun/channel/test" + +func init() { + test.SetRandomizer(&Randomizer{}) + +} diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go new file mode 100644 index 0000000..fafaedc --- /dev/null +++ b/channel/test/randomizer.go @@ -0,0 +1,17 @@ +package test + +import ( + "math/rand" + + "perun.network/go-perun/channel" + "perun.network/go-perun/channel/test" + "perun.network/perun-stellar-backend/channel/types" +) + +type Randomizer struct{} + +func (*Randomizer) NewRandomAsset(*rand.Rand) channel.Asset { + return &types.StellarAsset{} +} + +var _ test.Randomizer = (*Randomizer)(nil) diff --git a/channel/types/asset.go b/channel/types/asset.go index 7c08989..2ca6575 100644 --- a/channel/types/asset.go +++ b/channel/types/asset.go @@ -7,20 +7,28 @@ import ( "perun.network/go-perun/channel" ) -const AddressLen = 32 +const HashLenXdr = 32 type StellarAsset struct { - address keypair.FromAddress + 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.address.MarshalBinary() + return s.contractID.MarshalBinary() } -func (s StellarAsset) UnmarshalBinary(data []byte) error { - var addr [AddressLen]byte +func (s *StellarAsset) UnmarshalBinary(data []byte) error { + var addr [HashLenXdr]byte copy(addr[:], data) - err := s.address.UnmarshalBinary(data) + err := s.contractID.UnmarshalBinary(data) if err != nil { panic(err) } @@ -33,7 +41,8 @@ func (s StellarAsset) Equal(asset channel.Asset) bool { } func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) { - scvAddr, err := MakeAccountAddress(&s.address) + hash := s.contractID + scvAddr, err := MakeContractAddress(hash) if err != nil { panic(err) } @@ -41,11 +50,12 @@ func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) { } func (s *StellarAsset) FromScAddress(address xdr.ScAddress) error { - addr, err := ToAccountAddress(address) - if err != nil { - panic(err) + addrType := address.Type + if addrType != xdr.ScAddressTypeScAddressTypeContract { + return errors.New("invalid address type") } - s.address = addr + + s.contractID = *address.ContractId return nil } @@ -82,6 +92,14 @@ func MakeAccountAddress(kp keypair.KP) (xdr.ScAddress, error) { return xdr.NewScAddress(xdr.ScAddressTypeScAddressTypeAccount, accountId) } +func MakeContractAddress(contractID xdr.Hash) (xdr.ScAddress, error) { + if len(contractID) != HashLenXdr { + return xdr.ScAddress{}, errors.New("invalid contractID length for xdr.Hash") + } + + 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") diff --git a/channel/types/asset_test.go b/channel/types/asset_test.go new file mode 100644 index 0000000..915ab87 --- /dev/null +++ b/channel/types/asset_test.go @@ -0,0 +1,44 @@ +package types_test + +import ( + "bytes" + "github.com/stellar/go/keypair" + "github.com/stellar/go/xdr" + "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() + if err != nil { + t.Fatal(err) + } + + newAsset := &types.StellarAsset{} + err = newAsset.UnmarshalBinary(data) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal([]byte(newAsset.ContractID().HexString()), []byte(asset.ContractID().HexString())) { + t.Fatalf("expected %x, got %x", asset.ContractID(), newAsset.ContractID()) + } +} + +func TestMakeAccountAddress(t *testing.T) { + kp, _ := keypair.Random() + + address, err := types.MakeAccountAddress(kp) + if err != nil { + t.Fatal(err) + } + + if address.Type != xdr.ScAddressTypeScAddressTypeAccount { + t.Fatalf("expected account address type, got %v", address.Type) + } +} diff --git a/go.mod b/go.mod index f3f6a03..b116acc 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,27 @@ module perun.network/perun-stellar-backend go 1.19 -require github.com/stellar/go v0.0.0-20230801100123-5ded27e97f53 +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/go-chi/chi v4.0.3+incompatible // indirect + github.com/go-errors/errors v0.0.0-20150906023321-a41850380601 // indirect + github.com/gorilla/schema v1.1.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 // 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 + github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/stretchr/objx v0.5.0 // indirect + golang.org/x/sys v0.11.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 862d07d..c0069d1 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,82 @@ +github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmDlfhUrweEy1R1Fj1gu5iIM= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +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/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/gavv/monotime v0.0.0-20161010190848-47d58efa6955 h1:gmtGRvSexPU4B1T/yYo0sLOKzER1YT+b4kPxPpm0Ty4= +github.com/go-chi/chi v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY= +github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-errors/errors v0.0.0-20150906023321-a41850380601 h1:jxTbmDuqQUTI6MscgbqB39vtxGfr2fi61nYIcFQUnlE= +github.com/go-errors/errors v0.0.0-20150906023321-a41850380601/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-querystring v0.0.0-20160401233042-9235644dd9e5 h1:oERTZ1buOUYlpmKaqlO5fYmz8cZ1rYu5DieJzF4ZVmU= +github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/jarcoal/httpmock v0.0.0-20161210151336-4442edb3db31 h1:Aw95BEvxJ3K6o9GGv5ppCd1P8hkeIeEJ30FO+OhOJpM= +github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= +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/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 h1:ykXz+pRRTibcSjG1yRhpdSHInF8yZY/mfn+Rz2Nd1rE= +github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739/go.mod h1:zUx1mhth20V3VKgL5jbd1BSQcW4Fy6Qs4PZvQwRFwzM= +github.com/moul/http2curl v0.0.0-20161031194548-4e24498b31db h1:eZgFHVkk9uOTaOQLC6tgjkzdp7Ays8eEVecBcfHZlJQ= +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/stellar/go v0.0.0-20230801100123-5ded27e97f53 h1:U/W5fqYM9zgmLpLT3jGOY9LbkinXaaqkW+P8bzBTO+0= -github.com/stellar/go v0.0.0-20230801100123-5ded27e97f53/go.mod h1:m9ABBUAExq/TfafyQrLie0owyyAR6IYCRhujttELLHU= +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/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 h1:S4OC0+OBKz6mJnzuHioeEat74PuQ4Sgvbf8eus695sc= +github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2/go.mod h1:8zLRYR5npGjaOXgPSKat5+oOh+UHd8OdbS18iqX9F6Y= +github.com/sergi/go-diff v0.0.0-20161205080420-83532ca1c1ca h1:oR/RycYTFTVXzND5r4FdsvbnBn0HJXSVeNAnwaTXRwk= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stellar/go v0.0.0-20230901231653-c146db6b0c11 h1:b47q1il6CaPIBPdcUrVhtc4Gn+EWN6dFRjd/bsTNQ+U= +github.com/stellar/go v0.0.0-20230901231653-c146db6b0c11/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= +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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= 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= +github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076 h1:KM4T3G70MiR+JtqplcYkNVoNz7pDwYaBxWBXQK804So= +github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c h1:XZWnr3bsDQWAZg4Ne+cPoXRPILrNlPNQfxBuwLl43is= +github.com/xeipuuv/gojsonschema v0.0.0-20161231055540-f06f290571ce h1:cVSRGH8cOveJNwFEEZLXtB+XMnRqKLjUP6V/ZFYQCXI= +github.com/yalp/jsonpath v0.0.0-20150812003900-31a79c7593bb h1:06WAhQa+mYv7BiOk13B/ywyTlkoE/S7uu6TBKU6FHnE= +github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d h1:yJIizrfO599ot2kQ6Af1enICnwBD3XoxgX3MrMwot2M= +github.com/yudai/golcs v0.0.0-20150405163532-d1c525dea8ce h1:888GrqRxabUce7lj4OaoShPxodm3kXOMpSa85wdYzfY= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/gavv/httpexpect.v1 v1.0.0-20170111145843-40724cf1e4a0 h1:r5ptJ1tBxVAeqw4CrYWhXIMr0SybY3CDHuIbCg5CFVw= 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= diff --git a/wallet/account.go b/wallet/account.go index 5676c76..e373def 100644 --- a/wallet/account.go +++ b/wallet/account.go @@ -41,6 +41,21 @@ 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 types.NewParticipant(a.ParticipantAddress, a.privateKey.Public().(ed25519.PublicKey)) diff --git a/wallet/test/randomizer.go b/wallet/test/randomizer.go new file mode 100644 index 0000000..72d0f3a --- /dev/null +++ b/wallet/test/randomizer.go @@ -0,0 +1,29 @@ +package test + +import ( + "math/rand" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wallet/test" + swallet "perun.network/perun-stellar-backend/wallet" +) + +type Randomizer struct { + Account *swallet.Account +} + +// NewRandomAddress implements test.Randomizer +func (r *Randomizer) NewRandomAddress(rng *rand.Rand) wallet.Address { + return swallet.NewRandomAddress(rng) +} + +// NewWallet implements test.Randomizer +func (*Randomizer) NewWallet() test.Wallet { + panic("unimplemented") +} + +// RandomWallet implements test.Randomizer +func (*Randomizer) RandomWallet() test.Wallet { + panic("unimplemented") +} + +var _ test.Randomizer = (*Randomizer)(nil) diff --git a/wire/balances.go b/wire/balances.go index 9c8c225..bb1d658 100644 --- a/wire/balances.go +++ b/wire/balances.go @@ -226,3 +226,20 @@ func ToBigInt(i xdr.Int128Parts) (*big.Int, error) { binary.BigEndian.PutUint64(b[8:], uint64(i.Lo)) return new(big.Int).SetBytes(b), nil } + +func makeAllocation(assets []channel.Asset, balA, balB *big.Int) (channel.Allocation, error) { + balanceForAssets := []channel.Bal{balA, balB} + + alloc := channel.Allocation{ + Assets: assets, + Balances: [][]channel.Bal{ + balanceForAssets, + }, + } + + if err := alloc.Valid(); err != nil { + return channel.Allocation{}, err + } + + return alloc, nil +} diff --git a/wire/channel.go b/wire/channel.go index cd97b59..a45b557 100644 --- a/wire/channel.go +++ b/wire/channel.go @@ -41,6 +41,9 @@ func (c Channel) ToScVal() (xdr.ScVal, error) { }, []xdr.ScVal{params, state, control}, ) + if err != nil { + return xdr.ScVal{}, err + } return scval.WrapScMap(m) } diff --git a/wire/control.go b/wire/control.go index 78dd7d5..7d352cb 100644 --- a/wire/control.go +++ b/wire/control.go @@ -70,6 +70,9 @@ func (c Control) ToScVal() (xdr.ScVal, error) { }, []xdr.ScVal{fundedA, fundedB, closed, withdrawnA, withdrawnB, disputed, timestamp}, ) + if err != nil { + return xdr.ScVal{}, err + } return scval.WrapScMap(m) } diff --git a/wire/params.go b/wire/params.go index e6c3a98..9dfc68e 100644 --- a/wire/params.go +++ b/wire/params.go @@ -55,6 +55,9 @@ func (p Params) ToScVal() (xdr.ScVal, error) { }, []xdr.ScVal{a, b, nonce, challengeDuration}, ) + if err != nil { + return xdr.ScVal{}, err + } return scval.WrapScMap(m) } diff --git a/wire/participant.go b/wire/participant.go index 7b0e848..b689c62 100644 --- a/wire/participant.go +++ b/wire/participant.go @@ -32,6 +32,9 @@ func (p Participant) ToScVal() (xdr.ScVal, error) { return xdr.ScVal{}, errors.New("invalid public key length") } pubKey, err := scval.WrapScBytes(p.PubKey) + if err != nil { + return xdr.ScVal{}, err + } m, err := MakeSymbolScMap( []xdr.ScSymbol{ SymbolParticipantAddr, @@ -39,6 +42,9 @@ func (p Participant) ToScVal() (xdr.ScVal, error) { }, []xdr.ScVal{addr, pubKey}, ) + if err != nil { + return xdr.ScVal{}, err + } return scval.WrapScMap(m) } @@ -63,6 +69,9 @@ func (p *Participant) FromScVal(v xdr.ScVal) error { return err } pubKey, ok := pubKeyVal.GetBytes() + if !ok { + return errors.New("expected bytes") + } if len(pubKey) != PubKeyLength { return errors.New("invalid public key length") } diff --git a/wire/state.go b/wire/state.go index 6973814..85c9c59 100644 --- a/wire/state.go +++ b/wire/state.go @@ -3,9 +3,11 @@ package wire import ( "bytes" "errors" + "fmt" xdr3 "github.com/stellar/go-xdr/xdr3" "github.com/stellar/go/xdr" "perun.network/go-perun/channel" + "perun.network/perun-stellar-backend/channel/types" "perun.network/perun-stellar-backend/wire/scval" ) @@ -22,7 +24,7 @@ type State struct { ChannelID xdr.ScBytes Balances Balances Version xdr.Uint64 - Finalized bool // Bool + Finalized bool } func (s State) ToScVal() (xdr.ScVal, error) { @@ -54,6 +56,9 @@ func (s State) ToScVal() (xdr.ScVal, error) { }, []xdr.ScVal{channelID, balances, version, finalized}, ) + if err != nil { + return xdr.ScVal{}, err + } return scval.WrapScMap(m) } @@ -166,4 +171,59 @@ func MakeState(state channel.State) (State, error) { }, nil } -// TODO: Check if ToState is needed +func scBytesToByteArray(bytesXdr xdr.ScBytes) ([types.HashLenXdr]byte, error) { + if len(bytesXdr) != types.HashLenXdr { + return [types.HashLenXdr]byte{}, fmt.Errorf("expected length of %d bytes, got %d", types.HashLenXdr, len(bytesXdr)) + } + var arr [types.HashLenXdr]byte + copy(arr[:], bytesXdr[:types.HashLenXdr]) + return arr, nil +} + +func ToState(stellarState State) (channel.State, error) { + ChanID, err := scBytesToByteArray(stellarState.ChannelID) + if err != nil { + return channel.State{}, err + } + + BalA, err := ToBigInt(stellarState.Balances.BalA) + if err != nil { + return channel.State{}, err + } + BalB, err := ToBigInt(stellarState.Balances.BalB) + if err != nil { + return channel.State{}, err + } + + Assets, err := convertAsset(stellarState.Balances.Token) + if err != nil { + return channel.State{}, err + } + + Alloc, err := makeAllocation(Assets, BalA, BalB) + if err != nil { + return channel.State{}, err + } + + PerunState := channel.State{ID: ChanID, + Version: uint64(stellarState.Version), + Allocation: Alloc, + IsFinal: stellarState.Finalized, + App: channel.NoApp(), + Data: channel.NoData(), + } + + if PerunState.Valid() != nil { + return channel.State{}, err + } + + return PerunState, nil +} + +func convertAsset(contractID xdr.ScAddress) ([]channel.Asset, error) { + stellarAsset, err := types.NewStellarAssetFromScAddress(contractID) + if err != nil { + return nil, err + } + return []channel.Asset{stellarAsset}, nil +} diff --git a/wire/state_test.go b/wire/state_test.go deleted file mode 100644 index 625ee98..0000000 --- a/wire/state_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package wire_test - -import ( - "fmt" - "github.com/stretchr/testify/require" - "perun.network/perun-stellar-backend/wire" - "testing" -) - -func TestState(t *testing.T) { - // State XDR generated by soroban contract - x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 48, 207, 108, 223, 126, 81, 182, 30, 80, 205, 206, 164, 29, 186, 161, 49, 19, 220, 169, 154, 82, 230, 27, 46, 112, 182, 98, 53, 61, 128, 204, 23, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 135, 93, 16, 248, 119, 202, 15, 58, 94, 69, 71, 246, 71, 210, 225, 253, 36, 204, 170, 27, 91, 210, 4, 152, 129, 94, 12, 28, 183, 59, 230, 206, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} - state := &wire.State{} - err := state.UnmarshalBinary(x) - require.NoError(t, err) - fmt.Println(state) - res, err := state.MarshalBinary() - require.NoError(t, err) - require.Equal(t, x, res) -} diff --git a/wire/balances_test.go b/wire/test/balances_test.go similarity index 98% rename from wire/balances_test.go rename to wire/test/balances_test.go index 3e6f5ee..1cc651d 100644 --- a/wire/balances_test.go +++ b/wire/test/balances_test.go @@ -1,4 +1,4 @@ -package wire_test +package test import ( "fmt" diff --git a/wire/channel_test.go b/wire/test/channel_test.go similarity index 99% rename from wire/channel_test.go rename to wire/test/channel_test.go index dd356ad..ea59f79 100644 --- a/wire/channel_test.go +++ b/wire/test/channel_test.go @@ -1,4 +1,4 @@ -package wire_test +package test import ( "github.com/stretchr/testify/require" diff --git a/wire/control_test.go b/wire/test/control_test.go similarity index 98% rename from wire/control_test.go rename to wire/test/control_test.go index 149f085..504d523 100644 --- a/wire/control_test.go +++ b/wire/test/control_test.go @@ -1,4 +1,4 @@ -package wire_test +package test import ( "github.com/stretchr/testify/require" diff --git a/wire/test/init.go b/wire/test/init.go new file mode 100644 index 0000000..943706e --- /dev/null +++ b/wire/test/init.go @@ -0,0 +1,11 @@ +package test + +import ( + "perun.network/go-perun/wallet/test" + _ "perun.network/perun-stellar-backend/channel/test" + wtest "perun.network/perun-stellar-backend/wallet/test" +) + +func init() { + test.SetRandomizer(&wtest.Randomizer{}) +} diff --git a/wire/params_test.go b/wire/test/params_test.go similarity index 50% rename from wire/params_test.go rename to wire/test/params_test.go index e294a13..bf05b70 100644 --- a/wire/params_test.go +++ b/wire/test/params_test.go @@ -1,8 +1,15 @@ -package wire_test +package test import ( "github.com/stretchr/testify/require" + "math/big" + "perun.network/go-perun/channel" + ptest "perun.network/go-perun/channel/test" + schannel "perun.network/perun-stellar-backend/channel" + "perun.network/perun-stellar-backend/wire" + + pkgtest "polycry.pt/poly-go/test" "testing" ) @@ -15,3 +22,52 @@ func TestParams(t *testing.T) { require.NoError(t, err) require.Equal(t, x, res) } + +func TestParamsConversion(t *testing.T) { + rng := pkgtest.Prng(t) + + numParts := 2 + + perunFirstParams := *ptest.NewRandomParams(rng, ptest.WithNumLocked(0).Append( + ptest.WithNumParts(numParts), + ptest.WithBalancesInRange(big.NewInt(0), big.NewInt(1<<60)), + ptest.WithLedgerChannel(true), + ptest.WithVirtualChannel(false), + ptest.WithNumAssets(1), + ptest.WithoutApp(), + )) + + stellarFirstParams, err := wire.MakeParams(perunFirstParams) + require.NoError(t, err) + + perunLastParams, err := wire.ToParams(stellarFirstParams) + require.NoError(t, err) + + checkPerunParamsEquality(t, perunFirstParams, perunLastParams, numParts) + + stellarLastParams, err := wire.MakeParams(perunLastParams) + require.NoError(t, err) + + checkStellarParamsEquality(t, stellarFirstParams, stellarLastParams) +} + +func checkPerunParamsEquality(t *testing.T, first, last channel.Params, numParts int) { + require.Equal(t, first.ID(), schannel.Backend.CalcID(&last)) + + for i := 0; i < numParts; i++ { + require.True(t, last.Parts[i].Equal(first.Parts[i])) + } + + require.Equal(t, first.ChallengeDuration, last.ChallengeDuration) + require.Equal(t, first.Nonce, last.Nonce) + require.Equal(t, first.App, last.App) + require.Equal(t, first.LedgerChannel, last.LedgerChannel) + require.Equal(t, first.VirtualChannel, last.VirtualChannel) +} + +func checkStellarParamsEquality(t *testing.T, first, last wire.Params) { + require.Equal(t, first.A, last.A) + require.Equal(t, first.B, last.B) + require.Equal(t, first.ChallengeDuration, last.ChallengeDuration) + require.Equal(t, first.Nonce, last.Nonce) +} diff --git a/wire/participant_test.go b/wire/test/participant_test.go similarity index 98% rename from wire/participant_test.go rename to wire/test/participant_test.go index ef82384..66f8930 100644 --- a/wire/participant_test.go +++ b/wire/test/participant_test.go @@ -1,4 +1,4 @@ -package wire_test +package test import ( "github.com/stretchr/testify/require" diff --git a/wire/test/state_test.go b/wire/test/state_test.go new file mode 100644 index 0000000..99475a1 --- /dev/null +++ b/wire/test/state_test.go @@ -0,0 +1,82 @@ +package test + +import ( + "fmt" + "github.com/stretchr/testify/require" + "math/big" + "perun.network/go-perun/channel" + ptest "perun.network/go-perun/channel/test" + "perun.network/perun-stellar-backend/wire" + polytest "polycry.pt/poly-go/test" + "testing" +) + +func TestState(t *testing.T) { + // State XDR generated by soroban contract + x := []byte{0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 15, 0, 0, 0, 8, 98, 97, 108, 97, 110, 99, 101, 115, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 97, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 15, 0, 0, 0, 5, 98, 97, 108, 95, 98, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 15, 0, 0, 0, 5, 116, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 1, 48, 207, 108, 223, 126, 81, 182, 30, 80, 205, 206, 164, 29, 186, 161, 49, 19, 220, 169, 154, 82, 230, 27, 46, 112, 182, 98, 53, 61, 128, 204, 23, 0, 0, 0, 15, 0, 0, 0, 10, 99, 104, 97, 110, 110, 101, 108, 95, 105, 100, 0, 0, 0, 0, 0, 13, 0, 0, 0, 32, 135, 93, 16, 248, 119, 202, 15, 58, 94, 69, 71, 246, 71, 210, 225, 253, 36, 204, 170, 27, 91, 210, 4, 152, 129, 94, 12, 28, 183, 59, 230, 206, 0, 0, 0, 15, 0, 0, 0, 9, 102, 105, 110, 97, 108, 105, 122, 101, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 7, 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0} + state := &wire.State{} + err := state.UnmarshalBinary(x) + require.NoError(t, err) + fmt.Println(state) + res, err := state.MarshalBinary() + require.NoError(t, err) + require.Equal(t, x, res) +} + +func TestStateConversion(t *testing.T) { + rng := polytest.Prng(t) + perunFirstState := *ptest.NewRandomState(rng, + ptest.WithNumParts(2), + ptest.WithNumAssets(1), + ptest.WithNumLocked(0), + ptest.WithoutApp(), + ptest.WithBalancesInRange(big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000_000)), big.NewInt(0).Mul(big.NewInt(1), big.NewInt(100_000_000))), + ) + + stellarFirstState, err := wire.MakeState(perunFirstState) + require.NoError(t, err) + + perunLastState, err := wire.ToState(stellarFirstState) + require.NoError(t, err) + + validatePerunStates(t, perunFirstState, perunLastState) + + stellarLastState, err := wire.MakeState(perunLastState) + require.NoError(t, err) + + checkStellarStateEquality(t, stellarFirstState, stellarLastState) +} + +func validatePerunStates(t *testing.T, first, last channel.State) { + checkAssetsEquality(t, first, last) + checkNoLockedAmount(t, first) + checkNoLockedAmount(t, last) + checkPerunStateEquality(t, first, last) +} + +func checkAssetsEquality(t *testing.T, first, last channel.State) { + for i, asset := range first.Allocation.Assets { + require.True(t, asset.Equal(last.Allocation.Assets[i])) + } +} + +func checkNoLockedAmount(t *testing.T, state channel.State) { + if len(state.Allocation.Locked) != 0 { + t.Fatal("locked amount should be empty") + } +} + +func checkPerunStateEquality(t *testing.T, first, last channel.State) { + require.Equal(t, first.IsFinal, last.IsFinal) + require.Equal(t, first.ID, last.ID) + require.Equal(t, first.Version, last.Version) +} + +func checkStellarStateEquality(t *testing.T, first, last wire.State) { + require.Equal(t, first.Version, last.Version) + require.Equal(t, first.ChannelID, last.ChannelID) + require.Equal(t, first.Finalized, last.Finalized) + require.Equal(t, first.Balances.BalA, last.Balances.BalA) + require.Equal(t, first.Balances.BalB, last.Balances.BalB) + require.Equal(t, first.Balances.Token, last.Balances.Token) +} From 199ae5494b9b55a3e9eb3bcfd1d8b794c132b1ee Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Fri, 8 Sep 2023 17:51:37 +0200 Subject: [PATCH 09/10] channel: Fix NewRandomAsset method * Adjust the remaining subpackages to this change * Change error handling t.Fatal -> require wire: Move test files to wire, rename package wallet: Move Randomizer initialization to wallet/test go.mod, go.sum: Adjust packages to fixes --- channel/test/init.go | 1 - channel/test/randomizer.go | 14 ++++++++--- channel/types/asset.go | 4 --- channel/types/asset_test.go | 23 ++++++----------- go.mod | 8 ------ go.sum | 38 ----------------------------- {wire => wallet}/test/init.go | 3 +-- wire/{test => }/balances_test.go | 2 +- wire/{test => }/channel_test.go | 2 +- wire/{test => }/control_test.go | 2 +- wire/{test => }/params_test.go | 3 ++- wire/{test => }/participant_test.go | 2 +- wire/{test => }/state_test.go | 2 +- 13 files changed, 26 insertions(+), 78 deletions(-) rename {wire => wallet}/test/init.go (57%) rename wire/{test => }/balances_test.go (98%) rename wire/{test => }/channel_test.go (99%) rename wire/{test => }/control_test.go (98%) rename wire/{test => }/params_test.go (98%) rename wire/{test => }/participant_test.go (98%) rename wire/{test => }/state_test.go (99%) diff --git a/channel/test/init.go b/channel/test/init.go index 7283b97..a39657d 100644 --- a/channel/test/init.go +++ b/channel/test/init.go @@ -4,5 +4,4 @@ import "perun.network/go-perun/channel/test" func init() { test.SetRandomizer(&Randomizer{}) - } diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index fafaedc..9ed0f6b 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -1,17 +1,25 @@ package test 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" + "time" ) type Randomizer struct{} +var _ test.Randomizer = (*Randomizer)(nil) + func (*Randomizer) NewRandomAsset(*rand.Rand) channel.Asset { - return &types.StellarAsset{} + return NewRandomStellarAsset() } -var _ test.Randomizer = (*Randomizer)(nil) +func NewRandomStellarAsset() *types.StellarAsset { + var contractID xdr.Hash + rand.Seed(time.Now().UnixNano()) + rand.Read(contractID[:]) + return types.NewStellarAsset(contractID) +} diff --git a/channel/types/asset.go b/channel/types/asset.go index 2ca6575..2c555b6 100644 --- a/channel/types/asset.go +++ b/channel/types/asset.go @@ -93,10 +93,6 @@ func MakeAccountAddress(kp keypair.KP) (xdr.ScAddress, error) { } func MakeContractAddress(contractID xdr.Hash) (xdr.ScAddress, error) { - if len(contractID) != HashLenXdr { - return xdr.ScAddress{}, errors.New("invalid contractID length for xdr.Hash") - } - return xdr.NewScAddress(xdr.ScAddressTypeScAddressTypeContract, contractID) } diff --git a/channel/types/asset_test.go b/channel/types/asset_test.go index 915ab87..0285035 100644 --- a/channel/types/asset_test.go +++ b/channel/types/asset_test.go @@ -1,10 +1,11 @@ package types_test import ( - "bytes" "github.com/stellar/go/keypair" "github.com/stellar/go/xdr" + "github.com/stretchr/testify/require" "perun.network/perun-stellar-backend/channel/types" + "testing" ) @@ -15,30 +16,20 @@ func TestMarshalAndUnmarshalBinary(t *testing.T) { asset := types.NewStellarAsset(hash) data, err := asset.MarshalBinary() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) newAsset := &types.StellarAsset{} err = newAsset.UnmarshalBinary(data) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) - if !bytes.Equal([]byte(newAsset.ContractID().HexString()), []byte(asset.ContractID().HexString())) { - t.Fatalf("expected %x, got %x", asset.ContractID(), newAsset.ContractID()) - } + 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) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) - if address.Type != xdr.ScAddressTypeScAddressTypeAccount { - t.Fatalf("expected account address type, got %v", address.Type) - } + require.Equal(t, xdr.ScAddressTypeScAddressTypeAccount, address.Type, "Expected account address type, got %v", address.Type) } diff --git a/go.mod b/go.mod index b116acc..3687df9 100644 --- a/go.mod +++ b/go.mod @@ -13,16 +13,8 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-chi/chi v4.0.3+incompatible // indirect - github.com/go-errors/errors v0.0.0-20150906023321-a41850380601 // indirect - github.com/gorilla/schema v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - github.com/stretchr/objx v0.5.0 // indirect - golang.org/x/sys v0.11.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c0069d1..ddebfba 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,13 @@ -github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmDlfhUrweEy1R1Fj1gu5iIM= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 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/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/gavv/monotime v0.0.0-20161010190848-47d58efa6955 h1:gmtGRvSexPU4B1T/yYo0sLOKzER1YT+b4kPxPpm0Ty4= -github.com/go-chi/chi v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY= -github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-errors/errors v0.0.0-20150906023321-a41850380601 h1:jxTbmDuqQUTI6MscgbqB39vtxGfr2fi61nYIcFQUnlE= -github.com/go-errors/errors v0.0.0-20150906023321-a41850380601/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-querystring v0.0.0-20160401233042-9235644dd9e5 h1:oERTZ1buOUYlpmKaqlO5fYmz8cZ1rYu5DieJzF4ZVmU= -github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= -github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= -github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= -github.com/jarcoal/httpmock v0.0.0-20161210151336-4442edb3db31 h1:Aw95BEvxJ3K6o9GGv5ppCd1P8hkeIeEJ30FO+OhOJpM= -github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739 h1:ykXz+pRRTibcSjG1yRhpdSHInF8yZY/mfn+Rz2Nd1rE= -github.com/manucorporat/sse v0.0.0-20160126180136-ee05b128a739/go.mod h1:zUx1mhth20V3VKgL5jbd1BSQcW4Fy6Qs4PZvQwRFwzM= -github.com/moul/http2curl v0.0.0-20161031194548-4e24498b31db h1:eZgFHVkk9uOTaOQLC6tgjkzdp7Ays8eEVecBcfHZlJQ= 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= @@ -35,44 +18,23 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 h1:S4OC0+OBKz6mJnzuHioeEat74PuQ4Sgvbf8eus695sc= -github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2/go.mod h1:8zLRYR5npGjaOXgPSKat5+oOh+UHd8OdbS18iqX9F6Y= -github.com/sergi/go-diff v0.0.0-20161205080420-83532ca1c1ca h1:oR/RycYTFTVXzND5r4FdsvbnBn0HJXSVeNAnwaTXRwk= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/stellar/go v0.0.0-20230901231653-c146db6b0c11 h1:b47q1il6CaPIBPdcUrVhtc4Gn+EWN6dFRjd/bsTNQ+U= -github.com/stellar/go v0.0.0-20230901231653-c146db6b0c11/go.mod h1:5/qoLl0pexA5OPi0BZvDsOc3532CJlHuRg1dnBxbsGg= 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= github.com/xdrpp/goxdr v0.1.1 h1:E1B2c6E8eYhOVyd7yEpOyopzTPirUeF6mVOfXfGyJyc= -github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076 h1:KM4T3G70MiR+JtqplcYkNVoNz7pDwYaBxWBXQK804So= -github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c h1:XZWnr3bsDQWAZg4Ne+cPoXRPILrNlPNQfxBuwLl43is= -github.com/xeipuuv/gojsonschema v0.0.0-20161231055540-f06f290571ce h1:cVSRGH8cOveJNwFEEZLXtB+XMnRqKLjUP6V/ZFYQCXI= -github.com/yalp/jsonpath v0.0.0-20150812003900-31a79c7593bb h1:06WAhQa+mYv7BiOk13B/ywyTlkoE/S7uu6TBKU6FHnE= -github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d h1:yJIizrfO599ot2kQ6Af1enICnwBD3XoxgX3MrMwot2M= -github.com/yudai/golcs v0.0.0-20150405163532-d1c525dea8ce h1:888GrqRxabUce7lj4OaoShPxodm3kXOMpSa85wdYzfY= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/gavv/httpexpect.v1 v1.0.0-20170111145843-40724cf1e4a0 h1:r5ptJ1tBxVAeqw4CrYWhXIMr0SybY3CDHuIbCg5CFVw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 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= diff --git a/wire/test/init.go b/wallet/test/init.go similarity index 57% rename from wire/test/init.go rename to wallet/test/init.go index 943706e..4117711 100644 --- a/wire/test/init.go +++ b/wallet/test/init.go @@ -3,9 +3,8 @@ package test import ( "perun.network/go-perun/wallet/test" _ "perun.network/perun-stellar-backend/channel/test" - wtest "perun.network/perun-stellar-backend/wallet/test" ) func init() { - test.SetRandomizer(&wtest.Randomizer{}) + test.SetRandomizer(&Randomizer{}) } diff --git a/wire/test/balances_test.go b/wire/balances_test.go similarity index 98% rename from wire/test/balances_test.go rename to wire/balances_test.go index 1cc651d..3e6f5ee 100644 --- a/wire/test/balances_test.go +++ b/wire/balances_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "fmt" diff --git a/wire/test/channel_test.go b/wire/channel_test.go similarity index 99% rename from wire/test/channel_test.go rename to wire/channel_test.go index ea59f79..dd356ad 100644 --- a/wire/test/channel_test.go +++ b/wire/channel_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "github.com/stretchr/testify/require" diff --git a/wire/test/control_test.go b/wire/control_test.go similarity index 98% rename from wire/test/control_test.go rename to wire/control_test.go index 504d523..149f085 100644 --- a/wire/test/control_test.go +++ b/wire/control_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "github.com/stretchr/testify/require" diff --git a/wire/test/params_test.go b/wire/params_test.go similarity index 98% rename from wire/test/params_test.go rename to wire/params_test.go index bf05b70..51ba24b 100644 --- a/wire/test/params_test.go +++ b/wire/params_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "github.com/stretchr/testify/require" @@ -7,6 +7,7 @@ import ( ptest "perun.network/go-perun/channel/test" schannel "perun.network/perun-stellar-backend/channel" + _ "perun.network/perun-stellar-backend/wallet/test" "perun.network/perun-stellar-backend/wire" pkgtest "polycry.pt/poly-go/test" diff --git a/wire/test/participant_test.go b/wire/participant_test.go similarity index 98% rename from wire/test/participant_test.go rename to wire/participant_test.go index 66f8930..ef82384 100644 --- a/wire/test/participant_test.go +++ b/wire/participant_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "github.com/stretchr/testify/require" diff --git a/wire/test/state_test.go b/wire/state_test.go similarity index 99% rename from wire/test/state_test.go rename to wire/state_test.go index 99475a1..1c93888 100644 --- a/wire/test/state_test.go +++ b/wire/state_test.go @@ -1,4 +1,4 @@ -package test +package wire_test import ( "fmt" From d5535bf7f6a5df7832c7c042126af5ff991a8e74 Mon Sep 17 00:00:00 2001 From: Ilja von Hoessle Date: Mon, 11 Sep 2023 12:19:40 +0200 Subject: [PATCH 10/10] channel: Remove manual seed in randomizer, return errors instead of panic in StellarAsset methods wire: Use methods from go-perun to make allocation for go-perun state --- channel/test/randomizer.go | 2 -- channel/types/asset.go | 4 ++-- wire/balances.go | 15 +++++---------- wire/state.go | 6 +++--- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index 9ed0f6b..92cb05f 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -6,7 +6,6 @@ import ( "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" "perun.network/perun-stellar-backend/channel/types" - "time" ) type Randomizer struct{} @@ -19,7 +18,6 @@ func (*Randomizer) NewRandomAsset(*rand.Rand) channel.Asset { func NewRandomStellarAsset() *types.StellarAsset { var contractID xdr.Hash - rand.Seed(time.Now().UnixNano()) rand.Read(contractID[:]) return types.NewStellarAsset(contractID) } diff --git a/channel/types/asset.go b/channel/types/asset.go index 2c555b6..362b4d5 100644 --- a/channel/types/asset.go +++ b/channel/types/asset.go @@ -30,7 +30,7 @@ func (s *StellarAsset) UnmarshalBinary(data []byte) error { copy(addr[:], data) err := s.contractID.UnmarshalBinary(data) if err != nil { - panic(err) + return errors.New("could not unmarshal contract id") } return nil } @@ -44,7 +44,7 @@ func (s StellarAsset) MakeScAddress() (xdr.ScAddress, error) { hash := s.contractID scvAddr, err := MakeContractAddress(hash) if err != nil { - panic(err) + return xdr.ScAddress{}, errors.New("could not generate contract address") } return scvAddr, nil } diff --git a/wire/balances.go b/wire/balances.go index bb1d658..b63f210 100644 --- a/wire/balances.go +++ b/wire/balances.go @@ -227,18 +227,13 @@ func ToBigInt(i xdr.Int128Parts) (*big.Int, error) { return new(big.Int).SetBytes(b), nil } -func makeAllocation(assets []channel.Asset, balA, balB *big.Int) (channel.Allocation, error) { - balanceForAssets := []channel.Bal{balA, balB} - - alloc := channel.Allocation{ - Assets: assets, - Balances: [][]channel.Bal{ - balanceForAssets, - }, - } +func makeAllocation(asset channel.Asset, balA, balB *big.Int) (*channel.Allocation, error) { + alloc := channel.NewAllocation(2, asset) + alloc.SetBalance(0, asset, balA) + alloc.SetBalance(1, asset, balB) if err := alloc.Valid(); err != nil { - return channel.Allocation{}, err + return nil, err } return alloc, nil diff --git a/wire/state.go b/wire/state.go index 85c9c59..70c4aa8 100644 --- a/wire/state.go +++ b/wire/state.go @@ -207,7 +207,7 @@ func ToState(stellarState State) (channel.State, error) { PerunState := channel.State{ID: ChanID, Version: uint64(stellarState.Version), - Allocation: Alloc, + Allocation: *Alloc, IsFinal: stellarState.Finalized, App: channel.NoApp(), Data: channel.NoData(), @@ -220,10 +220,10 @@ func ToState(stellarState State) (channel.State, error) { return PerunState, nil } -func convertAsset(contractID xdr.ScAddress) ([]channel.Asset, error) { +func convertAsset(contractID xdr.ScAddress) (channel.Asset, error) { stellarAsset, err := types.NewStellarAssetFromScAddress(contractID) if err != nil { return nil, err } - return []channel.Asset{stellarAsset}, nil + return stellarAsset, nil }