Skip to content

Commit 29fce72

Browse files
arajasekZenGround0
andauthored
FIP-0027: Implement a Union type for deal labels (filecoin-project#188)
* FIP-0027: Implement a Union type for deal labels * CBOR -> Label tests * more Label -> CBOR tests * Fix unused test vars Co-authored-by: zenground0 <[email protected]>
1 parent 0976d97 commit 29fce72

File tree

4 files changed

+125
-3
lines changed

4 files changed

+125
-3
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actors/market/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ log = "0.4.14"
2727
anyhow = "1.0.56"
2828
fvm_ipld_blockstore = { version = "0.1" }
2929
fvm_ipld_encoding = "0.1.0"
30+
libipld-core = { version = "0.13.1", features = ["serde-codec"] }
3031

3132
[dev-dependencies]
3233
fil_actors_runtime = { version = "8.0.0-alpha.1", path = "../runtime", features = ["test_utils", "sector-default"] }

actors/market/src/deal.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
use cid::{Cid, Version};
55
use fil_actors_runtime::DealWeight;
66
use fvm_ipld_encoding::tuple::*;
7-
use fvm_ipld_encoding::Cbor;
7+
use fvm_ipld_encoding::{BytesSer, Cbor};
88
use fvm_shared::address::Address;
99
use fvm_shared::bigint::bigint_ser;
1010
use fvm_shared::clock::ChainEpoch;
1111
use fvm_shared::commcid::{FIL_COMMITMENT_UNSEALED, SHA2_256_TRUNC254_PADDED};
1212
use fvm_shared::crypto::signature::Signature;
1313
use fvm_shared::econ::TokenAmount;
1414
use fvm_shared::piece::PaddedPieceSize;
15+
use libipld_core::ipld::Ipld;
16+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
17+
use std::convert::{TryFrom, TryInto};
1518

1619
/// Cid prefix for piece Cids
1720
pub fn is_piece_cid(c: &Cid) -> bool {
@@ -22,6 +25,47 @@ pub fn is_piece_cid(c: &Cid) -> bool {
2225
&& c.hash().size() == 32
2326
}
2427

28+
#[derive(Debug)]
29+
pub enum Label {
30+
String(String),
31+
Bytes(Vec<u8>),
32+
}
33+
34+
/// Serialize the Label like an untagged enum.
35+
impl Serialize for Label {
36+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37+
where
38+
S: Serializer,
39+
{
40+
match self {
41+
Label::String(v) => v.serialize(serializer),
42+
Label::Bytes(v) => BytesSer(v).serialize(serializer),
43+
}
44+
}
45+
}
46+
47+
impl TryFrom<Ipld> for Label {
48+
type Error = String;
49+
50+
fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
51+
match ipld {
52+
Ipld::String(s) => Ok(Label::String(s)),
53+
Ipld::Bytes(b) => Ok(Label::Bytes(b)),
54+
other => Err(format!("Expected `Ipld::String` or `Ipld::Bytes`, got {:#?}", other)),
55+
}
56+
}
57+
}
58+
59+
/// Deserialize the Label like an untagged enum.
60+
impl<'de> Deserialize<'de> for Label {
61+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62+
where
63+
D: Deserializer<'de>,
64+
{
65+
Ipld::deserialize(deserializer).and_then(|ipld| ipld.try_into().map_err(de::Error::custom))
66+
}
67+
}
68+
2569
/// Note: Deal Collateral is only released and returned to clients and miners
2670
/// when the storage deal stops counting towards power. In the current iteration,
2771
/// it will be released when the sector containing the storage deals expires,

actors/market/tests/market_actor_test.rs

+78-2
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ use std::collections::HashMap;
55

66
use fil_actor_market::balance_table::{BalanceTable, BALANCE_TABLE_BITWIDTH};
77
use fil_actor_market::{
8-
ext, Actor as MarketActor, Method, State, WithdrawBalanceParams, PROPOSALS_AMT_BITWIDTH,
8+
ext, Actor as MarketActor, Label, Method, State, WithdrawBalanceParams, PROPOSALS_AMT_BITWIDTH,
99
STATES_AMT_BITWIDTH,
1010
};
11+
use fil_actors_runtime::cbor::deserialize;
1112
use fil_actors_runtime::runtime::Runtime;
1213
use fil_actors_runtime::test_utils::*;
1314
use fil_actors_runtime::{
1415
make_empty_map, ActorError, SetMultimap, STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
1516
};
1617
use fvm_ipld_amt::Amt;
17-
use fvm_ipld_encoding::RawBytes;
18+
use fvm_ipld_encoding::{to_vec, RawBytes};
1819
use fvm_shared::address::Address;
1920
use fvm_shared::bigint::bigint_ser::BigIntDe;
2021
use fvm_shared::clock::EPOCH_UNDEFINED;
@@ -96,6 +97,81 @@ fn simple_construction() {
9697
assert_eq!(state_data.last_cron, EPOCH_UNDEFINED);
9798
}
9899

100+
#[test]
101+
fn label_cbor() {
102+
let label = Label::String("i_am_random_string____i_am_random_string____".parse().unwrap());
103+
let _ = to_vec(&label)
104+
.map_err(|e| ActorError::from(e).wrap("failed to serialize DealProposal"))
105+
.unwrap();
106+
107+
let label2 = Label::Bytes(b"i_am_random_____i_am_random_____".to_vec());
108+
println!("{:?}", (b"i_am_random_____i_am_random_____".to_vec()));
109+
let _ = to_vec(&label2)
110+
.map_err(|e| ActorError::from(e).wrap("failed to serialize DealProposal"))
111+
.unwrap();
112+
113+
let empty_string_label = Label::String("".parse().unwrap());
114+
let sv_bz = to_vec(&empty_string_label).unwrap();
115+
assert_eq!(vec![0x60], sv_bz);
116+
117+
let empty_bytes_label = Label::Bytes(b"".to_vec());
118+
let sv_bz = to_vec(&empty_bytes_label).unwrap();
119+
assert_eq!(vec![0x40], sv_bz);
120+
}
121+
122+
#[test]
123+
fn label_from_cbor() {
124+
// empty string, b001_00000
125+
let empty_cbor_text = vec![0x60];
126+
let label1: Label = deserialize(&RawBytes::from(empty_cbor_text), "empty cbor string").unwrap();
127+
if let Label::String(s) = label1 {
128+
assert_eq!("", s)
129+
} else {
130+
panic!("expected string label not bytes")
131+
}
132+
133+
// valid utf8 string b011_01000 "deadbeef"
134+
let end_valid_cbor_text = b"deadbeef".to_vec();
135+
let mut valid_cbor_text = vec![0x68];
136+
for i in end_valid_cbor_text {
137+
valid_cbor_text.push(i);
138+
}
139+
let label2: Label = deserialize(&RawBytes::from(valid_cbor_text), "valid cbor string").unwrap();
140+
if let Label::String(s) = label2 {
141+
assert_eq!("deadbeef", s)
142+
} else {
143+
panic!("expected string label not bytes")
144+
}
145+
146+
// invalid utf8 string 0b011_00100 0xde 0xad 0xbe 0xeef
147+
let invalid_cbor_text = vec![0x64, 0xde, 0xad, 0xbe, 0xef];
148+
let out = deserialize::<Label>(&RawBytes::from(invalid_cbor_text), "invalid cbor string");
149+
out.expect_err("invalid utf8 string in maj typ 3 should fail deser");
150+
151+
// empty bytes, b010_00000
152+
let empty_cbor_bytes = vec![0x40];
153+
let label3: Label = deserialize(&RawBytes::from(empty_cbor_bytes), "empty cbor bytes").unwrap();
154+
if let Label::Bytes(b) = label3 {
155+
assert_eq!(Vec::<u8>::new(), b)
156+
} else {
157+
panic!("expected bytes label not string")
158+
}
159+
160+
// bytes b010_00100 0xde 0xad 0xbe 0xef
161+
let cbor_bytes = vec![0x44, 0xde, 0xad, 0xbe, 0xef];
162+
let label4: Label = deserialize(&RawBytes::from(cbor_bytes), "cbor bytes").unwrap();
163+
if let Label::Bytes(b) = label4 {
164+
assert_eq!(vec![0xde, 0xad, 0xbe, 0xef], b)
165+
} else {
166+
panic!("expected bytes label not string")
167+
}
168+
169+
// bad major type, array of empty array b100_00001 b100_00000
170+
let bad_bytes = vec![0x81, 0x80];
171+
let out = deserialize::<Label>(&RawBytes::from(bad_bytes), "cbor array, unexpected major type");
172+
out.expect_err("major type 4 should not be recognized by union type and deser should fail");
173+
}
174+
99175
#[ignore]
100176
#[test]
101177
fn add_provider_escrow_funds() {

0 commit comments

Comments
 (0)