github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/paych/paych_actor.go (about)

     1  package paych
     2  
     3  import (
     4  	"bytes"
     5  
     6  	addr "github.com/filecoin-project/go-address"
     7  	"github.com/filecoin-project/go-state-types/abi"
     8  	"github.com/filecoin-project/go-state-types/big"
     9  	"github.com/filecoin-project/go-state-types/cbor"
    10  	"github.com/filecoin-project/go-state-types/exitcode"
    11  	paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych"
    12  	paych2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/paych"
    13  
    14  	"github.com/ipfs/go-cid"
    15  
    16  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    17  	"github.com/filecoin-project/specs-actors/v4/actors/runtime"
    18  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    19  )
    20  
    21  const (
    22  	ErrChannelStateUpdateAfterSettled = exitcode.FirstActorSpecificExitCode + iota
    23  )
    24  
    25  type Actor struct{}
    26  
    27  func (a Actor) Exports() []interface{} {
    28  	return []interface{}{
    29  		builtin.MethodConstructor: a.Constructor,
    30  		2:                         a.UpdateChannelState,
    31  		3:                         a.Settle,
    32  		4:                         a.Collect,
    33  	}
    34  }
    35  
    36  func (a Actor) Code() cid.Cid {
    37  	return builtin.PaymentChannelActorCodeID
    38  }
    39  
    40  func (a Actor) State() cbor.Er {
    41  	return new(State)
    42  }
    43  
    44  var _ runtime.VMActor = Actor{}
    45  
    46  //type ConstructorParams struct {
    47  //	From addr.Address // Payer
    48  //	To   addr.Address // Payee
    49  //}
    50  type ConstructorParams = paych0.ConstructorParams
    51  
    52  // Constructor creates a payment channel actor. See State for meaning of params.
    53  func (pca *Actor) Constructor(rt runtime.Runtime, params *ConstructorParams) *abi.EmptyValue {
    54  	// Only InitActor can create a payment channel actor. It creates the actor on
    55  	// behalf of the payer/payee.
    56  	rt.ValidateImmediateCallerType(builtin.InitActorCodeID)
    57  
    58  	// check that both parties are capable of signing vouchers
    59  	to, err := pca.resolveAccount(rt, params.To)
    60  	builtin.RequireNoErr(rt, err, exitcode.Unwrap(err, exitcode.ErrIllegalState), "failed to resolve to address: %s", params.To)
    61  	from, err := pca.resolveAccount(rt, params.From)
    62  	builtin.RequireNoErr(rt, err, exitcode.Unwrap(err, exitcode.ErrIllegalState), "failed to resolve from address: %s", params.From)
    63  
    64  	emptyArr, err := adt.MakeEmptyArray(adt.AsStore(rt), LaneStatesAmtBitwidth)
    65  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to create empty array")
    66  	emptyArrCid, err := emptyArr.Root()
    67  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to persist empty array")
    68  
    69  	st := ConstructState(from, to, emptyArrCid)
    70  	rt.StateCreate(st)
    71  
    72  	return nil
    73  }
    74  
    75  // Resolves an address to a canonical ID address and requires it to address an account actor.
    76  func (pca *Actor) resolveAccount(rt runtime.Runtime, raw addr.Address) (addr.Address, error) {
    77  	resolved, err := builtin.ResolveToIDAddr(rt, raw)
    78  	if err != nil {
    79  		return addr.Undef, exitcode.ErrIllegalState.Wrapf("failed to resolve address %v: %w", raw, err)
    80  	}
    81  
    82  	codeCID, ok := rt.GetActorCodeCID(resolved)
    83  	if !ok {
    84  		return addr.Undef, exitcode.ErrIllegalArgument.Wrapf("no code for address %v", resolved)
    85  	}
    86  	if codeCID != builtin.AccountActorCodeID {
    87  		return addr.Undef, exitcode.ErrForbidden.Wrapf("actor %v must be an account (%v), was %v", raw,
    88  			builtin.AccountActorCodeID, codeCID)
    89  	}
    90  
    91  	return resolved, nil
    92  }
    93  
    94  ////////////////////////////////////////////////////////////////////////////////
    95  // Payment Channel state operations
    96  ////////////////////////////////////////////////////////////////////////////////
    97  
    98  // type UpdateChannelStateParams struct {
    99  // 	Sv     SignedVoucher
   100  // 	Secret []byte
   101  // }
   102  type UpdateChannelStateParams = paych2.UpdateChannelStateParams
   103  
   104  // A voucher is sent by `From` to `To` off-chain in order to enable
   105  // `To` to redeem payments on-chain in the future
   106  //type SignedVoucher struct {
   107  //	// ChannelAddr is the address of the payment channel this signed voucher is valid for
   108  //	ChannelAddr addr.Address
   109  //	// TimeLockMin sets a min epoch before which the voucher cannot be redeemed
   110  //	TimeLockMin abi.ChainEpoch
   111  //	// TimeLockMax sets a max epoch beyond which the voucher cannot be redeemed
   112  //	// TimeLockMax set to 0 means no timeout
   113  //	TimeLockMax abi.ChainEpoch
   114  //	// (optional) The SecretPreImage is used by `To` to validate
   115  //	SecretPreimage []byte
   116  //	// (optional) Extra can be specified by `From` to add a verification method to the voucher.
   117  //	Extra *ModVerifyParams
   118  //	// Specifies which lane the Voucher merges into (will be created if does not exist)
   119  //	Lane uint64
   120  //	// Nonce is set by `From` to prevent redemption of stale vouchers on a lane
   121  //	Nonce uint64
   122  //	// Amount voucher can be redeemed for
   123  //	Amount big.Int
   124  //	// (optional) MinSettleHeight can extend channel MinSettleHeight if needed
   125  //	MinSettleHeight abi.ChainEpoch
   126  //
   127  //	// (optional) Set of lanes to be merged into `Lane`
   128  //	Merges []Merge
   129  //
   130  //	// Sender's signature over the voucher
   131  //	Signature *crypto.Signature
   132  //}
   133  type SignedVoucher = paych0.SignedVoucher
   134  
   135  // Modular Verification method
   136  //type ModVerifyParams struct {
   137  //	// Actor on which to invoke the method.
   138  //	Actor addr.Address
   139  //	// Method to invoke.
   140  //	Method abi.MethodNum
   141  //	// Pre-serialized method parameters.
   142  //	Params []byte
   143  //}
   144  type ModVerifyParams = paych0.ModVerifyParams
   145  
   146  // Specifies which `Lane`s to be merged with what `Nonce` on channelUpdate
   147  //type Merge struct {
   148  //	Lane  uint64
   149  //	Nonce uint64
   150  //}
   151  type Merge = paych0.Merge
   152  
   153  func (pca Actor) UpdateChannelState(rt runtime.Runtime, params *UpdateChannelStateParams) *abi.EmptyValue {
   154  	var st State
   155  	rt.StateReadonly(&st)
   156  
   157  	// both parties must sign voucher: one who submits it, the other explicitly signs it
   158  	rt.ValidateImmediateCallerIs(st.From, st.To)
   159  	var signer addr.Address
   160  	if rt.Caller() == st.From {
   161  		signer = st.To
   162  	} else {
   163  		signer = st.From
   164  	}
   165  	sv := params.Sv
   166  
   167  	if sv.Signature == nil {
   168  		rt.Abortf(exitcode.ErrIllegalArgument, "voucher has no signature")
   169  	}
   170  
   171  	if st.SettlingAt != 0 && rt.CurrEpoch() >= st.SettlingAt {
   172  		rt.Abortf(ErrChannelStateUpdateAfterSettled, "no vouchers can be processed after SettlingAt epoch")
   173  	}
   174  
   175  	if len(params.Secret) > MaxSecretSize {
   176  		rt.Abortf(exitcode.ErrIllegalArgument, "secret must be at most 256 bytes long")
   177  	}
   178  
   179  	vb, err := sv.SigningBytes()
   180  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to serialize signedvoucher")
   181  
   182  	err = rt.VerifySignature(*sv.Signature, signer, vb)
   183  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "voucher signature invalid")
   184  
   185  	pchAddr := rt.Receiver()
   186  	svpchIDAddr, found := rt.ResolveAddress(sv.ChannelAddr)
   187  	if !found {
   188  		rt.Abortf(exitcode.ErrIllegalArgument, "voucher payment channel address %s does not resolve to an ID address", sv.ChannelAddr)
   189  	}
   190  	if pchAddr != svpchIDAddr {
   191  		rt.Abortf(exitcode.ErrIllegalArgument, "voucher payment channel address %s does not match receiver %s", svpchIDAddr, pchAddr)
   192  	}
   193  
   194  	if rt.CurrEpoch() < sv.TimeLockMin {
   195  		rt.Abortf(exitcode.ErrIllegalArgument, "cannot use this voucher yet!")
   196  	}
   197  
   198  	if sv.TimeLockMax != 0 && rt.CurrEpoch() > sv.TimeLockMax {
   199  		rt.Abortf(exitcode.ErrIllegalArgument, "this voucher has expired!")
   200  	}
   201  
   202  	if sv.Amount.Sign() < 0 {
   203  		rt.Abortf(exitcode.ErrIllegalArgument, "voucher amount must be non-negative, was %v", sv.Amount)
   204  	}
   205  
   206  	if len(sv.SecretPreimage) > 0 {
   207  		hashedSecret := rt.HashBlake2b(params.Secret)
   208  		if !bytes.Equal(hashedSecret[:], sv.SecretPreimage) {
   209  			rt.Abortf(exitcode.ErrIllegalArgument, "incorrect secret!")
   210  		}
   211  	}
   212  
   213  	if sv.Extra != nil {
   214  
   215  		code := rt.Send(
   216  			sv.Extra.Actor,
   217  			sv.Extra.Method,
   218  			builtin.CBORBytes(sv.Extra.Data),
   219  			abi.NewTokenAmount(0),
   220  			&builtin.Discard{},
   221  		)
   222  		builtin.RequireSuccess(rt, code, "spend voucher verification failed")
   223  	}
   224  
   225  	rt.StateTransaction(&st, func() {
   226  		laneFound := true
   227  
   228  		lstates, err := adt.AsArray(adt.AsStore(rt), st.LaneStates, LaneStatesAmtBitwidth)
   229  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load lanes")
   230  
   231  		// Find the voucher lane, creating if necessary.
   232  		laneId := sv.Lane
   233  		laneState := findLane(rt, lstates, sv.Lane)
   234  
   235  		if laneState == nil {
   236  			laneState = &LaneState{
   237  				Redeemed: big.Zero(),
   238  				Nonce:    0,
   239  			}
   240  			laneFound = false
   241  		}
   242  
   243  		if laneFound {
   244  			if laneState.Nonce >= sv.Nonce {
   245  				rt.Abortf(exitcode.ErrIllegalArgument, "voucher has an outdated nonce, existing nonce: %d, voucher nonce: %d, cannot redeem",
   246  					laneState.Nonce, sv.Nonce)
   247  			}
   248  		}
   249  
   250  		// The next section actually calculates the payment amounts to update the payment channel state
   251  		// 1. (optional) sum already redeemed value of all merging lanes
   252  		redeemedFromOthers := big.Zero()
   253  		for _, merge := range sv.Merges {
   254  			if merge.Lane == sv.Lane {
   255  				rt.Abortf(exitcode.ErrIllegalArgument, "voucher cannot merge lanes into its own lane")
   256  			}
   257  
   258  			otherls := findLane(rt, lstates, merge.Lane)
   259  			if otherls == nil {
   260  				rt.Abortf(exitcode.ErrIllegalArgument, "voucher specifies invalid merge lane %v", merge.Lane)
   261  				return // makes linters happy
   262  			}
   263  
   264  			if otherls.Nonce >= merge.Nonce {
   265  				rt.Abortf(exitcode.ErrIllegalArgument, "merged lane in voucher has outdated nonce, cannot redeem")
   266  			}
   267  
   268  			redeemedFromOthers = big.Add(redeemedFromOthers, otherls.Redeemed)
   269  			otherls.Nonce = merge.Nonce
   270  			err = lstates.Set(merge.Lane, otherls)
   271  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to store lane %d", merge.Lane)
   272  		}
   273  
   274  		// 2. To prevent double counting, remove already redeemed amounts (from
   275  		// voucher or other lanes) from the voucher amount
   276  		laneState.Nonce = sv.Nonce
   277  		balanceDelta := big.Sub(sv.Amount, big.Add(redeemedFromOthers, laneState.Redeemed))
   278  		// 3. set new redeemed value for merged-into lane
   279  		laneState.Redeemed = sv.Amount
   280  
   281  		newSendBalance := big.Add(st.ToSend, balanceDelta)
   282  
   283  		// 4. check operation validity
   284  		if newSendBalance.LessThan(big.Zero()) {
   285  			rt.Abortf(exitcode.ErrIllegalArgument, "voucher would leave channel balance negative")
   286  		}
   287  		if newSendBalance.GreaterThan(rt.CurrentBalance()) {
   288  			rt.Abortf(exitcode.ErrIllegalArgument, "not enough funds in channel to cover voucher")
   289  		}
   290  
   291  		// 5. add new redemption ToSend
   292  		st.ToSend = newSendBalance
   293  
   294  		// update channel settlingAt and MinSettleHeight if delayed by voucher
   295  		if sv.MinSettleHeight != 0 {
   296  			if st.SettlingAt != 0 && st.SettlingAt < sv.MinSettleHeight {
   297  				st.SettlingAt = sv.MinSettleHeight
   298  			}
   299  			if st.MinSettleHeight < sv.MinSettleHeight {
   300  				st.MinSettleHeight = sv.MinSettleHeight
   301  			}
   302  		}
   303  
   304  		err = lstates.Set(laneId, laneState)
   305  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to store lane", laneId)
   306  
   307  		st.LaneStates, err = lstates.Root()
   308  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save lanes")
   309  	})
   310  	return nil
   311  }
   312  
   313  func (pca Actor) Settle(rt runtime.Runtime, _ *abi.EmptyValue) *abi.EmptyValue {
   314  	var st State
   315  	rt.StateTransaction(&st, func() {
   316  		rt.ValidateImmediateCallerIs(st.From, st.To)
   317  
   318  		if st.SettlingAt != 0 {
   319  			rt.Abortf(exitcode.ErrIllegalState, "channel already settling")
   320  		}
   321  
   322  		st.SettlingAt = rt.CurrEpoch() + SettleDelay
   323  		if st.SettlingAt < st.MinSettleHeight {
   324  			st.SettlingAt = st.MinSettleHeight
   325  		}
   326  	})
   327  	return nil
   328  }
   329  
   330  func (pca Actor) Collect(rt runtime.Runtime, _ *abi.EmptyValue) *abi.EmptyValue {
   331  	var st State
   332  	rt.StateReadonly(&st)
   333  	rt.ValidateImmediateCallerIs(st.From, st.To)
   334  
   335  	if st.SettlingAt == 0 || rt.CurrEpoch() < st.SettlingAt {
   336  		rt.Abortf(exitcode.ErrForbidden, "payment channel not settling or settled")
   337  	}
   338  
   339  	// send ToSend to "To"
   340  	codeTo := rt.Send(
   341  		st.To,
   342  		builtin.MethodSend,
   343  		nil,
   344  		st.ToSend,
   345  		&builtin.Discard{},
   346  	)
   347  	builtin.RequireSuccess(rt, codeTo, "Failed to send funds to `To`")
   348  
   349  	// the remaining balance will be returned to "From" upon deletion.
   350  	rt.DeleteActor(st.From)
   351  
   352  	return nil
   353  }
   354  
   355  // Returns the insertion index for a lane ID, with the matching lane state if found, or nil.
   356  func findLane(rt runtime.Runtime, ls *adt.Array, id uint64) *LaneState {
   357  	if id > MaxLane {
   358  		rt.Abortf(exitcode.ErrIllegalArgument, "maximum lane ID is 2^63-1")
   359  	}
   360  
   361  	var out LaneState
   362  	found, err := ls.Get(id, &out)
   363  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load lane %d", id)
   364  
   365  	if !found {
   366  		return nil
   367  	}
   368  
   369  	return &out
   370  }