Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement UTxO by Address local state query #341

Merged
merged 35 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
51ec5d1
add stake distribution result primitives
falcucci Nov 22, 2023
8aee567
remove usuless display import
falcucci Nov 22, 2023
2b74a11
remove useless extra dep
falcucci Nov 23, 2023
9393ef8
rmeove useless import
falcucci Nov 23, 2023
22a3430
debugging
falcucci Nov 23, 2023
a55dfa8
add test case for the local state query protocol
falcucci Nov 23, 2023
e20e721
Merge branch 'feat/stake-distribution' into feat/utxo-by-address
falcucci Nov 23, 2023
9ddfb28
debugging
falcucci Nov 24, 2023
36c7f59
find out the out date CDDL tag notation and update it
falcucci Nov 25, 2023
abe5e30
add UTxO response primitives
falcucci Nov 26, 2023
d42e533
clean up some noise
falcucci Nov 26, 2023
15ed4bd
merge 'main'
falcucci Nov 26, 2023
8364cee
add UTxO by Address tests
falcucci Nov 26, 2023
1004e07
uncomment main.rs code
falcucci Nov 26, 2023
0496f35
rollback main.rs
falcucci Nov 26, 2023
aa4fca2
rollback debugging code
falcucci Nov 26, 2023
5a49b3a
remove debugging code.
falcucci Nov 26, 2023
d561289
rename var to address
falcucci Nov 26, 2023
240d0cf
remove debugging code
falcucci Nov 26, 2023
76cbc99
passing decode value directly
falcucci Nov 27, 2023
9285c40
minor changes
falcucci Nov 27, 2023
10ffd42
minor changes
falcucci Nov 27, 2023
862e3f7
update n2c example
falcucci Nov 27, 2023
c2600f7
refactor to get UTxO by addresses example and support Cbor as tags for
falcucci Nov 29, 2023
5258351
create local state query metadatum and remove dependencies
falcucci Nov 29, 2023
8498e5f
replace `txid` with `hash` in `utxo`
falcucci Dec 1, 2023
0b0e6fb
removing dependency on pallas-addresses
falcucci Dec 5, 2023
3236c3a
update `metadatum` to `value` in `codec.rs`
falcucci Dec 5, 2023
6d6a4e2
refactor applying misinterpretation of the response protocol
falcucci Dec 5, 2023
ec097b8
update codec and mod modules
falcucci Dec 5, 2023
b21b1cd
minor change
falcucci Dec 6, 2023
d3dbac1
add support for inline datum in utxobyaddress
falcucci Dec 11, 2023
7a81182
remove inlinedatum struct
falcucci Dec 11, 2023
789e7df
add inline datum to localstate tests
falcucci Dec 11, 2023
930140b
minor changes
falcucci Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions examples/n2c-miniprotocols/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use pallas::network::{
facades::NodeClient,
miniprotocols::{chainsync, localstate::queries_v16, Point, PRE_PRODUCTION_MAGIC},
use pallas::{
ledger::addresses::Address,
network::{
facades::NodeClient,
miniprotocols::{
chainsync,
localstate::queries_v16::{self, Addr, Addrs},
Point, PRE_PRODUCTION_MAGIC,
},
},
};
use tracing::info;

Expand Down Expand Up @@ -28,6 +35,20 @@ async fn do_localstate_query(client: &mut NodeClient) {
.unwrap();
info!("result: {:?}", result);

let addrx = "addr_test1vr80076l3x5uw6n94nwhgmv7ssgy6muzf47ugn6z0l92rhg2mgtu0".to_string();
let addrx: Address = Address::from_bech32(&addrx).unwrap();
let addrx: Addr = addrx.to_vec().into();

let addry = "008c5bf0f2af6f1ef08bb3f6ec702dd16e1c514b7e1d12f7549b47db9f4d943c7af0aaec774757d4745d1a2c8dd3220e6ec2c9df23f757a2f8".to_string();
let addry: Address = Address::from_hex(&addry).unwrap();
let addry: Addr = addry.to_vec().into();

let addrs: Addrs = vec![addrx, addry];
let result = queries_v16::get_utxo_by_address(client, era, addrs)
.await
.unwrap();
info!("result: {:?}", result);

client.send_release().await.unwrap();
}

Expand Down
6 changes: 3 additions & 3 deletions pallas-codec/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl<C, const N: usize> minicbor::Encode<C> for SkipCbor<N> {
/// canonicalization for isomorphic decoding / encoding operators, we use a Vec
/// as the underlaying struct for storage of the items (as opposed to a BTreeMap
/// or HashMap).
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(from = "Vec::<(K, V)>", into = "Vec::<(K, V)>")]
pub enum KeyValuePairs<K, V>
where
Expand Down Expand Up @@ -475,7 +475,7 @@ where
}

/// A uint structure that preserves original int length
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Eq, Ord)]
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Eq, Ord, Hash)]
pub enum AnyUInt {
MajorByte(u8),
U8(u8),
Expand Down Expand Up @@ -881,7 +881,7 @@ impl fmt::Display for Bytes {
}

#[derive(
Serialize, Deserialize, Clone, Copy, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord,
Serialize, Deserialize, Clone, Copy, Encode, Decode, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[cbor(transparent)]
#[serde(into = "i128")]
Expand Down
47 changes: 44 additions & 3 deletions pallas-network/src/miniprotocols/localstate/queries_v16/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ impl Encode<()> for BlockQuery {
e.array(1)?;
e.u16(5)?;
}
BlockQuery::GetUTxOByAddress(x) => {
BlockQuery::GetUTxOByAddress(addrs) => {
e.array(2)?;
e.u16(6)?;
e.encode(x)?;
e.encode(addrs)?;
}
BlockQuery::GetUTxOWhole => {
e.encode((7,))?;
Expand Down Expand Up @@ -129,7 +129,7 @@ impl<'b> Decode<'b, ()> for BlockQuery {
3 => Ok(Self::GetCurrentPParams),
4 => Ok(Self::GetProposedPParamsUpdates),
5 => Ok(Self::GetStakeDistribution),
// 6 => Ok(Self::GetUTxOByAddress(())),
6 => Ok(Self::GetUTxOByAddress(d.decode()?)),
// 7 => Ok(Self::GetUTxOWhole),
// 8 => Ok(Self::DebugEpochState),
// 9 => Ok(Self::GetCBOR(())),
Expand Down Expand Up @@ -264,3 +264,44 @@ impl<'b> Decode<'b, ()> for Request {
}
}
}

impl<'b, C> minicbor::decode::Decode<'b, C> for Value {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
match d.datatype()? {
minicbor::data::Type::U8 => Ok(Value::Coin(d.decode_with(ctx)?)),
minicbor::data::Type::U16 => Ok(Value::Coin(d.decode_with(ctx)?)),
minicbor::data::Type::U32 => Ok(Value::Coin(d.decode_with(ctx)?)),
minicbor::data::Type::U64 => Ok(Value::Coin(d.decode_with(ctx)?)),
minicbor::data::Type::Array => {
d.array()?;
let coin = d.decode_with(ctx)?;
let multiasset = d.decode_with(ctx)?;
Ok(Value::Multiasset(coin, multiasset))
}
_ => Err(minicbor::decode::Error::message(
"unknown cbor data type for Value enum",
)),
}
}
}

impl<C> minicbor::encode::Encode<C> for Value {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
Value::Coin(coin) => {
e.encode_with(coin, ctx)?;
}
Value::Multiasset(coin, other) => {
e.array(2)?;
e.encode_with(coin, ctx)?;
e.encode_with(other, ctx)?;
}
};

Ok(())
}
}
68 changes: 66 additions & 2 deletions pallas-network/src/miniprotocols/localstate/queries_v16/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// TODO: this should move to pallas::ledger crate at some point

use pallas_crypto::hash::Hash;
use std::hash::Hash as StdHash;
// required for derive attrs to work
use pallas_codec::minicbor::{self};

use pallas_codec::utils::{Bytes, KeyValuePairs};
use pallas_codec::utils::{AnyUInt, Bytes, KeyValuePairs, TagWrap};
use pallas_codec::{
minicbor::{Decode, Encode},
utils::AnyCbor,
Expand All @@ -25,7 +27,7 @@ pub enum BlockQuery {
GetCurrentPParams,
GetProposedPParamsUpdates,
GetStakeDistribution,
GetUTxOByAddress(AnyCbor),
GetUTxOByAddress(Addrs),
GetUTxOWhole,
DebugEpochState,
GetCBOR(AnyCbor),
Expand Down Expand Up @@ -69,6 +71,12 @@ pub enum Request {
GetChainPoint,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Value {
Coin(Coin),
Multiasset(Coin, Multiasset<Coin>),
}

#[derive(Debug, Encode, Decode, PartialEq)]
pub struct SystemStart {
#[n(0)]
Expand Down Expand Up @@ -105,6 +113,49 @@ pub struct Fraction {
pub dem: u64,
}

pub type Addr = Bytes;

pub type Addrs = Vec<Addr>;

pub type Coin = AnyUInt;

pub type PolicyId = Hash<28>;

pub type AssetName = Bytes;

pub type Multiasset<A> = KeyValuePairs<PolicyId, KeyValuePairs<AssetName, A>>;

#[derive(Debug, Encode, Decode, PartialEq, Clone)]
pub struct UTxOByAddress {
#[n(0)]
pub utxo: KeyValuePairs<UTxO, Values>,
}

// Bytes CDDL -> #6.121([ * #6.121([ *datum ]) ])
pub type Datum = (Era, TagWrap<Bytes, 24>);

#[derive(Debug, Encode, Decode, PartialEq, Clone)]
#[cbor(map)]
pub struct Values {
#[n(0)]
pub address: Bytes,

#[n(1)]
pub amount: Value,

#[n(2)]
pub inline_datum: Option<Datum>,
}

#[derive(Debug, Encode, Decode, PartialEq, Clone, StdHash, Eq)]
pub struct UTxO {
#[n(0)]
pub transaction_id: Hash<32>,

#[n(1)]
pub index: AnyUInt,
}

pub async fn get_chain_point(client: &mut Client) -> Result<Point, ClientError> {
let query = Request::GetChainPoint;
let result = client.query(query).await?;
Expand Down Expand Up @@ -148,3 +199,16 @@ pub async fn get_stake_distribution(

Ok(result)
}

pub async fn get_utxo_by_address(
client: &mut Client,
era: u16,
addrs: Addrs,
) -> Result<UTxOByAddress, ClientError> {
let query = BlockQuery::GetUTxOByAddress(addrs);
let query = LedgerQuery::BlockQuery(era, query);
let query = Request::LedgerQuery(query);
let result = client.query(query).await?;

Ok(result)
}
106 changes: 104 additions & 2 deletions pallas-network/tests/protocols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use std::fs;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::time::Duration;

use pallas_codec::utils::{AnyCbor, KeyValuePairs};
use pallas_codec::utils::{AnyCbor, AnyUInt, KeyValuePairs, TagWrap};
use pallas_crypto::hash::Hash;
use pallas_network::facades::{NodeClient, PeerClient, PeerServer};
use pallas_network::miniprotocols::blockfetch::BlockRequest;
use pallas_network::miniprotocols::chainsync::{ClientRequest, HeaderContent, Tip};
use pallas_network::miniprotocols::handshake::n2n::VersionData;
use pallas_network::miniprotocols::localstate::queries_v16::{Addr, Addrs, Value};
use pallas_network::miniprotocols::localstate::ClientQueryRequest;
use pallas_network::miniprotocols::{
blockfetch,
Expand Down Expand Up @@ -539,7 +541,59 @@ pub async fn local_state_query_server_and_client_happy_path() {
let pools = KeyValuePairs::from(pools);

let result = AnyCbor::from_encode(localstate::queries_v16::StakeDistribution { pools });
server.statequery().send_result(result).await.unwrap();

// server receives query from client

let query: localstate::queries_v16::Request =
match server.statequery().recv_while_acquired().await.unwrap() {
ClientQueryRequest::Query(q) => q.into_decode().unwrap(),
x => panic!("unexpected message from client: {x:?}"),
};

let addr_hex = "981D186018CE18F718FB185F188918A918C7186A186518AC18DD1874186D189E188410184D186F1882184D187D18C4184F1842187F18CA18A118DD";
let addr = hex::decode(addr_hex).unwrap();
let addr: Addr = addr.to_vec().into();
let addrs: Addrs = Vec::from([addr]);

assert_eq!(
query,
localstate::queries_v16::Request::LedgerQuery(
localstate::queries_v16::LedgerQuery::BlockQuery(
5,
localstate::queries_v16::BlockQuery::GetUTxOByAddress(addrs),
),
)
);

assert_eq!(*server.statequery().state(), localstate::State::Querying);

let tx_hex = "1e4e5cf2889d52f1745b941090f04a65dea6ce56c5e5e66e69f65c8e36347c17";
let txbytes: [u8; 32] = hex::decode(tx_hex).unwrap().try_into().unwrap();
let transaction_id = Hash::from(txbytes);
let index = AnyUInt::MajorByte(2);
let lovelace = AnyUInt::MajorByte(2);
let hex_datum = "9118D81879189F18D81879189F1858181C18C918CF18711866181E185316189118BA";
let datum = hex::decode(hex_datum).unwrap().into();
let tag = TagWrap::<_, 24>::new(datum);
let inline_datum = Some((1_u16, tag));
let values = localstate::queries_v16::Values {
address: b"addr_test1vr80076l3x5uw6n94nwhgmv7ssgy6muzf47ugn6z0l92rhg2mgtu0"
.to_vec()
.into(),
amount: Value::Coin(lovelace),
inline_datum,
};

let utxo = KeyValuePairs::from(vec![(
localstate::queries_v16::UTxO {
transaction_id,
index,
},
values,
)]);

let result = AnyCbor::from_encode(localstate::queries_v16::UTxOByAddress { utxo });
server.statequery().send_result(result).await.unwrap();

assert_eq!(*server.statequery().state(), localstate::State::Acquired);
Expand Down Expand Up @@ -650,8 +704,56 @@ pub async fn local_state_query_server_and_client_happy_path() {

assert_eq!(result, localstate::queries_v16::StakeDistribution { pools });

// client sends a ReAquire
let addr_hex = "981D186018CE18F718FB185F188918A918C7186A186518AC18DD1874186D189E188410184D186F1882184D187D18C4184F1842187F18CA18A118DD";
let addr = hex::decode(addr_hex).unwrap();
let addr: Addr = addr.to_vec().into();
let addrs: Addrs = Vec::from([addr]);

let request = AnyCbor::from_encode(localstate::queries_v16::Request::LedgerQuery(
localstate::queries_v16::LedgerQuery::BlockQuery(
5,
localstate::queries_v16::BlockQuery::GetUTxOByAddress(addrs),
),
));

client.statequery().send_query(request).await.unwrap();

let result: localstate::queries_v16::UTxOByAddress = client
.statequery()
.recv_while_querying()
.await
.unwrap()
.into_decode()
.unwrap();

let tx_hex = "1e4e5cf2889d52f1745b941090f04a65dea6ce56c5e5e66e69f65c8e36347c17";
let txbytes: [u8; 32] = hex::decode(tx_hex).unwrap().try_into().unwrap();
let transaction_id = Hash::from(txbytes);
let index = AnyUInt::MajorByte(2);
let lovelace = AnyUInt::MajorByte(2);
let hex_datum = "9118D81879189F18D81879189F1858181C18C918CF18711866181E185316189118BA";
let datum = hex::decode(hex_datum).unwrap().into();
let tag = TagWrap::<_, 24>::new(datum);
let inline_datum = Some((1_u16, tag));
let values = localstate::queries_v16::Values {
address: b"addr_test1vr80076l3x5uw6n94nwhgmv7ssgy6muzf47ugn6z0l92rhg2mgtu0"
.to_vec()
.into(),
amount: Value::Coin(lovelace),
inline_datum,
};

let utxo = KeyValuePairs::from(vec![(
localstate::queries_v16::UTxO {
transaction_id,
index,
},
values,
)]);

assert_eq!(result, localstate::queries_v16::UTxOByAddress { utxo });

// client sends a ReAquire
client
.statequery()
.send_reacquire(Some(Point::Specific(1337, vec![1, 2, 3])))
Expand Down