Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

feat: add cbor decoding for DealProposal and MarketDealNotifyParams #332

Merged
merged 9 commits into from
Mar 3, 2023
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build: build_api build_mock_api build_tests

build_tests: verify_solc build_leb128_test
./bin/solc solidity-cborutils=${PWD}/node_modules/solidity-cborutils/ @ensdomains=${PWD}/node_modules/@ensdomains/ contracts/v0.8/tests/market.test.sol --output-dir ./build/v0.8/tests --overwrite --bin --hashes --opcodes --abi
./bin/solc solidity-cborutils=${PWD}/node_modules/solidity-cborutils/ @ensdomains=${PWD}/node_modules/@ensdomains/ contracts/v0.8/tests/marketcbor.test.sol --output-dir ./build/v0.8/tests --overwrite --bin --hashes --opcodes --abi
./bin/solc solidity-cborutils=${PWD}/node_modules/solidity-cborutils/ @ensdomains=${PWD}/node_modules/@ensdomains/ contracts/v0.8/tests/miner.test.sol --output-dir ./build/v0.8/tests --overwrite --bin --hashes --opcodes --abi
./bin/solc solidity-cborutils=${PWD}/node_modules/solidity-cborutils/ @ensdomains=${PWD}/node_modules/@ensdomains/ contracts/v0.8/tests/power.test.sol --output-dir ./build/v0.8/tests --overwrite --bin --hashes --opcodes --abi
./bin/solc solidity-cborutils=${PWD}/node_modules/solidity-cborutils/ @ensdomains=${PWD}/node_modules/@ensdomains/ contracts/v0.8/tests/account.test.sol --output-dir ./build/v0.8/tests --overwrite --bin --hashes --opcodes --abi
Expand Down Expand Up @@ -119,6 +120,7 @@ test_frc0042:

test_cbor_decode: build build_builtin_actors
cd testing && cargo test cbor_decode_test -- --nocapture
cd testing && cargo test market_cbor_tests -- --nocapture

test_leb128: build build_builtin_actors
cd testing && cargo test leb128 -- --nocapture
Expand Down
83 changes: 83 additions & 0 deletions contracts/v0.8/cbor/MarketCbor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import "../utils/CborDecode.sol";

import "../types/MarketTypes.sol";
import "../utils/Misc.sol";
import "./FilecoinCbor.sol";
import "../types/CommonTypes.sol";
import "../utils/FilAddresses.sol";

/// @title This library is a set of functions meant to handle CBOR parameters serialization and return values deserialization for Market actor exported methods.
/// @author Zondax AG
Expand Down Expand Up @@ -190,4 +193,84 @@ library MarketCBOR {

return buf.data();
}

function deserializeMarketDealNotifyParams(bytes memory rawResp) internal pure returns (MarketTypes.MarketDealNotifyParams memory ret) {
uint byteIdx = 0;
uint len;

(len, byteIdx) = rawResp.readFixedArray(byteIdx);
assert(len == 2);

(ret.dealProposal, byteIdx) = rawResp.readBytes(byteIdx);
(ret.dealId, byteIdx) = rawResp.readUInt64(byteIdx);

}

function serializeDealProposal(MarketTypes.DealProposal memory dealProposal) internal pure returns (bytes memory) {
CBOR.CBORBuffer memory buf = CBOR.create(64);

bool isLabelStr = bytes(dealProposal.label.dataStr).length > 0;
bool isLabelBts = dealProposal.label.dataBts.length > 0;
require(!(isLabelStr && isLabelBts), "deal label must be either string or bytes");


buf.startFixedArray(11);

buf.writeCid(dealProposal.piece_cid.data);
buf.writeUInt64(dealProposal.piece_size);
buf.writeBool(dealProposal.verified_deal);
buf.writeBytes(dealProposal.client.data);
buf.writeBytes(dealProposal.provider.data);

isLabelStr ? buf.writeString(dealProposal.label.dataStr) : buf.writeBytes(dealProposal.label.dataBts);

buf.writeInt64(dealProposal.start_epoch);
buf.writeInt64(dealProposal.end_epoch);
buf.writeBytes(dealProposal.storage_price_per_epoch.serializeBigInt());
buf.writeBytes(dealProposal.provider_collateral.serializeBigInt());
buf.writeBytes(dealProposal.client_collateral.serializeBigInt());

return buf.data();
}


function deserializeDealProposal(bytes memory rawResp) internal pure returns (MarketTypes.DealProposal memory ret) {
uint byteIdx = 0;
uint len;
bytes memory tmp;
string memory tmpString;

(len, byteIdx) = rawResp.readFixedArray(byteIdx);
assert(len == 11);

(tmp, byteIdx) = rawResp.readBytes(byteIdx);
ret.piece_cid = CommonTypes.Cid(tmp);

(ret.piece_size, byteIdx) = rawResp.readUInt64(byteIdx);
(ret.verified_deal, byteIdx) = rawResp.readBool(byteIdx);
(tmp, byteIdx) = rawResp.readBytes(byteIdx);
ret.client = FilAddresses.fromBytes(tmp);

(tmp, byteIdx) = rawResp.readBytes(byteIdx);
ret.provider = FilAddresses.fromBytes(tmp);

(tmpString, byteIdx) = rawResp.readString(byteIdx);
ret.label = CommonTypes.DealLabel(new bytes(0), tmpString);

(ret.start_epoch, byteIdx) = rawResp.readInt64(byteIdx);
(ret.end_epoch, byteIdx) = rawResp.readInt64(byteIdx);

bytes memory storage_price_per_epoch_bytes;
(storage_price_per_epoch_bytes, byteIdx) = rawResp.readBytes(byteIdx);
ret.storage_price_per_epoch = storage_price_per_epoch_bytes.deserializeBigInt();

bytes memory provider_collateral_bytes;
(provider_collateral_bytes, byteIdx) = rawResp.readBytes(byteIdx);
ret.provider_collateral = provider_collateral_bytes.deserializeBigInt();

bytes memory client_collateral_bytes;
(client_collateral_bytes, byteIdx) = rawResp.readBytes(byteIdx);
ret.client_collateral = client_collateral_bytes.deserializeBigInt();

}
}
50 changes: 50 additions & 0 deletions contracts/v0.8/tests/marketcbor.test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { MarketTypes } from "../types/MarketTypes.sol";
import { CommonTypes } from "../types/CommonTypes.sol";
import { MarketCBOR } from "../cbor/MarketCbor.sol";
import { BigIntCBOR } from "../cbor/BigIntCbor.sol";
import "../utils/FilAddresses.sol";

contract MarketCBORTest {

using MarketCBOR for *;

function bigIntEqual(CommonTypes.BigInt memory n1, CommonTypes.BigInt memory n2) internal pure {
require(keccak256(n1.val)==keccak256(n2.val));
require(n1.neg == n2.neg);
}

function testDealProposalSerDes() view public {

MarketTypes.DealProposal memory proposal;
proposal.piece_cid = CommonTypes.Cid(hex"000181E2039220206B86B273FF34FCE19D6B804EFF5A3F5747ADA4EAA22F1D49C01E52DDB7875B4B");
proposal.piece_size = 1024;
proposal.verified_deal = true;
proposal.client = FilAddresses.fromBytes(hex"001234");
proposal.provider = FilAddresses.fromBytes(hex"005678");
proposal.label = CommonTypes.DealLabel(new bytes(0), "test");
proposal.start_epoch = 20000;
proposal.end_epoch = 40000;
proposal.storage_price_per_epoch = CommonTypes.BigInt(hex"50", false);
proposal.provider_collateral = CommonTypes.BigInt(hex"10", false);
proposal.client_collateral = CommonTypes.BigInt(hex"20", false);

bytes memory cborBytes = proposal.serializeDealProposal();

MarketTypes.DealProposal memory deserProposal = cborBytes.deserializeDealProposal();
require(keccak256(proposal.piece_cid.data) == keccak256(deserProposal.piece_cid.data), "unequal piece_cid");
require(proposal.piece_size == deserProposal.piece_size, "unequal piece_size");
require(proposal.verified_deal == deserProposal.verified_deal, "unequal verified_deal");
require(keccak256(proposal.client.data) == keccak256(deserProposal.client.data), "unequal client");
require(keccak256(proposal.provider.data) == keccak256(deserProposal.provider.data), "unequal provider");
require(keccak256(abi.encodePacked(proposal.label.dataStr)) == keccak256(abi.encodePacked(deserProposal.label.dataStr)), "unequal label");
require(proposal.start_epoch == deserProposal.start_epoch, "unequal start_epoch");
require(proposal.end_epoch == deserProposal.end_epoch, "unequal end_epoch");
bigIntEqual(proposal.storage_price_per_epoch, deserProposal.storage_price_per_epoch);
bigIntEqual(proposal.provider_collateral, deserProposal.provider_collateral);
bigIntEqual(proposal.client_collateral, deserProposal.client_collateral);

}
}
5 changes: 5 additions & 0 deletions contracts/v0.8/types/MarketTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,9 @@ library MarketTypes {
DealProposal proposal;
bytes client_signature;
}

struct MarketDealNotifyParams {
bytes dealProposal;
uint64 dealId;
}
}
77 changes: 77 additions & 0 deletions testing/tests/marketCbor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use fil_actor_eam::Return;
use fil_actor_evm::Method as EvmMethods;
use fil_actors_runtime::EAM_ACTOR_ADDR;
use fvm::executor::{ApplyKind, Executor};
use fvm_integration_tests::dummy::DummyExterns;
use fvm_integration_tests::tester::Account;
use fvm_ipld_encoding::strict_bytes;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::message::Message;
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};

use testing::setup;

const WASM_COMPILED_PATH: &str = "../build/v0.8/tests/MarketCBORTest.bin";

#[derive(SerdeSerialize, SerdeDeserialize)]
#[serde(transparent)]
pub struct CreateExternalParams(#[serde(with = "strict_bytes")] pub Vec<u8>);

#[test]
fn market_cbor_tests() {
println!("Testing Market cbor lib");

let (mut tester, _manifest) = setup::setup_tester();

let sender: [Account; 1] = tester.create_accounts().unwrap();

// Instantiate machine
tester.instantiate_machine(DummyExterns).unwrap();

let executor = tester.executor.as_mut().unwrap();

println!("Calling init actor (EVM)");

let evm_bin = setup::load_evm(WASM_COMPILED_PATH);

let constructor_params = CreateExternalParams(evm_bin);

let message = Message {
from: sender[0].1,
to: EAM_ACTOR_ADDR,
gas_limit: 1000000000,
method_num: 4,
sequence: 0,
params: RawBytes::serialize(constructor_params).unwrap(),
..Message::default()
};

let res = executor
.execute_message(message, ApplyKind::Explicit, 100)
.unwrap();

assert_eq!(res.msg_receipt.exit_code.value(), 0);

let exec_return: Return = RawBytes::deserialize(&res.msg_receipt.return_data).unwrap();

let contract_actor_id = exec_return.actor_id;

println!("Calling `testDealProposalSerDes`");

let message = Message {
from: sender[0].1,
to: Address::new_id(contract_actor_id),
gas_limit: 1000000000,
method_num: EvmMethods::InvokeContract as u64,
sequence: 1,
params: RawBytes::new(hex::decode("445945a4e4").unwrap()),
..Message::default()
};

let res = executor
.execute_message(message, ApplyKind::Explicit, 100)
.unwrap();

assert_eq!(res.msg_receipt.exit_code.value(), 0);
}