mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			282 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
    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 <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    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<vm::Cell> 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<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
struct Promise {
 | 
						|
  td::uint64 channel_id;
 | 
						|
  td::uint64 promise_A{0};
 | 
						|
  td::uint64 promise_B{0};
 | 
						|
  td::Ref<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key);
 | 
						|
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg);
 | 
						|
 | 
						|
struct MsgClose {
 | 
						|
  td::uint64 extra_A{0};
 | 
						|
  td::uint64 extra_B{0};
 | 
						|
  td::Ref<vm::CellSlice> signed_promise;
 | 
						|
  td::Ref<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
struct MsgTimeout {
 | 
						|
  td::Ref<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
struct MsgPayout {
 | 
						|
  td::Ref<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
struct SignedPromise {
 | 
						|
  Promise promise;
 | 
						|
  td::optional<td::SecureString> o_signature;
 | 
						|
 | 
						|
  bool unpack(td::Ref<vm::Cell> cell);
 | 
						|
  static td::SecureString signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
 | 
						|
  static td::Ref<vm::Cell> create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise);
 | 
						|
  static td::Ref<vm::Cell> create_and_serialize(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& 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<vm::Cell> 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<vm::Cell> config;
 | 
						|
  td::Ref<vm::Cell> state;
 | 
						|
 | 
						|
  static td::Ref<vm::Cell> init_state();
 | 
						|
 | 
						|
  td::Ref<vm::Cell> serialize() const;
 | 
						|
};
 | 
						|
 | 
						|
template <class T>
 | 
						|
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<T&&>(*this);
 | 
						|
  }
 | 
						|
  T&& with_b_key(td::Ed25519::PrivateKey* key) && {
 | 
						|
    b_key = key;
 | 
						|
    return static_cast<T&&>(*this);
 | 
						|
  }
 | 
						|
 | 
						|
  td::Ref<vm::Cell> finalize() && {
 | 
						|
    block::gen::ChanSignedMsg::Record rec;
 | 
						|
    auto msg = static_cast<T&&>(*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<vm::Cell> res;
 | 
						|
    CHECK(tlb::pack_cell(res, op_rec));
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
struct MsgInitBuilder : public MsgBuilder<MsgInitBuilder> {
 | 
						|
  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<MsgTimeoutBuilder> {
 | 
						|
  MsgTimeout msg;
 | 
						|
};
 | 
						|
 | 
						|
struct MsgPayoutBuilder : public MsgBuilder<MsgPayoutBuilder> {
 | 
						|
  MsgPayout msg;
 | 
						|
};
 | 
						|
 | 
						|
struct MsgCloseBuilder : public MsgBuilder<MsgCloseBuilder> {
 | 
						|
  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<vm::Cell> signed_promise) && {
 | 
						|
    msg.signed_promise = vm::load_cell_slice_ref(signed_promise);
 | 
						|
    return std::move(*this);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
struct SignedPromiseBuilder {
 | 
						|
  Promise promise;
 | 
						|
  td::optional<td::SecureString> 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<vm::Cell> 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<pchan::StateInit, pchan::StateClose, pchan::StatePayout> state;
 | 
						|
    std::string description;
 | 
						|
  };
 | 
						|
  td::Result<Info> get_info() const;
 | 
						|
 | 
						|
  static td::Ref<PaymentChannel> create(State state) {
 | 
						|
    return td::Ref<PaymentChannel>(true, std::move(state));
 | 
						|
  }
 | 
						|
  static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
 | 
						|
  static td::Ref<PaymentChannel> 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
 |