|
5 | 5 |
|
6 | 6 | #include "vm/actor/builtin/payment_channel/payment_channel_actor.hpp"
|
7 | 7 |
|
8 |
| -#include "vm/actor/actor_method.hpp" |
9 |
| -#include "vm/exit_code/exit_code.hpp" |
10 |
| - |
11 |
| -using fc::outcome::result; |
12 |
| -using fc::primitives::address::Protocol; |
13 |
| -using fc::vm::actor::decodeActorParams; |
14 |
| -using fc::vm::actor::kAccountCodeCid; |
15 |
| -using fc::vm::runtime::InvocationOutput; |
16 |
| -using fc::vm::runtime::Runtime; |
17 |
| -namespace outcome = fc::outcome; |
18 |
| -using namespace fc::vm::actor::builtin::payment_channel; |
19 |
| - |
20 |
| -ACTOR_METHOD_IMPL(Construct) { |
21 |
| - OUTCOME_TRY(code, runtime.getActorCodeID(runtime.getImmediateCaller())); |
22 |
| - if (code != kAccountCodeCid) return VMExitCode::PAYMENT_CHANNEL_WRONG_CALLER; |
23 |
| - |
24 |
| - if (params.to.getProtocol() != Protocol::ID) |
25 |
| - return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
26 |
| - OUTCOME_TRY(target_actor_code_id, runtime.getActorCodeID(params.to)); |
27 |
| - if (target_actor_code_id != kAccountCodeCid) |
28 |
| - return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
29 |
| - |
30 |
| - PaymentChannelActorState state{ |
31 |
| - runtime.getImmediateCaller(), params.to, 0, 0, 0, {}}; |
32 |
| - |
33 |
| - OUTCOME_TRY(runtime.commitState(state)); |
34 |
| - return fc::outcome::success(); |
35 |
| -} |
36 |
| - |
37 |
| -ACTOR_METHOD_IMPL(UpdateChannelState) { |
38 |
| - OUTCOME_TRY(state, |
39 |
| - runtime.getCurrentActorStateCbor<PaymentChannelActorState>()); |
40 |
| - Address signer = runtime.getImmediateCaller(); |
41 |
| - if (signer != state.from || signer != state.to) { |
42 |
| - return VMExitCode::PAYMENT_CHANNEL_WRONG_CALLER; |
| 8 | +namespace fc::vm::actor::builtin::payment_channel { |
| 9 | + using primitives::address::Protocol; |
| 10 | + |
| 11 | + outcome::result<Address> resolveAccount(Runtime &runtime, |
| 12 | + const Address &address) { |
| 13 | + OUTCOME_TRY(resolved, runtime.resolveAddress(address)); |
| 14 | + OUTCOME_TRY(code, runtime.getActorCodeID(resolved)); |
| 15 | + if (code != kAccountCodeCid) { |
| 16 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 17 | + } |
| 18 | + return std::move(resolved); |
43 | 19 | }
|
44 | 20 |
|
45 |
| - // TODO (a.chernyshov) not implemented yet FIL-129 |
| 21 | + outcome::result<State> assertCallerInChannel(Runtime &runtime) { |
| 22 | + OUTCOME_TRY(state, runtime.getCurrentActorStateCbor<State>()); |
| 23 | + if (runtime.getImmediateCaller() != state.from |
| 24 | + && runtime.getImmediateCaller() != state.to) { |
| 25 | + return VMExitCode::PAYMENT_CHANNEL_WRONG_CALLER; |
| 26 | + } |
| 27 | + return std::move(state); |
| 28 | + } |
46 | 29 |
|
47 |
| - return fc::outcome::success(); |
48 |
| -} |
| 30 | + ACTOR_METHOD_IMPL(Construct) { |
| 31 | + if (runtime.getImmediateCaller() != kInitAddress) { |
| 32 | + return VMExitCode::PAYMENT_CHANNEL_WRONG_CALLER; |
| 33 | + } |
| 34 | + OUTCOME_TRY(to, resolveAccount(runtime, params.to)); |
| 35 | + OUTCOME_TRY(from, resolveAccount(runtime, params.from)); |
| 36 | + OUTCOME_TRY(runtime.commitState(State{from, to, 0, 0, 0, {}})); |
| 37 | + return fc::outcome::success(); |
| 38 | + } |
49 | 39 |
|
50 |
| -ACTOR_METHOD_IMPL(Settle) { |
51 |
| - // TODO (a.chernyshov) not implemented yet FIL-129 |
| 40 | + ACTOR_METHOD_IMPL(UpdateChannelState) { |
| 41 | + OUTCOME_TRY(state, assertCallerInChannel(runtime)); |
| 42 | + auto &voucher = params.signed_voucher; |
| 43 | + if (!voucher.signature) { |
| 44 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 45 | + } |
| 46 | + auto voucher_signable = voucher; |
| 47 | + voucher_signable.signature = boost::none; |
| 48 | + OUTCOME_TRY(voucher_signable_bytes, codec::cbor::encode(voucher_signable)); |
| 49 | + auto &signer = |
| 50 | + runtime.getImmediateCaller() != state.to ? state.to : state.from; |
| 51 | + OUTCOME_TRY(verified, |
| 52 | + runtime.verifySignature( |
| 53 | + *voucher.signature, signer, voucher_signable_bytes)); |
| 54 | + if (!verified) { |
| 55 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 56 | + } |
| 57 | + if (runtime.getCurrentEpoch() < voucher.time_lock_min) { |
| 58 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 59 | + } |
| 60 | + if (voucher.time_lock_max != 0 |
| 61 | + && runtime.getCurrentEpoch() > voucher.time_lock_max) { |
| 62 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 63 | + } |
| 64 | + if (!voucher.secret_preimage.empty() |
| 65 | + && gsl::make_span(runtime.hashBlake2b(params.secret)) |
| 66 | + != gsl::make_span(voucher.secret_preimage)) { |
| 67 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 68 | + } |
| 69 | + if (voucher.extra) { |
| 70 | + OUTCOME_TRY(params2, |
| 71 | + encodeActorParams( |
| 72 | + PaymentVerifyParams{voucher.extra->data, params.proof})); |
| 73 | + OUTCOME_TRY(runtime.send( |
| 74 | + voucher.extra->actor, voucher.extra->method, params2, 0)); |
| 75 | + } |
52 | 76 |
|
53 |
| - return fc::outcome::success(); |
54 |
| -} |
| 77 | + auto lane_it = state.findLane(voucher.lane); |
| 78 | + if (lane_it == state.lanes.end() || lane_it->id != voucher.lane) { |
| 79 | + if (state.lanes.size() >= kLaneLimit) { |
| 80 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 81 | + } |
| 82 | + lane_it = state.lanes.insert(lane_it, LaneState{voucher.lane, 0, 0}); |
| 83 | + } |
| 84 | + auto &lane = *lane_it; |
| 85 | + if (lane.nonce > voucher.nonce) { |
| 86 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 87 | + } |
| 88 | + auto redeem = lane.redeem; |
| 89 | + for (auto &merge : voucher.merges) { |
| 90 | + if (merge.lane == voucher.lane) { |
| 91 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 92 | + } |
| 93 | + lane_it = state.findLane(merge.lane); |
| 94 | + if (lane_it == state.lanes.end() || lane_it->nonce >= merge.nonce) { |
| 95 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_ARGUMENT; |
| 96 | + } |
| 97 | + redeem += lane_it->redeem; |
| 98 | + lane_it->nonce = merge.nonce; |
| 99 | + } |
| 100 | + lane.nonce = voucher.nonce; |
| 101 | + state.to_send += voucher.amount - redeem; |
| 102 | + lane.redeem = voucher.amount; |
| 103 | + OUTCOME_TRY(balance, runtime.getCurrentBalance()); |
| 104 | + if (state.to_send < 0 || state.to_send > balance) { |
| 105 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_STATE; |
| 106 | + } |
| 107 | + if (voucher.min_close_height != 0) { |
| 108 | + if (state.settling_at != 0) { |
| 109 | + state.settling_at = |
| 110 | + std::max(state.settling_at, voucher.min_close_height); |
| 111 | + } |
| 112 | + state.min_settling_height = |
| 113 | + std::max(state.min_settling_height, voucher.min_close_height); |
| 114 | + } |
55 | 115 |
|
56 |
| -ACTOR_METHOD_IMPL(Collect) { |
57 |
| - // TODO (a.chernyshov) not implemented yet FIL-129 |
| 116 | + OUTCOME_TRY(runtime.commitState(state)); |
| 117 | + return fc::outcome::success(); |
| 118 | + } |
58 | 119 |
|
59 |
| - return fc::outcome::success(); |
60 |
| -} |
| 120 | + ACTOR_METHOD_IMPL(Settle) { |
| 121 | + OUTCOME_TRY(state, assertCallerInChannel(runtime)); |
| 122 | + if (state.settling_at != 0) { |
| 123 | + return VMExitCode::PAYMENT_CHANNEL_ILLEGAL_STATE; |
| 124 | + } |
| 125 | + state.settling_at = std::max(state.min_settling_height, |
| 126 | + runtime.getCurrentEpoch() + kSettleDelay); |
| 127 | + OUTCOME_TRY(runtime.commitState(state)); |
| 128 | + return fc::outcome::success(); |
| 129 | + } |
| 130 | + |
| 131 | + ACTOR_METHOD_IMPL(Collect) { |
| 132 | + OUTCOME_TRY(state, assertCallerInChannel(runtime)); |
| 133 | + if (state.settling_at == 0 |
| 134 | + || runtime.getCurrentEpoch() < state.settling_at) { |
| 135 | + return VMExitCode::PAYMENT_CHANNEL_FORBIDDEN; |
| 136 | + } |
| 137 | + OUTCOME_TRY(balance, runtime.getCurrentBalance()); |
| 138 | + OUTCOME_TRY(runtime.sendFunds(state.from, balance - state.to_send)); |
| 139 | + OUTCOME_TRY(runtime.sendFunds(state.to, state.to_send)); |
| 140 | + state.to_send = 0; |
| 141 | + OUTCOME_TRY(runtime.commitState(state)); |
| 142 | + return fc::outcome::success(); |
| 143 | + } |
| 144 | +} // namespace fc::vm::actor::builtin::payment_channel |
0 commit comments