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 }