Skip to content

Commit c6cd1f6

Browse files
authored
Payment channel actor methods (#150)
Signed-off-by: turuslan <[email protected]>
1 parent 4cc3b53 commit c6cd1f6

File tree

12 files changed

+587
-210
lines changed

12 files changed

+587
-210
lines changed

core/api/rpc/json.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,8 @@ namespace fc::api {
483483

484484
ENCODE(SignedVoucher) {
485485
Value j{rapidjson::kObjectType};
486-
Set(j, "TimeLock", v.time_lock);
486+
Set(j, "TimeLockMin", v.time_lock_min);
487+
Set(j, "TimeLockMax", v.time_lock_max);
487488
Set(j, "SecretPreimage", gsl::make_span(v.secret_preimage));
488489
Set(j, "Extra", v.extra);
489490
Set(j, "Lane", v.lane);
@@ -496,7 +497,8 @@ namespace fc::api {
496497
}
497498

498499
DECODE(SignedVoucher) {
499-
decode(v.time_lock, Get(j, "TimeLock"));
500+
decode(v.time_lock_min, Get(j, "TimeLockMin"));
501+
decode(v.time_lock_max, Get(j, "TimeLockMax"));
500502
decode(v.secret_preimage, Get(j, "SecretPreimage"));
501503
decode(v.extra, Get(j, "Extra"));
502504
decode(v.lane, Get(j, "Lane"));

core/vm/actor/builtin/multisig/multisig_actor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ namespace fc::vm::actor::builtin::multisig {
109109
pending_tx.approved.push_back(caller);
110110

111111
// check threshold
112-
OUTCOME_TRY(balance, runtime.getBalance(runtime.getCurrentReceiver()));
112+
OUTCOME_TRY(balance, runtime.getCurrentBalance());
113113
if (pending_tx.approved.size() >= threshold) {
114114
if (balance < pending_tx.value)
115115
return VMExitCode::MULTISIG_ACTOR_INSUFFICIENT_FUNDS;

core/vm/actor/builtin/payment_channel/payment_channel_actor.cpp

Lines changed: 130 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,140 @@
55

66
#include "vm/actor/builtin/payment_channel/payment_channel_actor.hpp"
77

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);
4319
}
4420

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+
}
4629

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+
}
4939

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+
}
5276

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+
}
55115

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+
}
58119

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

core/vm/actor/builtin/payment_channel/payment_channel_actor.hpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,24 @@
66
#ifndef CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_HPP
77
#define CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_HPP
88

9-
#include "codec/cbor/streams_annotation.hpp"
10-
#include "common/outcome.hpp"
119
#include "primitives/address/address_codec.hpp"
1210
#include "vm/actor/actor_method.hpp"
1311
#include "vm/actor/builtin/payment_channel/payment_channel_actor_state.hpp"
14-
#include "vm/runtime/runtime.hpp"
15-
#include "vm/runtime/runtime_types.hpp"
1612

1713
namespace fc::vm::actor::builtin::payment_channel {
14+
using primitives::EpochDuration;
1815

19-
using fc::vm::runtime::InvocationOutput;
20-
using fc::vm::runtime::Runtime;
16+
constexpr size_t kLaneLimit{256};
17+
constexpr EpochDuration kSettleDelay{1};
2118

2219
struct Construct : ActorMethodBase<1> {
2320
struct Params {
24-
/** Voucher receiver */
21+
Address from;
2522
Address to;
2623
};
2724
ACTOR_METHOD_DECL();
2825
};
29-
CBOR_TUPLE(Construct::Params, to)
26+
CBOR_TUPLE(Construct::Params, from, to)
3027

3128
struct UpdateChannelState : ActorMethodBase<2> {
3229
struct Params {
@@ -45,7 +42,6 @@ namespace fc::vm::actor::builtin::payment_channel {
4542
struct Collect : ActorMethodBase<4> {
4643
ACTOR_METHOD_DECL();
4744
};
48-
4945
} // namespace fc::vm::actor::builtin::payment_channel
5046

5147
#endif // CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_HPP

core/vm/actor/builtin/payment_channel/payment_channel_actor_state.hpp

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,52 @@
66
#ifndef CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_STATE_HPP
77
#define CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_STATE_HPP
88

9-
#include "codec/cbor/streams_annotation.hpp"
10-
#include "common/buffer.hpp"
119
#include "crypto/signature/signature.hpp"
12-
#include "primitives/address/address.hpp"
13-
#include "primitives/big_int.hpp"
14-
#include "primitives/chain_epoch/chain_epoch.hpp"
10+
#include "primitives/types.hpp"
1511
#include "vm/actor/actor.hpp"
1612

1713
namespace fc::vm::actor::builtin::payment_channel {
18-
1914
using common::Buffer;
2015
using crypto::signature::Signature;
21-
using fc::vm::actor::MethodNumber;
22-
using primitives::BigInt;
2316
using primitives::ChainEpoch;
17+
using primitives::TokenAmount;
2418
using primitives::address::Address;
2519

20+
using LaneId = uint64_t;
21+
2622
struct LaneState {
27-
int64_t state{};
28-
BigInt redeem{};
29-
int64_t nonce{};
23+
LaneId id{};
24+
TokenAmount redeem{};
25+
uint64_t nonce{};
3026

3127
inline bool operator==(const LaneState &other) const {
32-
return state == other.state && redeem == other.redeem
33-
&& nonce == other.nonce;
28+
return id == other.id && redeem == other.redeem && nonce == other.nonce;
3429
}
3530
};
31+
CBOR_TUPLE(LaneState, id, redeem, nonce)
32+
33+
struct State {
34+
inline auto findLane(LaneId lane_id) {
35+
return std::lower_bound(
36+
lanes.begin(), lanes.end(), lane_id, [](auto &lane, auto lane_id) {
37+
return lane.id < lane_id;
38+
});
39+
}
3640

37-
struct PaymentChannelActorState {
3841
Address from;
3942
Address to;
40-
BigInt to_send{};
43+
TokenAmount to_send{};
4144
ChainEpoch settling_at;
4245
ChainEpoch min_settling_height;
4346
std::vector<LaneState> lanes{};
4447
};
48+
CBOR_TUPLE(State, from, to, to_send, settling_at, min_settling_height, lanes)
4549

4650
struct Merge {
47-
uint64_t lane{};
51+
LaneId lane{};
4852
uint64_t nonce{};
4953
};
54+
CBOR_TUPLE(Merge, lane, nonce)
5055

5156
/**
5257
* Modular Verification method
@@ -56,35 +61,23 @@ namespace fc::vm::actor::builtin::payment_channel {
5661
MethodNumber method;
5762
Buffer data;
5863
};
64+
CBOR_TUPLE(ModularVerificationParameter, actor, method, data)
5965

6066
struct SignedVoucher {
61-
uint64_t time_lock{};
67+
ChainEpoch time_lock_min{};
68+
ChainEpoch time_lock_max{};
6269
Buffer secret_preimage;
63-
ModularVerificationParameter extra;
64-
uint64_t lane{};
70+
boost::optional<ModularVerificationParameter> extra;
71+
LaneId lane{};
6572
uint64_t nonce{};
66-
BigInt amount;
67-
uint64_t min_close_height{};
73+
TokenAmount amount;
74+
ChainEpoch min_close_height{};
6875
std::vector<Merge> merges{};
69-
Signature signature;
76+
boost::optional<Signature> signature;
7077
};
71-
72-
CBOR_TUPLE(LaneState, state, redeem, nonce)
73-
74-
CBOR_TUPLE(PaymentChannelActorState,
75-
from,
76-
to,
77-
to_send,
78-
settling_at,
79-
min_settling_height,
80-
lanes)
81-
82-
CBOR_TUPLE(ModularVerificationParameter, actor, method, data)
83-
84-
CBOR_TUPLE(Merge, lane, nonce)
85-
8678
CBOR_TUPLE(SignedVoucher,
87-
time_lock,
79+
time_lock_min,
80+
time_lock_max,
8881
secret_preimage,
8982
extra,
9083
lane,
@@ -94,5 +87,11 @@ namespace fc::vm::actor::builtin::payment_channel {
9487
merges,
9588
signature)
9689

90+
struct PaymentVerifyParams {
91+
Buffer extra;
92+
Buffer proof;
93+
};
94+
CBOR_TUPLE(PaymentVerifyParams, extra, proof);
9795
} // namespace fc::vm::actor::builtin::payment_channel
96+
9897
#endif // CPP_FILECOIN_VM_ACTOR_BUILTIN_PAYMENT_CHANNEL_ACTOR_STATE_HPP

core/vm/actor/builtin/reward/reward_actor.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ namespace fc::vm::actor::builtin::reward {
118118
return VMExitCode::REWARD_ACTOR_WRONG_CALLER;
119119
}
120120
assert(params.gas_reward == runtime.getValueReceived());
121-
OUTCOME_TRY(prior_balance,
122-
runtime.getBalance(runtime.getMessage().get().to));
121+
OUTCOME_TRY(prior_balance, runtime.getCurrentBalance());
123122
TokenAmount penalty{0};
124123
OUTCOME_TRY(state, runtime.getCurrentActorStateCbor<State>());
125124

0 commit comments

Comments
 (0)