diff --git a/pkg/chain/message.go b/pkg/chain/message.go index 752a8fc..4f9aab0 100644 --- a/pkg/chain/message.go +++ b/pkg/chain/message.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/chain-validation/pkg/state/actors" "github.com/filecoin-project/chain-validation/pkg/state/actors/initialize" "github.com/filecoin-project/chain-validation/pkg/state/actors/multsig" + "github.com/filecoin-project/chain-validation/pkg/state/actors/paych" "github.com/filecoin-project/chain-validation/pkg/state/actors/strgminr" "github.com/filecoin-project/chain-validation/pkg/state/actors/strgpwr" "github.com/filecoin-project/chain-validation/pkg/state/address" @@ -60,6 +61,13 @@ const ( MultiSigSwapSigner MultiSigChangeRequirement + PaymentChannelConstructor + PaymentChannelUpdate + PaymentChannelClose + PaymentChannelCollect + PaymentChannelGetOwner + PaymentChannelGetToSend + GetSectorSize CronConstructor CronTick @@ -328,4 +336,33 @@ func (mp *MessageProducer) MultiSigChangeRequirement(to, from address.Address, n return mp.Build(from, to, nonce, MultiSigChangeRequirement, params, opts...) } +// +// Payment Channel Actor Methods +// + +func (mp *MessageProducer) PaychUpdateChannelState(to, from address.Address, nonce uint64, sv types.SignedVoucher, secret, proof []byte, opts ...MsgOpt) (interface{}, error) { + params, err := types.Serialize(&paych.PaymentChannelUpdateParams{ + Sv: sv, + Secret: secret, + Proof: proof, + }) + if err != nil { + return nil, err + } + return mp.Build(from, to, nonce, PaymentChannelUpdate, params, opts...) +} + +func (mp *MessageProducer) PaychClose(to, from address.Address, nonce uint64, opts ...MsgOpt) (interface{}, error) { + return mp.Build(from, to, nonce, PaymentChannelClose, noParams, opts...) +} +func (mp *MessageProducer) PaychCollect(to, from address.Address, nonce uint64, opts ...MsgOpt) (interface{}, error) { + return mp.Build(from, to, nonce, PaymentChannelCollect, noParams, opts...) +} +func (mp *MessageProducer) PaychGetOwner(to, from address.Address, nonce uint64, opts ...MsgOpt) (interface{}, error) { + return mp.Build(from, to, nonce, PaymentChannelGetOwner, noParams, opts...) +} +func (mp *MessageProducer) PaychGetToSend(to, from address.Address, nonce uint64, opts ...MsgOpt) (interface{}, error) { + return mp.Build(from, to, nonce, PaymentChannelGetToSend, noParams, opts...) +} + var noParams []byte diff --git a/pkg/gen/main.go b/pkg/gen/main.go index e7e60ca..e666327 100644 --- a/pkg/gen/main.go +++ b/pkg/gen/main.go @@ -18,7 +18,6 @@ func main() { types.SignedVoucher{}, types.Merge{}, types.ModVerifyParams{}, - types.Signature{}, ); err != nil { panic(err) } @@ -35,6 +34,9 @@ func main() { paych.PaymentInfo{}, paych.PaymentChannelActorState{}, paych.LaneState{}, + paych.PaymentChannelConstructorParams{}, + paych.PaymentChannelUpdateParams{}, + paych.PaymentVerifyParams{}, ); err != nil { panic(err) } diff --git a/pkg/state/actors/paych/cbor_gen.go b/pkg/state/actors/paych/cbor_gen.go index 1d0b16d..bfef351 100644 --- a/pkg/state/actors/paych/cbor_gen.go +++ b/pkg/state/actors/paych/cbor_gen.go @@ -5,10 +5,9 @@ import ( "io" "sort" + "github.com/filecoin-project/chain-validation/pkg/state/types" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" - - "github.com/filecoin-project/chain-validation/pkg/state/types" ) /* This file was generated by github.com/whyrusleeping/cbor-gen */ @@ -405,3 +404,218 @@ func (t *LaneState) UnmarshalCBOR(r io.Reader) error { t.Nonce = uint64(extra) return nil } + +func (t *PaymentChannelConstructorParams) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{129}); err != nil { + return err + } + + // t.t.To (address.Address) (struct) + if err := t.To.MarshalCBOR(w); err != nil { + return err + } + return nil +} + +func (t *PaymentChannelConstructorParams) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 1 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.To (address.Address) (struct) + + { + + if err := t.To.UnmarshalCBOR(br); err != nil { + return err + } + + } + return nil +} + +func (t *PaymentChannelUpdateParams) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{131}); err != nil { + return err + } + + // t.t.Sv (types.SignedVoucher) (struct) + if err := t.Sv.MarshalCBOR(w); err != nil { + return err + } + + // t.t.Secret ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Secret)))); err != nil { + return err + } + if _, err := w.Write(t.Secret); err != nil { + return err + } + + // t.t.Proof ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Proof)))); err != nil { + return err + } + if _, err := w.Write(t.Proof); err != nil { + return err + } + return nil +} + +func (t *PaymentChannelUpdateParams) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 3 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.Sv (types.SignedVoucher) (struct) + + { + + if err := t.Sv.UnmarshalCBOR(br); err != nil { + return err + } + + } + // t.t.Secret ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Secret: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Secret = make([]byte, extra) + if _, err := io.ReadFull(br, t.Secret); err != nil { + return err + } + // t.t.Proof ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Proof: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Proof = make([]byte, extra) + if _, err := io.ReadFull(br, t.Proof); err != nil { + return err + } + return nil +} + +func (t *PaymentVerifyParams) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{130}); err != nil { + return err + } + + // t.t.Extra ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Extra)))); err != nil { + return err + } + if _, err := w.Write(t.Extra); err != nil { + return err + } + + // t.t.Proof ([]uint8) (slice) + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Proof)))); err != nil { + return err + } + if _, err := w.Write(t.Proof); err != nil { + return err + } + return nil +} + +func (t *PaymentVerifyParams) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 2 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.t.Extra ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Extra: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Extra = make([]byte, extra) + if _, err := io.ReadFull(br, t.Extra); err != nil { + return err + } + // t.t.Proof ([]uint8) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if extra > 8192 { + return fmt.Errorf("t.Proof: array too large (%d)", extra) + } + + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + t.Proof = make([]byte, extra) + if _, err := io.ReadFull(br, t.Proof); err != nil { + return err + } + return nil +} diff --git a/pkg/state/actors/paych/payment_channel.go b/pkg/state/actors/paych/payment_channel.go index 7e29421..7704cb5 100644 --- a/pkg/state/actors/paych/payment_channel.go +++ b/pkg/state/actors/paych/payment_channel.go @@ -34,3 +34,18 @@ type PaymentChannelActorState struct { // waiting on refmt#35 to be fixed LaneStates map[string]*LaneState } + +type PaymentChannelConstructorParams struct { + To address.Address +} + +type PaymentChannelUpdateParams struct { + Sv types.SignedVoucher + Secret []byte + Proof []byte +} + +type PaymentVerifyParams struct { + Extra []byte + Proof []byte +} diff --git a/pkg/state/types/cbor_gen.go b/pkg/state/types/cbor_gen.go index c7245ce..22d36e9 100644 --- a/pkg/state/types/cbor_gen.go +++ b/pkg/state/types/cbor_gen.go @@ -369,75 +369,3 @@ func (t *ModVerifyParams) UnmarshalCBOR(r io.Reader) error { } return nil } - -func (t *Signature) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - if _, err := w.Write([]byte{130}); err != nil { - return err - } - - // t.t.Type (string) (string) - if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Type)))); err != nil { - return err - } - if _, err := w.Write([]byte(t.Type)); err != nil { - return err - } - - // t.t.Data ([]uint8) (slice) - if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.Data)))); err != nil { - return err - } - if _, err := w.Write(t.Data); err != nil { - return err - } - return nil -} - -func (t *Signature) UnmarshalCBOR(r io.Reader) error { - br := cbg.GetPeeker(r) - - maj, extra, err := cbg.CborReadHeader(br) - if err != nil { - return err - } - if maj != cbg.MajArray { - return fmt.Errorf("cbor input should be of type array") - } - - if extra != 2 { - return fmt.Errorf("cbor input had wrong number of fields") - } - - // t.t.Type (string) (string) - - { - sval, err := cbg.ReadString(br) - if err != nil { - return err - } - - t.Type = string(sval) - } - // t.t.Data ([]uint8) (slice) - - maj, extra, err = cbg.CborReadHeader(br) - if err != nil { - return err - } - if extra > 8192 { - return fmt.Errorf("t.Data: array too large (%d)", extra) - } - - if maj != cbg.MajByteString { - return fmt.Errorf("expected byte array") - } - t.Data = make([]byte, extra) - if _, err := io.ReadFull(br, t.Data); err != nil { - return err - } - return nil -} diff --git a/pkg/state/types/signature.go b/pkg/state/types/signature.go index b91822f..b1792d5 100644 --- a/pkg/state/types/signature.go +++ b/pkg/state/types/signature.go @@ -1,6 +1,93 @@ package types +import ( + "fmt" + "io" + + cbg "github.com/whyrusleeping/cbor-gen" +) + +const SignatureMaxLength = 200 + +const ( + KTSecp256k1 = "secp256k1" + KTBLS = "bls" +) + +const ( + IKTUnknown = -1 + + IKTSecp256k1 = iota + IKTBLS +) + type Signature struct { Type string Data []byte } + +func (s *Signature) TypeCode() int { + switch s.Type { + case KTSecp256k1: + return IKTSecp256k1 + case KTBLS: + return IKTBLS + default: + return IKTUnknown + } +} + +func (s *Signature) MarshalCBOR(w io.Writer) error { + if s == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + header := cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(s.Data)+1)) + + if _, err := w.Write(header); err != nil { + return err + } + + if _, err := w.Write([]byte{byte(s.TypeCode())}); err != nil { + return err + } + + if _, err := w.Write(s.Data); err != nil { + return err + } + + return nil +} + +func (s *Signature) UnmarshalCBOR(br io.Reader) error { + maj, l, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + + if maj != cbg.MajByteString { + return fmt.Errorf("cbor input for signature was not a byte string") + } + + if l > SignatureMaxLength { + return fmt.Errorf("cbor byte array for signature was too long") + } + + buf := make([]byte, l) + if _, err := io.ReadFull(br, buf); err != nil { + return err + } + + switch buf[0] { + default: + return fmt.Errorf("invalid signature type in cbor input: %d", buf[0]) + case IKTSecp256k1: + s.Type = KTSecp256k1 + case IKTBLS: + s.Type = KTBLS + } + s.Data = buf[1:] + + return nil +} diff --git a/pkg/state/types/voucher.go b/pkg/state/types/voucher.go index 6eb8df1..72f10de 100644 --- a/pkg/state/types/voucher.go +++ b/pkg/state/types/voucher.go @@ -1,6 +1,10 @@ package types -import "github.com/filecoin-project/chain-validation/pkg/state/address" +import ( + "bytes" + + "github.com/filecoin-project/chain-validation/pkg/state/address" +) type SignedVoucher struct { TimeLock uint64 @@ -16,6 +20,18 @@ type SignedVoucher struct { Signature *Signature } +func (sv *SignedVoucher) SigningBytes() ([]byte, error) { + osv := *sv + osv.Signature = nil + + buf := new(bytes.Buffer) + if err := osv.MarshalCBOR(buf); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + type Merge struct { Lane uint64 Nonce uint64 diff --git a/pkg/state/wrapper.go b/pkg/state/wrapper.go index 6143231..938806c 100644 --- a/pkg/state/wrapper.go +++ b/pkg/state/wrapper.go @@ -1,6 +1,7 @@ package state import ( + "context" "github.com/filecoin-project/chain-validation/pkg/state/actors" "github.com/ipfs/go-cid" @@ -23,6 +24,9 @@ type Wrapper interface { // Creates a new private key and returns the associated address. NewAccountAddress() (address.Address, error) + // Sign data with addr's key. + Sign(ctx context.Context, addr address.Address, data []byte) (*types.Signature, error) + // Installs a new actor in the state tree. // This signature will probably become a little more complex when the actor state is non-empty. SetActor(address address.Address, code actors.ActorCodeID, balance types.BigInt) (Actor, Storage, error) diff --git a/pkg/suites/driver.go b/pkg/suites/driver.go index 3015225..d5d8e9e 100644 --- a/pkg/suites/driver.go +++ b/pkg/suites/driver.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/chain-validation/pkg/state" "github.com/filecoin-project/chain-validation/pkg/state/actors" "github.com/filecoin-project/chain-validation/pkg/state/actors/multsig" + "github.com/filecoin-project/chain-validation/pkg/state/actors/paych" "github.com/filecoin-project/chain-validation/pkg/state/actors/strgminr" "github.com/filecoin-project/chain-validation/pkg/state/address" "github.com/filecoin-project/chain-validation/pkg/state/types" @@ -106,3 +107,27 @@ func (d *StateDriver) AssertMultisigState(multisigAddr address.Address, expected assert.Contains(d.tb, multisig.Signers, e, fmt.Sprintf("expected Signer: %v, actual Signer: %v", e, multisig.Signers)) } } + +func (d *StateDriver) AssertPayChState(paychAddr address.Address, expected paych.PaymentChannelActorState) { + paychActor, err := d.State().Actor(paychAddr) + require.NoError(d.tb, err) + + paychStorage, err := d.State().Storage(paychAddr) + require.NoError(d.tb, err) + + var paychState paych.PaymentChannelActorState + require.NoError(d.tb, paychStorage.Get(paychActor.Head(), &paychState)) + + assert.NotNil(d.tb, paychState) + assert.Equal(d.tb, expected.To, paychState.To, fmt.Sprintf("expected To: %v, actual To: %v", expected.To, paychState.To)) + assert.Equal(d.tb, expected.From, paychState.From, fmt.Sprintf("expected From: %v, actual From: %v", expected.From, paychState.From)) + assert.Equal(d.tb, expected.ClosingAt, paychState.ClosingAt, fmt.Sprintf("expected ClosingAt: %v, actual ClosingAt: %v", expected.ClosingAt, paychState.ClosingAt)) + assert.Equal(d.tb, expected.MinCloseHeight, paychState.MinCloseHeight, fmt.Sprintf("expected MinCloseHeight: %v, actual MinCloseHeight: %v", expected.MinCloseHeight, paychState.MinCloseHeight)) + assert.Equal(d.tb, expected.ToSend, paychState.ToSend, fmt.Sprintf("expected ToSend: %v, actual ToSend: %v", expected.ToSend, paychState.ToSend)) + + assert.Equal(d.tb, len(expected.LaneStates), len(paychState.LaneStates), fmt.Sprintf("expected LaneState size: %v, actual LaneState size: %v", len(expected.LaneStates), len(paychState.LaneStates))) + for k, _ := range expected.LaneStates { + assert.Equal(d.tb, expected.LaneStates[k], paychState.LaneStates[k], fmt.Sprintf("expected LaneStates: %v, actual LaneStates: %v", expected.LaneStates, paychState.LaneStates)) + } + +} diff --git a/pkg/suites/multisig.go b/pkg/suites/multisig.go index fe15bc7..e4f05f5 100644 --- a/pkg/suites/multisig.go +++ b/pkg/suites/multisig.go @@ -336,7 +336,6 @@ func mustCreateMultisigActor(gdg *multiSigTestingWrapper, nonce, value uint64, r msgReceipt, err := gdg.Validator.ApplyMessage(gdg.ExeCtx, gdg.Driver.State(), msg) require.NoError(gdg.T, err) - require.NoError(gdg.T, err) gdg.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ ExitCode: 0, ReturnValue: ms.Bytes(), diff --git a/pkg/suites/paych.go b/pkg/suites/paych.go new file mode 100644 index 0000000..4bb0674 --- /dev/null +++ b/pkg/suites/paych.go @@ -0,0 +1,270 @@ +package suites + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/chain-validation/pkg/chain" + "github.com/filecoin-project/chain-validation/pkg/state/actors" + "github.com/filecoin-project/chain-validation/pkg/state/actors/paych" + "github.com/filecoin-project/chain-validation/pkg/state/address" + "github.com/filecoin-project/chain-validation/pkg/state/types" +) + +// taken from lotus /build/params_shared.go +const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours + +type paychTestingWrapper struct { + T testing.TB + Driver *StateDriver + Producer *chain.MessageProducer + Validator *chain.Validator + ExeCtx *chain.ExecutionContext +} + +func paychTestSetup(t testing.TB, factory Factories) *paychTestingWrapper { + drv := NewStateDriver(t, factory.NewState()) + gasPrice := types.NewInt(1) + gasLimit := types.GasUnit(1000000) + + _, _, err := drv.State().SetSingletonActor(actors.InitAddress, types.NewInt(0)) + require.NoError(t, err) + _, _, err = drv.State().SetSingletonActor(actors.BurntFundsAddress, types.NewInt(0)) + require.NoError(t, err) + _, _, err = drv.State().SetSingletonActor(actors.NetworkAddress, TotalNetworkBalance) + require.NoError(t, err) + _, _, err = drv.State().SetSingletonActor(actors.StoragePowerAddress, types.NewInt(0)) + require.NoError(t, err) + + producer := chain.NewMessageProducer(factory.NewMessageFactory(drv.State()), gasLimit, gasPrice) + validator := chain.NewValidator(factory) + + testMiner := drv.NewAccountActor(0) + exeCtx := chain.NewExecutionContext(1, testMiner) + + return &paychTestingWrapper{ + T: t, + Driver: drv, + Producer: producer, + Validator: validator, + ExeCtx: exeCtx, + } + +} + +func PayChActorConstructor(t testing.TB, factory Factories) { + const initialBal = 200000000000 + const valueSend = 10 + + w := paychTestSetup(t, factory) + + alice := w.Driver.NewAccountActor(initialBal) + bob := w.Driver.NewAccountActor(initialBal) + + paychAddr, err := address.NewIDAddress(103) + require.NoError(t, err) + + // alice creates a payment channel with bob. + mustCreatePaychActor(w, 0, valueSend, paychAddr, alice, bob) + w.Driver.AssertBalance(paychAddr, valueSend) + w.Driver.AssertPayChState(paychAddr, paych.PaymentChannelActorState{ + From: alice, + To: bob, + ToSend: types.NewInt(0), + ClosingAt: 0, + MinCloseHeight: 0, + LaneStates: nil, + }) +} + +func PayChActorUpdate(t testing.TB, factory Factories) { + const initialBal = 200000000000 + const paychInitBal = 0 + const paychVoucherAmount = 50 + const paychUpdateBal = 100 + + w := paychTestSetup(t, factory) + + alice := w.Driver.NewAccountActor(initialBal) + bob := w.Driver.NewAccountActor(initialBal) + + paychAddr, err := address.NewIDAddress(103) + require.NoError(t, err) + + // alice creates a payment channel with bob. + mustCreatePaychActor(w, 0, paychInitBal, paychAddr, alice, bob) + w.Driver.AssertBalance(paychAddr, paychInitBal) + w.Driver.AssertPayChState(paychAddr, paych.PaymentChannelActorState{ + From: alice, + To: bob, + ToSend: types.NewInt(0), + ClosingAt: 0, + MinCloseHeight: 0, + LaneStates: nil, + }) + + sv := &types.SignedVoucher{ + Nonce: 0, + Amount: types.NewInt(paychVoucherAmount), + } + signMe, err := sv.SigningBytes() + require.NoError(t, err) + sig, err := w.Driver.State().Sign(context.TODO(), alice, signMe) + require.NoError(t, err) + sv.Signature = sig + + // unused but required + proof, secret := []byte{}, []byte{} + // alice updates the payment channel + mustUpdatePaychActor(w, 1, paychUpdateBal, paychAddr, alice, proof, secret, *sv) + w.Driver.AssertBalance(paychAddr, paychUpdateBal) + w.Driver.AssertPayChState(paychAddr, paych.PaymentChannelActorState{ + From: alice, + To: bob, + ToSend: types.NewInt(paychVoucherAmount), + ClosingAt: 0, + MinCloseHeight: 0, + LaneStates: map[string]*paych.LaneState{ + "0": { + Redeemed: types.NewInt(paychVoucherAmount), + Closed: false, + Nonce: 0, + }, + }, + }) + + // alice asserts that they own the channel and the amount to send is correct. + assertPaychOwner(w, 2, 0, paychAddr, alice, alice) + assertPaychToSend(w, 3, 0, paychAddr, alice, types.NewInt(paychVoucherAmount)) + + // alice closes the channel + mustClosePaych(w, 4, 0, paychAddr, alice) + w.Driver.AssertPayChState(paychAddr, paych.PaymentChannelActorState{ + From: alice, + To: bob, + ToSend: types.NewInt(paychVoucherAmount), + ClosingAt: PaymentChannelClosingDelay + w.ExeCtx.Epoch, + MinCloseHeight: 0, + LaneStates: map[string]*paych.LaneState{ + "0": { + Redeemed: types.NewInt(paychVoucherAmount), + Closed: false, + Nonce: 0, + }, + }, + }) + + // advance the ChainEpoch to cause the channel to close + w.ExeCtx.Epoch += PaymentChannelClosingDelay + // bob collects the payment from alice + const gasPaiedByBob = 360 + mustCollectPaych(w, 0, 0, paychAddr, bob) + w.Driver.AssertPayChState(paychAddr, paych.PaymentChannelActorState{ + From: alice, + To: bob, + ToSend: types.NewInt(0), // the funds must have moved to bob. + ClosingAt: PaymentChannelClosingDelay + 1, + MinCloseHeight: 0, + LaneStates: map[string]*paych.LaneState{ + "0": { + Redeemed: types.NewInt(paychVoucherAmount), + Closed: false, + Nonce: 0, + }, + }, + }) + + // This will break if gas ever changes..and it will.. + w.Driver.AssertBalance(bob, initialBal+paychVoucherAmount-gasPaiedByBob) + +} + +func mustCreatePaychActor(w *paychTestingWrapper, nonce, value uint64, paychAddr, creator, paychTo address.Address) { + paychConstructParams, err := types.Serialize(&paych.PaymentChannelConstructorParams{To: paychTo}) + require.NoError(w.T, err) + + msg, err := w.Producer.InitExec(creator, nonce, actors.PaymentChannelActorCodeCid, paychConstructParams, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: paychAddr.Bytes(), + GasUsed: 0, + }) +} + +func mustUpdatePaychActor(w *paychTestingWrapper, nonce, value uint64, to, from address.Address, secret, proof []byte, sv types.SignedVoucher) { + msg, err := w.Producer.PaychUpdateChannelState(to, from, nonce, sv, secret, proof, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: nil, + GasUsed: 0, + }) +} + +func mustClosePaych(w *paychTestingWrapper, nonce, value uint64, paychAddr, from address.Address) { + msg, err := w.Producer.PaychClose(paychAddr, from, nonce, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: nil, + GasUsed: 0, + }) +} + +func mustCollectPaych(w *paychTestingWrapper, nonce, value uint64, paychAddr, from address.Address) { + msg, err := w.Producer.PaychCollect(paychAddr, from, nonce, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: nil, + GasUsed: 0, + }) + +} + +func assertPaychOwner(w *paychTestingWrapper, nonce, value uint64, paychAddr, from, owner address.Address) { + msg, err := w.Producer.PaychGetOwner(paychAddr, from, nonce, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: owner.Bytes(), + GasUsed: 0, + }) +} + +func assertPaychToSend(w *paychTestingWrapper, nonce, value uint64, paychAddr, from address.Address, toSend types.BigInt) { + msg, err := w.Producer.PaychGetToSend(paychAddr, from, nonce, chain.Value(value)) + require.NoError(w.T, err) + + msgReceipt, err := w.Validator.ApplyMessage(w.ExeCtx, w.Driver.State(), msg) + require.NoError(w.T, err) + + w.Driver.AssertReceipt(msgReceipt, chain.MessageReceipt{ + ExitCode: 0, + ReturnValue: toSend.Bytes(), + GasUsed: 0, + }) +}