/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "vm/cells.h" #include "vm/cellslice.h" #include "Ed25519.h" #include "block/block-auto.h" #include "block/block-parse.h" #include "td/utils/Variant.h" #include "SmartContract.h" #include "SmartContractCode.h" namespace ton { namespace pchan { // // Payment channels // struct Config { td::uint32 init_timeout{0}; td::uint32 close_timeout{0}; td::SecureString a_key; td::SecureString b_key; block::StdAddress a_addr; block::StdAddress b_addr; td::uint64 channel_id{0}; td::uint64 min_A_extra{0}; td::Ref serialize() const; }; struct MsgInit { td::uint64 inc_A{0}; td::uint64 inc_B{0}; td::uint64 min_A{0}; td::uint64 min_B{0}; td::uint64 channel_id{0}; td::Ref serialize() const; }; struct Promise { td::uint64 channel_id; td::uint64 promise_A{0}; td::uint64 promise_B{0}; td::Ref serialize() const; }; td::Ref maybe_sign(const td::Ref& msg, const td::Ed25519::PrivateKey* key); td::Ref maybe_ref(td::Ref msg); struct MsgClose { td::uint64 extra_A{0}; td::uint64 extra_B{0}; td::Ref signed_promise; td::Ref serialize() const; }; struct MsgTimeout { td::Ref serialize() const; }; struct MsgPayout { td::Ref serialize() const; }; struct SignedPromise { Promise promise; td::optional o_signature; bool unpack(td::Ref cell); static td::SecureString signature(const td::Ed25519::PrivateKey* key, const td::Ref& promise); static td::Ref create_and_serialize(td::Slice signature, const td::Ref& promise); static td::Ref create_and_serialize(const td::Ed25519::PrivateKey* key, const td::Ref& promise); }; struct StateInit { bool signed_A{false}; bool signed_B{false}; td::uint64 min_A{0}; td::uint64 min_B{0}; td::uint64 A{0}; td::uint64 B{0}; td::uint32 expire_at{0}; td::Ref serialize() const; }; struct StateClose { bool signed_A{false}; bool signed_B{false}; td::uint64 promise_A{0}; td::uint64 promise_B{0}; td::uint64 A{0}; td::uint64 B{0}; td::uint32 expire_at{0}; }; struct StatePayout { td::uint64 A{0}; td::uint64 B{0}; }; struct Data { td::Ref config; td::Ref state; static td::Ref init_state(); td::Ref serialize() const; }; template struct MsgBuilder { td::Ed25519::PrivateKey* a_key{nullptr}; td::Ed25519::PrivateKey* b_key{nullptr}; T&& with_a_key(td::Ed25519::PrivateKey* key) && { a_key = key; return static_cast(*this); } T&& with_b_key(td::Ed25519::PrivateKey* key) && { b_key = key; return static_cast(*this); } td::Ref finalize() && { block::gen::ChanSignedMsg::Record rec; auto msg = static_cast(*this).msg.serialize(); rec.msg = vm::load_cell_slice_ref(msg); rec.sig_A = maybe_ref(maybe_sign(msg, a_key)); rec.sig_B = maybe_ref(maybe_sign(msg, b_key)); block::gen::ChanOp::Record op_rec; CHECK(tlb::csr_pack(op_rec.msg, rec)); LOG(ERROR) << op_rec.msg->size(); td::Ref res; CHECK(tlb::pack_cell(res, op_rec)); return res; } }; struct MsgInitBuilder : public MsgBuilder { MsgInit msg; MsgInitBuilder&& min_A(td::uint64 value) && { msg.min_A = value; return std::move(*this); } MsgInitBuilder&& min_B(td::uint64 value) && { msg.min_B = value; return std::move(*this); } MsgInitBuilder&& inc_A(td::uint64 value) && { msg.inc_A = value; return std::move(*this); } MsgInitBuilder&& inc_B(td::uint64 value) && { msg.inc_B = value; return std::move(*this); } MsgInitBuilder&& channel_id(td::uint64 value) && { msg.channel_id = value; return std::move(*this); } }; struct MsgTimeoutBuilder : public MsgBuilder { MsgTimeout msg; }; struct MsgPayoutBuilder : public MsgBuilder { MsgPayout msg; }; struct MsgCloseBuilder : public MsgBuilder { MsgClose msg; MsgCloseBuilder&& extra_A(td::uint64 value) && { msg.extra_A = value; return std::move(*this); } MsgCloseBuilder&& extra_B(td::uint64 value) && { msg.extra_B = value; return std::move(*this); } MsgCloseBuilder&& signed_promise(td::Ref signed_promise) && { msg.signed_promise = vm::load_cell_slice_ref(signed_promise); return std::move(*this); } }; struct SignedPromiseBuilder { Promise promise; td::optional o_signature; td::Ed25519::PrivateKey* key{nullptr}; SignedPromiseBuilder& with_key(td::Ed25519::PrivateKey* key) { this->key = key; return *this; } SignedPromiseBuilder& promise_A(td::uint64 value) { promise.promise_A = value; return *this; } SignedPromiseBuilder& promise_B(td::uint64 value) { promise.promise_B = value; return *this; } SignedPromiseBuilder& channel_id(td::uint64 value) { promise.channel_id = value; return *this; } SignedPromiseBuilder& signature(td::SecureString signature) { o_signature = std::move(signature); return *this; } bool check_signature(td::Slice signature, const td::Ed25519::PublicKey& pk) { return pk.verify_signature(promise.serialize()->get_hash().as_slice(), signature).is_ok(); } td::SecureString calc_signature() { CHECK(key); return SignedPromise::signature(key, promise.serialize()); } td::Ref finalize() { if (o_signature) { return SignedPromise::create_and_serialize(o_signature.value().copy(), promise.serialize()); } else { return SignedPromise::create_and_serialize(key, promise.serialize()); } } }; } // namespace pchan class PaymentChannel : public SmartContract { public: PaymentChannel(State state) : SmartContract(std::move(state)) { } struct Info { pchan::Config config; td::Variant state; std::string description; }; td::Result get_info() const; static td::Ref create(State state) { return td::Ref(true, std::move(state)); } static td::optional guess_revision(const vm::Cell::Hash& code_hash); static td::Ref create(const pchan::Config& config, td::int32 revision) { State state; state.code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, revision); pchan::Data data; data.config = config.serialize(); pchan::StateInit init; data.state = init.serialize(); state.data = data.serialize(); return create(std::move(state)); } }; } // namespace ton