2020-02-13 00:28:23 +00:00
|
|
|
package paychmgr
|
2019-09-16 13:46:05 +00:00
|
|
|
|
|
|
|
import (
|
2020-02-29 03:23:55 +00:00
|
|
|
"bytes"
|
2019-09-16 13:46:05 +00:00
|
|
|
"context"
|
|
|
|
|
2020-02-12 23:52:19 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin"
|
|
|
|
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
2019-09-16 13:46:05 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
2019-12-01 21:52:24 +00:00
|
|
|
"golang.org/x/xerrors"
|
2019-09-16 13:46:05 +00:00
|
|
|
|
2019-12-19 20:13:17 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2020-02-12 23:52:19 +00:00
|
|
|
|
2020-06-04 13:54:37 +00:00
|
|
|
"github.com/filecoin-project/lotus/build"
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2019-09-16 13:46:05 +00:00
|
|
|
)
|
|
|
|
|
2020-04-17 00:25:06 +00:00
|
|
|
func (pm *Manager) createPaych(ctx context.Context, from, to address.Address, amt types.BigInt) (cid.Cid, error) {
|
2020-02-29 02:10:11 +00:00
|
|
|
params, aerr := actors.SerializeParams(&paych.ConstructorParams{From: from, To: to})
|
2019-09-16 13:46:05 +00:00
|
|
|
if aerr != nil {
|
2020-04-17 00:25:06 +00:00
|
|
|
return cid.Undef, aerr
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 23:52:19 +00:00
|
|
|
enc, aerr := actors.SerializeParams(&init_.ExecParams{
|
2020-02-25 20:54:58 +00:00
|
|
|
CodeCID: builtin.PaymentChannelActorCodeID,
|
2020-02-12 23:52:19 +00:00
|
|
|
ConstructorParams: params,
|
2019-09-16 13:46:05 +00:00
|
|
|
})
|
2019-09-17 08:15:26 +00:00
|
|
|
if aerr != nil {
|
2020-04-17 00:25:06 +00:00
|
|
|
return cid.Undef, aerr
|
2019-09-17 08:15:26 +00:00
|
|
|
}
|
2019-09-16 13:46:05 +00:00
|
|
|
|
|
|
|
msg := &types.Message{
|
2020-02-25 20:54:58 +00:00
|
|
|
To: builtin.InitActorAddr,
|
2019-09-16 13:46:05 +00:00
|
|
|
From: from,
|
|
|
|
Value: amt,
|
2020-02-12 23:52:19 +00:00
|
|
|
Method: builtin.MethodsInit.Exec,
|
2019-09-16 13:46:05 +00:00
|
|
|
Params: enc,
|
2020-07-15 17:53:27 +00:00
|
|
|
GasLimit: 100_000_000,
|
2019-09-16 13:46:05 +00:00
|
|
|
GasPrice: types.NewInt(0),
|
|
|
|
}
|
|
|
|
|
2019-09-17 08:15:26 +00:00
|
|
|
smsg, err := pm.mpool.MpoolPushMessage(ctx, msg)
|
2019-09-16 13:46:05 +00:00
|
|
|
if err != nil {
|
2020-04-17 00:25:06 +00:00
|
|
|
return cid.Undef, xerrors.Errorf("initializing paych actor: %w", err)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
2020-04-21 20:48:43 +00:00
|
|
|
mcid := smsg.Cid()
|
|
|
|
go pm.waitForPaychCreateMsg(ctx, mcid)
|
|
|
|
return mcid, nil
|
2020-04-17 00:25:06 +00:00
|
|
|
}
|
2019-09-16 13:46:05 +00:00
|
|
|
|
2020-04-20 21:44:27 +00:00
|
|
|
// WaitForPaychCreateMsg waits for mcid to appear on chain and returns the robust address of the
|
|
|
|
// created payment channel
|
2020-04-21 20:48:43 +00:00
|
|
|
// TODO: wait outside the store lock!
|
|
|
|
// (tricky because we need to setup channel tracking before we know its address)
|
|
|
|
func (pm *Manager) waitForPaychCreateMsg(ctx context.Context, mcid cid.Cid) {
|
|
|
|
defer pm.store.lk.Unlock()
|
2020-06-03 21:42:06 +00:00
|
|
|
mwait, err := pm.state.StateWaitMsg(ctx, mcid, build.MessageConfidence)
|
2019-09-16 13:46:05 +00:00
|
|
|
if err != nil {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Errorf("wait msg: %w", err)
|
2020-06-05 17:59:28 +00:00
|
|
|
return
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if mwait.Receipt.ExitCode != 0 {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Errorf("payment channel creation failed (exit code %d)", mwait.Receipt.ExitCode)
|
2020-06-05 18:06:42 +00:00
|
|
|
return
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 03:23:55 +00:00
|
|
|
var decodedReturn init_.ExecReturn
|
|
|
|
err = decodedReturn.UnmarshalCBOR(bytes.NewReader(mwait.Receipt.Return))
|
2019-09-16 13:46:05 +00:00
|
|
|
if err != nil {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Error(err)
|
2020-06-05 18:06:42 +00:00
|
|
|
return
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
2020-02-29 03:23:55 +00:00
|
|
|
paychaddr := decodedReturn.RobustAddress
|
2019-09-16 13:46:05 +00:00
|
|
|
|
2020-07-09 22:27:39 +00:00
|
|
|
ci, err := pm.loadStateChannelInfo(ctx, paychaddr, DirOutbound)
|
2019-09-16 17:23:48 +00:00
|
|
|
if err != nil {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Errorf("loading channel info: %w", err)
|
2020-06-05 18:06:42 +00:00
|
|
|
return
|
2019-09-16 17:23:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := pm.store.trackChannel(ci); err != nil {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Errorf("tracking channel: %w", err)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 00:25:06 +00:00
|
|
|
func (pm *Manager) addFunds(ctx context.Context, ch address.Address, from address.Address, amt types.BigInt) (cid.Cid, error) {
|
2019-09-16 13:46:05 +00:00
|
|
|
msg := &types.Message{
|
|
|
|
To: ch,
|
|
|
|
From: from,
|
|
|
|
Value: amt,
|
|
|
|
Method: 0,
|
2020-07-15 17:53:27 +00:00
|
|
|
GasLimit: 100_000_000,
|
2019-09-16 13:46:05 +00:00
|
|
|
GasPrice: types.NewInt(0),
|
|
|
|
}
|
|
|
|
|
2019-09-17 08:15:26 +00:00
|
|
|
smsg, err := pm.mpool.MpoolPushMessage(ctx, msg)
|
2019-09-16 13:46:05 +00:00
|
|
|
if err != nil {
|
2020-04-17 00:25:06 +00:00
|
|
|
return cid.Undef, err
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
2020-04-21 20:48:43 +00:00
|
|
|
mcid := smsg.Cid()
|
|
|
|
go pm.waitForAddFundsMsg(ctx, mcid)
|
|
|
|
return mcid, nil
|
2020-04-17 00:25:06 +00:00
|
|
|
}
|
|
|
|
|
2020-04-20 21:44:27 +00:00
|
|
|
// WaitForAddFundsMsg waits for mcid to appear on chain and returns error, if any
|
2020-04-21 20:48:43 +00:00
|
|
|
// TODO: wait outside the store lock!
|
|
|
|
// (tricky because we need to setup channel tracking before we know it's address)
|
|
|
|
func (pm *Manager) waitForAddFundsMsg(ctx context.Context, mcid cid.Cid) {
|
|
|
|
defer pm.store.lk.Unlock()
|
2020-06-03 21:42:06 +00:00
|
|
|
mwait, err := pm.state.StateWaitMsg(ctx, mcid, build.MessageConfidence)
|
2019-09-16 13:46:05 +00:00
|
|
|
if err != nil {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Error(err)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if mwait.Receipt.ExitCode != 0 {
|
2020-04-21 20:48:43 +00:00
|
|
|
log.Errorf("voucher channel creation failed: adding funds (exit code %d)", mwait.Receipt.ExitCode)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pm *Manager) GetPaych(ctx context.Context, from, to address.Address, ensureFree types.BigInt) (address.Address, cid.Cid, error) {
|
2020-04-22 21:09:43 +00:00
|
|
|
pm.store.lk.Lock() // unlock only on err; wait funcs will defer unlock
|
2020-04-17 00:25:06 +00:00
|
|
|
var mcid cid.Cid
|
2019-09-16 13:46:05 +00:00
|
|
|
ch, err := pm.store.findChan(func(ci *ChannelInfo) bool {
|
|
|
|
if ci.Direction != DirOutbound {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return ci.Control == from && ci.Target == to
|
|
|
|
})
|
|
|
|
if err != nil {
|
2020-04-22 20:58:26 +00:00
|
|
|
pm.store.lk.Unlock()
|
2019-12-01 21:52:24 +00:00
|
|
|
return address.Undef, cid.Undef, xerrors.Errorf("findChan: %w", err)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
|
|
|
if ch != address.Undef {
|
|
|
|
// TODO: Track available funds
|
2020-04-17 00:25:06 +00:00
|
|
|
mcid, err = pm.addFunds(ctx, ch, from, ensureFree)
|
|
|
|
} else {
|
|
|
|
mcid, err = pm.createPaych(ctx, from, to, ensureFree)
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|
2020-04-22 20:58:26 +00:00
|
|
|
if err != nil {
|
|
|
|
pm.store.lk.Unlock()
|
|
|
|
}
|
2020-04-17 00:25:06 +00:00
|
|
|
return ch, mcid, err
|
2019-09-16 13:46:05 +00:00
|
|
|
}
|